From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/extension/.cvsignore | 5 + src/extension/Makefile_insert | 37 + src/extension/api.cpp | 92 + src/extension/db.cpp | 239 ++ src/extension/db.h | 81 + src/extension/dependency.cpp | 261 ++ src/extension/dependency.h | 83 + src/extension/dxf2svg/GPL.txt | 340 ++ src/extension/dxf2svg/LGPL.txt | 504 +++ src/extension/dxf2svg/Makefile | 21 + src/extension/dxf2svg/README | 41 + src/extension/dxf2svg/aci2rgb.cpp | 114 + src/extension/dxf2svg/blocks.cpp | 88 + src/extension/dxf2svg/blocks.h | 38 + src/extension/dxf2svg/dxf2svg.cpp | 106 + src/extension/dxf2svg/dxf_input.inx | 16 + src/extension/dxf2svg/dxf_input_windows.inx | 17 + src/extension/dxf2svg/entities.cpp | 1031 ++++++ src/extension/dxf2svg/entities.h | 256 ++ src/extension/dxf2svg/entities2elements.cpp | 681 ++++ src/extension/dxf2svg/entities2elements.h | 53 + src/extension/dxf2svg/read_dxf.cpp | 273 ++ src/extension/dxf2svg/read_dxf.h | 31 + src/extension/dxf2svg/tables.cpp | 211 ++ src/extension/dxf2svg/tables.h | 71 + src/extension/dxf2svg/tables2svg_info.cpp | 35 + src/extension/dxf2svg/tables2svg_info.h | 9 + src/extension/dxf2svg/test_dxf.cpp | 99 + src/extension/effect.cpp | 200 + src/extension/effect.h | 91 + src/extension/error-file.cpp | 109 + src/extension/error-file.h | 45 + src/extension/extension-forward.h | 34 + src/extension/extension.cpp | 637 ++++ src/extension/extension.h | 207 ++ src/extension/implementation/.cvsignore | 5 + src/extension/implementation/Makefile_insert | 13 + src/extension/implementation/implementation.cpp | 181 + src/extension/implementation/implementation.h | 127 + src/extension/implementation/makefile.in | 17 + src/extension/implementation/plugin-link.h | 68 + src/extension/implementation/plugin.cpp | 328 ++ src/extension/implementation/plugin.h | 121 + src/extension/implementation/script.cpp | 1053 ++++++ src/extension/implementation/script.h | 86 + src/extension/init.cpp | 243 ++ src/extension/init.h | 36 + src/extension/input.cpp | 261 ++ src/extension/input.h | 60 + src/extension/internal/.cvsignore | 5 + src/extension/internal/Makefile_insert | 42 + src/extension/internal/bluredge.cpp | 157 + src/extension/internal/bluredge.h | 43 + src/extension/internal/eps-out.cpp | 108 + src/extension/internal/eps-out.h | 47 + src/extension/internal/gdkpixbuf-input.cpp | 176 + src/extension/internal/gdkpixbuf-input.h | 31 + src/extension/internal/gimpgrad.cpp | 233 ++ src/extension/internal/gimpgrad.h | 49 + src/extension/internal/gnome.cpp | 443 +++ src/extension/internal/gnome.h | 58 + src/extension/internal/grid.cpp | 272 ++ src/extension/internal/grid.h | 43 + src/extension/internal/latex-pstricks-out.cpp | 128 + src/extension/internal/latex-pstricks-out.h | 49 + src/extension/internal/latex-pstricks.cpp | 362 ++ src/extension/internal/latex-pstricks.h | 70 + src/extension/internal/makefile.in | 17 + src/extension/internal/pov-out.cpp | 482 +++ src/extension/internal/pov-out.h | 52 + src/extension/internal/ps-out.cpp | 94 + src/extension/internal/ps-out.h | 45 + src/extension/internal/ps.cpp | 1264 +++++++ src/extension/internal/ps.h | 108 + src/extension/internal/svg.cpp | 248 ++ src/extension/internal/svg.h | 48 + src/extension/internal/svgz.cpp | 99 + src/extension/internal/svgz.h | 41 + src/extension/internal/win32.cpp | 499 +++ src/extension/internal/win32.h | 91 + src/extension/makefile.in | 17 + src/extension/output.cpp | 262 ++ src/extension/output.h | 58 + src/extension/parameter.cpp | 778 ++++ src/extension/parameter.h | 71 + src/extension/plugin/.cvsignore | 7 + src/extension/plugin/Makefile_insert | 30 + src/extension/plugin/makefile.in | 17 + src/extension/prefdialog.cpp | 48 + src/extension/prefdialog.h | 44 + src/extension/print.cpp | 125 + src/extension/print.h | 84 + src/extension/script/.cvsignore | 3 + src/extension/script/InkscapeBinding.cpp | 199 + src/extension/script/InkscapeBinding.h | 168 + src/extension/script/InkscapeInterpreter.cpp | 93 + src/extension/script/InkscapeInterpreter.h | 68 + src/extension/script/InkscapePerl.cpp | 80 + src/extension/script/InkscapePerl.h | 73 + src/extension/script/InkscapePython.cpp | 120 + src/extension/script/InkscapePython.h | 74 + src/extension/script/InkscapeScript.cpp | 177 + src/extension/script/InkscapeScript.h | 87 + src/extension/script/Makefile.tmp | 89 + src/extension/script/Makefile_insert | 36 + src/extension/script/README.txt | 41 + src/extension/script/bindtest.cpp | 86 + src/extension/script/cpptest.cpp | 22 + src/extension/script/inkscape_perl.i | 77 + src/extension/script/inkscape_perl.pm | 178 + src/extension/script/inkscape_perl.pm.h | 187 + src/extension/script/inkscape_perl_wrap.cpp | 1334 +++++++ src/extension/script/inkscape_py.i | 6 + src/extension/script/inkscape_py.py | 130 + src/extension/script/inkscape_py.py.h | 139 + src/extension/script/inkscape_py_wrap.cpp | 1408 +++++++ src/extension/script/js/Makefile | 1471 ++++++++ src/extension/script/js/README | 20 + src/extension/script/js/fdlibm/e_acos.c | 147 + src/extension/script/js/fdlibm/e_acosh.c | 105 + src/extension/script/js/fdlibm/e_asin.c | 156 + src/extension/script/js/fdlibm/e_atan2.c | 165 + src/extension/script/js/fdlibm/e_atanh.c | 110 + src/extension/script/js/fdlibm/e_cosh.c | 133 + src/extension/script/js/fdlibm/e_exp.c | 202 + src/extension/script/js/fdlibm/e_fmod.c | 184 + src/extension/script/js/fdlibm/e_gamma.c | 71 + src/extension/script/js/fdlibm/e_gamma_r.c | 70 + src/extension/script/js/fdlibm/e_hypot.c | 173 + src/extension/script/js/fdlibm/e_j0.c | 524 +++ src/extension/script/js/fdlibm/e_j1.c | 523 +++ src/extension/script/js/fdlibm/e_jn.c | 315 ++ src/extension/script/js/fdlibm/e_lgamma.c | 71 + src/extension/script/js/fdlibm/e_lgamma_r.c | 347 ++ src/extension/script/js/fdlibm/e_log.c | 184 + src/extension/script/js/fdlibm/e_log10.c | 134 + src/extension/script/js/fdlibm/e_pow.c | 386 ++ src/extension/script/js/fdlibm/e_rem_pio2.c | 221 ++ src/extension/script/js/fdlibm/e_remainder.c | 120 + src/extension/script/js/fdlibm/e_scalb.c | 89 + src/extension/script/js/fdlibm/e_sinh.c | 122 + src/extension/script/js/fdlibm/e_sqrt.c | 497 +++ src/extension/script/js/fdlibm/fdlibm.h | 273 ++ src/extension/script/js/fdlibm/k_cos.c | 134 + src/extension/script/js/fdlibm/k_rem_pio2.c | 354 ++ src/extension/script/js/fdlibm/k_sin.c | 114 + src/extension/script/js/fdlibm/k_standard.c | 785 ++++ src/extension/script/js/fdlibm/k_tan.c | 170 + src/extension/script/js/fdlibm/s_asinh.c | 101 + src/extension/script/js/fdlibm/s_atan.c | 175 + src/extension/script/js/fdlibm/s_cbrt.c | 133 + src/extension/script/js/fdlibm/s_ceil.c | 120 + src/extension/script/js/fdlibm/s_copysign.c | 72 + src/extension/script/js/fdlibm/s_cos.c | 118 + src/extension/script/js/fdlibm/s_erf.c | 356 ++ src/extension/script/js/fdlibm/s_expm1.c | 267 ++ src/extension/script/js/fdlibm/s_fabs.c | 70 + src/extension/script/js/fdlibm/s_finite.c | 71 + src/extension/script/js/fdlibm/s_floor.c | 121 + src/extension/script/js/fdlibm/s_frexp.c | 99 + src/extension/script/js/fdlibm/s_ilogb.c | 85 + src/extension/script/js/fdlibm/s_isnan.c | 74 + src/extension/script/js/fdlibm/s_ldexp.c | 66 + src/extension/script/js/fdlibm/s_lib_version.c | 73 + src/extension/script/js/fdlibm/s_log1p.c | 211 ++ src/extension/script/js/fdlibm/s_logb.c | 79 + src/extension/script/js/fdlibm/s_matherr.c | 64 + src/extension/script/js/fdlibm/s_modf.c | 132 + src/extension/script/js/fdlibm/s_nextafter.c | 124 + src/extension/script/js/fdlibm/s_rint.c | 131 + src/extension/script/js/fdlibm/s_scalbn.c | 107 + src/extension/script/js/fdlibm/s_signgam.c | 40 + src/extension/script/js/fdlibm/s_significand.c | 68 + src/extension/script/js/fdlibm/s_sin.c | 118 + src/extension/script/js/fdlibm/s_tan.c | 112 + src/extension/script/js/fdlibm/s_tanh.c | 122 + src/extension/script/js/fdlibm/w_acos.c | 78 + src/extension/script/js/fdlibm/w_acosh.c | 78 + src/extension/script/js/fdlibm/w_asin.c | 80 + src/extension/script/js/fdlibm/w_atan2.c | 79 + src/extension/script/js/fdlibm/w_atanh.c | 81 + src/extension/script/js/fdlibm/w_cosh.c | 77 + src/extension/script/js/fdlibm/w_exp.c | 88 + src/extension/script/js/fdlibm/w_fmod.c | 78 + src/extension/script/js/fdlibm/w_gamma.c | 85 + src/extension/script/js/fdlibm/w_gamma_r.c | 81 + src/extension/script/js/fdlibm/w_hypot.c | 78 + src/extension/script/js/fdlibm/w_j0.c | 105 + src/extension/script/js/fdlibm/w_j1.c | 106 + src/extension/script/js/fdlibm/w_jn.c | 128 + src/extension/script/js/fdlibm/w_lgamma.c | 85 + src/extension/script/js/fdlibm/w_lgamma_r.c | 81 + src/extension/script/js/fdlibm/w_log.c | 78 + src/extension/script/js/fdlibm/w_log10.c | 81 + src/extension/script/js/fdlibm/w_pow.c | 99 + src/extension/script/js/fdlibm/w_remainder.c | 77 + src/extension/script/js/fdlibm/w_scalb.c | 95 + src/extension/script/js/fdlibm/w_sinh.c | 77 + src/extension/script/js/fdlibm/w_sqrt.c | 77 + src/extension/script/js/js.c | 2333 ++++++++++++ src/extension/script/js/js.mak | 4025 ++++++++++++++++++++ src/extension/script/js/js.msg | 251 ++ src/extension/script/js/jsapi.c | 4187 +++++++++++++++++++++ src/extension/script/js/jsapi.h | 1752 +++++++++ src/extension/script/js/jsarena.c | 565 +++ src/extension/script/js/jsarena.h | 302 ++ src/extension/script/js/jsarray.c | 1429 +++++++ src/extension/script/js/jsarray.h | 77 + src/extension/script/js/jsatom.c | 911 +++++ src/extension/script/js/jsatom.h | 409 ++ src/extension/script/js/jsautocfg.h | 50 + src/extension/script/js/jsbit.h | 113 + src/extension/script/js/jsbool.c | 244 ++ src/extension/script/js/jsbool.h | 62 + src/extension/script/js/jsclist.h | 139 + src/extension/script/js/jscntxt.c | 702 ++++ src/extension/script/js/jscntxt.h | 496 +++ src/extension/script/js/jscompat.h | 57 + src/extension/script/js/jsconfig.h | 489 +++ src/extension/script/js/jscpucfg.c | 377 ++ src/extension/script/js/jscpucfg.h | 200 + src/extension/script/js/jsdate.c | 2234 +++++++++++ src/extension/script/js/jsdate.h | 118 + src/extension/script/js/jsdbgapi.c | 1240 +++++++ src/extension/script/js/jsdbgapi.h | 345 ++ src/extension/script/js/jsdhash.c | 763 ++++ src/extension/script/js/jsdhash.h | 573 +++ src/extension/script/js/jsdtoa.c | 3155 ++++++++++++++++ src/extension/script/js/jsdtoa.h | 130 + src/extension/script/js/jsemit.c | 4471 ++++++++++++++++++++++ src/extension/script/js/jsemit.h | 547 +++ src/extension/script/js/jsexn.c | 1081 ++++++ src/extension/script/js/jsexn.h | 102 + src/extension/script/js/jsfile.c | 2610 +++++++++++++ src/extension/script/js/jsfile.h | 50 + src/extension/script/js/jsfun.c | 2059 +++++++++++ src/extension/script/js/jsfun.h | 151 + src/extension/script/js/jsgc.c | 1423 +++++++ src/extension/script/js/jsgc.h | 230 ++ src/extension/script/js/jshash.c | 479 +++ src/extension/script/js/jshash.h | 152 + src/extension/script/js/jsinterp.c | 4284 +++++++++++++++++++++ src/extension/script/js/jsinterp.h | 292 ++ src/extension/script/js/jslibmath.h | 290 ++ src/extension/script/js/jslock.c | 1241 +++++++ src/extension/script/js/jslock.h | 289 ++ src/extension/script/js/jslocko.asm | 59 + src/extension/script/js/jslog2.c | 83 + src/extension/script/js/jslong.c | 281 ++ src/extension/script/js/jslong.h | 437 +++ src/extension/script/js/jsmath.c | 477 +++ src/extension/script/js/jsmath.h | 55 + src/extension/script/js/jsnum.c | 1058 ++++++ src/extension/script/js/jsnum.h | 280 ++ src/extension/script/js/jsobj.c | 3900 ++++++++++++++++++++ src/extension/script/js/jsobj.h | 464 +++ src/extension/script/js/jsopcode.c | 2660 +++++++++++++ src/extension/script/js/jsopcode.h | 273 ++ src/extension/script/js/jsopcode.tbl | 333 ++ src/extension/script/js/jsosdep.h | 127 + src/extension/script/js/jsotypes.h | 211 ++ src/extension/script/js/jsparse.c | 3547 ++++++++++++++++++ src/extension/script/js/jsparse.h | 337 ++ src/extension/script/js/jsprf.c | 1212 ++++++ src/extension/script/js/jsprf.h | 148 + src/extension/script/js/jsprvtd.h | 174 + src/extension/script/js/jspubtd.h | 564 +++ src/extension/script/js/jsregexp.c | 3773 +++++++++++++++++++ src/extension/script/js/jsregexp.h | 168 + src/extension/script/js/jsscan.c | 1315 +++++++ src/extension/script/js/jsscan.h | 264 ++ src/extension/script/js/jsscope.c | 1581 ++++++++ src/extension/script/js/jsscope.h | 386 ++ src/extension/script/js/jsscript.c | 1287 +++++++ src/extension/script/js/jsscript.h | 178 + src/extension/script/js/jsshell.msg | 50 + src/extension/script/js/jsstddef.h | 83 + src/extension/script/js/jsstr.c | 4502 +++++++++++++++++++++++ src/extension/script/js/jsstr.h | 439 +++ src/extension/script/js/jstypes.h | 388 ++ src/extension/script/js/jsutil.c | 157 + src/extension/script/js/jsutil.h | 106 + src/extension/script/js/jsxdrapi.c | 690 ++++ src/extension/script/js/jsxdrapi.h | 193 + src/extension/script/js/prmjtime.c | 646 ++++ src/extension/script/js/prmjtime.h | 95 + src/extension/script/js/resource.h | 15 + src/extension/script/makefile.in | 17 + src/extension/script/quotefile.pl | 82 + src/extension/script/runme.py | 8 + src/extension/script/wrap_swig_module.sh | 27 + src/extension/system.cpp | 476 +++ src/extension/system.h | 44 + src/extension/timer.cpp | 214 ++ src/extension/timer.h | 72 + 295 files changed, 115641 insertions(+) create mode 100644 src/extension/.cvsignore create mode 100644 src/extension/Makefile_insert create mode 100644 src/extension/api.cpp create mode 100644 src/extension/db.cpp create mode 100644 src/extension/db.h create mode 100644 src/extension/dependency.cpp create mode 100644 src/extension/dependency.h create mode 100644 src/extension/dxf2svg/GPL.txt create mode 100644 src/extension/dxf2svg/LGPL.txt create mode 100644 src/extension/dxf2svg/Makefile create mode 100644 src/extension/dxf2svg/README create mode 100644 src/extension/dxf2svg/aci2rgb.cpp create mode 100644 src/extension/dxf2svg/blocks.cpp create mode 100644 src/extension/dxf2svg/blocks.h create mode 100644 src/extension/dxf2svg/dxf2svg.cpp create mode 100644 src/extension/dxf2svg/dxf_input.inx create mode 100644 src/extension/dxf2svg/dxf_input_windows.inx create mode 100644 src/extension/dxf2svg/entities.cpp create mode 100644 src/extension/dxf2svg/entities.h create mode 100644 src/extension/dxf2svg/entities2elements.cpp create mode 100644 src/extension/dxf2svg/entities2elements.h create mode 100644 src/extension/dxf2svg/read_dxf.cpp create mode 100644 src/extension/dxf2svg/read_dxf.h create mode 100644 src/extension/dxf2svg/tables.cpp create mode 100644 src/extension/dxf2svg/tables.h create mode 100644 src/extension/dxf2svg/tables2svg_info.cpp create mode 100644 src/extension/dxf2svg/tables2svg_info.h create mode 100644 src/extension/dxf2svg/test_dxf.cpp create mode 100644 src/extension/effect.cpp create mode 100644 src/extension/effect.h create mode 100644 src/extension/error-file.cpp create mode 100644 src/extension/error-file.h create mode 100644 src/extension/extension-forward.h create mode 100644 src/extension/extension.cpp create mode 100644 src/extension/extension.h create mode 100644 src/extension/implementation/.cvsignore create mode 100644 src/extension/implementation/Makefile_insert create mode 100644 src/extension/implementation/implementation.cpp create mode 100644 src/extension/implementation/implementation.h create mode 100644 src/extension/implementation/makefile.in create mode 100644 src/extension/implementation/plugin-link.h create mode 100644 src/extension/implementation/plugin.cpp create mode 100644 src/extension/implementation/plugin.h create mode 100644 src/extension/implementation/script.cpp create mode 100644 src/extension/implementation/script.h create mode 100644 src/extension/init.cpp create mode 100644 src/extension/init.h create mode 100644 src/extension/input.cpp create mode 100644 src/extension/input.h create mode 100644 src/extension/internal/.cvsignore create mode 100644 src/extension/internal/Makefile_insert create mode 100644 src/extension/internal/bluredge.cpp create mode 100644 src/extension/internal/bluredge.h create mode 100644 src/extension/internal/eps-out.cpp create mode 100644 src/extension/internal/eps-out.h create mode 100644 src/extension/internal/gdkpixbuf-input.cpp create mode 100644 src/extension/internal/gdkpixbuf-input.h create mode 100644 src/extension/internal/gimpgrad.cpp create mode 100644 src/extension/internal/gimpgrad.h create mode 100644 src/extension/internal/gnome.cpp create mode 100644 src/extension/internal/gnome.h create mode 100644 src/extension/internal/grid.cpp create mode 100644 src/extension/internal/grid.h create mode 100644 src/extension/internal/latex-pstricks-out.cpp create mode 100644 src/extension/internal/latex-pstricks-out.h create mode 100644 src/extension/internal/latex-pstricks.cpp create mode 100644 src/extension/internal/latex-pstricks.h create mode 100644 src/extension/internal/makefile.in create mode 100644 src/extension/internal/pov-out.cpp create mode 100644 src/extension/internal/pov-out.h create mode 100644 src/extension/internal/ps-out.cpp create mode 100644 src/extension/internal/ps-out.h create mode 100644 src/extension/internal/ps.cpp create mode 100644 src/extension/internal/ps.h create mode 100644 src/extension/internal/svg.cpp create mode 100644 src/extension/internal/svg.h create mode 100644 src/extension/internal/svgz.cpp create mode 100644 src/extension/internal/svgz.h create mode 100644 src/extension/internal/win32.cpp create mode 100644 src/extension/internal/win32.h create mode 100644 src/extension/makefile.in create mode 100644 src/extension/output.cpp create mode 100644 src/extension/output.h create mode 100644 src/extension/parameter.cpp create mode 100644 src/extension/parameter.h create mode 100644 src/extension/plugin/.cvsignore create mode 100644 src/extension/plugin/Makefile_insert create mode 100644 src/extension/plugin/makefile.in create mode 100644 src/extension/prefdialog.cpp create mode 100644 src/extension/prefdialog.h create mode 100644 src/extension/print.cpp create mode 100644 src/extension/print.h create mode 100644 src/extension/script/.cvsignore create mode 100644 src/extension/script/InkscapeBinding.cpp create mode 100644 src/extension/script/InkscapeBinding.h create mode 100644 src/extension/script/InkscapeInterpreter.cpp create mode 100644 src/extension/script/InkscapeInterpreter.h create mode 100644 src/extension/script/InkscapePerl.cpp create mode 100644 src/extension/script/InkscapePerl.h create mode 100644 src/extension/script/InkscapePython.cpp create mode 100644 src/extension/script/InkscapePython.h create mode 100644 src/extension/script/InkscapeScript.cpp create mode 100644 src/extension/script/InkscapeScript.h create mode 100644 src/extension/script/Makefile.tmp create mode 100644 src/extension/script/Makefile_insert create mode 100644 src/extension/script/README.txt create mode 100644 src/extension/script/bindtest.cpp create mode 100644 src/extension/script/cpptest.cpp create mode 100644 src/extension/script/inkscape_perl.i create mode 100644 src/extension/script/inkscape_perl.pm create mode 100644 src/extension/script/inkscape_perl.pm.h create mode 100644 src/extension/script/inkscape_perl_wrap.cpp create mode 100644 src/extension/script/inkscape_py.i create mode 100644 src/extension/script/inkscape_py.py create mode 100644 src/extension/script/inkscape_py.py.h create mode 100644 src/extension/script/inkscape_py_wrap.cpp create mode 100644 src/extension/script/js/Makefile create mode 100644 src/extension/script/js/README create mode 100644 src/extension/script/js/fdlibm/e_acos.c create mode 100644 src/extension/script/js/fdlibm/e_acosh.c create mode 100644 src/extension/script/js/fdlibm/e_asin.c create mode 100644 src/extension/script/js/fdlibm/e_atan2.c create mode 100644 src/extension/script/js/fdlibm/e_atanh.c create mode 100644 src/extension/script/js/fdlibm/e_cosh.c create mode 100644 src/extension/script/js/fdlibm/e_exp.c create mode 100644 src/extension/script/js/fdlibm/e_fmod.c create mode 100644 src/extension/script/js/fdlibm/e_gamma.c create mode 100644 src/extension/script/js/fdlibm/e_gamma_r.c create mode 100644 src/extension/script/js/fdlibm/e_hypot.c create mode 100644 src/extension/script/js/fdlibm/e_j0.c create mode 100644 src/extension/script/js/fdlibm/e_j1.c create mode 100644 src/extension/script/js/fdlibm/e_jn.c create mode 100644 src/extension/script/js/fdlibm/e_lgamma.c create mode 100644 src/extension/script/js/fdlibm/e_lgamma_r.c create mode 100644 src/extension/script/js/fdlibm/e_log.c create mode 100644 src/extension/script/js/fdlibm/e_log10.c create mode 100644 src/extension/script/js/fdlibm/e_pow.c create mode 100644 src/extension/script/js/fdlibm/e_rem_pio2.c create mode 100644 src/extension/script/js/fdlibm/e_remainder.c create mode 100644 src/extension/script/js/fdlibm/e_scalb.c create mode 100644 src/extension/script/js/fdlibm/e_sinh.c create mode 100644 src/extension/script/js/fdlibm/e_sqrt.c create mode 100644 src/extension/script/js/fdlibm/fdlibm.h create mode 100644 src/extension/script/js/fdlibm/k_cos.c create mode 100644 src/extension/script/js/fdlibm/k_rem_pio2.c create mode 100644 src/extension/script/js/fdlibm/k_sin.c create mode 100644 src/extension/script/js/fdlibm/k_standard.c create mode 100644 src/extension/script/js/fdlibm/k_tan.c create mode 100644 src/extension/script/js/fdlibm/s_asinh.c create mode 100644 src/extension/script/js/fdlibm/s_atan.c create mode 100644 src/extension/script/js/fdlibm/s_cbrt.c create mode 100644 src/extension/script/js/fdlibm/s_ceil.c create mode 100644 src/extension/script/js/fdlibm/s_copysign.c create mode 100644 src/extension/script/js/fdlibm/s_cos.c create mode 100644 src/extension/script/js/fdlibm/s_erf.c create mode 100644 src/extension/script/js/fdlibm/s_expm1.c create mode 100644 src/extension/script/js/fdlibm/s_fabs.c create mode 100644 src/extension/script/js/fdlibm/s_finite.c create mode 100644 src/extension/script/js/fdlibm/s_floor.c create mode 100644 src/extension/script/js/fdlibm/s_frexp.c create mode 100644 src/extension/script/js/fdlibm/s_ilogb.c create mode 100644 src/extension/script/js/fdlibm/s_isnan.c create mode 100644 src/extension/script/js/fdlibm/s_ldexp.c create mode 100644 src/extension/script/js/fdlibm/s_lib_version.c create mode 100644 src/extension/script/js/fdlibm/s_log1p.c create mode 100644 src/extension/script/js/fdlibm/s_logb.c create mode 100644 src/extension/script/js/fdlibm/s_matherr.c create mode 100644 src/extension/script/js/fdlibm/s_modf.c create mode 100644 src/extension/script/js/fdlibm/s_nextafter.c create mode 100644 src/extension/script/js/fdlibm/s_rint.c create mode 100644 src/extension/script/js/fdlibm/s_scalbn.c create mode 100644 src/extension/script/js/fdlibm/s_signgam.c create mode 100644 src/extension/script/js/fdlibm/s_significand.c create mode 100644 src/extension/script/js/fdlibm/s_sin.c create mode 100644 src/extension/script/js/fdlibm/s_tan.c create mode 100644 src/extension/script/js/fdlibm/s_tanh.c create mode 100644 src/extension/script/js/fdlibm/w_acos.c create mode 100644 src/extension/script/js/fdlibm/w_acosh.c create mode 100644 src/extension/script/js/fdlibm/w_asin.c create mode 100644 src/extension/script/js/fdlibm/w_atan2.c create mode 100644 src/extension/script/js/fdlibm/w_atanh.c create mode 100644 src/extension/script/js/fdlibm/w_cosh.c create mode 100644 src/extension/script/js/fdlibm/w_exp.c create mode 100644 src/extension/script/js/fdlibm/w_fmod.c create mode 100644 src/extension/script/js/fdlibm/w_gamma.c create mode 100644 src/extension/script/js/fdlibm/w_gamma_r.c create mode 100644 src/extension/script/js/fdlibm/w_hypot.c create mode 100644 src/extension/script/js/fdlibm/w_j0.c create mode 100644 src/extension/script/js/fdlibm/w_j1.c create mode 100644 src/extension/script/js/fdlibm/w_jn.c create mode 100644 src/extension/script/js/fdlibm/w_lgamma.c create mode 100644 src/extension/script/js/fdlibm/w_lgamma_r.c create mode 100644 src/extension/script/js/fdlibm/w_log.c create mode 100644 src/extension/script/js/fdlibm/w_log10.c create mode 100644 src/extension/script/js/fdlibm/w_pow.c create mode 100644 src/extension/script/js/fdlibm/w_remainder.c create mode 100644 src/extension/script/js/fdlibm/w_scalb.c create mode 100644 src/extension/script/js/fdlibm/w_sinh.c create mode 100644 src/extension/script/js/fdlibm/w_sqrt.c create mode 100644 src/extension/script/js/js.c create mode 100644 src/extension/script/js/js.mak create mode 100644 src/extension/script/js/js.msg create mode 100644 src/extension/script/js/jsapi.c create mode 100644 src/extension/script/js/jsapi.h create mode 100644 src/extension/script/js/jsarena.c create mode 100644 src/extension/script/js/jsarena.h create mode 100644 src/extension/script/js/jsarray.c create mode 100644 src/extension/script/js/jsarray.h create mode 100644 src/extension/script/js/jsatom.c create mode 100644 src/extension/script/js/jsatom.h create mode 100644 src/extension/script/js/jsautocfg.h create mode 100644 src/extension/script/js/jsbit.h create mode 100644 src/extension/script/js/jsbool.c create mode 100644 src/extension/script/js/jsbool.h create mode 100644 src/extension/script/js/jsclist.h create mode 100644 src/extension/script/js/jscntxt.c create mode 100644 src/extension/script/js/jscntxt.h create mode 100644 src/extension/script/js/jscompat.h create mode 100644 src/extension/script/js/jsconfig.h create mode 100644 src/extension/script/js/jscpucfg.c create mode 100644 src/extension/script/js/jscpucfg.h create mode 100644 src/extension/script/js/jsdate.c create mode 100644 src/extension/script/js/jsdate.h create mode 100644 src/extension/script/js/jsdbgapi.c create mode 100644 src/extension/script/js/jsdbgapi.h create mode 100644 src/extension/script/js/jsdhash.c create mode 100644 src/extension/script/js/jsdhash.h create mode 100644 src/extension/script/js/jsdtoa.c create mode 100644 src/extension/script/js/jsdtoa.h create mode 100644 src/extension/script/js/jsemit.c create mode 100644 src/extension/script/js/jsemit.h create mode 100644 src/extension/script/js/jsexn.c create mode 100644 src/extension/script/js/jsexn.h create mode 100644 src/extension/script/js/jsfile.c create mode 100644 src/extension/script/js/jsfile.h create mode 100644 src/extension/script/js/jsfun.c create mode 100644 src/extension/script/js/jsfun.h create mode 100644 src/extension/script/js/jsgc.c create mode 100644 src/extension/script/js/jsgc.h create mode 100644 src/extension/script/js/jshash.c create mode 100644 src/extension/script/js/jshash.h create mode 100644 src/extension/script/js/jsinterp.c create mode 100644 src/extension/script/js/jsinterp.h create mode 100644 src/extension/script/js/jslibmath.h create mode 100644 src/extension/script/js/jslock.c create mode 100644 src/extension/script/js/jslock.h create mode 100644 src/extension/script/js/jslocko.asm create mode 100644 src/extension/script/js/jslog2.c create mode 100644 src/extension/script/js/jslong.c create mode 100644 src/extension/script/js/jslong.h create mode 100644 src/extension/script/js/jsmath.c create mode 100644 src/extension/script/js/jsmath.h create mode 100644 src/extension/script/js/jsnum.c create mode 100644 src/extension/script/js/jsnum.h create mode 100644 src/extension/script/js/jsobj.c create mode 100644 src/extension/script/js/jsobj.h create mode 100644 src/extension/script/js/jsopcode.c create mode 100644 src/extension/script/js/jsopcode.h create mode 100644 src/extension/script/js/jsopcode.tbl create mode 100644 src/extension/script/js/jsosdep.h create mode 100644 src/extension/script/js/jsotypes.h create mode 100644 src/extension/script/js/jsparse.c create mode 100644 src/extension/script/js/jsparse.h create mode 100644 src/extension/script/js/jsprf.c create mode 100644 src/extension/script/js/jsprf.h create mode 100644 src/extension/script/js/jsprvtd.h create mode 100644 src/extension/script/js/jspubtd.h create mode 100644 src/extension/script/js/jsregexp.c create mode 100644 src/extension/script/js/jsregexp.h create mode 100644 src/extension/script/js/jsscan.c create mode 100644 src/extension/script/js/jsscan.h create mode 100644 src/extension/script/js/jsscope.c create mode 100644 src/extension/script/js/jsscope.h create mode 100644 src/extension/script/js/jsscript.c create mode 100644 src/extension/script/js/jsscript.h create mode 100644 src/extension/script/js/jsshell.msg create mode 100644 src/extension/script/js/jsstddef.h create mode 100644 src/extension/script/js/jsstr.c create mode 100644 src/extension/script/js/jsstr.h create mode 100644 src/extension/script/js/jstypes.h create mode 100644 src/extension/script/js/jsutil.c create mode 100644 src/extension/script/js/jsutil.h create mode 100644 src/extension/script/js/jsxdrapi.c create mode 100644 src/extension/script/js/jsxdrapi.h create mode 100644 src/extension/script/js/prmjtime.c create mode 100644 src/extension/script/js/prmjtime.h create mode 100644 src/extension/script/js/resource.h create mode 100644 src/extension/script/makefile.in create mode 100644 src/extension/script/quotefile.pl create mode 100644 src/extension/script/runme.py create mode 100644 src/extension/script/wrap_swig_module.sh create mode 100644 src/extension/system.cpp create mode 100644 src/extension/system.h create mode 100644 src/extension/timer.cpp create mode 100644 src/extension/timer.h (limited to 'src/extension') diff --git a/src/extension/.cvsignore b/src/extension/.cvsignore new file mode 100644 index 000000000..e8014d011 --- /dev/null +++ b/src/extension/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +makefile +.dirstamp diff --git a/src/extension/Makefile_insert b/src/extension/Makefile_insert new file mode 100644 index 000000000..d4590cfe6 --- /dev/null +++ b/src/extension/Makefile_insert @@ -0,0 +1,37 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +extension/all: extension/libextension.a extension/implementation/all extension/internal/all + +extension/clean: extension/implementation/clean extension/internal/clean + rm -f extension/libextension.a $(extension_libextension_a_OBJECTS) + +extension_libextension_a_SOURCES = \ + extension/extension.cpp \ + extension/extension.h \ + extension/extension-forward.h \ + extension/db.cpp \ + extension/db.h \ + extension/dependency.cpp \ + extension/dependency.h \ + extension/error-file.cpp \ + extension/error-file.h \ + extension/init.cpp \ + extension/init.h \ + extension/parameter.h \ + extension/parameter.cpp \ + extension/prefdialog.cpp \ + extension/prefdialog.h \ + extension/system.cpp \ + extension/system.h \ + extension/timer.cpp \ + extension/timer.h \ + \ + extension/input.h \ + extension/input.cpp \ + extension/output.h \ + extension/output.cpp \ + extension/effect.h \ + extension/effect.cpp \ + extension/print.h \ + extension/print.cpp + diff --git a/src/extension/api.cpp b/src/extension/api.cpp new file mode 100644 index 000000000..d88541f3f --- /dev/null +++ b/src/extension/api.cpp @@ -0,0 +1,92 @@ +namespace Inkscape { +namespace Extension { +namespace API { + +doc file_open (gchar * uri, Inkscape::Extension::Input * ext) { } +bool file_save (SPDocument * doc, gchar * uri, Inkscape::Extension::Output * ext) { } +bool print_doc (SPDOcument * doc) { } + +array getElements(string element_name) { } +string getElementName() { } +string getElementByID(string id) { } +string getElementID() { } +hash getAttributes() { } +string getAttribute(string attribute) { } +bool SetAttribute(string name, string value) { } +bool SetAttributes(hash) { } +bool hasChildren() { } +array getChildren() { } +obj getFirstChild() { } +obj getLastChild() { } +array getSiblings() { } +obj getNextSibling() { } +obj getPrevSibling() { } +int getChildIndex() { } +obj getChildAtIndex(int index) { } +obj getParent() { } +array getParents() { } +bool isAncestor(obj) { } +bool isDescendant(obj) { } +cdata getCDATA() { } + +/** looks up the numerical ID of the given verb name. (E.g., it could + pull it out of the SPVerbActionDef props[] static object) */ +int verb_find(string verb_name) { } + +/** retrieves the Action object for the verb in the given view */ +action verb_get_action(int verb, int view) { } + +/** retrieves the view, given the action object */ +view action_get_view(int action) { } + +/** invokes the Action for the verb. It validates the parameters and + calls the appropriate listener that will handle the action. */ +void action_perform(int action, int data, int pdata) { } + +void action_set_active(int action, int state, int data) { } + +void action_set_sensitive(int action, int state, int data) { } + +void prefs_set_int_attribute(string path, string attr, int value) { } + +int prefs_get_int_attribute(string path, string attr, gint def) { } + +int prefs_get_int_attribute_limited(string path, string attr, int def, int min, int max) { } + +void prefs_set_double_attribute(string path, string attr, double value) { } + +double prefs_get_double_attribute(string path, string attr, double def) { } + +double prefs_get_double_attribute_limited(string path, string attr, double def, double min, double max) { } + +string prefs_get_string_attribute(string path, string attr) { } + +void prefs_set_string_attribute(string path, string attr, string value) { } + +void prefs_set_recent_file(string uri, string name) { } + +string[] prefs_get_recent_files() { } + +/** returns a pointer to an instance of the desired stock object in + the current doc, importing the object if necessary. */ +obj get_stock_item(string urn) { } + +/** adds a new stock SVG def to the Inkscape Application. All + currently loaded and new documents will have access to the item. */ +void set_stock_item_from_svg(void) { } + +/** This function gets the variable by the name 'name' with the data + in 'data'. It can be a boolean, integer or a string. */ +get_param (gchar * name, bool data) { } +get_param (gchar * name, int data) { } +get_param (gchar * name, gchar * data) { } + +/** This function sets the variable by the name 'name' with the data + in 'data'. It can be a boolean, integer or a string. In the + case of the string it is copied. */ +set_param (gchar * name, bool data) { } +set_param (gchar * name, int data) { } +set_param (gchar * name, gchar * data) { } + + +} } } /* namespaces Inkscape::Extension::API */ diff --git a/src/extension/db.cpp b/src/extension/db.cpp new file mode 100644 index 000000000..342a18b84 --- /dev/null +++ b/src/extension/db.cpp @@ -0,0 +1,239 @@ +/* + * Functions to keep a listing of all modules in the system. Has its + * own file mostly for abstraction reasons, but is pretty simple + * otherwise. + * + * Authors: + * Ted Gould + * Lauris Kaplinski + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "db.h" +#include "input.h" +#include "output.h" +#include "effect.h" + +/* Globals */ + +/* Namespaces */ + +namespace Inkscape { +namespace Extension { + +/** This is the actual database object. There is only one of these */ +DB db; + +/* Types */ + +DB::DB (void) { +} + +/** + \brief Add a module to the module database + \param module The module to be registered. +*/ +void +DB::register_ext (Extension *module) +{ + g_return_if_fail(module != NULL); + g_return_if_fail(module->get_id() != NULL); + + // only add to list if it's a never-before-seen module + bool add_to_list = + ( moduledict.find(module->get_id()) == moduledict.end()); + + //printf("Registering: '%s' add:%d\n", module->get_id(), add_to_list); + moduledict[module->get_id()] = module; + + if (add_to_list) modulelist.push_back(module); +} + +/** + \brief This function removes a module from the database + \param module The module to be removed. +*/ +void +DB::unregister_ext (Extension * module) +{ + g_return_if_fail(module != NULL); + g_return_if_fail(module->get_id() != NULL); + + // printf("Extension DB: removing %s\n", module->get_id()); + moduledict.erase(moduledict.find(module->get_id())); + // only remove if it's not there any more + if ( moduledict.find(module->get_id()) != moduledict.end()) + modulelist.remove(module); +} + +/** + \return A reference to the Inkscape::Extension::Extension specified by the input key. + \brief This function looks up a Inkscape::Extension::Extension by using its unique + id. It then returns a reference to that module. + \param key The unique ID of the module + + Retrieves a module by name; if non-NULL, it refs the returned + module; the caller is responsible for releasing that reference + when it is no longer needed. +*/ +Extension * +DB::get (const gchar *key) +{ + if (key == NULL) return NULL; + + Extension *mod = moduledict[key]; + if ( !mod || mod->deactivated() ) + return NULL; + + return mod; +} + +/** + \return none + \brief A function to execute another function with every entry + in the database as a parameter. + \param in_func The function to execute for every module + \param in_data A data pointer that is also passed to in_func + + Enumerates the modules currently in the database, calling a given + callback for each one. +*/ +void +DB::foreach (void (*in_func)(Extension * in_plug, gpointer in_data), gpointer in_data) +{ + std::list ::iterator cur; + + for (cur = modulelist.begin(); cur != modulelist.end(); cur++) { + // printf("foreach: %s\n", (*cur)->get_id()); + in_func((*cur), in_data); + } +} + +/** + \return none + \brief The function to look at each module and see if it is + an input module, then add it to the list. + \param in_plug Module to be examined + \param data The list to be attached to + + The first thing that is checked is if this module is an input + module. If it is, then it is added to the list which is passed + in through \c data. +*/ +void +DB::input_internal (Extension * in_plug, gpointer data) +{ + if (dynamic_cast(in_plug)) { + InputList * ilist; + Input * imod; + + imod = dynamic_cast(in_plug); + ilist = reinterpret_cast(data); + + ilist->push_back(imod); + // printf("Added to input list: %s\n", imod->get_id()); + } +} + +/** + \return none + \brief The function to look at each module and see if it is + an output module, then add it to the list. + \param in_plug Module to be examined + \param data The list to be attached to + + The first thing that is checked is if this module is an output + module. If it is, then it is added to the list which is passed + in through \c data. +*/ +void +DB::output_internal (Extension * in_plug, gpointer data) +{ + if (dynamic_cast(in_plug)) { + OutputList * olist; + Output * omod; + + omod = dynamic_cast(in_plug); + olist = reinterpret_cast(data); + + olist->push_back(omod); + // printf("Added to output list: %s\n", omod->get_id()); + } + + return; +} + +/** + \return none + \brief The function to look at each module and see if it is + an effect module, then add it to the list. + \param in_plug Module to be examined + \param data The list to be attached to + + The first thing that is checked is if this module is an effect + module. If it is, then it is added to the list which is passed + in through \c data. +*/ +void +DB::effect_internal (Extension * in_plug, gpointer data) +{ + if (dynamic_cast(in_plug)) { + EffectList * elist; + Effect * emod; + + emod = dynamic_cast(in_plug); + elist = reinterpret_cast(data); + + elist->push_back(emod); + // printf("Added to effect list: %s\n", emod->get_id()); + } + + return; +} + +/** + \brief Creates a list of all the Input extensions + \param ou_list The list that is used to put all the extensions in + + Calls the database \c foreach function with \c input_internal. +*/ +DB::InputList & +DB::get_input_list (DB::InputList &ou_list) +{ + foreach(input_internal, (gpointer)&ou_list); + return ou_list; +} + +/** + \brief Creates a list of all the Output extensions + \param ou_list The list that is used to put all the extensions in + + Calls the database \c foreach function with \c output_internal. +*/ +DB::OutputList & +DB::get_output_list (DB::OutputList &ou_list) +{ + foreach(output_internal, (gpointer)&ou_list); + return ou_list; +} + +/** + \brief Creates a list of all the Effect extensions + \param ou_list The list that is used to put all the extensions in + + Calls the database \c foreach function with \c effect_internal. +*/ +DB::EffectList & +DB::get_effect_list (DB::EffectList &ou_list) +{ + foreach(effect_internal, (gpointer)&ou_list); + return ou_list; +} + +} } /* namespace Extension, Inkscape */ diff --git a/src/extension/db.h b/src/extension/db.h new file mode 100644 index 000000000..264e76539 --- /dev/null +++ b/src/extension/db.h @@ -0,0 +1,81 @@ +/* + * Functions to keep a listing of all modules in the system. Has its + * own file mostly for abstraction reasons, but is pretty simple + * otherwise. + * + * Authors: + * Ted Gould + * Lauris Kaplinski + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef __MODULES_DB_H__ +#define __MODULES_DB_H__ + +#include +#include + +#include "extension/extension.h" + +namespace Inkscape { +namespace Extension { + +class DB { +private: + /** A string comparison function to be used in the moduledict + to find the different extensions in the hash map. */ + struct ltstr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) < 0; + } + }; + /** This is the actual database. It has all of the modules in it, + indexed by their ids. It's a hash table for faster lookups */ + std::map moduledict; + /** Maintain an ordered list of modules for generating the extension + lists via "foreach" */ + std::list modulelist; + + static void foreach_internal (gpointer in_key, gpointer in_value, gpointer in_data); + +public: + DB (void); + Extension * get (const gchar *key); + void register_ext (Extension *module); + void unregister_ext (Extension *module); + void foreach (void (*in_func)(Extension * in_plug, gpointer in_data), gpointer in_data); + +private: + static void input_internal (Extension * in_plug, gpointer data); + static void output_internal (Extension * in_plug, gpointer data); + static void effect_internal (Extension * in_plug, gpointer data); + +public: + typedef std::list OutputList; + typedef std::list InputList; + typedef std::list EffectList; + + InputList &get_input_list (InputList &ou_list); + OutputList &get_output_list (OutputList &ou_list); + EffectList &get_effect_list (EffectList &ou_list); +}; /* class DB */ + +extern DB db; + +} } /* namespace Extension, Inkscape */ + +#endif /* __MODULES_DB_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/dependency.cpp b/src/extension/dependency.cpp new file mode 100644 index 000000000..4ccf103f6 --- /dev/null +++ b/src/extension/dependency.cpp @@ -0,0 +1,261 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include + +#include "config.h" +#include "path-prefix.h" + +#include "dependency.h" +#include "db.h" + +namespace Inkscape { +namespace Extension { + +// These strings are for XML attribute comparisons and should not be translated +gchar const * Dependency::_type_str[] = { + "executable", + "file", + "extension", + "plugin", +}; + +// These strings are for XML attribute comparisons and should not be translated +gchar const * Dependency::_location_str[] = { + "path", + "extensions", + "absolute", +}; + +/** + \brief Create a dependency using an XML definition + \param in_repr XML definition of the dependency + + This function mostly looks for the 'location' and 'type' attributes + and turns them into the enums of the same name. This makes things + a little bit easier to use later. Also, a pointer to the core + content is pulled out -- also to make things easier. +*/ +Dependency::Dependency (Inkscape::XML::Node * in_repr) +{ + _type = TYPE_FILE; + _location = LOCATION_PATH; + _repr = in_repr; + _string = NULL; + _description = NULL; + + Inkscape::GC::anchor(_repr); + + const gchar * location = _repr->attribute("location"); + for (int i = 0; i < LOCATION_CNT && location != NULL; i++) { + if (!strcmp(location, _location_str[i])) { + _location = (location_t)i; + break; + } + } + + const gchar * type = _repr->attribute("type"); + for (int i = 0; i < TYPE_CNT && type != NULL; i++) { + if (!strcmp(type, _type_str[i])) { + _type = (type_t)i; + break; + } + } + + _string = sp_repr_children(_repr)->content(); + + _description = _repr->attribute("description"); + if (_description == NULL) + _description = _repr->attribute("_description"); + + return; +} + +/** + \brief This depenency is not longer needed + + Unreference the XML structure. +*/ +Dependency::~Dependency (void) +{ + Inkscape::GC::release(_repr); +} + +/** + \brief Check if the dependency passes. + \return Whether or not the dependency passes. + + This function depends largely on all of the enums. The first level + that is evaluted is the \c _type. + + If the type is \c TYPE_EXTENSION then the id for the extension is + looked up in the database. If the extension is found, and it is + not deactivated, the dependency passes. + + If the type is \c TYPE_PLUGIN then the path for the plugin is found + using the Glib::Module routines. When the path is found, then there + is a check to see if the file exists using the \c file_test function. + + If the type is \c TYPE_EXECUTABLE or \c TYPE_FILE things are getting + even more interesting because now the \c _location variable is also + taken into account. First, the difference between the two is that + the file test for \c TYPE_EXECUTABLE also tests to make sure the + file is executable, besides checking that it exists. + + If the \c _location is \c LOCATION_EXTENSIONS then the \c INKSCAPE_EXTENSIONDIR + is put on the front of the string with \c build_filename. Then the + appopriate filetest is run. + + If the \c _location is \c LOCATION_ABSOLUTE then the file test is + run directly on the string. + + If the \c _location is \c LOCATION_PATH or not specified then the + path is used to find the file. Each entry in the path is stepped + through, attached to the string, and then tested. If the file is + found then a TRUE is returned. If we get all the way through the + path then a FALSE is returned, the command could not be found. +*/ +bool +Dependency::check (void) const +{ + // std::cout << "Checking: " << *this << std::endl; + + if (_string == NULL) return FALSE; + + switch (_type) { + case TYPE_EXTENSION: { + Extension * myext = db.get(_string); + if (myext == NULL) return FALSE; + if (myext->deactivated()) return FALSE; + break; + } + case TYPE_PLUGIN: { + if (!Glib::Module::get_supported()) { + return FALSE; + } + + std::string path = Glib::Module::build_path(INKSCAPE_PLUGINDIR, _string); + if (!Glib::file_test(path, Glib::FILE_TEST_EXISTS)) + return FALSE; + break; + } + case TYPE_EXECUTABLE: + case TYPE_FILE: { + Glib::FileTest filetest = Glib::FILE_TEST_EXISTS; + if (_type == TYPE_EXECUTABLE) { + filetest |= Glib::FILE_TEST_IS_EXECUTABLE; + } + + std::string location(_string); + switch (_location) { + case LOCATION_EXTENSIONS: { + for (unsigned int i=0; i + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_DEPENDENCY_H__ +#define INKSCAPE_EXTENSION_DEPENDENCY_H__ + +#include +#include "xml/repr.h" + +namespace Inkscape { +namespace Extension { + +/** \brief A class to represent a dependency for an extension. There + are different things that can be done in a dependency, and + this class takes care of all of them. */ +class Dependency { + /** \brief The XML representation of the dependency. */ + Inkscape::XML::Node * _repr; + /** \brief The string that is in the XML tags pulled out. */ + const gchar * _string; + /** \brief The description of the dependency for the users. */ + const gchar * _description; + + /** \brief All the possible types of dependencies. */ + enum type_t { + TYPE_EXECUTABLE, /**< Look for an executable */ + TYPE_FILE, /**< Look to make sure a file exists */ + TYPE_EXTENSION, /**< Make sure a specific extension is loaded and functional */ + TYPE_PLUGIN, /**< Look for a library to be loaded as a plugin */ + TYPE_CNT /**< Number of types */ + }; + /** \brief Storing the type of this particular dependency. */ + type_t _type; + + /** \brief All of the possible locations to look for the dependency. */ + enum location_t { + LOCATION_PATH, /**< Look in the PATH for this depdendency */ + LOCATION_EXTENSIONS, /**< Look in the extensions directory */ + LOCATION_ABSOLUTE, /**< This dependency is already defined in absolute terms */ + LOCATION_CNT /**< Number of locations to look */ + }; + /** \brief The location to look for this particular dependency. */ + location_t _location; + + /** \brief Strings to reperesent the different enum values in + \c type_t in the XML */ + static gchar const * _type_str[TYPE_CNT]; + /** \brief Strings to reperesent the different enum values in + \c location_t in the XML */ + static gchar const * _location_str[LOCATION_CNT]; + +public: + Dependency (Inkscape::XML::Node * in_repr); + ~Dependency (void); + bool check (void) const; + Glib::ustring &get_help (void) const; + Glib::ustring &get_link (void) const; + + friend std::ostream & operator<< (std::ostream &out_file, const Dependency & in_dep); +}; /* class Dependency */ + +std::ostream & operator<< (std::ostream &out_file, const Dependency & in_dep); + +} } /* namespace Extension, Inkscape */ + +#endif /* INKSCAPE_EXTENSION_DEPENDENCY_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/dxf2svg/GPL.txt b/src/extension/dxf2svg/GPL.txt new file mode 100644 index 000000000..3912109b5 --- /dev/null +++ b/src/extension/dxf2svg/GPL.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/extension/dxf2svg/LGPL.txt b/src/extension/dxf2svg/LGPL.txt new file mode 100644 index 000000000..8add30ad5 --- /dev/null +++ b/src/extension/dxf2svg/LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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/src/extension/dxf2svg/Makefile b/src/extension/dxf2svg/Makefile new file mode 100644 index 000000000..d0f06daf0 --- /dev/null +++ b/src/extension/dxf2svg/Makefile @@ -0,0 +1,21 @@ +objs = read_dxf.o entities.o entities2elements.o tables.o tables2svg_info.o blocks.o +flags = -O1 + +dxf2svg : dxf2svg.cpp $(objs) + g++ $(flags) -o dxf2svg dxf2svg.cpp $(objs) + +test : test_dxf.cpp $(objs) + g++ $(flags) -o test test_dxf.cpp $(objs) + +%.o : %.cpp %.h + g++ $(flags) -c $< + +clean : + rm dxf2svg $(objs) + +install: + echo ****User intervention required**** + echo + echo Copy dxf2svg executable and dxf2svg.inx to share/extension directory + echo Make sure file permissions are set correctly + diff --git a/src/extension/dxf2svg/README b/src/extension/dxf2svg/README new file mode 100644 index 000000000..dbf98d2cc --- /dev/null +++ b/src/extension/dxf2svg/README @@ -0,0 +1,41 @@ +26 Aug 05 + +To use: +I have only gotten this to work with Inkscape under linux (debian sarge). But the converter works from the commandline if you want to test it otherwise. + +1. Run make and copy dxf2svg to somewhere that is looked at by the PATH variable. Or add the current directory to your path +2. Copy the dxf_import.inx to ../Inkscape/share/extensions +3. Run Inkscape +4. Click on open or import and the dxf option should be there + +I have compiled and then succesfully converted dxf files to svg files with the following setups + +WinXP Pro, Cygwin, g++ 3.3.3 +Debian Sarge, AMD x86_64, g++ 4.0.2 +RedHat(?), x86(?), g++ 3.2.3 + +I have weakly tried and failed to compile using +Borland's free compiler bcc32 +MinGW + +What is supported: +polylines, lines, arc, circles, blocks, text, and a little linetypes + +currently not supported but I hope will be in the near future (probably after Sept 05) +layers, colors, trace, splines, dimentions + +If you don't have dxf files of your own, but would like to easily test some I would suggest a few places... + +www.newfocus.com -- look under optomechanics. There are a variety of mounts and such that are posted in dxf and pdf +www.thorlabs.com -- dido what I said about newfocus + +Free CAD programs: +Windows -- A free and not too bad CAD program for windows is CadStd Lite. It exports to dxf and has a variety of samples. +Windows/Linux -- QCad is ok. It has a lot of nice features but when I used it a year ago it would import dxf files but then you couldn't edit what was imported. It is free but with a time limit. My experience is that it can be a little annoying. Because of that and others comments I didn't use their libraries for dxf reading. + + +Matt Squires +squiresm@colorado.edu + +27 Aug 05, Fixed a fair number of bugs, and broken conversions. LWPOLYLINE works now, text rotation is ok but needs multiple transfomations to get it right, can read broken dxf files (w/o dxf EOF information) + diff --git a/src/extension/dxf2svg/aci2rgb.cpp b/src/extension/dxf2svg/aci2rgb.cpp new file mode 100644 index 000000000..57e488bfc --- /dev/null +++ b/src/extension/dxf2svg/aci2rgb.cpp @@ -0,0 +1,114 @@ +#include +#include +#include + + +char* RGB(double R, double G, double B); +char* RGB(double R, double G, double B){ + int r = int (R); + int g = int (G); + int b = int (B); + + char out[6]; + char *chr_ptr; + string output; + stringstream oss; + + if (r < 16 ){ + oss << 0; + } + oss << hex << r; + + if (g < 16 ){ + oss << 0; + } + oss << hex << g; + + if (b < 16 ){ + oss << 0; + } + oss << hex << b; + + output = oss.str(); + + for (int i = 0; i < 6; i++){ + out[i] = output[i]; + } + chr_ptr = &out; + return chr_ptr; +} + + +float aci_to_rgb(int aci); + +float aci_to_rgb(int aci) + { + aci = abs(aci); // hidden layers have negative color values + if (aci<10 || aci>249) // values of these ranges are special colors + { + switch (aci) + { + case 1: return RGB(255,0,0); // basic colors + case 2: return RGB(255,255,0); + case 3: return RGB(0,255,0); + case 4: return RGB(0,255,255); + case 5: return RGB(0,0,255); + case 6: return RGB(255,0,255); + case 7: return RGB(255,255,255); + case 8: return RGB(128,128,128); + case 9: return RGB(192,192,192); + case 250: return RGB(51,51,51); // grey shades + case 251: return RGB(91,91,91); + case 252: return RGB(132,132,132); + case 253: return RGB(173,173,173); + case 254: return RGB(214,214,214); + case 255: return RGB(255,255,255); + case 256: // "by layer" + // Here you should decide how to handle "by layer" logical color. + // Maybe it is a good idea to return a value like -1. + // The outer code will find what is the color of the layer which + // this entity belongs to. + return -1; + } + } + // for all the rest of ACI codes + float H,S,L, R,G,B; + int remainder = aci % 10; + H = 1.5f * (aci - remainder - 10); // hue in range 0-360 + S = ((aci % 2) ? 0.5f : 1.0f); // odd colors have 50% of saturation, even - 100% + // set lighteness, the last digit of aci code stands for this + if (reminder == 0 || reminder == 1) L = 1.0f; + if (reminder == 2 || reminder == 3) L = 0.8f; + if (reminder == 4 || reminder == 5) L = 0.6f; + if (reminder == 6 || reminder == 7) L = 0.5f; + if (reminder == 8 || reminder == 9) L = 0.3f; + // here we have H,S,L set already + // let's convert it to RGB, first without consideration of S and L + if (H<=120) + { + R = (120-H)/60; + G = H/60; + B = 0; + } + if (H>120 && H<=240) + { + R = 0; + G = (240-H)/60; + B = (H-120)/60; + } + if (H>240 && H<=360) + { + R = (H-240)/60; + G = 0; + B = (360-H)/60; + } + R = min(R, 1); + G = min(G, 1); + B = min(B, 1); + // influence of S and L + float max_value = max(R,max(G,B)); + R = (max_value-S*(max_value-R)) * L * 255; + G = (max_value-S*(max_value-G)) * L * 255; + B = (max_value-S*(max_value-B)) * L * 255; + return RGB(R,G,B); + } \ No newline at end of file diff --git a/src/extension/dxf2svg/blocks.cpp b/src/extension/dxf2svg/blocks.cpp new file mode 100644 index 000000000..75f348bde --- /dev/null +++ b/src/extension/dxf2svg/blocks.cpp @@ -0,0 +1,88 @@ +/* + * Read Blocks from file and convert to vectors of entities + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + +#include"blocks.h" +#include + +block::block(std::vector< std::vector< dxfpair > > sections) : entities( sections ){ + // Inherit most of the functionality of the entitites section + + basic_entity( sections[0] ); + block_info( sections[0] ); +} + +char* block::name(char* string){ + return( strcpy(string,block_name) ); +} + + +void block::block_info( std::vector< dxfpair > info){ + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 2: // Block name + strcpy( string," "); // Clear the string out + info[i].value_char(string); + strcpy(block_name,string); + break; + } + } +} + + + + + + +blocks::blocks(std::vector< std::vector< dxfpair > > sections){ + // Read the main information about the entities section and then put it in the enetites class + int value; + char string[10000]; + std::vector< dxfpair > single_line; + std::vector< std::vector< dxfpair > > ents; + ents.clear(); + single_line.clear(); + + int n_loop = sections.size(); + n_loop--; + //for(int i = 0; i < (sections.size()-1); i++){ // It is odd but the last value seems to be bad so don't use it + // I am not really sure if I need the -1. I needed it once upon a time to make things work but I don't have time to test it well right now + // But sections.size() is an unsigned int so when you subtract 1 it becomes 4294967295 and tries to run the loop so work around that by making n_loop that is signed + for(int i = 0; i < n_loop; i++){ // It is odd but the last value seems to be bad so don't use it + sections[i][0].value_char(string); + ents.clear(); // First clear out the pline information + + + // Get everything from the start of the BLOCK designation to an ENDBLK value + if ( strncmp(string,"BLOCK",5) == 0 && (i < sections.size())){ + do{ + ents.push_back( sections[i] ); + sections[++i][0].value_char(string); + }while( strncmp(string,"ENDBLK",6) != 0 && (i < sections.size()-1) ); + blocks_blocks.push_back( block( ents ) ); + } + } +} + +block blocks::ret_block(char block_name[10000]){ + int string_len = 0; + char temp[10000]; + + for (int i = 0; i < blocks_blocks.size();i++){ + string_len = strlen(blocks_blocks[i].name(temp)); + if (strncmp(blocks_blocks[i].name(temp),block_name,string_len) == 0 ) return blocks_blocks[i]; + } + return blocks_blocks[0]; +} + + + diff --git a/src/extension/dxf2svg/blocks.h b/src/extension/dxf2svg/blocks.h new file mode 100644 index 000000000..27e0b8be1 --- /dev/null +++ b/src/extension/dxf2svg/blocks.h @@ -0,0 +1,38 @@ +/* Read Blocks from file and convert to vectors of entities +Matt Squires +Google SOC 2005 +*/ + +#ifndef DXF_BLOCKS_H +#define DXF_BLOCKS_H + +#include"read_dxf.h" +#include"entities.h" +#include + + +class block : public entity, public entities{// : public entities, { + public: + block( std::vector< std::vector< dxfpair > > sections ); // Group all of the blocks as entities + char* name(char* string); + //void blocks_display(); + + + protected: + char block_name[10000]; + double rotation; + + private: + void block_info(std::vector< dxfpair > info); +}; + +class blocks{ + public: + blocks(std::vector< std::vector< dxfpair > > sections); + block ret_block(char block_name[10000]); + + protected: + std::vector< block > blocks_blocks; +}; + +#endif diff --git a/src/extension/dxf2svg/dxf2svg.cpp b/src/extension/dxf2svg/dxf2svg.cpp new file mode 100644 index 000000000..7606e3531 --- /dev/null +++ b/src/extension/dxf2svg/dxf2svg.cpp @@ -0,0 +1,106 @@ +/* + * Build a SVG from an dxf, will support conversion to Inkscape types + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + +#include +#include +#include"read_dxf.h" +#include"entities.h" +#include"blocks.h" +#include"entities2elements.h" + + + + + + + + +int main(int argc,char *argv[]){ + // Later include options for different conversions like converting as much as possible into paths + int ink = 1; // Assume for now there is no inkscape stuff to add extra + + if(argc > 1){ + double scaling = 90; // converstion from in to pt + + // Read the DXF file + std::vector< std::vector< dxfpair > > output, entities_info, tables_info, blocks_info; + //std::cout << "About to read file \n" << std::endl; + output = dxf_get_sections(argv[1]); + //std::cout << "Finished reading file \n" << std::endl; + + entities_info = separate_parts(output[4]); // Entities is the 5th part of the file. + entities ents(entities_info); // Sort entities into their respective parts + + tables_info = separate_parts(output[2]); // Tables is the 3rd part of a dxf file. + tables tbls(tables_info); // Sort the information in the tables + + blocks_info = separate_parts(output[3]); // Tables is the 4th part of a dxf file. + blocks blks(blocks_info); // Sort the information in the tables + + + + // Get the various file informations + /*std::vector< polyline > plines = ents.ret_plines(); + std::vector< lwpolyline > lwplines = ents.ret_lwplines(); + std::vector< arc > arcs = ents.ret_arcs(); + std::vector< circle > circs = ents.ret_circles(); + std::vector< line > lns = ents.ret_lines(); + std::vector< text > txts = ents.ret_texts(); + std::vector< insert > ins = ents.ret_inserts(); + */ + + + std::vector< layer > layers = tbls.ret_layers(); + + char units[5] = "in"; + char tmp_char[100000]; + char layer_string[500]; + + if (ink < 1){ + // Write a general svg header + std::cout << "\n\n\n"; + std::cout << "\tx=\"0.00000000\"\n\ty=\"0.00000000\"\n\twidth=\"744.09448\"\n\theight=\"-1052.3622\"" << std::endl; + } + else{ + std::cout << "" << std::endl; + std::cout << "" << std::endl; + std::cout << "" << std::endl; + + } + + + // Now write SVG elements to file + if ( layers.size() < 1 ){ + write_all(0, ents, tbls, blks, scaling, units, tmp_char); + } + else{ + for (int i = 0; i < layers.size(); i++){ + std::cout << "\t" << std::endl; + write_by_layer(0, ents, tbls, blks, scaling, units, layers[i].name(layer_string), tmp_char); + std::cout << "\t" << std::endl; + } + } + + // Close SVG + std::cout << ""; + } + + return 0; +} diff --git a/src/extension/dxf2svg/dxf_input.inx b/src/extension/dxf2svg/dxf_input.inx new file mode 100644 index 000000000..b96274ade --- /dev/null +++ b/src/extension/dxf2svg/dxf_input.inx @@ -0,0 +1,16 @@ + + DXF Input + org.inkscape.input.dxf + dxf2svg + org.inkscape.input.svg + + .dxf + image/x-svgz + AutoCAD DXF (*.dxf) + Import AutoCAD's Document Exchange Format + org.inkscape.output.svg + + + diff --git a/src/extension/dxf2svg/dxf_input_windows.inx b/src/extension/dxf2svg/dxf_input_windows.inx new file mode 100644 index 000000000..7dbe6a11b --- /dev/null +++ b/src/extension/dxf2svg/dxf_input_windows.inx @@ -0,0 +1,17 @@ + + DXF Input + org.inkscape.input.dxf + dxf2svg.exe + org.inkscape.input.svg + + .dxf + image/x-svgz + AutoCAD DXF (*.dxf) + Import AutoCAD's Document Exchange Format + org.inkscape.output.svg + + + diff --git a/src/extension/dxf2svg/entities.cpp b/src/extension/dxf2svg/entities.cpp new file mode 100644 index 000000000..d0d33503e --- /dev/null +++ b/src/extension/dxf2svg/entities.cpp @@ -0,0 +1,1031 @@ +/* + * Class for interpereting the entities found in a DXF file + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + +#include"entities.h" +#include +#include + +int detmine_entity(char* value){ + // Common Elements as far as I am concerend + if ( strncmp(value,"POLYLINE",8) == 0 ) return 0; + if ( strncmp(value,"ARC",3) == 0 ) return 1; + if ( strncmp(value,"CIRCLE",6) == 0 ) return 2; + if ( strncmp(value,"LINE",4) == 0 ) return 3; + if ( strncmp(value,"SPLINE",6) == 0 ) return 4; + if ( strncmp(value,"XLINE",5) == 0 ) return 5; + if ( strncmp(value,"RAY",3) == 0 ) return 6; + if ( strncmp(value,"DIMENSION",9) == 0 ) return 7; + if ( strncmp(value,"ELLIPSE",7) == 0 ) return 8; + if ( strncmp(value,"INSERT",6) == 0 ) return 9; + if ( strncmp(value,"VERTEX",6) == 0 ) return 10; + if ( strncmp(value,"TEXT",4) == 0 ) return 11; + + // Less Common eletities as far as I am concerend + if ( strncmp(value,"3DSOLID",7) == 0 ) return 12; + if ( strncmp(value,"ACAD_PROXY_ENTITY",17) == 0 ) return 13; + if ( strncmp(value,"ARCALIGNEDTEXT",14) == 0 ) return 14; + if ( strncmp(value,"ATTDEF",6) == 0 ) return 15; + if ( strncmp(value,"ATTRIB",6) == 0 ) return 16; + if ( strncmp(value,"BODY",4) == 0 ) return 17; + if ( strncmp(value,"HATCH",5) == 0 ) return 18; + if ( strncmp(value,"IMAGE",5) == 0 ) return 19; + if ( strncmp(value,"LEADER",6) == 0 ) return 20; + if ( strncmp(value,"LWPOLYLINE",10) == 0 ) return 21; + if ( strncmp(value,"MLINE",5) == 0 ) return 22; + if ( strncmp(value,"MTEXT",5) == 0 ) return 23; + if ( strncmp(value,"OLEFRAME",8) == 0 ) return 24; + if ( strncmp(value,"POINT",5) == 0 ) return 25; + if ( strncmp(value,"REGION",6) == 0 ) return 26; + if ( strncmp(value,"RTEXT",5) == 0 ) return 27; + if ( strncmp(value,"SEQEND",6) == 0 ) return 28; + if ( strncmp(value,"SHAPE",5) == 0 ) return 29; + if ( strncmp(value,"SOLID",5) == 0 ) return 30; + if ( strncmp(value,"3DFACE",6) == 0 ) return 31; + if ( strncmp(value,"TOLERANCE",9) == 0 ) return 32; + if ( strncmp(value,"TRACE",5) == 0 ) return 33; + if ( strncmp(value,"VIEWPORT",8) == 0 ) return 34; + if ( strncmp(value,"WIPEOUT",7) == 0 ) return 35; + else return -1; +} + + +void entity::basic_entity( std::vector< dxfpair > info){ + // Extract all of the typical entity information (e.g. layer name, positions) + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 6: + strcpy( string," "); // Clear the string out + info[i].value_char(string); + strcpy(linetype,string); + break; + case 8: + strcpy( string," "); // Clear the string out + info[i].value_char(string); + strcpy(layer,string); + break; + case 10: + info[i].value_char(string); + x = atof(string); + if ( x < min_x ){ + min_x = x; + } + if ( x > max_x ){ + max_x = x; + } + break; + case 20: + info[i].value_char(string); + y = atof(string); + if ( y < min_y ){ + min_y = y; + } + if ( y > max_y ){ + max_y = y; + } + break; + case 30: + info[i].value_char(string); + z = atof(string); + break; + } + } + +} + +void entity::entity_display(){ + std::cout << "\tlayer = " << layer << "\n\tlinetype = " << linetype << "\n\tx = " << x << "\ty = " << y << "\tz = " << z << std::flush; +} + +double entity::ret_x(){ + return x; +} + +double entity::ret_y(){ + return y; +} + +double entity::ret_z(){ + return z; +} + + +char* entity::ret_layer_name(char* string){ + return( strcpy(string,layer) ); +} + +char* entity::ret_ltype_name(char* string){ + return( strcpy(string,linetype) ); +} + +double entity::ret_min_x(){ + return min_x; +} + +double entity::ret_max_x(){ + return max_x; +} + +double entity::ret_min_y(){ + return min_y; +} + +double entity::ret_max_y(){ + return max_y; +} + +void entity::test_coord(double x, double y){ + if ( x < min_x ){ + min_x = x; + } + if ( x > max_x ){ + max_x = x; + } + if ( y < min_y ){ + min_y = y; + } + if ( y > max_y ){ + max_y = y; + } +} + + +void entity::reset_extents(){ + min_x = -1e20; + max_x = 1e20; + min_y = -1e20; + max_y = 1e20; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// VERTEX +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +vertex::vertex( std::vector< dxfpair > info){ + // Get the vertex information + + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 42: + info[i].value_char(string); + bulge = atof(string); + break; + } + } +} + +double vertex::ret_bulge(){ + return bulge; +} + +void vertex::display(){ + std::cout << "VERTEX\n"; + std::cout << "\tx = " << x << "\ty = " << y << "\tz = " << z << "\tbulge = " << bulge << std::flush; +} + + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// POLYLINE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// The polyline is handled a little differently compared to the other entities because a POLYLINE is built from a bunch of VERTEX entities +polyline::polyline( std::vector< std::vector< dxfpair > > sections ){ + reset_extents(); + // get the polyline information + basic_entity( sections[0] ); + points.clear(); + static char string[10000]; + //char string[10000]; + pline_flag = 0; + curves_flag = 0; + for (int i = 0; i < sections[0].size(); i++){ + switch( sections[0][i].group_code ){ + case 70: + sections[0][i].value_char(string); + pline_flag = atoi(string); + break; + case 40: + sections[0][i].value_char(string); + start_width = atoi(string); + break; + case 41: + sections[0][i].value_char(string); + end_width = atoi(string); + break; + case 75: + sections[0][i].value_char(string); + curves_flag = atoi(string); + break; + } + } + // Now add the VERTEX entities to the POLYLINE + for (int i = 1; i < sections.size(); i++){ + points.push_back( vertex( sections[i] ) ); + } +} + + +std::vector< vertex > polyline::ret_points(){ + return points; +} + +double polyline::bulge(int point){ + return points[point].ret_bulge(); +} + +double polyline::bulge_r(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + return r; +} + +double polyline::bulge_start_angle(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double xmid = dx/2 + points[point].ret_x(); + double ymid = dy/2 + points[point].ret_y(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + double a = fabs(bulge*l/2); + double sb = bulge/fabs(bulge); //sign of bulge + double theta_p = 4*atan(bulge); + double theta_c; + dx != 0 ? theta_c = atan(dy/dx) : theta_c = 1.57079632679489661923; // Check to make sure that dx is not zero and will give a negative number + if (dx > 0) sb *= -1; // Correct for different point ordering and bulge direction + + double cx = xmid + sb*(r-a)*sin(theta_c); + double cy = ymid - sb*(r-a)*cos(theta_c); + + // Now calculate the angle + double theta = asin(points[point].ret_x()/r); + if (dy < 0) theta = 6.2831853 - theta; // The angle is greater than pi so fix this because max(asin) = pi + + return theta; +} + +double polyline::bulge_end_angle(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double xmid = dx/2 + points[point].ret_x(); + double ymid = dy/2 + points[point].ret_y(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + double a = fabs(bulge*l/2); + double sb = bulge/fabs(bulge); //sign of bulge + double theta_p = 4*atan(bulge); + double theta_c; + dx != 0 ? theta_c = atan(dy/dx) : theta_c = 1.57079632679489661923; // Check to make sure that dx is not zero and will give a negative number + if (dx > 0) sb *= -1; // Correct for different point ordering and bulge direction + + double cx = xmid + sb*(r-a)*sin(theta_c); + double cy = ymid - sb*(r-a)*cos(theta_c); + + // Now calculate the angle + double theta = asin(points[point+1].ret_x()/r); + if (dy < 0) theta = 6.2831853 - theta; // The angle is greater than pi so fix this because max(asin) = pi + + return theta; +} + + +bool polyline::is_closed(){ + // pline-flag holds info about closed pline in the 1 bit. The info is bit wise encoded so use bit wise operators + return bool(pline_flag&1); +} + +void polyline::display(){ + std::cout << "POLYLINE\n"; + entity_display(); + std::cout << std::endl; + for (int i = 0; i < points.size(); i++){ + points[i].display(); + std::cout << std::endl; + } + std::cout << std::endl; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// LWPOLYLINE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// The lwpolyline is different than the polyline because there are no vertex entities. Use the same basic process as the polyline but parse it out differently +lwpolyline::lwpolyline( std::vector< dxfpair > section ){ + + // First break up the data into the same format that is used by the polyline entity + std::vector< std::vector< dxfpair > > sections; + std::vector< dxfpair > first; + std::vector< dxfpair > others; + + sections.clear(); + first.clear(); + others.clear(); + + int gc; // make a shorter name for group_code; + + int vertex_part; + int already_found = 0; + + for (int i = 0; i < section.size(); i++){ + gc = section[i].group_code; + // Encode bitwise information to keep track of what has been found + vertex_part = 0; + if (gc == 10 ) vertex_part += 1; + if (gc == 20 ) vertex_part += 2; + if (gc == 30 ) vertex_part += 4; + if (gc == 40 ) vertex_part += 8; + if (gc == 41 ) vertex_part += 16; + if (gc == 42 ) vertex_part += 32; + //std::cout << "\n\nvertex_part = " << vertex_part << std::endl << "already_found = " << already_found << std::endl << "(vertex_part&already_found) = " << (vertex_part&already_found) << std::endl; + if ( vertex_part == 0 ){ + // If header stuff has been found save it under first. + // I.E. in a polyline the first set of information is linetype and layer, all of what should be in here + first.push_back( section[i] ); + } + else if ( (vertex_part&already_found) == 0 ){ + // Now work on what would be the vertex information + // New information is still being found so keep saving it + others.push_back( section[i] ); + //std::cout << "add to others" << std::endl; + already_found += vertex_part; // Keep track of what has been found + } + else{ + sections.push_back( others ); + //std::cout << "sections.size() = " << sections.size() << std::endl; + // Now clear the information out and start over + others.clear(); + others.push_back( section[i] ); + already_found = vertex_part; + } + } + + // Now put on the last data that was found + if (others.size() > 0 ){ + sections.push_back( others ); + } + + reset_extents(); + basic_entity( first ); + points.clear(); + static char string[10000]; + //char string[10000]; + pline_flag = 0; + curves_flag = 0; + for (int i = 0; i < first.size(); i++){ + switch( sections[0][i].group_code ){ + case 70: + first[i].value_char(string); + pline_flag = atoi(string); + break; + case 40: + first[i].value_char(string); + start_width = atoi(string); + break; + case 41: + first[i].value_char(string); + end_width = atoi(string); + break; + case 75: + first[i].value_char(string); + curves_flag = atoi(string); + break; + } + } + // Now add the VERTEX entities to the POLYLINE + for (int i = 0; i < sections.size(); i++){ + points.push_back( vertex( sections[i] ) ); + } +} + + + +std::vector< vertex > lwpolyline::ret_points(){ + return points; +} + + +double lwpolyline::bulge(int point){ + return points[point].ret_bulge(); +} + +double lwpolyline::bulge_r(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + return r; +} + +double lwpolyline::bulge_start_angle(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double xmid = dx/2 + points[point].ret_x(); + double ymid = dy/2 + points[point].ret_y(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + double a = fabs(bulge*l/2); + double sb = bulge/fabs(bulge); //sign of bulge + double theta_p = 4*atan(bulge); + double theta_c; + dx != 0 ? theta_c = atan(dy/dx) : theta_c = 1.57079632679489661923; // Check to make sure that dx is not zero and will give a negative number + if (dx > 0) sb *= -1; // Correct for different point ordering and bulge direction + + double cx = xmid + sb*(r-a)*sin(theta_c); + double cy = ymid - sb*(r-a)*cos(theta_c); + + // Now calculate the angle + double theta = asin(points[point].ret_x()/r); + if (dy < 0) theta = 6.2831853 - theta; // The angle is greater than pi so fix this because max(asin) = pi + + return theta; +} + +double lwpolyline::bulge_end_angle(int point){ + // Make sure we are not exceeding the bounds of the points vector + if (point >= (points.size()-1)) return 0; + + double dx = points[point+1].ret_x() - points[point].ret_x(); + double dy = points[point+1].ret_y() - points[point].ret_y(); + double bulge = points[point].ret_bulge(); + double xmid = dx/2 + points[point].ret_x(); + double ymid = dy/2 + points[point].ret_y(); + double l = sqrt(dx*dx + dy*dy); + double r = fabs(l*(bulge*bulge+1)/bulge/4); + + double a = fabs(bulge*l/2); + double sb = bulge/fabs(bulge); //sign of bulge + double theta_p = 4*atan(bulge); + double theta_c; + dx != 0 ? theta_c = atan(dy/dx) : theta_c = 1.57079632679489661923; // Check to make sure that dx is not zero and will give a negative number + if (dx > 0) sb *= -1; // Correct for different point ordering and bulge direction + + double cx = xmid + sb*(r-a)*sin(theta_c); + double cy = ymid - sb*(r-a)*cos(theta_c); + + // Now calculate the angle + double theta = asin(points[point+1].ret_x()/r); + if (dy < 0) theta = 6.2831853 - theta; // The angle is greater than pi so fix this because max(asin) = pi + + return theta; +} + +bool lwpolyline::is_closed(){ + // pline-flag holds info about closed pline in the 1 bit. The info is bit wise encoded so use bit wise operators + return bool(pline_flag&1); +} + +void lwpolyline::display(){ + std::cout << "lwpolyline\n"; + entity_display(); + std::cout << std::endl; + for (int i = 0; i < points.size(); i++){ + points[i].display(); + std::cout << std::endl; + } + std::cout << std::endl; +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ARC +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +arc::arc( std::vector< dxfpair > info){ + + reset_extents(); + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 40: + info[i].value_char(string); + radius = atof(string); + test_coord(x+radius,y+radius); + test_coord(x-radius,y-radius); + break; + case 50: + info[i].value_char(string); + start_angle = atof(string); + break; + case 51: + info[i].value_char(string); + end_angle = atof(string); + break; + default: + break; + } + } +} + +double arc::ret_radius(){ + return radius; +} + + +double arc::ret_srt_ang(){ + return start_angle; +} + + +double arc::ret_end_ang(){ + return end_angle; +} + + +void arc::display(){ + std::cout << "ARC\n"; + entity_display(); + std::cout << "\n\tradius = " << radius << "\tstart_angle = " << start_angle << "end_angle = " << end_angle << std::flush; +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CIRCLE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +circle::circle( std::vector< dxfpair > info){ + + reset_extents(); + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 40: + info[i].value_char(string); + radius = atof(string); + test_coord(x+radius,y+radius); + test_coord(x-radius,y-radius); + break; + } + } +} + +void circle::display(){ + std::cout << "CIRCLE\n"; + entity_display(); + std::cout << "\n\tradius = " << radius << std::flush; +} + + +double circle::ret_radius(){ + return radius; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// LINE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +line::line( std::vector< dxfpair > info){ + + reset_extents(); + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 11: + info[i].value_char(string); + xf = atof(string); + break; + case 21: + info[i].value_char(string); + yf = atof(string); + break; + case 31: + info[i].value_char(string); + zf = atof(string); + break; + } + } + test_coord(xf,yf); + +} + +void line::display(){ + std::cout << "LINE\n"; + entity_display(); + std::cout << "\n\txf = " << xf << "\tyf = " << yf << "\tzf = " << zf << std::flush; +} + + +double line::ret_xf(){ + return xf; +} + +double line::ret_yf(){ + return yf; +} + +double line::ret_zf(){ + return zf; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TEXT +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +text::text( std::vector< dxfpair > info){ + + reset_extents(); + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 1: + info[i].value_char(dxf_text); // directly copy the text into a string + break; + case 40: + info[i].value_char(string); + text_height = atof(string); + break; + case 50: + info[i].value_char(string); + text_rotation = atof(string); + break; + } + } +} + +char * text::ret_text(char *string){ + return( strcpy(string,dxf_text) ); +} + +double text::ret_txt_ht(){ + return text_height; +} + +double text::ret_txt_rot(){ + return text_rotation; +} + +void text::display(){ + char tmp[10000]; + std::cout << "TEXT\n"; + entity_display(); + std::cout << "\ndxf_text = " << ret_text(tmp) << std::flush; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// INSERT +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +insert::insert( std::vector< dxfpair > info){ + + basic_entity( info ); + static char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 2: + info[i].value_char(block_name); // directly copy the text into a string + break; + case 41: + info[i].value_char(string); + x_scale_factor = atof(string); + break; + case 42: + info[i].value_char(string); + y_scale_factor = atof(string); + break; + case 43: + info[i].value_char(string); + z_scale_factor = atof(string); + break; + case 50: + info[i].value_char(string); + rotation = atof(string); + break; + } + } +} + + +char * insert::name(char *string){ + return( strcpy(string,block_name) ); +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// entities +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + +entities::entities(std::vector< std::vector< dxfpair > > sections){ + // Read the main information about the entities section and then put it in the enetites class + int value; + char string[10000]; + std::vector< dxfpair > single_line; + std::vector< std::vector< dxfpair > > pline; + pline.clear(); + single_line.clear(); + + for(int i = 0; i < sections.size(); i++){ + sections[i][0].value_char(string); + value = detmine_entity(string); + switch( value ){ + case 0: + // Get everything from the start of the polyline designation to an SEQEND value + pline.clear(); // First clear out the pline information + do{ + pline.push_back( sections[i] ); + sections[++i][0].value_char(string); + }while( strncmp(string,"SEQEND",6) != 0 ); + ents_polyline.push_back( polyline( pline ) ); + break; + + case 1: + // ARC + ents_arc.push_back( arc( sections[i] ) ); + break; + + case 2: + // CIRCLE + ents_circle.push_back( circle( sections[i] ) ); + break; + + case 3: + // LINE + ents_line.push_back( line( sections[i] ) ); + break; + case 11: + // TEXT + ents_text.push_back( text( sections[i] ) ); + break; + case 21: + // LWPOLYLINE + ents_lwpolyline.push_back( lwpolyline( sections[i] ) ); + case 9: + // INSERT + ents_insert.push_back( insert( sections[i] ) ); + + //default: + // Nothing here + } + + } + +} + + +// Maybe all of this could be turned into fewer function by using templates, but no time right now. MBS + +std::vector< polyline > entities::ret_plines(){ + std::vector< polyline > pls; + pls.clear(); + for(int i = 0; i < ents_polyline.size();i++){ + pls.push_back( ents_polyline[i] ); + } + return pls; +} + +std::vector< lwpolyline > entities::ret_lwplines(){ + std::vector< lwpolyline > lwpls; + lwpls.clear(); + for(int i = 0; i < ents_lwpolyline.size();i++){ + lwpls.push_back( ents_lwpolyline[i] ); + } + return lwpls; +} + + +std::vector< arc > entities::ret_arcs(){ + std::vector< arc > a; + a.clear(); + for(int i = 0; i < ents_arc.size();i++){ + a.push_back( ents_arc[i] ); + } + return a; +} + +std::vector< circle > entities::ret_circles(){ + std::vector< circle > circs; + circs.clear(); + for(int i = 0; i < ents_circle.size();i++){ + circs.push_back( ents_circle[i] ); + } + return circs; +} + + +std::vector< line > entities::ret_lines(){ + std::vector< line > lns; + lns.clear(); + for(int i = 0; i < ents_line.size();i++){ + lns.push_back( ents_line[i] ); + } + return lns; +} + + +std::vector< text > entities::ret_texts(){ + std::vector< text > txts; + txts.clear(); + for(int i = 0; i < ents_text.size();i++){ + txts.push_back( ents_text[i] ); + } + return txts; +} + + +std::vector< insert > entities::ret_inserts(){ + std::vector< insert > ins; + ins.clear(); + for(int i = 0; i < ents_insert.size();i++){ + ins.push_back( ents_insert[i] ); + } + return ins; +} + + + +// Overload the return function to depend on the layer +std::vector< polyline > entities::ret_plines(char * layer){ + char temp[10000]; + std::vector< polyline > pls; + pls.clear(); + + for(int i = 0; i < ents_polyline.size();i++){ + if ( strcmp( layer,ents_polyline[i].ret_layer_name(temp) ) == 0 ){ + pls.push_back( ents_polyline[i] ); + } + } + return pls; +} + + +std::vector< lwpolyline > entities::ret_lwplines(char * layer){ + char temp[10000]; + std::vector< lwpolyline > lwpls; + lwpls.clear(); + + for(int i = 0; i < ents_lwpolyline.size();i++){ + if ( strcmp( layer,ents_lwpolyline[i].ret_layer_name(temp) ) == 0 ){ + lwpls.push_back( ents_lwpolyline[i] ); + } + } + return lwpls; +} + + +std::vector< circle > entities::ret_circles(char * layer){ + char temp[10000]; + std::vector< circle > circs; + circs.clear(); + + for(int i = 0; i < ents_circle.size();i++){ + if ( strcmp( layer,ents_circle[i].ret_layer_name(temp) ) == 0 ){ + circs.push_back( ents_circle[i] ); + } + } + return circs; +} + + +std::vector< line > entities::ret_lines(char * layer){ + char temp[10000]; + std::vector< line > lns; + lns.clear(); + + for(int i = 0; i < ents_line.size();i++){ + if ( strcmp( layer,ents_line[i].ret_layer_name(temp) ) == 0 ){ + lns.push_back( ents_line[i] ); + } + } + return lns; +} + + +std::vector< text > entities::ret_texts(char * layer){ + char temp[10000]; + std::vector< text > txts; + txts.clear(); + + for(int i = 0; i < ents_text.size();i++){ + if ( strcmp( layer,ents_text[i].ret_layer_name(temp) ) == 0 ){ + txts.push_back( ents_text[i] ); + } + } + return txts; +} + + +/*std::vector< ellipse > entities::ret_ellipses(char * layer){ + char temp[10000]; + std::vector< polyline > pls; + pls.clear(); + + for(int i = 0; i < ents_polyline.size();i++){ + if ( strcmp( layer,ents_polyline[i].ret_layer_name(temp) ) == 0 ){ + pls.push_back( ents_polyline[i] ); + } + } + return pls; +}*/ + + +std::vector< arc > entities::ret_arcs(char * layer){ + char temp[10000]; + std::vector< arc > a; + a.clear(); + + for(int i = 0; i < ents_arc.size();i++){ + if ( strcmp( layer,ents_arc[i].ret_layer_name(temp) ) == 0 ){ + a.push_back( ents_arc[i] ); + } + } + return a; +} + + +std::vector< insert > entities::ret_inserts(char * layer){ + char temp[10000]; + std::vector< insert > ins; + ins.clear(); + + for(int i = 0; i < ents_insert.size();i++){ + if ( strcmp( layer,ents_insert[i].ret_layer_name(temp) ) == 0 ){ + ins.push_back( ents_insert[i] ); + } + } + return ins; +} + + + + +void entities::display_all(){ + for (int i = 0; i < ents_polyline.size(); i++){ + ents_polyline[i].display(); + } + std::cout << std::endl; + for (int i = 0; i < ents_lwpolyline.size(); i++){ + ents_lwpolyline[i].display(); + } + std::cout << std::endl; + for (int i = 0; i < ents_circle.size(); i++){ + ents_circle[i].display(); + } + std::cout << std::endl; + for (int i = 0; i < ents_text.size(); i++){ + ents_text[i].display(); + } +} + + diff --git a/src/extension/dxf2svg/entities.h b/src/extension/dxf2svg/entities.h new file mode 100644 index 000000000..96d863d94 --- /dev/null +++ b/src/extension/dxf2svg/entities.h @@ -0,0 +1,256 @@ +/* Class for interpereting the entities found in a DXF file +Matt Squires +Google SOC +2 July 05 +*/ + +#ifndef DXF_ENTITIES_H +#define DXF_ENTITIES_H + +#include"read_dxf.h" +#include + + + + +class entity{ + public: + void basic_entity( std::vector< dxfpair > info); // Extract all of the typical entity information (e.g. layer name, positions) + void entity_display(); + double ret_x(); + double ret_y(); + double ret_z(); + char* ret_layer_name(char* string); + char* ret_ltype_name(char* string); + double ret_min_x(); + double ret_max_x(); + double ret_min_y(); + double ret_max_y(); + + + protected: + char layer[10000]; + char linetype[10000]; + double x; + double y; + double z; + double min_x; + double max_x; + double min_y; + double max_y; + void reset_extents(); + void test_coord(double x, double y); +}; + + + + + +class vertex : public entity { + public: + vertex( std::vector< dxfpair > info); + void display(); + double ret_bulge(); + + private: + double bulge; +}; + + + + + +class polyline : public entity { + public: + polyline( std::vector< std::vector< dxfpair > > sections ); + std::vector< vertex > ret_points(); + double bulge(int point); + double bulge_r(int point); + double bulge_start_angle(int point); + double bulge_end_angle(int point); + bool is_closed(); + void display(); + + private: + double buldge; + int pline_flag; // 70 + double start_width; // 40 + double end_width; // 41 + int curves_flag; + std::vector< vertex > points; +}; + +class lwpolyline : public entity { + public: + lwpolyline( std::vector< dxfpair > section ); + std::vector< vertex > ret_points(); + double bulge(int point); + double bulge_r(int point); + double bulge_start_angle(int point); + double bulge_end_angle(int point); + bool is_closed(); + void display(); + + private: + double buldge; + int pline_flag; // 70 + double start_width; // 40 + double end_width; // 41 + int curves_flag; + std::vector< vertex > points; +}; + +class arc : public entity { + public: + arc( std::vector< dxfpair > info); + double ret_radius(); + double ret_srt_ang(); + double ret_end_ang(); + void display(); + + private: + double radius; + double start_angle; + double end_angle; +}; + + + + +class circle : public entity { + public: + circle( std::vector< dxfpair > info); + void display(); + double ret_radius(); + + private: + double radius; +}; + + +class line : public entity { + public: + line( std::vector< dxfpair > info); + void display(); + double ret_xf(); + double ret_yf(); + double ret_zf(); + + private: + double xf; + double yf; + double zf; +}; + +class ellipse : public entity { + public: + ellipse( std::vector< dxfpair > info); + void display(); + double ret_x_ma; + double ret_y_ma; + double ret_z_ma; + double ret_ratio; + double ret_start_p; + double ret_end_p; + + + private: + double x_major_axis; + double y_major_axis; + double z_major_axis; + double ratio; + double start_param; + double end_param; +}; + + + +class text : public entity { + public: + text( std::vector< dxfpair > info); + void display(); + char * ret_text(char *string); + double ret_txt_ht(); + double ret_txt_rot(); + + private: + char dxf_text[10000]; + double text_height; // dxf 40 + double text_rotation; //dxf 50 +}; + + +class insert : public entity { + public: + insert( std::vector< dxfpair > info); + void display(); + char* name(char* string); + double ret_x_sf; + double ret_y_sf; + double ret_z_sf; + double ret_rotation; + + private: + char block_name[10000]; + double x_scale_factor; + double y_scale_factor; + double z_scale_factor; + double rotation; +}; + + + + + + +class entities{ + // Well I said that I would only use STL containers internally, but I would have to use a dynamically linked list, and I haven't done for a long time soo STL is my crutch. + // I also think that there are others in my same boat that prefer stl containers because they are much easier to use + public: + entities(std::vector< std::vector< dxfpair > > sections); // Put the various entities into their respective vectors + void display_all(); + std::vector< polyline > ret_plines(); + std::vector< circle > ret_circles(); + std::vector< line > ret_lines(); + std::vector< text > ret_texts(); + std::vector< ellipse > ret_ellipses(); + std::vector< arc > ret_arcs(); + std::vector< lwpolyline > ret_lwplines(); + std::vector< insert > ret_inserts(); + // Overload the return function to depend on the layer + std::vector< polyline > ret_plines(char * layer); + std::vector< circle > ret_circles(char * layer); + std::vector< line > ret_lines(char * layer); + std::vector< text > ret_texts(char * layer); + std::vector< ellipse > ret_ellipses(char * layer); + std::vector< arc > ret_arcs(char * layer); + std::vector< lwpolyline > ret_lwplines(char * layer); + std::vector< insert > ret_inserts(char * layer); + + int plines_size(); + int circles_size(); + int lines_size(); + int texts_size(); + + + + private: + void add_polyline(polyline pline); + void add_circle(circle circ); + void add_line(line ln); + + std::vector< polyline > ents_polyline; + std::vector< arc > ents_arc; + std::vector< circle > ents_circle; + std::vector< line > ents_line; + std::vector< ellipse > ents_ellipse; + std::vector< text > ents_text; + std::vector< lwpolyline > ents_lwpolyline; + std::vector< insert > ents_insert; + + +}; + + + +#endif diff --git a/src/extension/dxf2svg/entities2elements.cpp b/src/extension/dxf2svg/entities2elements.cpp new file mode 100644 index 000000000..cf42dac32 --- /dev/null +++ b/src/extension/dxf2svg/entities2elements.cpp @@ -0,0 +1,681 @@ +/* + * Code for converting dxf entities to SVG elements + * There are multiple ways for converting different items + * If possible most DXF enetities will be converted to paths because that is the most flexable object + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + + +/* + +Matt Squires +SoC 2005 + +*/ + +#include"entities2elements.h" +#include"tables2svg_info.h" +#include +#include +// The names indicate the DXF entitiy first and the SVG element last + +// Common elements +char* to_arc(double bulge, double r, double start_ang, double end_ang, int precision,char* delim, char * units, double scaling, char *out){ + // This is used for arcs, polylines, and lwpolylines + char temp[50]; + + // Assume that we are adding to the input and not starting over + strcat(out," A "); + // For arcs there is only one radius + strcat(out,gcvt(scaling*r,precision,temp) ); + strcat(out,","); + strcat(out,gcvt(scaling*r,precision,temp) ); + + strcat(out," 0"); // For arc assume no x-axis rotation. That seems to apply to elipse elements only + // Determine if it is a large arc + if ( (end_ang > start_ang) && ( (end_ang - start_ang) > 180) ){ + //strcat(out," 1,0 "); // Large arc flag...Always use a zero sweep flag + strcat(out," 1, "); // Large arc flag...Always use a zero sweep flag + } + else{ + //strcat(out," 0,0 "); // Small arc flag...Always use a zero sweep flag + strcat(out," 0,"); + } + // This may be easier if I allow r to be plus and minus, but for now this works + if (bulge > 0){ + strcat(out,"0 "); + } + else{ + strcat(out,"1 "); + } +} + +// Build Coordinate +void coord(entity *ent, int precision,char* delim, char * units, double scaling, char *out); // Pairs of coords with units will be used so often build a function +void coord(entity *ent, int precision,char* delim, char * units, double scaling, char *out){ + // Pairs of coords with units will be used so often build a function build a dedicated function for returning such + + char temp[20]; + if (units != NULL) scaling = 1; // If units have been defined then ignore the scaling parameter + strcat(out, gcvt(scaling*ent->ret_x(),precision,temp) ); // There must be a better function for double to ascii conversion that is defined in most libraries + if (units != NULL) strcat(out, units); + strcat(out, delim); + strcat(out, gcvt(-scaling*ent->ret_y(),precision,temp) ); // Because SVG has a the Y-axis pointed down multiply by -1 + if (units != NULL) strcat(out, units); + strcat(out, " "); +} + +// DXF Polyline -> SVG +void pline2svg(polyline pline, int type, int precision, char * units, double scaling, tables plot_info, char *out); // General function for the conversion of a pline to a SVG element. Very similar functions just make accomidations for parts that may not be supported +void pline2svg(polyline pline, int type, int precision, char * units, double scaling, tables plot_info, char *out){ + // 0 is pline2path + // 1 is pline2pline + // 2 is pline2polygon + + + char delim[1]; + double mag_bulge = 0; + double prev_mag_bulge = 0; + + std::vector< vertex >::iterator vver_iter; + std::vector< vertex > points = pline.ret_points(); + + if (type < 1){ + // Put the first Move To at the first, everything else will be a lineto + strcpy(delim," "); + strcat(out, "M "); + coord( &points[0], precision, delim, units, scaling, out ); + prev_mag_bulge = sqrt(pow(points[0].ret_bulge(),2)); // Because the bulge value can be positive or negative calculate the magnitude + if ( prev_mag_bulge > pow(0.1,precision) ){ + to_arc(pline.bulge(0), pline.bulge_r(0), pline.bulge_start_angle(0), pline.bulge_end_angle(0), precision, delim, units, scaling, out); + } + for (int i = 1; i < points.size(); i++){ + if ( prev_mag_bulge < pow(0.1,precision) ){ + // If the previous point was a bulge then don't use a line to + strcat(out, "L "); + } + coord( &points[i], precision, delim, units, scaling, out ); + // If bulge > some precsion then add bulge + mag_bulge = sqrt(pow(points[i].ret_bulge(),2)); + if ( (mag_bulge > pow(0.1,precision)) && (i < (points.size() - 1) )){ + to_arc(pline.bulge(i), pline.bulge_r(i), pline.bulge_start_angle(i), pline.bulge_end_angle(i), precision, delim, units, scaling, out); + } + prev_mag_bulge = mag_bulge; + } + if ( pline.is_closed() ){ + strcat(out,"z"); + } + strcat(out,"\" "); + } + else{ + strcpy(delim,","); + for (int i = 0; i < points.size(); i++){ + coord( &points[i], precision, delim, NULL, scaling, out ); + // If bulge > some precsion then add bulge + } + // if the element is a SVG::pline and the DXF::pline is closed then simulate by adding an extra point + if ( (type == 1) && pline.is_closed() ){ + coord( &points[0], precision, delim, NULL, scaling, out ); + } + } + +} + +char* pline2path(polyline pline, char * units, double scaling, tables plot_info){ + // Convert a dxf polyline to a SVG path. This is the closest conversion of the DXF polyline to an SVG element + char *out_ptr; + char out[10000] = "ret_ltype_name(temp),ent_ptr->ret_layer_name(temp) ), precision, scaling, out); // Add the linetype information + + strcat(out," />"); + out_ptr = out; + return out_ptr; +} + +char* pline2pline(polyline pline, char * units, double scaling, tables plot_info){ + // Convert a dxf polyline to a SVG polyline. The conversion is not 1:1 because the SVG pline doesn't support closed objects or curves + entity *ent_ptr = &pline; + char temp[2000]; + int precision = 6; + + char out[10000] = "ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)); + pattern2dasharray(linfo, precision, scaling, out); // Add the linetype information + //strcpy(temp," "); + + strcat(out,"points=\""); + + //strcat(out,""); + char *out_ptr = out; + return out_ptr; +} +char* pline2polygon(polyline pline, char * units, double scaling, tables plot_info){ + // Convert a dxf polyline to a SVG polygon. The conversion is not 1:1 because the SVG polygone assumes a closed path. If the pline is not closed it will be forced closed + //return pline2svg(pline, 2, 6, units, double scaling,out); +} + + +// DXF LWPolyline -> SVG + + +// This could be a template with polyline and lwpolyline but right now it is not that important +void lwpline2svg(lwpolyline pline, int type, int precision, char * units, double scaling, tables plot_info, char *out); // General function for the conversion of a pline to a SVG element. Very similar functions just make accomidations for parts that may not be supported +void lwpline2svg(lwpolyline pline, int type, int precision, char * units, double scaling, tables plot_info, char *out){ + // 0 is pline2path + // 1 is pline2pline + // 2 is pline2polygon + + + char delim[1]; + double mag_bulge = 0; + double prev_mag_bulge = 0; + + std::vector< vertex >::iterator vver_iter; + std::vector< vertex > points = pline.ret_points(); + + if (type < 1){ + // Put the first Move To at the first, everything else will be a lineto + strcpy(delim," "); + strcat(out, "M "); + coord( &points[0], precision, delim, NULL, scaling, out ); + prev_mag_bulge = sqrt(pow(points[0].ret_bulge(),2)); // Because the bulge value can be positive or negative calculate the magnitude + if ( prev_mag_bulge > pow(0.1,precision) ){ + to_arc(pline.bulge(0),pline.bulge_r(0), pline.bulge_start_angle(0), pline.bulge_end_angle(0), precision, delim, NULL, scaling, out); + } + + for (int i = 1; i < points.size(); i++){ + if ( prev_mag_bulge < pow(0.1,precision) ){ + // If the previous point was a bulge then don't use a line to + strcat(out, "L "); + } + coord( &points[i], precision, delim, NULL , scaling, out ); + // If bulge > some precsion then add bulge + mag_bulge = sqrt(pow(points[i].ret_bulge(),2)); + if ( ( mag_bulge > pow(0.1,precision) ) && (i < (points.size() - 1) )){ // Make sure the final point doesn't add a bulge on accident + to_arc(pline.bulge(i), pline.bulge_r(i), pline.bulge_start_angle(i), pline.bulge_end_angle(i), precision, delim, units, scaling, out); + } + prev_mag_bulge = mag_bulge; + } + if ( pline.is_closed() ){ + strcat(out,"z"); + } + strcat(out,"\" "); + } + else{ + strcpy(delim,","); + for (int i = 0; i < points.size(); i++){ + coord( &points[i], precision, delim, units, scaling, out ); + // If bulge > some precsion then add bulge + } + // if the element is a SVG::pline and the DXF::pline is closed then simulate by adding an extra point + if ( (type == 1) && pline.is_closed() ){ + coord( &points[0], precision, delim, units, scaling, out ); + } + } + +} + +char* lwpline2path(lwpolyline pline, char * units, double scaling, tables plot_info){ + // Convert a dxf polyline to a SVG path. This is the closest conversion of the DXF polyline to an SVG element + char *out_ptr; + char out[10000] = "ret_ltype_name(temp),ent_ptr->ret_layer_name(temp) ), precision, scaling, out); // Add the linetype information + + strcat(out," />"); + out_ptr = out; + return out_ptr; +} + +// DXF ARC -> SVG +char* arc2path(arc a, int precision, char * units, double scaling, tables plot_info, char *out){ + // So far this appears to be the only way to convert arcs into something recognized by SVG + char *out_ptr; + char temp[20]; + entity *ent_ptr = &a; + + strcpy(out,"ret_x()+a.ret_radius()*cos( a.ret_srt_ang()*3.14159/180 )),precision,temp) ); + strcat(out," "); + strcat(out,gcvt(-1*scaling*(ent_ptr->ret_y()+a.ret_radius()*sin( a.ret_srt_ang()*3.14159/180 )),precision,temp) ); + strcat(out," A "); + // For arcs there is only one radius + strcat(out,gcvt(scaling*a.ret_radius(),precision,temp) ); + strcat(out,","); + strcat(out,gcvt(scaling*a.ret_radius(),precision,temp) ); + + strcat(out," 0"); // For arc assume no x-axis rotation. That seems to apply to elipse elements only + // Determine if it is a large arc + if ( (a.ret_end_ang() > a.ret_srt_ang()) && ( (a.ret_end_ang() - a.ret_srt_ang()) > 180) ){ + strcat(out," 1,0 "); // Large arc flag...Always use a zero sweep flag + } + else{ + strcat(out," 0,0 "); // Small arc flag...Always use a zero sweep flag + } + + //The final point + strcat(out,gcvt(scaling*(ent_ptr->ret_x()+a.ret_radius()*cos( a.ret_end_ang()*3.14159/180 )),precision,temp) ); + strcat(out,","); + strcat(out,gcvt(-1*scaling*(ent_ptr->ret_y()+a.ret_radius()*sin( a.ret_end_ang()*3.14159/180 )),precision,temp) ); + strcat(out,"\" fill=\"none\" stroke=\"black\" stroke-width=\"1\" "); + ltype linfo = plot_info.ret_ltype(ent_ptr->ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)); + pattern2dasharray(linfo, precision, scaling, out); // Add the linetype information + strcat(out, " />"); + + + out_ptr = out; + return out_ptr; + +} + + +// DXF Circle -> SVG +char* circle2circle(circle circ, int precision, char * units, double scaling, tables plot_info, char *out){ + // Direct conversion of DXF circle to SVG circle + char *out_ptr; + char temp[1000]="\" cy=\""; + entity *ent_ptr = ˆ + strcpy(out,"ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)); + //plot_info.ret_ltype(ent_ptr->ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)); + pattern2dasharray(linfo, precision, scaling, out); // Add the linetype information + //pattern2dasharray(plot_info.ret_ltype(ent_ptr->ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)), precision, scaling, out); // Add the linetype information + strcat(out, " />"); + out_ptr = out; + return out_ptr; +} + +char* circle2path(circle circ, int precision, char * units, double scaling, tables plot_info, char *out){ + // Conversion of DXF circle to SVG circle assuming the path will represent the circle + + char *out_ptr; + char temp[20]=","; + entity *ent_ptr = ˆ + + strcpy(out,"ret_x()-circ.ret_radius(),precision,temp) ); + strcat(out," "); + strcat(out,gcvt(ent_ptr->ret_y(),precision,temp) ); + + strcat(out," a"); + strcat(out,gcvt(circ.ret_radius(),precision,temp) ); + strcat(out,","); + strcat(out,gcvt(circ.ret_radius(),precision,temp) ); + strcat(out,"0 0,0 0,0\" fill=\"none\" stroke=\"black\" stroke-width=\"1\""); + + out_ptr = out; + return out_ptr; +} + + +// DXF Line -> SVG +char* line2line(line ln, int precision, char * units, double scaling, tables plot_info, char *out){ + // Directly convert DXF to SVG because it works + char *out_ptr; + char temp[20]; + entity *ent_ptr = &ln; + + strcpy(out,"ret_x(),precision,temp) ); + strcat(out,units); + strcat(out,"\" y1=\""); + strcat(out,gcvt(-1*ent_ptr->ret_y(),precision,temp) ); // Put in an extra minus because of the way SVG has defined the axis + strcat(out,units); + + strcat(out,"\" x2=\""); + strcat(out,gcvt(ln.ret_xf(),precision,temp) ); + strcat(out,units); + strcat(out,"\" y2=\""); + strcat(out,gcvt(-1*ln.ret_yf(),precision,temp) ); // Put in an extra minus because of the way SVG has defined the axis + strcat(out,units); + strcat(out,"\" stroke-width=\"1\" stroke=\"black\" "); + ltype linfo = plot_info.ret_ltype(ent_ptr->ret_ltype_name(temp), ent_ptr->ret_layer_name(temp)); + pattern2dasharray(linfo, precision, scaling, out); // Add the linetype information + strcat(out, " />"); + + out_ptr = out; + return out_ptr; +} + + +char* line2path(line ln, int precision, char * units, double scaling, tables plot_info, char *out){ + // Convert DXF line to SVG path + + char *out_ptr; + char temp[20]; + entity *ent_ptr = &ln; + + strcpy(out,"ret_x(),precision,temp) ); + strcat(out," "); + strcat(out,gcvt(scaling*ent_ptr->ret_y(),precision,temp) ); + + strcat(out," L"); + strcat(out,gcvt(scaling*ln.ret_xf(),precision,temp) ); + strcat(out," "); + strcat(out,gcvt(scaling*ln.ret_yf(),precision,temp) ); + strcat(out,"\" fill=\"none\" stroke=\"black\" stroke-width=\"1\" /"); + + out_ptr = out; + return out_ptr; +} + +// DXF Text -> SVG +char* text2text(text txt, int precision, char * units, double scaling, tables plot_info, char *out){ + // Directly convert DXF to SVG because it works + char *out_ptr; + char temp[10000]; + entity *ent_ptr = &txt; + + // If the text is rotated use the transform matrix + + if ( txt.ret_txt_rot() > precision ){ + double ca = cos(0.017453*txt.ret_txt_rot()); // ca = cosine(a) + double sa = sin(-0.017453*txt.ret_txt_rot()); // sa = sine(a) + double tx = ent_ptr->ret_x()*scaling; + double ty = -ent_ptr->ret_y()*scaling; + // Apply a translation to the orgin, then a rotation, then a translation back to the original position + double a = ca; + double b = sa; + double c = -sa; + double d = ca; + double e = -1*(tx*ca-ty*sa-tx); + double f = -1*(tx*sa+ty*ca-ty); + + strcpy(out, "\nret_x(),precision,temp) ); + strcat(out,units); + strcat(out,"\" y=\"-"); // Put in an extra minus because of the way SVG has defined the axis + strcat(out,gcvt(ent_ptr->ret_y(),precision,temp) ); + strcat(out,units); + */ + strcat(out,gcvt(ent_ptr->ret_x()*scaling,precision,temp) ); + //strcat(out,units); + strcat(out,"\" y=\"-"); // Put in an extra minus because of the way SVG has defined the axis + strcat(out,gcvt(ent_ptr->ret_y()*scaling,precision,temp) ); + //strcat(out,units); + strcat(out,"\" font-family=\"Verdana\" font-size=\""); + strcat(out,gcvt(scaling*txt.ret_txt_ht(),precision,temp) ); + strcat(out,"\" Fill=\"black\""); + + strcat(out," >"); + // Now put in the text + strcat(out,txt.ret_text(temp)); + + // Now close the text element + strcat(out,""); + // If the text was rotated finish off the tranform group + if ( txt.ret_txt_rot() > precision ){ + strcat(out,""); + } + + out_ptr = out; + return out_ptr; +} + + + +// DXF Insert -> SVG +char* insert2group(insert in, int precision, char * units, double scaling, tables plot_info, blocks blks, char *out){ + char *out_ptr; + char tmp_char[100000]; + + // get the block using the name from the insert information + block blk = blks.ret_block(in.name(tmp_char)); + + entity *ent_ptr = ∈ + entities *ents_ptr = &blk; + // For now just translations MBS 22 Aug 05 + strcpy(out, "ret_x(),precision,tmp_char) ); + strcat(out,","); + strcat(out,gcvt(-scaling*ent_ptr->ret_y(),precision,tmp_char) ); + strcat(out,")\" >\n"); + + + // Now convert the entities in the block + std::vector< polyline > plines = ents_ptr->ret_plines(); + std::vector< lwpolyline > lwplines = ents_ptr->ret_lwplines(); + std::vector< arc > arcs = ents_ptr->ret_arcs(); + std::vector< circle > circs = ents_ptr->ret_circles(); + std::vector< line > lns = ents_ptr->ret_lines(); + std::vector< text > txts = ents_ptr->ret_texts(); + + + + for(int i = 0; i < plines.size();i++){ + strcat( out,pline2pline(plines[i], units, scaling, plot_info ) ); + strcat( out, "\n" ); + } + for(int i = 0; i < lwplines.size();i++){ + strcat( out,lwpline2path(lwplines[i], units, scaling, plot_info ) ); + strcat( out, "\n" ); + } + for(int i = 0; i < arcs.size();i++){ + strcat( out, arc2path(arcs[i], 6,units, scaling, plot_info, tmp_char ) ); + strcat( out, "\n" ); + } + for(int i = 0; i < circs.size();i++){ + strcat( out, circle2circle(circs[i], 6, units, scaling, plot_info, tmp_char) ); + strcat( out, "\n" ); + } + for(int i = 0; i < lns.size();i++){ + strcat( out, line2line(lns[i], 6, units, scaling, plot_info, tmp_char) ); + strcat( out, "\n" ); + } + for(int i = 0; i < txts.size();i++){ + strcat( out, text2text(txts[i], 6, units, scaling, plot_info, tmp_char) ); + strcat( out, "\n" ); + } + // End the group + strcat(out,""); + + out_ptr = out; + return out_ptr; +} + + + +char* write_by_layer(int output_type, entities &ents, tables &tbls, blocks &blks, double scaling, char * units, char * layer, char * out){ + // output_type = 0 is to std:out + // output_type = 1 is to the input filename but with .dxf on the end + + // For now everything will go to stdout later may directed to other places + + // Get the various file informations as dependent on the layer type + std::vector< polyline > plines = ents.ret_plines(layer); + std::vector< lwpolyline > lwplines = ents.ret_lwplines(layer); + std::vector< arc > arcs = ents.ret_arcs(layer); + std::vector< circle > circs = ents.ret_circles(layer); + std::vector< line > lns = ents.ret_lines(layer); + std::vector< text > txts = ents.ret_texts(layer); + std::vector< insert > ins = ents.ret_inserts(layer); + + // It would be better to redirect stdout to different places. That would make the code cleaner but I don't think it will work better + char tmp_char[100000]; + for(int i = 0; i < plines.size();i++){ + if (output_type == 0){ + std::cout << "\t" << pline2path(plines[i], NULL, scaling, tbls ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << pline2path(plines[i], NULL, scaling, tbls ) << std::endl; + } + } + for(int i = 0; i < lwplines.size();i++){ + if (output_type == 0){ + std::cout << "\t" << lwpline2path(lwplines[i], units, scaling, tbls ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << lwpline2path(lwplines[i], units, scaling, tbls ) << std::endl; + } + } + for(int i = 0; i < arcs.size();i++){ + if (output_type == 0){ + std::cout << "\t" << arc2path(arcs[i], 6,units, scaling, tbls, tmp_char ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << arc2path(arcs[i], 6,units, scaling, tbls, tmp_char ) << std::endl; + } + } + for(int i = 0; i < circs.size();i++){ + if (output_type == 0){ + std::cout << "\t" << circle2circle(circs[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << circle2circle(circs[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < lns.size();i++){ + if (output_type == 0){ + std::cout << "\t" << line2line(lns[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << line2line(lns[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < txts.size();i++){ + if (output_type == 0){ + std::cout << "\t" << text2text(txts[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << text2text(txts[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < ins.size();i++){ + if (output_type == 0){ + std::cout << "\t" << insert2group(ins[i], 6, units, scaling, tbls, blks, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << insert2group(ins[i], 6, units, scaling, tbls, blks, tmp_char) << std::endl; + } + } +} + + + +char* write_all(int output_type, entities &ents, tables &tbls, blocks &blks, double scaling, char * units, char * out){ + // output_type = 0 is to std:out + // output_type = 1 is to the input filename but with .dxf on the end + + // For now everything will go to stdout later may directed to other places + + // Get the various file informations as dependent on the layer type + std::vector< polyline > plines = ents.ret_plines(); + std::vector< lwpolyline > lwplines = ents.ret_lwplines(); + std::vector< arc > arcs = ents.ret_arcs(); + std::vector< circle > circs = ents.ret_circles(); + std::vector< line > lns = ents.ret_lines(); + std::vector< text > txts = ents.ret_texts(); + std::vector< insert > ins = ents.ret_inserts(); + + // It would be better to redirect stdout to different places. That would make the code cleaner but I don't think it will work better + char tmp_char[100000]; + for(int i = 0; i < plines.size();i++){ + if (output_type == 0){ + std::cout << "\t" << pline2path(plines[i], NULL, scaling, tbls ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << pline2path(plines[i], NULL, scaling, tbls ) << std::endl; + } + } + for(int i = 0; i < lwplines.size();i++){ + if (output_type == 0){ + std::cout << "\t" << lwpline2path(lwplines[i], units, scaling, tbls ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << lwpline2path(lwplines[i], units, scaling, tbls ) << std::endl; + } + } + for(int i = 0; i < arcs.size();i++){ + if (output_type == 0){ + std::cout << "\t" << arc2path(arcs[i], 6,units, scaling, tbls, tmp_char ) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << arc2path(arcs[i], 6,units, scaling, tbls, tmp_char ) << std::endl; + } + } + for(int i = 0; i < circs.size();i++){ + if (output_type == 0){ + std::cout << "\t" << circle2circle(circs[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << circle2circle(circs[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < lns.size();i++){ + if (output_type == 0){ + std::cout << "\t" << line2line(lns[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << line2line(lns[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < txts.size();i++){ + if (output_type == 0){ + std::cout << "\t" << text2text(txts[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << text2text(txts[i], 6, units, scaling, tbls, tmp_char) << std::endl; + } + } + for(int i = 0; i < ins.size();i++){ + if (output_type == 0){ + std::cout << "\t" << insert2group(ins[i], 6, units, scaling, tbls, blks, tmp_char) << std::endl; + } + else if (output_type == 1){ + std::cout << "\t" << insert2group(ins[i], 6, units, scaling, tbls, blks, tmp_char) << std::endl; + } + } +} + + + diff --git a/src/extension/dxf2svg/entities2elements.h b/src/extension/dxf2svg/entities2elements.h new file mode 100644 index 000000000..cceefcf15 --- /dev/null +++ b/src/extension/dxf2svg/entities2elements.h @@ -0,0 +1,53 @@ +/* Method for converting dxf entities to SVG elements +There are multiple ways for converting different items + +If possible most DXF enetities will be converted to paths because that is the most flexable object + +Matt Squires +SoC 2005 + +*/ + +#include"entities.h" +#include"tables.h" +#include"blocks.h" + +// The names indicate the DXF entitiy first and the SVG element last + +// Common elements +char* to_arc(double bulge, double r, double start_ang, double end_ang, int precision,char* delim, char * units, double scaling, char *out); // This is used for arcs, polylines, and lwpolylines + +// DXF Polyline -> SVG + +char* pline2path(polyline pline, char * units, double scaling, tables plot_info); // Convert a dxf polyline to a SVG path. This is the closest conversion of the DXF polyline to an SVG element +char* pline2pline(polyline pline, char * units, double scaling, tables plot_info); // Convert a dxf polyline to a SVG polyline. The conversion is not 1:1 because the SVG pline doesn't support closed objects or curves +char* pline2polygon(polyline pline, char * units, double scaling, tables plot_info); // Convert a dxf polyline to a SVG polygon. The conversion is not 1:1 because the SVG polygone assumes a closed path. If the pline is not closed it will be forced closed + +// DXF LWPolyline -> SVG +char* lwpline2path(lwpolyline pline, char * units, double scaling, tables plot_info); // Convert a dxf polyline to a SVG path. This is the closest conversion of the DXF polyline to an SVG element + +// DXF ARC -> SVG +char* arc2path(arc a, int precision, char * units, double scaling, tables plot_info, char *out); // So far this appears to be the only way to convert arcs into something recognized by SVG + + +// DXF Circle -> SVG +char* circle2circle(circle circ, int precision, char * units, double scaling, tables plot_info, char *out); // Direct conversion of DXF circle to SVG circle +char* circle2path(circle circ, int precision, char * units, double scaling, tables plot_info, char *out); // Conversion of DXF circle to SVG circle assuming the path will represent the circle + + +// DXF Line -> SVG +char* line2line(line ln, int precision, char * units, double scaling, tables plot_info, char *out); // Directly convert DXF to SVG because it works +char* line2path(line ln, int precision, char * units, double scaling, tables plot_info, char *out); // Convert DXF line to SVG path + +// DXF Text -> SVG +char* text2text(text txt, int precision, char * units, double scaling, tables plot_info, char *out); // Directly convert DXF to SVF assuming VERANDA font + +// DXF Insert -> SVG +char* insert2group(insert in, int precision, char * units, double scaling, tables plot_info, blocks blks, char *out); + +// Write all of the possible enties with a variety of options + +char* write_by_layer(int output_type, entities &ents, tables &tbls, blocks &blks, double scaling, char * units, char * layer, char * out); // Write out the entities by layer designation +char* write_all(int output_type, entities &ents, tables &tbls, blocks &blks, double scaling, char * units, char * out); + + diff --git a/src/extension/dxf2svg/read_dxf.cpp b/src/extension/dxf2svg/read_dxf.cpp new file mode 100644 index 000000000..8a6a6d6ac --- /dev/null +++ b/src/extension/dxf2svg/read_dxf.cpp @@ -0,0 +1,273 @@ +/* + * For reading and slight parsing of dxf files + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + + +#include +#include +#include"read_dxf.h" + +#include +using namespace std; + + +int MAX_STR_LN = 10000; + +int section(char* value){ + if ( strncmp(value,"HEADER",6) == 0 ) return 0; + if ( strncmp(value,"CLASSES",7) == 0 ) return 1; + if ( strncmp(value,"TABLES",6) == 0 ) return 2; + if ( strncmp(value,"BLOCKS",6) == 0 ) return 3; + if ( strncmp(value,"ENTITIES",8) == 0 ) return 4; + if ( strncmp(value,"OBJECTS",7) == 0 ) return 5; + if ( strncmp(value,"THUMBNAILIMAGE",14) == 0 ) return 6; +} + + +dxfpair::dxfpair(int gcode, char val[10000]){ + group_code = gcode; + // Dynamically save the strings, otherwise the memory uses is bad + + for (int i = 0; i < strlen(val); i++){ + value.push_back(val[i]); + } +} + + +dxfpair::~dxfpair(){ + //delete [] value; +} + +char * dxfpair::value_char(char *string){ + int size = value.size(); + while( ( size > 0 ) && int(value[size-1]) < 33){ + // Strip off any control characters and spaces off the end of the string + size--; + } + for(int i = 0; i < size; i++){ + string[i] = value[i]; + } + string[size]=0; +} + +std::vector< std::vector< dxfpair > > dxf_get_sections(char* filename){ + // In the dxf format information is paired into group codes that indicate the information that follows on the next line. The information on the next line is called the value + + int n =0; + + int group_code; + char value[MAX_STR_LN]; + + int section_num; + + + + + std::vector< std::vector< dxfpair > > out; + + std::vector< dxfpair > header; + std::vector< dxfpair > classes; + std::vector< dxfpair > tables; + std::vector< dxfpair > blocks; + std::vector< dxfpair > entities; + std::vector< dxfpair > objects; + std::vector< dxfpair > thumbnailimage; + + header.clear(); + classes.clear(); + tables.clear(); + blocks.clear(); + entities.clear(); + objects.clear(); + thumbnailimage.clear(); + + + // Open dxf file for reading + std::ifstream file(filename); + + if (!file.is_open()){ + exit (1); // Change this to an exception + } + + // Find the first SECTION header + + while ( (!file.eof()) ){ + n++; + + // get the first group code and value + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + + do{ + + // TO DO set all the chars to be caps for later comparison + + // Find the SECTION codes + if ( (group_code == 0 ) && ( strncmp(value,"SECTION",7) == 0 ) ){ + // Directly after a section value is the type of section ( e.g. HEADER, TABLES ) + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + section_num = section( value ); + if ( group_code == 2 ){ + // Make sure the the group code is 2 for the SECTION name + // This is a big block of mostly repetitive code, it will result in larger code, but would be faster than putting the switch in another while loop. If I still live in a time when file size mattered alot I would change it + //std::cout << "section_num = " << section_num << std::endl; + switch ( section_num ){ + case 0: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + header.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + break; + case 1: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + if ( (group_code != 0) || (strncmp(value,"ENDSEC",6) != 0) ){ + // Some dxf files have blank sections. These are not handled by the do/while loop so break about if needed + do{ + classes.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + } + break; + case 2: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + tables.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); + break; + case 3: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + blocks.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + break; + case 4: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + entities.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + break; + case 5: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + objects.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + break; + case 6: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + do{ + thumbnailimage.push_back( dxfpair( group_code, value ) ); + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + }while( ( (group_code != 0) || strncmp(value,"ENDSEC",6) != 0 ) && (!file.eof()) ); // I put in the (group_code != 0) in the hope that it will be a faster bool compare than the string compare. Test this later + break; + default: + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + } + } + } + file.getline(value,MAX_STR_LN); + group_code = atoi(value); + file.getline(value,MAX_STR_LN); + + n++; + }while( ( strncmp(value,"EOF",3) != 0 ) && (!file.eof()) ); + } + + out.push_back(header); + out.push_back(classes); + out.push_back(tables); + out.push_back(blocks); + out.push_back(entities); + out.push_back(objects); + out.push_back(thumbnailimage); + + return out; +} + + +std::vector< std::vector< dxfpair > > separate_parts( std::vector< dxfpair > section){ + //std::cout << "1" << std::endl; + //std::cout << "section.size() = " << section.size() << std::endl; + // Find where the major sections are and break into smaller parts + // Major section is defined as anything beween group_code 0 to 0 + std::vector< dxfpair > inner; + std::vector< std::vector< dxfpair > > outer; + //std::cout << "2" << std::endl; + for (int i = 0; i < section.size(); i++){ + //std::cout << "i = " << i << std::endl; + //std::cout << "section[i].value.size() = " << section[i].value.size() << std::endl; + + // Make sure no control codes like LF or CR are making it past this section + if ( (section[i].value.size() > 0) && int(section[i].value.back()) < 32 ){ + section[i].value.pop_back(); + } + //for(int j = 0;j < section[i].value.size();j++ ) std::cout << section[i].value[j]; + //std::cout << std::endl; + + inner.push_back( section[i] ); + + // If the next group code is 0 then push the previously found info on outer and start looking for data again + if (section[i+1].group_code == 0){ + //std::cout << "inner.push_back" << std::endl; + outer.push_back( inner ); + inner.clear(); + } + } + // Because putting the data on outer depends on find a GC=0 the last bit of data may be left behind so it inner has data in it put it on outer + if ( inner.size() > 0 ){ + outer.push_back( inner ); + inner.clear(); + } + //std::cout << "3" << std::endl; + if (section.back().group_code == 0){ + //outer.push_back( inner ); // Put the last part on if there is information, but I don't think it needs to. + } + + return outer; +} + diff --git a/src/extension/dxf2svg/read_dxf.h b/src/extension/dxf2svg/read_dxf.h new file mode 100644 index 000000000..0708b6ce1 --- /dev/null +++ b/src/extension/dxf2svg/read_dxf.h @@ -0,0 +1,31 @@ +/* Header file for reading dxf information and basic parsing. Interprting information is found in other files +*/ + +#ifndef READ_DXF_H +#define READ_DXF_H + +#include + + + +class dxfpair{ + public: + dxfpair(int gcode, char val[10000]); + ~dxfpair(); + char * value_char(char *string); + + // Leave this data public + int group_code; + std::vector< char > value; +}; + + + + +int section(char* value); // Convert the section titles into integers + +std::vector< std::vector< dxfpair > > dxf_get_sections(char* filename); + +std::vector< std::vector< dxfpair > > separate_parts( std::vector< dxfpair > section ); // Find where the major sections are and break into smaller parts + +#endif diff --git a/src/extension/dxf2svg/tables.cpp b/src/extension/dxf2svg/tables.cpp new file mode 100644 index 000000000..fcaff33c0 --- /dev/null +++ b/src/extension/dxf2svg/tables.cpp @@ -0,0 +1,211 @@ +/* + * Code for the conversion of DXF information in the TABLES section + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + +#include"tables.h" +#include + + + + +int determine_table(char* value){ + // Common Elements as far as I am concerend + if ( strncmp(value,"LAYER",5) == 0 ) return 0; + if ( strncmp(value,"LTYPE",5) == 0 ) return 1; + if ( strncmp(value,"STYLE",5) == 0 ) return 2; + if ( strncmp(value,"UCS",3) == 0 ) return 3; + if ( strncmp(value,"VIEW",4) == 0 ) return 4; + if ( strncmp(value,"VPORT",4) == 0 ) return 5; + if ( strncmp(value,"APPID",5) == 0 ) return 6; + if ( strncmp(value,"BLOCK_RECORD",12) == 0 ) return 7; + else return -1; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TABLE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +int table::ret_maxN(){ + return max_number; +} + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// LAYER +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +layer::layer( std::vector< dxfpair > info){ + // Get the vertex information + + //basic_entity( info ); + //static char string[10000]; + char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 2: + info[i].value_char(layer_name); + break; + case 6: + info[i].value_char(ltype_name); + break; + + case 62: + info[i].value_char(string); + color_number = atoi(string); + //std::cout << "I found a color and its number = " << color_number << std::endl; + break; + case 290: + info[i].value_char(string); + plotting_flag = atoi(string); + break; + } + } +} + +void layer::display(){ + std::cout << "LAYER\n"; + //std::cout << "\tx = " << x << "\ty = " << y << "\tz = " << z << "\tbulge = " << bulge << std::flush; +} + +char* layer::name(char* string){ + return( strcpy(string,layer_name) ); +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// LTYPE +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +ltype::ltype( std::vector< dxfpair > info){ + // Get the linetype information + + //static char string[10000]; + char string[10000]; + for (int i = 0; i < info.size(); i++){ + switch( info[i].group_code ){ + case 2: + info[i].value_char(ltype_name); + break; + case 3: + info[i].value_char(descriptive_txt); + break; + case 73: + info[i].value_char(string); + num_elements = atoi(string); + break; + case 40: + info[i].value_char(string); + pattern_length = atof(string); + break; + case 49: + info[i].value_char(string); + pattern.push_back( atof(string) ); + break; + } + } +} + + + +char* ltype::name(char* string){ + return( strcpy(string,ltype_name) ); +} + + +std::vector< double > ltype::ret_pattern(){ + return pattern; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// tables +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + +tables::tables(std::vector< std::vector< dxfpair > > sections){ + // Read the main information about the entities section and then put it in the enetites class + int value; + char string[10000]; + + for(int i = 0; i < sections.size(); i++){ + //std::cout << "start" << std::endl; + sections[i][0].value_char(string); + value = determine_table(string); + //std::cout << "sections.size() = " << sections.size() << std::endl << "i = " << i << std::endl << "string = " << string << std::endl; + switch( value ){ + case 0: + // LAYER + //std::cout << "tables start layer " << std::endl; + tables_layer.push_back( layer( sections[i] ) ); + //std::cout << "tables end layer " << std::endl; + break; + + case 1: + // LTYPE + //std::cout << "tables start ltype " << std::endl; + tables_ltype.push_back( ltype( sections[i] ) ); + //std::cout << "tables end ltype " << std::endl; + break; + + //case 3: + // break; + + default: + break; + // Nothing here + } + + } + +} + + + + +ltype tables::ret_ltype(char ltype_name[10000], char layer_name[10000]){ + int string_len = 0; + char name[10000]; + char temp[10000]; + // The ltype information may be given in the entitity or in the layer information + // Assume that if there is a name defined in the linetype that it trumps any other layer information + if ( strlen(ltype_name) > 0 ) strcpy(name,ltype_name); + else strcpy(name,layer_name); + for (int i = 0; i < tables_ltype.size();i++){ + string_len = strlen(tables_ltype[i].name(temp)); + if (strncmp(tables_ltype[i].name(temp),name,string_len) == 0 ) return tables_ltype[i]; + } + return tables_ltype[0]; +} + + +layer tables::ret_layer(char layer_name[10000]){ + int string_len = 0; + char temp[10000]; + + for (int i = 0; i < tables_layer.size();i++){ + string_len = strlen(tables_layer[i].name(temp)); + if (strncmp(tables_layer[i].name(temp),layer_name,string_len) == 0 ) return tables_layer[i]; + } + return tables_layer[0]; +} + + +std::vector< layer > tables::ret_layers(){ + return tables_layer; +} diff --git a/src/extension/dxf2svg/tables.h b/src/extension/dxf2svg/tables.h new file mode 100644 index 000000000..f63122eaf --- /dev/null +++ b/src/extension/dxf2svg/tables.h @@ -0,0 +1,71 @@ +/* Code for the conversion of DXF information in the TABLES section +Matt Squires +SoC +2005*/ + +#ifndef DXF_TABLES_H +#define DXF_TABLES_H + +#include +#include"read_dxf.h" + +class table{ + public: + int ret_maxN(); + private: + int max_number; +}; + + +class layer : public table{ + public: + layer( std::vector< dxfpair > info); + void display(); + char* name(char* string); + private: + char layer_name[10000]; + char ltype_name[10000]; // The layer may also hold the ltype infomation + int color_number; + int plotting_flag; + +}; + + +class ltype : public table{ + public: + ltype( std::vector< dxfpair > info); + char* name(char* string); + std::vector< double > ret_pattern(); + private: + char ltype_name[10000]; + char descriptive_txt[10000]; + int num_elements; + double pattern_length; + std::vector< double > pattern; + +}; + + +class tables{ + // Well I said that I would only use STL containers internally, but I would have to use a dynamically linked list, and I haven't done for a long time soo STL is my crutch. + public: + tables(std::vector< std::vector< dxfpair > > sections); // Put the various entities into their respective vectors + void display_all(); + + ltype ret_ltype(char ltype_name[10000], char layer_name[10000]); + layer ret_layer(char layer_name[10000]); + + std::vector< layer > ret_layers(); + + + private: + //void add_dimstyle(polyline pline); + void add_layer(layer layr); + void add_ltype(ltype line_type); + + std::vector< layer > tables_layer; + std::vector< ltype > tables_ltype; +}; + + +#endif diff --git a/src/extension/dxf2svg/tables2svg_info.cpp b/src/extension/dxf2svg/tables2svg_info.cpp new file mode 100644 index 000000000..17bc47beb --- /dev/null +++ b/src/extension/dxf2svg/tables2svg_info.cpp @@ -0,0 +1,35 @@ +/* + * Convert DXF table information to a format that is recognized by SVG + * + * Author: + * Matt Squires + * + * Copyright (C) 2005 Matt Squires + * + * Released under GNU GPL and LGPL, read the file 'GPL.txt' and 'LGPL.txt' for details + */ + + +#include"tables2svg_info.h" +#include +#include + +char* pattern2dasharray(ltype info, int precision, double scaling, char* out){ + std::vector< double > pattern = info.ret_pattern(); + char temp[50]; + char *out_ptr; + + + if (pattern.size() > 0){ + strcat(out," stroke-dasharray=\""); + for(int i = 0; i < pattern.size()-1;i++){ + strcat(out,gcvt(scaling*sqrt(pow(pattern[i],2)),precision,temp) ); + strcat(out,","); + } + strcat( out,gcvt(scaling*sqrt(pow(pattern[pattern.size()-1],2)),precision,temp) ); + strcat(out,"\" "); + } + + out_ptr = out; + return out_ptr; +} diff --git a/src/extension/dxf2svg/tables2svg_info.h b/src/extension/dxf2svg/tables2svg_info.h new file mode 100644 index 000000000..2c99f034e --- /dev/null +++ b/src/extension/dxf2svg/tables2svg_info.h @@ -0,0 +1,9 @@ +/* Convert DXF table information to a format that is recognized by SVG +Matt Squires +SOC 2005 +*/ + + +#include"tables.h" + +char* pattern2dasharray(ltype info, int precision, double scaling, char* temp); diff --git a/src/extension/dxf2svg/test_dxf.cpp b/src/extension/dxf2svg/test_dxf.cpp new file mode 100644 index 000000000..62969ca6e --- /dev/null +++ b/src/extension/dxf2svg/test_dxf.cpp @@ -0,0 +1,99 @@ +/* test read_dxf */ +#include"read_dxf.h" +#include"entities.h" +#include"blocks.h" +#include"tables.h" +#include +#include"entities2elements.h" + +using namespace std; + +int main(){ + std::vector< std::vector< dxfpair > > output, entities_info, blocks_info, tables_info; + //output = dxf_get_sections("circ_sqr.dxf"); + //output = dxf_get_sections("Labjack.dxf"); + //output = dxf_get_sections("mini_post.dxf"); + //output = dxf_get_sections("../8599-E0W.dxf"); + //output = dxf_get_sections("../bulged_lwpoly.dxf"); + //output = dxf_get_sections("../aspheric_lens.dxf"); + output = dxf_get_sections("../layers_colors.dxf"); + /*std::vector< dxfpair > header; + std::vector< dxfpair > classes; + std::vector< dxfpair > tables; + std::vector< dxfpair > blocks; + std::vector< dxfpair > entities; + std::vector< dxfpair > objects; + std::vector< dxfpair > thumbnailimage; + */ + + + //dxf_get_sections("circ_sqr.dxf",header,classes,tables,blocks,entities,objects,thumbnailimage); + //dxf_get_sections("mini_post.dxf",header,classes,tables,blocks,entities,objects,thumbnailimage); + + //cout << entities.size() << endl; + for (int i=0;i pl_tmp= ents.ret_plines(); + //cout << "pline2pline\n" << pline2pline(pl_tmp[0], units ) << endl; + //cout << "pline2pline\n" << pline2pline(ents.ret_plines()[0], units ) << endl; + //cout << "pline2path\n" << pline2path(ents.ret_plines()[0], units ); + + char tmp_char[10000]; + + cout << "\nCircle conversion\n"; + //cout << "circle2circle\n" << circle2circle(ents.ret_circles()[0], 3, units, tmp_char) << endl; + + return 0; +} diff --git a/src/extension/effect.cpp b/src/extension/effect.cpp new file mode 100644 index 000000000..5a43cd74c --- /dev/null +++ b/src/extension/effect.cpp @@ -0,0 +1,200 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "inkscape-private.h" +#include "helper/action.h" +#include "document.h" +#include "prefdialog.h" +#include "implementation/implementation.h" +#include "effect.h" +#include "ui/view/view.h" + +/* Inkscape::Extension::Effect */ + +namespace Inkscape { +namespace Extension { + +Effect * Effect::_last_effect = NULL; +Inkscape::XML::Node * Effect::_effects_list = NULL; + +Effect::Effect (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) + : Extension(in_repr, in_imp), _verb(get_id(), get_name(), NULL, NULL, this), _menu_node(NULL) +{ + if (_effects_list == NULL) + find_effects_list(inkscape_get_menus(INKSCAPE)); + + if (_effects_list != NULL) { + _menu_node = sp_repr_new("verb"); + _menu_node->setAttribute("verb-id", this->get_id(), false); + _effects_list->parent()->appendChild(_menu_node); + Inkscape::GC::release(_menu_node); + } /*else { + printf("Effect %s not added\n", get_name()); + }*/ + + return; +} + +Effect::~Effect (void) +{ + if (get_last_effect() == this) + set_last_effect(NULL); + return; +} + +bool +Effect::check (void) +{ + if (!Extension::check()) { + /** \todo Check to see if parent has this as its only child, + if so, delete it too */ + if (_menu_node != NULL) + sp_repr_unparent(_menu_node); + _menu_node = NULL; + return false; + } + return true; +} + +bool +Effect::prefs (Inkscape::UI::View::View * doc) +{ + if (!loaded()) + set_state(Extension::STATE_LOADED); + if (!loaded()) return false; + + Gtk::Widget * controls; + controls = imp->prefs_effect(this, doc); + if (controls == NULL) { + // std::cout << "No preferences for Effect" << std::endl; + return true; + } + + PrefDialog * dialog = new PrefDialog(this->get_name(), controls); + int response = dialog->run(); + dialog->hide(); + + delete dialog; + + if (response == Gtk::RESPONSE_OK) return true; + + return false; +} + +/** + \brief The function that 'does' the effect itself + \param doc The Inkscape::UI::View::View to do the effect on + + This function first insures that the extension is loaded, and if not, + loads it. It then calls the implemention to do the actual work. It + also resets the last effect pointer to be this effect. Finally, it + executes a \c sp_document_done to commit the changes to the undo + stack. +*/ +void +Effect::effect (Inkscape::UI::View::View * doc) +{ + if (!loaded()) + set_state(Extension::STATE_LOADED); + if (!loaded()) return; + + set_last_effect(this); + imp->effect(this, doc); + + sp_document_done(doc->doc()); + + return; +} + +void +Effect::set_last_effect (Effect * in_effect) +{ + if (in_effect == NULL) { + Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, false); + Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, false); + } else if (_last_effect == NULL) { + Inkscape::Verb::get(SP_VERB_EFFECT_LAST)->sensitive(NULL, true); + Inkscape::Verb::get(SP_VERB_EFFECT_LAST_PREF)->sensitive(NULL, true); + } + + _last_effect = in_effect; + return; +} + +#define EFFECTS_LIST "effects-list" + +bool +Effect::find_effects_list (Inkscape::XML::Node * menustruct) +{ + if (menustruct == NULL) return false; + for (Inkscape::XML::Node * child = menustruct; + child != NULL; + child = child->next()) { + if (!strcmp(child->name(), EFFECTS_LIST)) { + _effects_list = menustruct; + return true; + } + Inkscape::XML::Node * firstchild = child->firstChild(); + if (firstchild != NULL) + if (find_effects_list(firstchild)) + return true; + } + return false; +} + +/** \brief Create an action for a \c EffectVerb + \param view Which view the action should be created for + \return The built action. + + Calls \c make_action_helper with the \c vector. +*/ +SPAction * +Effect::EffectVerb::make_action (Inkscape::UI::View::View * view) +{ + return make_action_helper(view, &vector, static_cast(_effect)); +} + +/** \brief Decode the verb code and take appropriate action */ +void +Effect::EffectVerb::perform (SPAction *action, void * data, void *pdata) +{ + Inkscape::UI::View::View * current_view = sp_action_get_view(action); +// SPDocument * current_document = current_view->doc; + Effect * effect = reinterpret_cast(data); + + if (effect == NULL) return; + if (current_view == NULL) return; + + // std::cout << "Executing: " << effect->get_name() << std::endl; + if (effect->prefs(current_view)) + effect->effect(current_view); + + return; +} + +/** + * Action vector to define functions called if a staticly defined file verb + * is called. + */ +SPActionEventVector Effect::EffectVerb::vector = + {{NULL},Effect::EffectVerb::perform, NULL, NULL, NULL}; + + +} } /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/effect.h b/src/extension/effect.h new file mode 100644 index 000000000..35d0d0a00 --- /dev/null +++ b/src/extension/effect.h @@ -0,0 +1,91 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#ifndef INKSCAPE_EXTENSION_EFFECT_H__ +#define INKSCAPE_EXTENSION_EFFECT_H__ + +#include + +#include +#include +#include "verbs.h" + +#include "extension.h" + +struct SPDocument; + +namespace Inkscape { +namespace UI { +namespace View { +typedef View View; +}; +}; + +namespace Extension { + +class Effect : public Extension { + static Effect * _last_effect; + static Inkscape::XML::Node * _effects_list; + bool find_effects_list (Inkscape::XML::Node * menustruct); + + class EffectVerb : public Inkscape::Verb { + private: + static void perform (SPAction * action, void * mydata, void * otherdata); + static SPActionEventVector vector; + + Effect * _effect; + protected: + virtual SPAction * make_action (Inkscape::UI::View::View * view); + public: + /** \brief Use the Verb initializer with the same parameters. */ + EffectVerb(gchar const * id, + gchar const * name, + gchar const * tip, + gchar const * image, + Effect * effect) : + Verb(id, _(name), _(tip), image), _effect(effect) { + /* No clue why, but this is required */ + this->set_default_sensitive(true); + } + }; + EffectVerb _verb; + Inkscape::XML::Node * _menu_node; +public: + Effect (Inkscape::XML::Node * in_repr, + Implementation::Implementation * in_imp); + virtual ~Effect (void); + virtual bool check (void); + bool prefs (Inkscape::UI::View::View * doc); + void effect (Inkscape::UI::View::View * doc); + Inkscape::Verb * get_verb (void) { return &_verb; }; + + static Effect * get_last_effect (void) { return _last_effect; }; + static void set_last_effect (Effect * in_effect); + + static void place_menus (void); + void place_menu (Inkscape::XML::Node * menus); + +private: + static gchar * remove_ (gchar * instr); +}; + +} } /* namespace Inkscape, Extension */ +#endif /* INKSCAPE_EXTENSION_EFFECT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/error-file.cpp b/src/extension/error-file.cpp new file mode 100644 index 000000000..4f6987930 --- /dev/null +++ b/src/extension/error-file.cpp @@ -0,0 +1,109 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +// #include +#include + + +#include "inkscape.h" +#include "prefs-utils.h" +#include "dialogs/extensions.h" +#include "extension/extension.h" + +#include "error-file.h" + +/** The name and group of the preference to say whether the error + dialog should be shown on startup. */ +#define PREFERENCE_ID "dialogs.extension-error", "show-on-startup" + +namespace Inkscape { +namespace Extension { + +/** \brief An initializer which builds the dialog + + Really a simple function. Basically the message dialog itself gets + built with the first initializer. The next step is to add in the + message, and attach the filename for the error file. After that + the checkbox is built, and has the call back attached to it. Also, + it is set based on the preferences setting for show on startup (really, + it should always be checked if you can see the dialog, but it is + probably good to check anyway). +*/ +ErrorFileNotice::ErrorFileNotice (void) : + Gtk::MessageDialog::MessageDialog( + "", /* message */ + false, /* use markup */ + Gtk::MESSAGE_WARNING, /* dialog type */ + Gtk::BUTTONS_OK, /* buttons */ + true /* modal */ + ) + +{ + /* This is some filler text, needs to change before relase */ + Glib::ustring dialog_text(_("One or more extensions failed to load\n\nThe failed extensions have been skipped. Inkscape will continue to run normally but those extensions will be unavailable. For details to troubleshoot this problem, please refer to the error log located at: ")); + gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME); + dialog_text += ext_error_file; + g_free(ext_error_file); + set_message(dialog_text, true); + + Gtk::VBox * vbox = get_vbox(); + + /* This is some filler text, needs to change before relase */ + checkbutton = new Gtk::CheckButton(_("Show dialog on startup")); + vbox->pack_start(*checkbutton, true, false, 5); + checkbutton->show(); + checkbutton->set_active(prefs_get_int_attribute(PREFERENCE_ID, 1) == 0 ? false : true); + + checkbutton->signal_toggled().connect(sigc::mem_fun(this, &ErrorFileNotice::checkbox_toggle)); + + set_resizable(true); + + //Gtk::HPaned *splitter = new Gtk::HPaned(); + Inkscape::UI::Dialogs::ExtensionsPanel* extens = new Inkscape::UI::Dialogs::ExtensionsPanel(); + extens->set_full(false); + vbox->pack_start( *extens, true, true ); + extens->show(); + + return; +} + +/** \brief Sets the preferences based on the checkbox value */ +void +ErrorFileNotice::checkbox_toggle (void) +{ + // std::cout << "Toggle value" << std::endl; + prefs_set_int_attribute(PREFERENCE_ID, checkbutton->get_active() ? 1 : 0); +} + +/** \brief Shows the dialog + + This function only shows the dialog if the preferences say that the + user wants to see the dialog, otherwise it just exits. +*/ +int +ErrorFileNotice::run (void) +{ + if (prefs_get_int_attribute(PREFERENCE_ID, 1) == 0) + return 0; + return Gtk::Dialog::run(); +} + +}; }; /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/error-file.h b/src/extension/error-file.h new file mode 100644 index 000000000..e710c0e4e --- /dev/null +++ b/src/extension/error-file.h @@ -0,0 +1,45 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_ERROR_FILE_H__ +#define INKSCAPE_EXTENSION_ERROR_FILE_H__ + +#include +#include + +namespace Inkscape { +namespace Extension { + +/** \brief A warning dialog to say that some extensions failed to load, + will not run if the preference controlling running is turned + off. */ +class ErrorFileNotice : public Gtk::MessageDialog { + /** The checkbutton, this is so we can figure out when it gets checked */ + Gtk::CheckButton * checkbutton; + + void checkbox_toggle(void); +public: + ErrorFileNotice (void); + int run (void); +}; + +}; }; /* namespace Inkscape, Extension */ + +#endif /* INKSCAPE_EXTENSION_ERROR_FILE_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/extension-forward.h b/src/extension/extension-forward.h new file mode 100644 index 000000000..f0346b488 --- /dev/null +++ b/src/extension/extension-forward.h @@ -0,0 +1,34 @@ +#ifndef SEEN_EXTENSION_FORWARD_H +#define SEEN_EXTENSION_FORWARD_H + +namespace Inkscape { +namespace Extension { + +class Effect; +class Extension; +class Input; +class Output; +class Print; + +class Dependency; +class Parameter; +class ExpirationTimer; + +namespace Implementation { +class Implementation; +} + +} } + +#endif /* !SEEN_EXTENSION_FORWARD_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp new file mode 100644 index 000000000..4aac7780a --- /dev/null +++ b/src/extension/extension.cpp @@ -0,0 +1,637 @@ +#define __SP_MODULE_C__ +/** \file + * + * Inkscape::Extension::Extension: + * the ability to have features that are more modular so that they + * can be added and removed easily. This is the basis for defining + * those actions. + */ + +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include +#include + +#include "inkscape.h" +#include "extension/implementation/implementation.h" + +#include "db.h" +#include "dependency.h" +#include "timer.h" +#include "parameter.h" + +namespace Inkscape { +namespace Extension { + +/* Inkscape::Extension::Extension */ + +std::vector Extension::search_path; +std::ofstream Extension::error_file; + +Parameter * param_shared (const gchar * name, GSList * list); + +/** + \return none + \brief Constructs an Extension from a Inkscape::XML::Node + \param in_repr The repr that should be used to build it + + This function is the basis of building an extension for Inkscape. It + currently extracts the fields from the Repr that are used in the + extension. The Repr will likely include other children that are + not related to the module directly. If the Repr does not include + a name and an ID the module will be left in an errored state. +*/ +Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) +{ + repr = in_repr; + Inkscape::GC::anchor(in_repr); + + id = NULL; + name = NULL; + _state = STATE_UNLOADED; + parameters = NULL; + + if (in_imp == NULL) { + imp = new Implementation::Implementation(); + } else { + imp = in_imp; + } + + // printf("Extension Constructor: "); + if (repr != NULL) { + Inkscape::XML::Node *child_repr = sp_repr_children(repr); + /* TODO: Handle what happens if we don't have these two */ + while (child_repr != NULL) { + char const * chname = child_repr->name(); + if (chname[0] == '_') /* Allow _ for translation of tags */ + chname++; + if (!strcmp(chname, "id")) { + gchar const *val = sp_repr_children(child_repr)->content(); + id = g_strdup (val); + } /* id */ + if (!strcmp(chname, "name")) { + name = g_strdup (sp_repr_children(child_repr)->content()); + } /* name */ + if (!strcmp(chname, "param")) { + Parameter * param; + param = Parameter::make(child_repr, this); + if (param != NULL) + parameters = g_slist_append(parameters, param); + } /* param */ + if (!strcmp(chname, "dependency")) { + _deps.push_back(new Dependency(child_repr)); + } /* param */ + child_repr = sp_repr_next(child_repr); + } + + db.register_ext (this); + } + // printf("%s\n", name); + timer = NULL; + + return; +} + +/** + \return none + \brief Destroys the Extension + + This function frees all of the strings that could be attached + to the extension and also unreferences the repr. This is better + than freeing it because it may (I wouldn't know why) be referenced + in another place. +*/ +Extension::~Extension (void) +{ +// printf("Extension Destructor: %s\n", name); + set_state(STATE_UNLOADED); + db.unregister_ext(this); + Inkscape::GC::release(repr); + g_free(id); + g_free(name); + delete timer; + timer = NULL; + /** \todo Need to do parameters here */ + + for (unsigned int i = 0 ; i < _deps.size(); i++) { + delete _deps[i]; + } + _deps.clear(); + + return; +} + +/** + \return none + \brief A function to set whether the extension should be loaded + or unloaded + \param in_state Which state should the extension be in? + + It checks to see if this is a state change or not. If we're changing + states it will call the appropriate function in the implementation, + load or unload. Currently, there is no error checking in this + function. There should be. +*/ +void +Extension::set_state (state_t in_state) +{ + if (_state == STATE_DEACTIVATED) return; + if (in_state != _state) { + /** \todo Need some more error checking here! */ + switch (in_state) { + case STATE_LOADED: + if (imp->load(this)) + _state = STATE_LOADED; + + if (timer != NULL) { + delete timer; + } + timer = new ExpirationTimer(this); + + break; + case STATE_UNLOADED: + // std::cout << "Unloading: " << name << std::endl; + imp->unload(this); + _state = STATE_UNLOADED; + + if (timer != NULL) { + delete timer; + timer = NULL; + } + break; + case STATE_DEACTIVATED: + _state = STATE_DEACTIVATED; + + if (timer != NULL) { + delete timer; + timer = NULL; + } + break; + default: + break; + } + } + + return; +} + +/** + \return The state the extension is in + \brief A getter for the state variable. +*/ +Extension::state_t +Extension::get_state (void) +{ + return _state; +} + +/** + \return Whether the extension is loaded or not + \brief A quick function to test the state of the extension +*/ +bool +Extension::loaded (void) +{ + return get_state() == STATE_LOADED; +} + +/** + \return A boolean saying whether the extension passed the checks + \brief A function to check the validity of the extension + + This function chekcs to make sure that there is an id, a name, a + repr and an implemenation for this extension. Then it checks all + of the dependencies to see if they pass. Finally, it asks the + implmentation to do a check of itself. + + On each check, if there is a failure, it will print a message to the + error log for that failure. It is important to note that the function + keeps executing if it finds an error, to try and get as many of them + into the error log as possible. This should help people debug + installations, and figure out what they need to get for the full + functionality of Inkscape to be available. +*/ +bool +Extension::check (void) +{ + bool retval = true; + + // static int i = 0; + // std::cout << "Checking module[" << i++ << "]: " << name << std::endl; + + const char * inx_failure = _(" This is caused by an improper .inx file for this extension." + " An improper .inx file could have been caused by a faulty installation of Inkscape."); + if (id == NULL) { + printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure); + retval = false; + } + if (name == NULL) { + printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure); + retval = false; + } + if (repr == NULL) { + printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure); + retval = false; + } + if (imp == NULL) { + printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure); + retval = false; + } + + for (unsigned int i = 0 ; i < _deps.size(); i++) { + if (_deps[i]->check() == FALSE) { + // std::cout << "Failed: " << *(_deps[i]) << std::endl; + printFailure(Glib::ustring(_("a dependency was not met."))); + error_file << *_deps[i] << std::endl; + retval = false; + } + } + + if (retval) + return imp->check(this); + return retval; +} + +/** \brief A quick function to print out a standard start of extension + errors in the log. + \param reason A string explaining why this failed + + Real simple, just put everything into \c error_file. +*/ +void +Extension::printFailure (Glib::ustring reason) +{ + error_file << _("Extension \"") << name << _("\" failed to load because "); + error_file << reason; + error_file << std::endl; + return; +} + +/** + \return The XML tree that is used to define the extension + \brief A getter for the internal Repr, does not add a reference. +*/ +Inkscape::XML::Node * +Extension::get_repr (void) +{ + return repr; +} + +/** + \return The textual id of this extension + \brief Get the ID of this extension - not a copy don't delete! +*/ +gchar * +Extension::get_id (void) +{ + return id; +} + +/** + \return The textual name of this extension + \brief Get the name of this extension - not a copy don't delete! +*/ +gchar * +Extension::get_name (void) +{ + return name; +} + +/** + \return None + \brief This function diactivates the extension (which makes it + unusable, but not deleted) + + This function is used to removed an extension from functioning, but + not delete it completely. It sets the state to \c STATE_DEACTIVATED to + mark to the world that it has been deactivated. It also removes + the current implementation and replaces it with a standard one. This + makes it so that we don't have to continually check if there is an + implementation, but we are gauranteed to have a benign one. + + \warning It is important to note that there is no 'activate' function. + Running this function is irreversable. +*/ +void +Extension::deactivate (void) +{ + set_state(STATE_DEACTIVATED); + + /* Removing the old implementation, and making this use the default. */ + /* This should save some memory */ + delete imp; + imp = new Implementation::Implementation(); + + return; +} + +/** + \return Whether the extension has been deactivated + \brief Find out the status of the extension +*/ +bool +Extension::deactivated (void) +{ + return get_state() == STATE_DEACTIVATED; +} + +/** + \return Parameter structure with a name of 'name' + \brief This function looks through the linked list for a parameter + structure with the name of the passed in name + \param name The name to search for + \param list The list to look for + + This is an inline function that is used by all the get_param and + set_param functions to find a param_t in the linked list with + the passed in name. It is done as an inline so that it will be + optimized into a 'jump' by the compiler. + + This function can throw a 'param_not_exist' exception if the + name is not found. + + The first thing that this function checks is if the list is NULL. + It could be NULL because there are no parameters for this extension + or because all of them have been checked (I'll spoil the ending and + tell you that this function is called recursively). If the list + is NULL then the 'param_not_exist' exception is thrown. + + Otherwise, the function looks at the current param_t that the element + list points to. If the name of that param_t matches the passed in + name then that param_t is returned. Otherwise, this function is + called again with g_slist_next as a parameter. +*/ +Parameter * +param_shared (const gchar * name, GSList * list) +{ + Parameter * output; + + if (name == NULL) { + throw Extension::param_not_exist(); + } + if (list == NULL) { + throw Extension::param_not_exist(); + } + + output = static_cast(list->data); + if (!strcmp(output->name(), name)) { + return output; + } + + return param_shared(name, g_slist_next(list)); +} + +/** + \return A constant pointer to the string held by the parameters. + \brief Gets a parameter identified by name with the string placed + in value. It isn't duplicated into the value string. + \param name The name of the parameter to get + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +const gchar * +Extension::get_param_string (const gchar * name, const Inkscape::XML::Document * doc) +{ + Parameter * param; + + param = param_shared(name, parameters); + return param->get_string(doc); +} + +/** + \return The value of the parameter identified by the name + \brief Gets a parameter identified by name with the bool placed + in value. + \param name The name of the parameter to get + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +bool +Extension::get_param_bool (const gchar * name, const Inkscape::XML::Document * doc) +{ + Parameter * param; + + param = param_shared(name, parameters); + return param->get_bool(doc); +} + +/** + \return The integer value for the parameter specified + \brief Gets a parameter identified by name with the integer placed + in value. + \param name The name of the parameter to get + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +int +Extension::get_param_int (const gchar * name, const Inkscape::XML::Document * doc) +{ + Parameter * param; + + param = param_shared(name, parameters); + return param->get_int(doc); +} + +/** + \return The float value for the parameter specified + \brief Gets a parameter identified by name with the float placed + in value. + \param name The name of the parameter to get + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +float +Extension::get_param_float (const gchar * name, const Inkscape::XML::Document * doc) +{ + Parameter * param; + param = param_shared(name, parameters); + return param->get_float(doc); +} + +/** + \return The passed in value + \brief Sets a parameter identified by name with the boolean + in the parameter value. + \param name The name of the parameter to set + \param value The value to set the parameter to + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +bool +Extension::set_param_bool (const gchar * name, bool value, Inkscape::XML::Document * doc) +{ + Parameter * param; + param = param_shared(name, parameters); + return param->set_bool(value, doc); +} + +/** + \return The passed in value + \brief Sets a parameter identified by name with the integer + in the parameter value. + \param name The name of the parameter to set + \param value The value to set the parameter to + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +int +Extension::set_param_int (const gchar * name, int value, Inkscape::XML::Document * doc) +{ + Parameter * param; + param = param_shared(name, parameters); + return param->set_int(value, doc); +} + +/** + \return The passed in value + \brief Sets a parameter identified by name with the integer + in the parameter value. + \param name The name of the parameter to set + \param value The value to set the parameter to + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +float +Extension::set_param_float (const gchar * name, float value, Inkscape::XML::Document * doc) +{ + Parameter * param; + param = param_shared(name, parameters); + return param->set_float(value, doc); +} + +/** + \return The passed in value + \brief Sets a parameter identified by name with the string + in the parameter value. + \param name The name of the parameter to set + \param value The value to set the parameter to + \param doc The document to look in for document specific parameters + + Look up in the parameters list, then execute the function on that + found parameter. +*/ +const gchar * +Extension::set_param_string (const gchar * name, const gchar * value, Inkscape::XML::Document * doc) +{ + Parameter * param; + param = param_shared(name, parameters); + return param->set_string(value, doc); +} + +/** \brief A function to open the error log file. */ +void +Extension::error_file_open (void) +{ + gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME); + gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL ); + error_file.open(filename); + if (!error_file.is_open()) { + g_warning(_("Could not create extension error log file '%s'"), + filename); + } + g_free(filename); + g_free(ext_error_file); +}; + +/** \brief A function to close the error log file. */ +void +Extension::error_file_close (void) +{ + error_file.close(); +}; + +/** \brief A function to automatically generate a GUI using the parameters + \return Generated widget + + This function just goes through each parameter, and calls it's 'get_widget' + function to get each widget. Then, each of those is placed into + a Gtk::VBox, which is then returned to the calling function. + + If there are no parameters, this function just returns NULL. +*/ +Gtk::Widget * +Extension::autogui (void) +{ + if (g_slist_length(parameters) == 0) return NULL; + + Gtk::VBox * vbox = new Gtk::VBox(); + vbox = new Gtk::VBox(); + + for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { + Parameter * param = reinterpret_cast(list->data); + Gtk::Widget * widg = param->get_widget(); + if (widg != NULL) + vbox->pack_start(*widg, true, true); + } + + vbox->show(); + return vbox; +}; + +/** + \brief A function to get the parameters in a string form + \return A string with all the parameters as command line arguements + + I don't really like this function, but it works for now. + + \todo Do this better. +*/ +Glib::ustring * +Extension::paramString (void) +{ + Glib::ustring * param_string = new Glib::ustring(""); + + for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { + Parameter * param = reinterpret_cast(list->data); + + *param_string += " --"; + *param_string += param->name(); + *param_string += "="; + Glib::ustring * paramstr = param->string(); + *param_string += *paramstr; + delete paramstr; + } + + return param_string; +} + +} /* namespace Extension */ +} /* namespace Inkscape */ + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/extension.h b/src/extension/extension.h new file mode 100644 index 000000000..a0c991cf2 --- /dev/null +++ b/src/extension/extension.h @@ -0,0 +1,207 @@ +#ifndef __INK_EXTENSION_H__ +#define __INK_EXTENSION_H__ + +/** \file + * Frontend to certain, possibly pluggable, actions. + */ + +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include +#include +#include "xml/repr.h" +#include "extension/extension-forward.h" + +/** The key that is used to identify that the I/O should be autodetected */ +#define SP_MODULE_KEY_AUTODETECT "autodetect" +/** This is the key for the SVG input module */ +#define SP_MODULE_KEY_INPUT_SVG "org.inkscape.input.svg" +#define SP_MODULE_KEY_INPUT_SVGZ "org.inkscape.input.svgz" +/** Specifies the input module that should be used if none are selected */ +#define SP_MODULE_KEY_INPUT_DEFAULT SP_MODULE_KEY_AUTODETECT +/** The key for outputing standard W3C SVG */ +#define SP_MODULE_KEY_OUTPUT_SVG "org.inkscape.output.svg.plain" +#define SP_MODULE_KEY_OUTPUT_SVGZ "org.inkscape.output.svgz.plain" +/** This is an output file that has SVG data with the Sodipodi namespace extensions */ +#define SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE "org.inkscape.output.svg.inkscape" +#define SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE "org.inkscape.output.svgz.inkscape" +/** Which output module should be used? */ +#define SP_MODULE_KEY_OUTPUT_DEFAULT SP_MODULE_KEY_AUTODETECT + +/** Defines the key for Postscript printing */ +#define SP_MODULE_KEY_PRINT_PS "org.inkscape.print.ps" +/** Defines the key for LaTeX printing */ +#define SP_MODULE_KEY_PRINT_LATEX "org.inkscape.print.latex" +/** Defines the key for printing with GNOME Print */ +#define SP_MODULE_KEY_PRINT_GNOME "org.inkscape.print.gnome" +/** Defines the key for printing under Win32 */ +#define SP_MODULE_KEY_PRINT_WIN32 "org.inkscape.print.win32" +#ifdef WIN32 +/** Defines the default printing to use */ +#define SP_MODULE_KEY_PRINT_DEFAULT SP_MODULE_KEY_PRINT_WIN32 +#else +#ifdef WITH_GNOME_PRINT +/** Defines the default printing to use */ +#define SP_MODULE_KEY_PRINT_DEFAULT SP_MODULE_KEY_PRINT_GNOME +#else +/** Defines the default printing to use */ +#define SP_MODULE_KEY_PRINT_DEFAULT SP_MODULE_KEY_PRINT_PS +#endif +#endif + +/** Mime type for SVG */ +#define MIME_SVG "image/svg+xml" + +/** Name of the extension error file */ +#define EXTENSION_ERROR_LOG_FILENAME "extension-errors.log" + +namespace Inkscape { +namespace Extension { + +/** The object that is the basis for the Extension system. This object + contains all of the information that all Extension have. The + individual items are detailed within. This is the interface that + those who want to _use_ the extensions system should use. This + is most likely to be those who are inside the Inkscape program. */ +class Extension { +public: + /** An enumeration to identify if the Extension has been loaded or not. */ + typedef enum { + STATE_LOADED, /**< The extension has been loaded successfully */ + STATE_UNLOADED, /**< The extension has not been loaded */ + STATE_DEACTIVATED /**< The extension is missing something which makes it unusable */ + } state_t; + static std::vector search_path; /**< A vector of paths to search for extensions */ + +private: + gchar *id; /**< The unique identifier for the Extension */ + gchar *name; /**< A user friendly name for the Extension */ + state_t _state; /**< Which state the Extension is currently in */ + std::vector _deps; /**< Dependencies for this extension */ + static std::ofstream error_file; /**< This is the place where errors get reported */ + +protected: + Inkscape::XML::Node *repr; /**< The XML description of the Extension */ + Implementation::Implementation * imp; /**< An object that holds all the functions for making this work */ + ExpirationTimer * timer; /**< Timeout to unload after a given time */ + +public: + Extension (Inkscape::XML::Node * in_repr, + Implementation::Implementation * in_imp); + virtual ~Extension (void); + + void set_state (state_t in_state); + state_t get_state (void); + bool loaded (void); + virtual bool check (void); + Inkscape::XML::Node * get_repr (void); + gchar * get_id (void); + gchar * get_name (void); + void deactivate (void); + bool deactivated (void); + void printFailure (Glib::ustring reason); + + +/* Parameter Stuff */ +private: + GSList * parameters; /**< A table to store the parameters for this extension. + This only gets created if there are parameters in this + extension */ + +public: + /** An error class for when a parameter is called on a type it is not */ + class param_wrong_type {}; + + /** An error class for when a parameter is looked for that just + * simply doesn't exist */ + class param_not_exist {}; + + /** An error class for when a filename already exists, but the user + * doesn't want to overwrite it */ + class no_overwrite {}; + +private: + void make_param (Inkscape::XML::Node * paramrepr); +#if 0 + inline param_t * param_shared (const gchar * name, + GSList * list); +#endif +public: + bool get_param_bool (const gchar * name, + const Inkscape::XML::Document * doc = NULL); + int get_param_int (const gchar * name, + const Inkscape::XML::Document * doc = NULL); + float get_param_float (const gchar * name, + const Inkscape::XML::Document * doc = NULL); + const gchar * get_param_string (const gchar * name, + const Inkscape::XML::Document * doc = NULL); + bool set_param_bool (const gchar * name, + bool value, + Inkscape::XML::Document * doc = NULL); + int set_param_int (const gchar * name, + int value, + Inkscape::XML::Document * doc = NULL); + float set_param_float (const gchar * name, + float value, + Inkscape::XML::Document * doc = NULL); + const gchar * set_param_string (const gchar * name, + const gchar * value, + Inkscape::XML::Document * doc = NULL); + + /* Error file handling */ +public: + static void error_file_open (void); + static void error_file_close (void); + +public: + Gtk::Widget * autogui (void); + Glib::ustring * paramString (void); +}; + + + +/* + +This is a prototype for how collections should work. Whoever gets +around to implementing this gets to decide what a 'folder' and an +'item' really is. That is the joy of implementing it, eh? + +class Collection : public Extension { + +public: + folder get_root (void); + int get_count (folder); + thumbnail get_thumbnail(item); + item[] get_items(folder); + folder[] get_folders(folder); + metadata get_metadata(item); + image get_image(item); + +}; +*/ + +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INK_EXTENSION_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/implementation/.cvsignore b/src/extension/implementation/.cvsignore new file mode 100644 index 000000000..e8014d011 --- /dev/null +++ b/src/extension/implementation/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +makefile +.dirstamp diff --git a/src/extension/implementation/Makefile_insert b/src/extension/implementation/Makefile_insert new file mode 100644 index 000000000..5d70216d2 --- /dev/null +++ b/src/extension/implementation/Makefile_insert @@ -0,0 +1,13 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +extension/implementation/all: extension/implementation/libimplementation.a + +extension/implementation/clean: + rm -f extension/implementation/libimplementation.a \ + $(extension_implementation_libimplementation_a_OBJECTS) + +extension_implementation_libimplementation_a_SOURCES = \ + extension/implementation/implementation.cpp \ + extension/implementation/implementation.h \ + extension/implementation/script.cpp \ + extension/implementation/script.h diff --git a/src/extension/implementation/implementation.cpp b/src/extension/implementation/implementation.cpp new file mode 100644 index 000000000..c959d50b3 --- /dev/null +++ b/src/extension/implementation/implementation.cpp @@ -0,0 +1,181 @@ +/* + Author: Ted Gould + Copyright (c) 2003-2005 + + This code is licensed under the GNU GPL. See COPYING for details. + + This file is the backend to the extensions system. These are + the parts of the system that most users will never see, but are + important for implementing the extensions themselves. This file + contains the base class for all of that. +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "implementation.h" + +#include +#include +#include + +namespace Inkscape { +namespace Extension { +namespace Implementation { + +/** + * \return Was the load sucessful? + * \brief This function is the stub load. It just returns success. + * \param module The Extension that should be loaded. + */ +bool +Implementation::load(Inkscape::Extension::Extension *module) { + return TRUE; +} /* Implementation::load */ + +void +Implementation::unload(Inkscape::Extension::Extension *module) { + return; +} /* Implementation::unload */ + +bool +Implementation::check(Inkscape::Extension::Extension *module) { + /* If there are no checks, they all pass */ + return TRUE; +} /* Implemenation::check */ + +Gtk::Widget * +Implementation::prefs_input(Inkscape::Extension::Input *module, gchar const *filename) { + return module->autogui(); +} /* Implementation::prefs_input */ + +SPDocument * +Implementation::open(Inkscape::Extension::Input *module, gchar const *filename) { + /* throw open_failed(); */ + return NULL; +} /* Implementation::open */ + +Gtk::Widget * +Implementation::prefs_output(Inkscape::Extension::Output *module) { + return module->autogui(); +} /* Implementation::prefs_output */ + +void +Implementation::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename) { + /* throw save_fail */ + return; +} /* Implementation::save */ + +Gtk::Widget * +Implementation::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *view) { + return module->autogui(); +} /* Implementation::prefs_effect */ + +void +Implementation::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document) { + /* throw filter_fail */ + return; +} /* Implementation::filter */ + +unsigned int +Implementation::setup(Inkscape::Extension::Print *module) +{ + return 0; +} + +unsigned int +Implementation::set_preview(Inkscape::Extension::Print *module) +{ + return 0; +} + + +unsigned int +Implementation::begin(Inkscape::Extension::Print *module, SPDocument *doc) +{ + return 0; +} + +unsigned int +Implementation::finish(Inkscape::Extension::Print *module) +{ + return 0; +} + + +/* Rendering methods */ +unsigned int +Implementation::bind(Inkscape::Extension::Print *module, NRMatrix const *transform, float opacity) +{ + return 0; +} + +unsigned int +Implementation::release(Inkscape::Extension::Print *module) +{ + return 0; +} + +unsigned int +Implementation::comment(Inkscape::Extension::Print *module, char const *comment) +{ + return 0; +} + +unsigned int +Implementation::fill(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + return 0; +} + +unsigned int +Implementation::stroke(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + return 0; +} + +unsigned int +Implementation::image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + NRMatrix const *transform, SPStyle const *style) +{ + return 0; +} + +unsigned int +Implementation::text(Inkscape::Extension::Print *module, char const *text, + NR::Point p, SPStyle const *style) +{ + return 0; +} + +/** + \brief Tell the printing engine whether text should be text or path + \retval true Render the text as a path + \retval false Render text using the text function (above) + + Default value is false because most printing engines will support + paths more than they'll support text. (at least they do today) +*/ +bool +Implementation::textToPath(Inkscape::Extension::Print *ext) +{ + return false; +} + + +} /* namespace Implementation */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h new file mode 100644 index 000000000..68c2eb04c --- /dev/null +++ b/src/extension/implementation/implementation.h @@ -0,0 +1,127 @@ +/* + Author: Ted Gould + Copyright (c) 2003-2005 + + This code is licensed under the GNU GPL. See COPYING for details. + + This file is the backend to the extensions system. These are + the parts of the system that most users will never see, but are + important for implementing the extensions themselves. This file + contains the base class for all of that. +*/ +#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_H__ +#define __INKSCAPE_EXTENSION_IMPLEMENTATION_H__ + +#include +#include +#include + +#include "forward.h" +#include "extension/extension-forward.h" +#include "libnr/nr-forward.h" +#include "libnr/nr-point.h" + +namespace Inkscape { +namespace Extension { +namespace Implementation { + +/** + * Base class for all implementations of modules. This is whether they are done systematically by + * having something like the scripting system, or they are implemented internally they all derive + * from this class. + */ +class Implementation { +public: + /* ----- Constructor / destructor ----- */ + Implementation() {} + + virtual ~Implementation() {} + + /* ----- Basic functions for all Extension ----- */ + virtual bool load(Inkscape::Extension::Extension *module); + + virtual void unload(Inkscape::Extension::Extension *module); + + /** Verify any dependencies. */ + virtual bool check(Inkscape::Extension::Extension *module); + + + /* ----- Input functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget *prefs_input(Inkscape::Extension::Input *module, + gchar const *filename); + + virtual SPDocument *open(Inkscape::Extension::Input *module, + gchar const *filename); + + /* ----- Output functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget *prefs_output(Inkscape::Extension::Output *module); + virtual void save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename); + + /* ----- Effect functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget * prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view); + /* TODO: need to figure out what we need here */ + + virtual void effect(Inkscape::Extension::Effect *module, + Inkscape::UI::View::View *document); + + /* ----- Print functions ----- */ + virtual unsigned setup(Inkscape::Extension::Print *module); + virtual unsigned set_preview(Inkscape::Extension::Print *module); + + virtual unsigned begin(Inkscape::Extension::Print *module, + SPDocument *doc); + virtual unsigned finish(Inkscape::Extension::Print *module); + virtual bool textToPath(Inkscape::Extension::Print *ext); + + /* ----- Rendering methods ----- */ + virtual unsigned bind(Inkscape::Extension::Print *module, + NRMatrix const *transform, + float opacity); + virtual unsigned release(Inkscape::Extension::Print *module); + virtual unsigned comment(Inkscape::Extension::Print *module, const char * comment); + virtual unsigned fill(Inkscape::Extension::Print *module, + NRBPath const *bpath, + NRMatrix const *ctm, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + virtual unsigned stroke(Inkscape::Extension::Print *module, + NRBPath const *bpath, + NRMatrix const *transform, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + virtual unsigned image(Inkscape::Extension::Print *module, + unsigned char *px, + unsigned int w, + unsigned int h, + unsigned int rs, + NRMatrix const *transform, + SPStyle const *style); + virtual unsigned text(Inkscape::Extension::Print *module, + char const *text, + NR::Point p, + SPStyle const *style); +}; + +} /* namespace Implementation */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/implementation/makefile.in b/src/extension/implementation/makefile.in new file mode 100644 index 000000000..c0bf21190 --- /dev/null +++ b/src/extension/implementation/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd ../.. && $(MAKE) extension/implementation/all + +clean %.a %.o: + cd ../.. && $(MAKE) extension/implementation/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/extension/implementation/plugin-link.h b/src/extension/implementation/plugin-link.h new file mode 100644 index 000000000..740b8952a --- /dev/null +++ b/src/extension/implementation/plugin-link.h @@ -0,0 +1,68 @@ +/** \file + * Plugin prototypes. + * + * This header describes which prototypes plugins should use when + * creating their functions. This header is also used by the internal + * plugins code to guarantee consistency. + * + * Author: Ted Gould + * Copyright (c) 2004-2005 + * + * This code is licensed under the GNU GPL. See COPYING for details. + */ + +#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__ +#define __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__ + +#include +#include + +/** \todo This needs to go away eventually. */ +#include "document.h" + +/** \brief A simple typedef to make it so that inkscape_extension can + be used before I figure out what makes sense here */ +typedef void inkscape_extension; +/** \brief The C prototype of a load function. */ +typedef int (*inkscape_plugin_load)(inkscape_extension * in_ext); +/** \brief The C prototype of an unload function. */ +typedef void (*inkscape_plugin_unload)(inkscape_extension * in_ext); +/** \brief The C prototype of an open function. */ +typedef SPDocument *(*inkscape_plugin_open)(inkscape_extension * in_ext, const gchar * filename); +/** \brief The C prototype of an input prefs function. */ +typedef Gtk::Widget * (*inkscape_plugin_prefs_input)(inkscape_extension * in_ext, gchar const * filename); +/** \brief The C prototype of an effect function. */ +typedef void (*inkscape_plugin_effect)(inkscape_extension * in_ext, Inkscape::UI::View::View * view); +/** \brief The C prototype of an effect prefs function. */ +typedef Gtk::Widget * (*inkscape_plugin_prefs_effect)(inkscape_extension * in_ext, Inkscape::UI::View::View * view); + +/** \brief The name of the symbol for the plugin. Should match + \c INKSCAPE_PLUGIN_NAME_STR (minus the quotes). */ +#define INKSCAPE_PLUGIN_NAME inkscape_plugin_table +/** \brief The name of the table to define the plugin as a string. This + should be the same as \c INKSCAPE_PLUGIN_NAME but with quotes. */ +#define INKSCAPE_PLUGIN_NAME_STR "inkscape_plugin_table" +/** \brief The version of the plugin interface that is being used. This + should always be used in the version entry in the \c inkscape_plugin_function_table + version entry. This way compiled plugins can be detected. */ +#define INKSCAPE_PLUGIN_VERSION 0 + +/** \brief A structure containing all the functions that should be called + to make the plugin work. */ +typedef struct { + int version; /**< The interface version used. Should + always be \c INKSCAPE_PLUGIN_VERSION. */ + inkscape_plugin_load load; /**< Load function, called on first use */ + inkscape_plugin_unload unload; /**< Unload function, called when Inkscape is + finished with the plugin */ + inkscape_plugin_open open; /**< Open function, called to open a file + for Inkscape */ + inkscape_plugin_prefs_input prefs_input; /**< Input preferences function, called to get + further parameters for an input plugin. */ + inkscape_plugin_effect effect; /**< Effect function, called to cause an effect + on a document. */ + inkscape_plugin_prefs_effect prefs_effect;/**< Effect preferences, on call could cause settings + on a document. */ +} inkscape_plugin_function_table; + +#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__ */ diff --git a/src/extension/implementation/plugin.cpp b/src/extension/implementation/plugin.cpp new file mode 100644 index 000000000..0bc3267f4 --- /dev/null +++ b/src/extension/implementation/plugin.cpp @@ -0,0 +1,328 @@ +/** \file + * The implementation of pluggable objects into Inkscape. + * + * Author: Ted Gould + * Copyright (c) 2004-2005 + * + * This code is licensed under the GNU GPL. See COPYING for details. + * + * This file implements loadable modules for Inkscape. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include +#include +#include +#include "extension/extension.h" +#include "xml/repr.h" +#include "plugin.h" +#include "plugin-link.h" + +namespace Inkscape { +namespace Extension { +namespace Implementation { + +/** \brief Create an object by nulling everything out. */ +Plugin::Plugin(void) +{ + _module = NULL; + _symTable = NULL; + + return; +} + +/** + \brief Oh, so someone actually wants to use this plugin! We better + go and grab it then! + \param module Unused except to pass to the plugin's load function. + \return Whether the load was successful. Hopefully always TRUE. + + Okay, first things first, are modules supported on this platform? That + is a good first check. Also, is this plugin already loaded? If so + don't reload it. + + If all those are true we need to figure out the filename that needs + to be loaded. This involves going through the definition of the plugin + for the \c name field. It is then run though the \c build_path function + to add the .so or .dll on the end. The path that is used is the + \c INKSCAPE_PLUGINDIR. + + The module is then loaded into RAM by Glib. I'm sure there is lots + of magic involved here -- but we don't have to worry about it, we + just check to make sure it worked. + + After it is loaded into memory the function lookup table is grabbed + and then the load function for the plugin is called. +*/ +bool +Plugin::load(Inkscape::Extension::Extension *module) +{ + if (!Glib::Module::get_supported()) { + return FALSE; + } + + if (module->loaded()) { + return TRUE; + } + + Inkscape::XML::Node * child_repr = sp_repr_children(module->get_repr()); + const gchar * name = NULL; + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "plugin")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "name")) { + name = sp_repr_children(child_repr)->content(); + } + child_repr = sp_repr_next(child_repr); + } + } + child_repr = sp_repr_next(child_repr); + } + + if (name == NULL) { + return FALSE; + } + + std::string path = Glib::Module::build_path(INKSCAPE_PLUGINDIR, name); + // std::cout << "Load path: " << path << std::endl; + _module = new Glib::Module(path); + + if (!(bool)_module || _module == NULL) { + printf("Loading failed\n"); + return FALSE; + } + + /* Grab symbols */ + void * voidpntr; + if (!_module->get_symbol(INKSCAPE_PLUGIN_NAME_STR, voidpntr)) { + // printf("Error loading library\n"); + // std::cout << "Last error: " << _module->get_last_error() << std::endl; + return FALSE; + } + _symTable = (inkscape_plugin_function_table *)voidpntr; + + if (_symTable->version != INKSCAPE_PLUGIN_VERSION) { + /* Someday this could adapt older versions so that we could + be compatible -- but not today */ + return FALSE; + } + + if (_symTable->load != NULL) { + return (bool)_symTable->load((inkscape_extension *)module); + } + + return TRUE; +} + +/** + \brief No one is interested in this plugin for now. It is removed + to save memory. + \param module The module of this implementation, passed to unload. + \return None. + + Call the unload function of the plugin, then delete the symbol table + and the module itself. Put everything back to NULL. +*/ +void +Plugin::unload(Inkscape::Extension::Extension *module) +{ + _symTable->unload((inkscape_extension *)module); + _symTable = NULL; + delete _module; + _module = NULL; + return; +} + +/** + \brief Just check to make sure everything exists. This makes sure + that the loadable file exits. + \param module Unused. + \return The status of the check. TRUE means that it passed. + + This function builds the path name out of the XML definition of the + plugin. It does this by looking for the \c name parameter. It then + uses \c build_path to add the .so or .dll and adds in the \c INKSCAPE_PLUGINDIR + to the front of it. Then the \c file_test function is used to make + sure it exists. +*/ +bool +Plugin::check(Inkscape::Extension::Extension *module) +{ + Inkscape::XML::Node * child_repr = sp_repr_children(module->get_repr()); + const gchar * name = NULL; + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "plugin")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "name")) { + name = sp_repr_children(child_repr)->content(); + } + child_repr = sp_repr_next(child_repr); + } + } + child_repr = sp_repr_next(child_repr); + } + + if (name == NULL) { + return FALSE; + } + + std::string path = Glib::Module::build_path(INKSCAPE_PLUGINDIR, name); + // std::cout << "Path: " << path << std::endl; + if (!Glib::file_test(path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_EXECUTABLE)) { + // std::cout << "Failed!" << std::endl; + return FALSE; + } + + // std::cout << "No Problem." << std::endl; + return TRUE; +} + +Gtk::Widget * +Plugin::prefs_input(Inkscape::Extension::Input *module, gchar const *filename) +{ + return Inkscape::Extension::Implementation::Implementation::prefs_input(module, filename); +} + +/* + \brief Function to call the open in the plugin. + \param module Passed on + \param filename Passed on + \return The document that is opened or NULL for error. + + This function looks in the symbol table to see if there is an open + function. If there is, it is called, otherwise the standard implementation + function is used instead. +*/ +SPDocument * +Plugin::open(Inkscape::Extension::Input *module, gchar const *filename) +{ + if (_symTable->open != NULL) { + return _symTable->open((inkscape_extension *)module, filename); + } else { + return Inkscape::Extension::Implementation::Implementation::open(module, filename); + } +} + +Gtk::Widget * +Plugin::prefs_output(Inkscape::Extension::Output *module) +{ + return Inkscape::Extension::Implementation::Implementation::prefs_output(module); +} + +void +Plugin::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename) +{ + return Inkscape::Extension::Implementation::Implementation::save(module, doc, filename); +} + +Gtk::Widget * +Plugin::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view) +{ + if (_symTable->prefs_effect != NULL) { + return _symTable->prefs_effect((inkscape_extension *)module, view); + } else { + return Inkscape::Extension::Implementation::Implementation::prefs_effect(module, view); + } +} + +void +Plugin::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document) +{ + if (_symTable->effect != NULL) { + return _symTable->effect((inkscape_extension *)module, document); + } else { + return Inkscape::Extension::Implementation::Implementation::effect(module, document); + } +} + +unsigned +Plugin::setup(Inkscape::Extension::Print *module) +{ + return Inkscape::Extension::Implementation::Implementation::setup(module); +} + +unsigned +Plugin::set_preview(Inkscape::Extension::Print *module) +{ + return Inkscape::Extension::Implementation::Implementation::set_preview(module); +} + +unsigned +Plugin::begin(Inkscape::Extension::Print *module, SPDocument *doc) +{ + return Inkscape::Extension::Implementation::Implementation::begin(module, doc); +} + +unsigned +Plugin::finish(Inkscape::Extension::Print *module) +{ + return Inkscape::Extension::Implementation::Implementation::finish(module); +} + +bool +Plugin::textToPath(Inkscape::Extension::Print *ext) +{ + return Inkscape::Extension::Implementation::Implementation::finish(ext); +} + +unsigned +Plugin::bind(Inkscape::Extension::Print *module, NRMatrix const *transform, float opacity) +{ + return Inkscape::Extension::Implementation::Implementation::bind(module, transform, opacity); +} + +unsigned +Plugin::release(Inkscape::Extension::Print *module) +{ + return Inkscape::Extension::Implementation::Implementation::release(module); +} + +unsigned +Plugin::comment(Inkscape::Extension::Print *module, const char * comment) +{ + return Inkscape::Extension::Implementation::Implementation::comment(module,comment); +} + +unsigned +Plugin::fill(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + return Inkscape::Extension::Implementation::Implementation::fill(module, bpath, ctm, style, pbox, dbox, bbox); +} + +unsigned +Plugin::stroke(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style, NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + return Inkscape::Extension::Implementation::Implementation::stroke(module, bpath, transform, style, pbox, dbox, bbox); +} + +unsigned +Plugin::image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, NRMatrix const *transform, SPStyle const *style) +{ + return Inkscape::Extension::Implementation::Implementation::image(module, px, w, h, rs, transform, style); +} + +unsigned +Plugin::text(Inkscape::Extension::Print *module, char const *text, NR::Point p, SPStyle const *style) +{ + return Inkscape::Extension::Implementation::Implementation::text(module, text, p, style); +} + +} /* namespace Implementation */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/implementation/plugin.h b/src/extension/implementation/plugin.h new file mode 100644 index 000000000..88658e954 --- /dev/null +++ b/src/extension/implementation/plugin.h @@ -0,0 +1,121 @@ +/** \file + * Inkscape::Extension::Implementation::Plugin + * + * Author: Ted Gould + * Copyright (c) 2004-2005 + * + * This code is licensed under the GNU GPL. See COPYING for details. + */ +#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__ +#define __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__ + +#include +#include +#include "plugin-link.h" + +namespace Inkscape { +namespace Extension { +namespace Implementation { + +/** \brief For the most part this is a direct steal from \c implementation.h + in that all the functions have the same prototypes. The two + added things are a pointer to the loaded module and a pointer + to the symbol table in that. */ +class Plugin : public Implementation { + /** \brief A pointer to the module created when loading the plugin. */ + Glib::Module * _module; + /** \brief The symbol table that is in the plugin. It is pulled out + here so that it doesn't have to be grabbed as often. */ + inkscape_plugin_function_table * _symTable; + +public: + Plugin(void); + + /* ----- Basic functions for all Extension ----- */ + virtual bool load(Inkscape::Extension::Extension *module); + + virtual void unload(Inkscape::Extension::Extension *module); + + /** Verify any dependencies. */ + virtual bool check(Inkscape::Extension::Extension *module); + + + /* ----- Input functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget *prefs_input(Inkscape::Extension::Input *module, + gchar const *filename); + + virtual SPDocument *open(Inkscape::Extension::Input *module, + gchar const *filename); + + /* ----- Output functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget *prefs_output(Inkscape::Extension::Output *module); + virtual void save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename); + + /* ----- Effect functions ----- */ + /** Find out information about the file. */ + virtual Gtk::Widget * prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view); + /* TODO: need to figure out what we need here */ + + virtual void effect(Inkscape::Extension::Effect *module, + Inkscape::UI::View::View *document); + + /* ----- Print functions ----- */ + virtual unsigned setup(Inkscape::Extension::Print *module); + virtual unsigned set_preview(Inkscape::Extension::Print *module); + + virtual unsigned begin(Inkscape::Extension::Print *module, + SPDocument *doc); + virtual unsigned finish(Inkscape::Extension::Print *module); + virtual bool textToPath(Inkscape::Extension::Print *ext); + + /* ----- Rendering methods ----- */ + virtual unsigned bind(Inkscape::Extension::Print *module, + NRMatrix const *transform, + float opacity); + virtual unsigned release(Inkscape::Extension::Print *module); + virtual unsigned comment(Inkscape::Extension::Print *module, const char * comment); + virtual unsigned fill(Inkscape::Extension::Print *module, + NRBPath const *bpath, + NRMatrix const *ctm, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + virtual unsigned stroke(Inkscape::Extension::Print *module, + NRBPath const *bpath, + NRMatrix const *transform, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + virtual unsigned image(Inkscape::Extension::Print *module, + unsigned char *px, + unsigned int w, + unsigned int h, + unsigned int rs, + NRMatrix const *transform, + SPStyle const *style); + virtual unsigned text(Inkscape::Extension::Print *module, + char const *text, + NR::Point p, + SPStyle const *style); +}; + +} /* namespace Implementation */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp new file mode 100644 index 000000000..f9cc2ee0b --- /dev/null +++ b/src/extension/implementation/script.cpp @@ -0,0 +1,1053 @@ +/** \file + * Code for handling extensions (i.e.\ scripts). + */ +/* + * Authors: + * Bryce Harrington + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include +#include + +#include "ui/view/view.h" +#include "desktop-handles.h" +#include "selection.h" +#include "sp-namedview.h" +#include "io/sys.h" +#include "prefs-utils.h" +#include "../system.h" +#include "extension/effect.h" +#include "extension/db.h" +#include "script.h" + +#include "util/glib-list-iterators.h" + +#ifdef WIN32 +#include +#endif + +/** This is the command buffer that gets allocated from the stack */ +#define BUFSIZE (255) + +/* Namespaces */ +namespace Inkscape { +namespace Extension { +namespace Implementation { + +/* Real functions */ +/** + \return A script object + \brief This function creates a script object and sets up the + variables. + + This function just sets the command to NULL. It should get built + officially in the load function. This allows for less allocation + of memory in the unloaded state. +*/ +Script::Script() : + Implementation(), + command(NULL), + helper_extension(NULL) +{ +} + +/** + \return A string with the complete string with the relative directory expanded + \brief This function takes in a Repr that contains a reldir entry + and returns that data with the relative directory expanded. + Mostly it is here so that relative directories all get used + the same way. + \param reprin The Inkscape::XML::Node with the reldir in it. + + Basically this function looks at an attribute of the Repr, and makes + a decision based on that. Currently, it is only working with the + 'extensions' relative directory, but there will be more of them. + One thing to notice is that this function always returns an allocated + string. This means that the caller of this function can always + free what they are given (and should do it too!). +*/ +gchar * +Script::solve_reldir(Inkscape::XML::Node *reprin) { + gchar const *reldir = reprin->attribute("reldir"); + + if (reldir == NULL) { + return g_strdup(sp_repr_children(reprin)->content()); + } + + if (!strcmp(reldir, "extensions")) { + for(unsigned int i=0; icontent(), NULL); + if ( Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) ) { + return filename; + } + g_free(filename); + } + } else { + return g_strdup(sp_repr_children(reprin)->content()); + } + + return NULL; +} + +/** + \return Whether the command given exists, including in the path + \brief This function is used to find out if something exists for + the check command. It can look in the path if required. + \param command The command or file that should be looked for + + The first thing that this function does is check to see if the + incoming file name has a directory delimiter in it. This would + mean that it wants to control the directories, and should be + used directly. + + If not, the path is used. Each entry in the path is stepped through, + attached to the string, and then tested. If the file is found + then a TRUE is returned. If we get all the way through the path + then a FALSE is returned, the command could not be found. +*/ +bool +Script::check_existance(gchar const *command) +{ + if (*command == '\0') { + /* We check the simple case first. */ + return FALSE; + } + + if (g_utf8_strchr(command, -1, G_DIR_SEPARATOR) != NULL) { + /* Don't search when it contains a slash. */ + if (Inkscape::IO::file_test(command, G_FILE_TEST_EXISTS)) + return TRUE; + else + return FALSE; + } + + + gchar *path = g_strdup(g_getenv("PATH")); + if (path == NULL) { + /* There is no `PATH' in the environment. + The default search path is the current directory */ + path = g_strdup(G_SEARCHPATH_SEPARATOR_S); + } + gchar *orig_path = path; + + for (; path != NULL;) { + gchar *const local_path = path; + path = g_utf8_strchr(path, -1, G_SEARCHPATH_SEPARATOR); + if (path == NULL) { + break; + } + /* Not sure whether this is UTF8 happy, but it would seem + like it considering that I'm searching (and finding) + the ':' character */ + if (path != local_path && path != NULL) { + path[0] = '\0'; + path++; + } else { + path = NULL; + } + + gchar *final_name; + if (local_path == '\0') { + final_name = g_strdup(command); + } else { + final_name = g_build_filename(local_path, command, NULL); + } + + if (Inkscape::IO::file_test(final_name, G_FILE_TEST_EXISTS)) { + g_free(final_name); + g_free(orig_path); + return TRUE; + } + + g_free(final_name); + } + + return FALSE; +} + +/** + \return none + \brief This function 'loads' an extention, basically it determines + the full command for the extention and stores that. + \param module The extention to be loaded. + + The most difficult part about this function is finding the actual + command through all of the Reprs. Basically it is hidden down a + couple of layers, and so the code has to move down too. When + the command is actually found, it has its relative directory + solved. + + At that point all of the loops are exited, and there is an + if statement to make sure they didn't exit because of not finding + the command. If that's the case, the extention doesn't get loaded + and should error out at a higher level. +*/ + +bool +Script::load(Inkscape::Extension::Extension *module) +{ + if (module->loaded()) { + return TRUE; + } + + helper_extension = NULL; + + /* This should probably check to find the executable... */ + Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr()); + gchar *command_text = NULL; + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "script")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "command")) { + command_text = solve_reldir(child_repr); + + const gchar * interpretstr = child_repr->attribute("interpreter"); + if (interpretstr != NULL) { + struct interpreter_t { + gchar * identity; + gchar * prefstring; + gchar * defaultval; + }; + const interpreter_t interpreterlst[] = { + {"perl", "perl-interpreter", "perl"}, + {"python", "python-interpreter", "python"}, + {"ruby", "ruby-interpreter", "ruby"}, + {"shell", "shell-interpreter", "sh"} + }; /* Change count below if you change structure */ + for (unsigned int i = 0; i < 4; i++) { + if (!strcmp(interpretstr, interpreterlst[i].identity)) { + const gchar * insertText = interpreterlst[i].defaultval; + if (prefs_get_string_attribute("extensions", interpreterlst[i].prefstring) != NULL) + insertText = prefs_get_string_attribute("extensions", interpreterlst[i].prefstring); +#ifdef _WIN32 + else { + char szExePath[MAX_PATH]; + char szCurrentDir[MAX_PATH]; + GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir); + if (reinterpret_cast(FindExecutable(command_text, szCurrentDir, szExePath)) > 32) + insertText = szExePath; + } +#endif + + gchar * temp = command_text; + command_text = g_strconcat(insertText, " ", temp, NULL); + g_free(temp); + + break; + } + } + } + } + if (!strcmp(child_repr->name(), "helper_extension")) { + helper_extension = g_strdup(sp_repr_children(child_repr)->content()); + } + child_repr = sp_repr_next(child_repr); + } + + break; + } + child_repr = sp_repr_next(child_repr); + } + + g_return_val_if_fail(command_text != NULL, FALSE); + + if (command != NULL) + g_free(command); + command = command_text; + + return TRUE; +} + +/** + \return None. + \brief Unload this puppy! + \param module Extension to be unloaded. + + This function just sets the module to unloaded. It free's the + command if it has been allocated. +*/ +void +Script::unload(Inkscape::Extension::Extension *module) +{ + if (command != NULL) { + g_free(command); + command = NULL; + } + if (helper_extension != NULL) { + g_free(helper_extension); + helper_extension = NULL; + } + + return; +} + +/** + \return Whether the check passed or not + \brief Check every dependency that was given to make sure we should keep this extension + \param module The Extension in question + +*/ +bool +Script::check(Inkscape::Extension::Extension *module) +{ + Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr()); + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "script")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "check")) { + gchar *command_text = solve_reldir(child_repr); + if (command_text != NULL) { + /* I've got the command */ + bool existance; + + existance = check_existance(command_text); + g_free(command_text); + if (!existance) + return FALSE; + } + } + + if (!strcmp(child_repr->name(), "helper_extension")) { + gchar const *helper = sp_repr_children(child_repr)->content(); + if (Inkscape::Extension::db.get(helper) == NULL) { + return FALSE; + } + } + + child_repr = sp_repr_next(child_repr); + } + + break; + } + child_repr = sp_repr_next(child_repr); + } + + return TRUE; +} + +/** + \return A dialog for preferences + \brief A stub funtion right now + \param module Module who's preferences need getting + \param filename Hey, the file you're getting might be important + + This function should really do something, right now it doesn't. +*/ +Gtk::Widget * +Script::prefs_input(Inkscape::Extension::Input *module, gchar const *filename) +{ + /*return module->autogui(); */ + return NULL; +} + +/** + \return A dialog for preferences + \brief A stub funtion right now + \param module Module whose preferences need getting + + This function should really do something, right now it doesn't. +*/ +Gtk::Widget * +Script::prefs_output(Inkscape::Extension::Output *module) +{ + /*return module->autogui();*/ + return NULL; +} + +/** + \return A dialog for preferences + \brief A stub funtion right now + \param module Module who's preferences need getting + + This function should really do something, right now it doesn't. +*/ +Gtk::Widget * +Script::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *view) +{ + return module->autogui(); +} + +/** + \return A new document that has been opened + \brief This function uses a filename that is put in, and calls + the extension's command to create an SVG file which is + returned. + \param module Extension to use. + \param filename File to open. + + First things first, this function needs a temporary file name. To + create on of those the function g_file_open_tmp is used with + the header of ink_ext_. + + The extension is then executed using the 'execute' function + with the filname coming in, and the temporary filename. After + That executing, the SVG should be in the temporary file. + + Finally, the temporary file is opened using the SVG input module and + a document is returned. That document has its filename set to + the incoming filename (so that it's not the temporary filename). + That document is then returned from this function. +*/ +SPDocument * +Script::open(Inkscape::Extension::Input *module, gchar const *filename) +{ + int data_read = 0; + gint tempfd; + gchar *tempfilename_out; + + // FIXME: process the GError instead of passing NULL + if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) { + /* Error, couldn't create temporary filename */ + if (errno == EINVAL) { + /* The last six characters of template were not XXXXXX. Now template is unchanged. */ + perror("Extension::Script: template for filenames is misconfigured.\n"); + exit(-1); + } else if (errno == EEXIST) { + /* Now the contents of template are undefined. */ + perror("Extension::Script: Could not create a unique temporary filename\n"); + return NULL; + } else { + perror("Extension::Script: Unknown error creating temporary filename\n"); + exit(-1); + } + } + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError *error = NULL; + gchar *local_filename = g_filename_from_utf8( filename, + -1, &bytesRead, &bytesWritten, &error); + + data_read = execute(command, local_filename, tempfilename_out); + g_free(local_filename); + + SPDocument *mydoc = NULL; + if (data_read > 10) { + if (helper_extension == NULL) { + mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out); + } else { + mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(helper_extension), tempfilename_out); + } + } + + if (mydoc != NULL) + sp_document_set_uri(mydoc, (const gchar *)filename); + + // make sure we don't leak file descriptors from g_file_open_tmp + close(tempfd); + // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name + unlink(tempfilename_out); + g_free(tempfilename_out); + + return mydoc; +} + +/** + \return none + \brief This function uses an extention to save a document. It first + creates an SVG file of the document, and then runs it through + the script. + \param module Extention to be used + \param doc Document to be saved + \param filename The name to save the final file as + + Well, at some point people need to save - it is really what makes + the entire application useful. And, it is possible that someone + would want to use an extetion for this, so we need a function to + do that eh? + + First things first, the document is saved to a temporary file that + is an SVG file. To get the temporary filename g_file_open_tmp is used with + ink_ext_ as a prefix. Don't worry, this file gets deleted at the + end of the function. + + After we have the SVG file, then extention_execute is called with + the temporary file name and the final output filename. This should + put the output of the script into the final output file. We then + delete the temporary file. +*/ +void +Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename) +{ + gint tempfd; + gchar *tempfilename_in; + // FIXME: process the GError instead of passing NULL + if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) { + /* Error, couldn't create temporary filename */ + if (errno == EINVAL) { + /* The last six characters of template were not XXXXXX. Now template is unchanged. */ + perror("Extension::Script: template for filenames is misconfigured.\n"); + exit(-1); + } else if (errno == EEXIST) { + /* Now the contents of template are undefined. */ + perror("Extension::Script: Could not create a unique temporary filename\n"); + return; + } else { + perror("Extension::Script: Unknown error creating temporary filename\n"); + exit(-1); + } + } + + if (helper_extension == NULL) { + Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), doc, tempfilename_in, FALSE, FALSE, FALSE); + } else { + Inkscape::Extension::save(Inkscape::Extension::db.get(helper_extension), doc, tempfilename_in, FALSE, FALSE, FALSE); + } + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError *error = NULL; + gchar *local_filename = g_filename_from_utf8( filename, + -1, &bytesRead, &bytesWritten, &error); + + execute(command, tempfilename_in, local_filename); + + g_free(local_filename); + + // make sure we don't leak file descriptors from g_file_open_tmp + close(tempfd); + // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name + unlink(tempfilename_in); + g_free(tempfilename_in); +} + +/** + \return none + \brief This function uses an extention as a effect on a document. + \param module Extention to effect with. + \param doc Document to run through the effect. + + This function is a little bit trickier than the previous two. It + needs two temporary files to get it's work done. Both of these + files have random names created for them using the g_file_open_temp function + with the sp_ext_ prefix in the temporary directory. Like the other + functions, the temporary files are deleted at the end. + + To save/load the two temporary documents (both are SVG) the internal + modules for SVG load and save are used. They are both used through + the module system function by passing their keys into the functions. + + The command itself is built a little bit differently than in other + functions because the effect support selections. So on the command + line a list of all the ids that are selected is included. Currently, + this only works for a single selected object, but there will be more. + The command string is filled with the data, and then after the execution + it is freed. + + The execute function is used at the core of this function + to execute the Script on the two SVG documents (actually only one + exists at the time, the other is created by that script). At that + point both should be full, and the second one is loaded. +*/ +void +Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc) +{ + int data_read = 0; + SPDocument * mydoc = NULL; + gint tempfd_in; + gchar *tempfilename_in; + + // FIXME: process the GError instead of passing NULL + if ((tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) { + /* Error, couldn't create temporary filename */ + if (errno == EINVAL) { + /* The last six characters of template were not XXXXXX. Now template is unchanged. */ + perror("Extension::Script: template for filenames is misconfigured.\n"); + exit(-1); + } else if (errno == EEXIST) { + /* Now the contents of template are undefined. */ + perror("Extension::Script: Could not create a unique temporary filename\n"); + return; + } else { + perror("Extension::Script: Unknown error creating temporary filename\n"); + exit(-1); + } + } + + gint tempfd_out; + gchar *tempfilename_out; + // FIXME: process the GError instead of passing NULL + if ((tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) { + /* Error, couldn't create temporary filename */ + if (errno == EINVAL) { + /* The last six characters of template were not XXXXXX. Now template is unchanged. */ + perror("Extension::Script: template for filenames is misconfigured.\n"); + exit(-1); + } else if (errno == EEXIST) { + /* Now the contents of template are undefined. */ + perror("Extension::Script: Could not create a unique temporary filename\n"); + return; + } else { + perror("Extension::Script: Unknown error creating temporary filename\n"); + exit(-1); + } + } + + Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), + doc->doc(), tempfilename_in, FALSE, FALSE, FALSE); + + Glib::ustring local_command(command); + + /* fixme: Should be some sort of checking here. Don't know how to do this with structs instead + * of classes. */ + SPDesktop *desktop = (SPDesktop *) doc; + if (desktop != NULL) { + using Inkscape::Util::GSListConstIterator; + GSListConstIterator selected = SP_DT_SELECTION(desktop)->itemList(); + while ( selected != NULL ) { + local_command += " --id="; + local_command += SP_OBJECT_ID(*selected); + ++selected; + } + } + + Glib::ustring * paramString = module->paramString(); + local_command += *paramString; + delete paramString; + + // std::cout << local_command << std::endl; + + data_read = execute(local_command.c_str(), tempfilename_in, tempfilename_out); + + if (data_read > 10) + mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out); + + // make sure we don't leak file descriptors from g_file_open_tmp + close(tempfd_in); + close(tempfd_out); + // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name + unlink(tempfilename_in); + g_free(tempfilename_in); + unlink(tempfilename_out); + g_free(tempfilename_out); + + /* Do something with mydoc.... */ + if (mydoc != NULL) { + doc->doc()->emitReconstructionStart(); + copy_doc(doc->doc()->rroot, mydoc->rroot); + doc->doc()->emitReconstructionFinish(); + mydoc->release(); + } +} + + +/** + \brief A function to take all the svg elements from one document + and put them in another. + \param oldroot The root node of the document to be replaced + \param newroot The root node of the document to replace it with + + This function first deletes all of the data in the old document. It + does this by creating a list of what needs to be deleted, and then + goes through the list. This two pass approach removes issues with + the list being change while parsing through it. Lots of nasty bugs. + + Then, it goes through the new document, duplicating all of the + elements and putting them into the old document. The copy + is then complete. +*/ +void +Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) +{ + std::vector delete_list; + for (Inkscape::XML::Node * child = oldroot->firstChild(); + child != NULL; + child = child->next()) { + if (!strcmp("sodipodi:namedview", child->name())) + continue; + if (!strcmp("svg:defs", child->name())) + continue; + delete_list.push_back(child); + } + for (unsigned int i = 0; i < delete_list.size(); i++) + sp_repr_unparent(delete_list[i]); + + for (Inkscape::XML::Node * child = newroot->firstChild(); + child != NULL; + child = child->next()) { + if (!strcmp("sodipodi:namedview", child->name())) + continue; + if (!strcmp("svg:defs", child->name())) + continue; + oldroot->appendChild(child->duplicate()); + } + + /** \todo Restore correct layer */ + /** \todo Restore correct selection */ +} + +/* Helper class used by Script::execute */ +class pipe_t { +public: + /* These functions set errno if they return false. + I'm not sure whether that's a good idea or not, but it should be reasonably + straightforward to change it if needed. */ + bool open(char *command, char const *errorFile, int mode); + bool close(); + + /* These return the number of bytes read/written. */ + size_t read(void *buffer, size_t size); + size_t write(void const *buffer, size_t size); + + enum { + mode_read = 1 << 0, + mode_write = 1 << 1, + }; + +private: +#ifdef WIN32 + /* This is used to translate win32 errors into errno errors. + It only recognizes a few win32 errors for the moment though. */ + static int translate_error(DWORD err); + + HANDLE hpipe; +#else + FILE *ppipe; +#endif +}; + +/** + \return none + \brief This is the core of the extension file as it actually does + the execution of the extension. + \param in_command The command to be executed + \param filein Filename coming in + \param fileout Filename of the out file + \return Number of bytes that were read into the output file. + + The first thing that this function does is build the command to be + executed. This consists of the first string (in_command) and then + the filename for input (filein). This file is put on the command + line. + + The next thing is that this function does is open a pipe to the + command and get the file handle in the ppipe variable. It then + opens the output file with the output file handle. Both of these + operations are checked extensively for errors. + + After both are opened, then the data is copied from the output + of the pipe into the file out using fread and fwrite. These two + functions are used because of their primitive nature they make + no assumptions about the data. A buffer is used in the transfer, + but the output of fread is stored so the exact number of bytes + is handled gracefully. + + At the very end (after the data has been copied) both of the files + are closed, and we return to what we were doing. +*/ +int +Script::execute (const gchar * in_command, const gchar * filein, const gchar * fileout) +{ + g_return_val_if_fail(in_command != NULL, 0); + // printf("Executing: %s\n", in_command); + + gchar * errorFile; + gint errorFileNum; + errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &errorFile, NULL); + if (errorFileNum != 0) { + close(errorFileNum); + } else { + g_free(errorFile); + errorFile = NULL; + } + + char *command = g_strdup_printf("%s \"%s\"", in_command, filein); + // std::cout << "Command to run: " << command << std::endl; + + pipe_t pipe; + bool open_success = pipe.open(command, errorFile, pipe_t::mode_read); + g_free(command); + + /* Run script */ + if (!open_success) { + /* Error - could not open pipe - check errno */ + if (errno == EINVAL) { + perror("Extension::Script: Invalid mode argument in popen\n"); + } else if (errno == ECHILD) { + perror("Extension::Script: Cannot obtain child extension status in popen\n"); + } else { + perror("Extension::Script: Unknown error for popen\n"); + } + return 0; + } + + Inkscape::IO::dump_fopen_call(fileout, "J"); + FILE *pfile = Inkscape::IO::fopen_utf8name(fileout, "w"); + + if (pfile == NULL) { + /* Error - could not open file */ + if (errno == EINVAL) { + perror("Extension::Script: The mode provided to fopen was invalid\n"); + } else { + perror("Extension::Script: Unknown error attempting to open temporary file\n"); + } + return 0; + } + + /* Copy pipe output to a temporary file */ + int amount_read = 0; + char buf[BUFSIZE]; + int num_read; + while ((num_read = pipe.read(buf, BUFSIZE)) != 0) { + amount_read += num_read; + fwrite(buf, 1, num_read, pfile); + } + + /* Close file */ + if (fclose(pfile) == EOF) { + if (errno == EBADF) { + perror("Extension::Script: The filedescriptor for the temporary file is invalid\n"); + return 0; + } else { + perror("Extension::Script: Unknown error closing temporary file\n"); + } + } + + /* Close pipe */ + if (!pipe.close()) { + if (errno == EINVAL) { + perror("Extension::Script: Invalid mode set for pclose\n"); + } else if (errno == ECHILD) { + perror("Extension::Script: Could not obtain child status for pclose\n"); + } else { + if (errorFile != NULL) { + checkStderr(errorFile, Gtk::MESSAGE_ERROR, + _("Inkscape has received an error from the script that it called. " + "The text returned with the error is included below. " + "Inkscape will continue working, but the action you requested has been cancelled.")); + } else { + perror("Extension::Script: Unknown error for pclose\n"); + } + } + /* Could be a lie, but if there is an error, we don't want + * to count on what was read being good */ + amount_read = 0; + } else { + if (errorFile != NULL) { + checkStderr(errorFile, Gtk::MESSAGE_INFO, + _("Inkscape has received additional data from the script executed. " + "The script did not return an error, but this may indicate the results will not be as expected.")); + } + } + + if (errorFile != NULL) { + unlink(errorFile); + g_free(errorFile); + } + + return amount_read; +} + +/** \brief This function checks the stderr file, and if it has data, + shows it in a warning dialog to the user + \param filename Filename of the stderr file +*/ +void +Script::checkStderr (gchar * filename, Gtk::MessageType type, gchar * message) +{ + // magic win32 crlf->lf conversion means the file length is not the same as + // the text length, but luckily gtk will accept crlf in textviews so we can + // just use binary mode + std::ifstream stderrf (filename, std::ios_base::in | std::ios_base::binary); + if (!stderrf.is_open()) return; + + stderrf.seekg(0, std::ios::end); + int length = stderrf.tellg(); + if (0 == length) return; + stderrf.seekg(0, std::ios::beg); + + Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true); + warning.set_resizable(true); + + Gtk::VBox * vbox = warning.get_vbox(); + + /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */ + Gtk::TextView * textview = new Gtk::TextView(); + textview->set_editable(false); + textview->set_wrap_mode(Gtk::WRAP_WORD); + textview->show(); + + char * buffer = new char [length]; + stderrf.read(buffer, length); + textview->get_buffer()->set_text(buffer, buffer + length); + delete buffer; + stderrf.close(); + + Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow(); + scrollwindow->add(*textview); + scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + scrollwindow->set_shadow_type(Gtk::SHADOW_IN); + scrollwindow->show(); + + vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */); + + warning.run(); + + return; +} + +#ifdef WIN32 + +bool pipe_t::open(char *command, char const *errorFile, int mode_p) { + HANDLE pipe_write; + + // Create pipe + { + SECURITY_ATTRIBUTES secattrs; + ZeroMemory(&secattrs, sizeof(secattrs)); + secattrs.nLength = sizeof(secattrs); + secattrs.lpSecurityDescriptor = 0; + secattrs.bInheritHandle = TRUE; + HANDLE t_pipe_read = 0; + if ( !CreatePipe(&t_pipe_read, &pipe_write, &secattrs, 0) ) { + errno = translate_error(GetLastError()); + return false; + } + // This duplicate handle makes the read pipe uninheritable + if ( !DuplicateHandle(GetCurrentProcess(), t_pipe_read, GetCurrentProcess(), &hpipe, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) ) { + int en = translate_error(GetLastError()); + CloseHandle(t_pipe_read); + CloseHandle(pipe_write); + errno = en; + return false; + } + } + // Open stderr file + HANDLE hStdErrFile = CreateFile(errorFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); + HANDLE hInheritableStdErr; + DuplicateHandle(GetCurrentProcess(), hStdErrFile, GetCurrentProcess(), &hInheritableStdErr, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + + // Create process + { + PROCESS_INFORMATION procinfo; + STARTUPINFO startupinfo; + ZeroMemory(&procinfo, sizeof(procinfo)); + ZeroMemory(&startupinfo, sizeof(startupinfo)); + startupinfo.cb = sizeof(startupinfo); + //startupinfo.lpReserved = 0; + //startupinfo.lpDesktop = 0; + //startupinfo.lpTitle = 0; + startupinfo.dwFlags = STARTF_USESTDHANDLES; + startupinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startupinfo.hStdOutput = pipe_write; + startupinfo.hStdError = hInheritableStdErr; + + if ( !CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &startupinfo, &procinfo) ) { + errno = translate_error(GetLastError()); + return false; + } + CloseHandle(procinfo.hThread); + CloseHandle(procinfo.hProcess); + } + + // Close our copy of the write handle + CloseHandle(hInheritableStdErr); + CloseHandle(pipe_write); + + return true; +} + +bool pipe_t::close() { + BOOL retval = CloseHandle(hpipe); + if ( !retval ) { + errno = translate_error(GetLastError()); + } + return retval != FALSE; +} + +size_t pipe_t::read(void *buffer, size_t size) { + DWORD bytes_read = 0; + ReadFile(hpipe, buffer, size, &bytes_read, 0); + return bytes_read; +} + +size_t pipe_t::write(void const *buffer, size_t size) { + DWORD bytes_written = 0; + WriteFile(hpipe, buffer, size, &bytes_written, 0); + return bytes_written; +} + +int pipe_t::translate_error(DWORD err) { + switch (err) { + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_PARAMETER: + return EINVAL; + default: + return 0; + } +} + +#else // Win32 + +bool pipe_t::open(char *command, char const *errorFile, int mode_p) { + char popen_mode[4] = {0,0,0,0}; + char *popen_mode_cur = popen_mode; + + if ( (mode_p & mode_read) != 0 ) { + *popen_mode_cur++ = 'r'; + } + + if ( (mode_p & mode_write) != 0 ) { + *popen_mode_cur++ = 'w'; + } + + /* Get the commandline to be run */ + if (errorFile != NULL) { + char * temp; + temp = g_strdup_printf("%s 2> %s", command, errorFile); + ppipe = popen(temp, popen_mode); + g_free(temp); + } else + ppipe = popen(command, popen_mode); + + return ppipe != NULL; +} + +bool pipe_t::close() { + return fclose(ppipe) == 0; +} + +size_t pipe_t::read(void *buffer, size_t size) { + return fread(buffer, 1, size, ppipe); +} + +size_t pipe_t::write(void const *buffer, size_t size) { + return fwrite(buffer, 1, size, ppipe); +} + +#endif // (Non-)Win32 + + +} /* Inkscape */ +} /* module */ +} /* Implementation */ + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/implementation/script.h b/src/extension/implementation/script.h new file mode 100644 index 000000000..e13ffbb13 --- /dev/null +++ b/src/extension/implementation/script.h @@ -0,0 +1,86 @@ +/* + * Code for handling extensions (i.e., scripts) + * + * Authors: + * Bryce Harrington + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ +#define __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ + +#include "implementation.h" +#include + +namespace Inkscape { +namespace XML { +class Node; +} +} + + +namespace Inkscape { +namespace Extension { +namespace Implementation { + +class Script : public Implementation { +private: + gchar * command; /**< The command that has been dirived from + the configuration file with appropriate + directories */ + gchar * helper_extension; + /**< This is the extension that will be used + as the helper to read in or write out the + data */ + /** This function actually does the work, everything else is preparing + for this function. It is the core here */ + int execute (gchar const *command, + gchar const *filein, + gchar const *fileout); + /** Just a quick function to find and resolve relative paths for + the incoming scripts */ + gchar * solve_reldir (Inkscape::XML::Node *reprin); + bool check_existance (gchar const *command); + void copy_doc (Inkscape::XML::Node * olddoc, Inkscape::XML::Node * newdoc); + void checkStderr (gchar * filename, Gtk::MessageType type, gchar * message); + +public: + Script (void); + virtual bool load (Inkscape::Extension::Extension *module); + virtual void unload (Inkscape::Extension::Extension *module); + virtual bool check (Inkscape::Extension::Extension *module); + virtual Gtk::Widget * prefs_input (Inkscape::Extension::Input *module, + gchar const *filename); + virtual SPDocument * open (Inkscape::Extension::Input *module, + gchar const *filename); + virtual Gtk::Widget * prefs_output (Inkscape::Extension::Output *module); + virtual void save (Inkscape::Extension::Output *module, + SPDocument *doc, + gchar const *filename); + virtual Gtk::Widget * + prefs_effect (Inkscape::Extension::Effect *module, + Inkscape::UI::View::View * view); + virtual void effect (Inkscape::Extension::Effect *module, + Inkscape::UI::View::View *doc); + +}; + +} /* Inkscape */ +} /* Extension */ +} /* Implementation */ +#endif /* __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/init.cpp b/src/extension/init.cpp new file mode 100644 index 000000000..c913def61 --- /dev/null +++ b/src/extension/init.cpp @@ -0,0 +1,243 @@ +/* + * This is what gets executed to initialize all of the modules. For + * the internal modules this invovles executing their initialization + * functions, for external ones it involves reading their .spmodule + * files and bringing them into Sodipodi. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "path-prefix.h" + + +#include "inkscape.h" +#include + +#include "system.h" +#include "db.h" +#include "internal/svgz.h" +#include "internal/ps.h" +#ifdef WITH_GNOME_PRINT +# include "internal/gnome.h" +#endif +#ifdef WIN32 +# include "internal/win32.h" +#endif +#include "internal/ps-out.h" +#include "internal/pov-out.h" +#include "internal/latex-pstricks-out.h" +#include "internal/latex-pstricks.h" +#include "internal/eps-out.h" +#include "internal/gdkpixbuf-input.h" +#include "internal/bluredge.h" +#include "internal/gimpgrad.h" +#include "internal/grid.h" +#include "prefs-utils.h" +#include "io/sys.h" + +extern gboolean inkscape_app_use_gui( Inkscape::Application const *app ); + +namespace Inkscape { +namespace Extension { + +/** This is the extention that all files are that are pulled from + the extension directory and parsed */ +#define SP_MODULE_EXTENSION "inx" + +static void build_module_from_dir(gchar const *dirname); +static void check_extensions(); + +/** + * \return none + * \brief Examines the given string preference and checks to see + * that at least one of the registered extensions matches + * it. If not, a default is assigned. + * \param pref_path Preference path to load + * \param pref_attr Attribute to load from the preference + * \param pref_default Default string to set + * \param extension_family List of extensions to search + */ +static void +update_pref(gchar const *pref_path, gchar const *pref_attr, + gchar const *pref_default) // , GSList *extension_family) +{ + gchar const *pref = prefs_get_string_attribute(pref_path,pref_attr); + /* + gboolean missing=TRUE; + for (GSList *list = extension_family; list; list = g_slist_next(list)) { + g_assert( list->data ); + + Inkscape::Extension *extension; + extension = reinterpret_cast(list->data); + + if (!strcmp(extension->get_id(),pref)) missing=FALSE; + } + */ + if (!Inkscape::Extension::db.get( pref ) /*missing*/) { + prefs_set_string_attribute(pref_path, pref_attr, pref_default); + } +} + +/** + * Invokes the init routines for internal modules. + * + * This should be a list of all the internal modules that need to initialized. This is just a + * convinent place to put them. Also, this function calls build_module_from_dir to parse the + * Inkscape extensions directory. + */ +void +init() +{ + /* TODO: Change to Internal */ + Internal::Svg::init(); + Internal::Svgz::init(); + Internal::PsOutput::init(); + Internal::EpsOutput::init(); + Internal::PrintPS::init(); +#ifdef WITH_GNOME_PRINT + Internal::PrintGNOME::init(); +#endif +#ifdef WIN32 + Internal::PrintWin32::init(); +#endif + Internal::PovOutput::init(); + Internal::PrintLatex::init(); + Internal::LatexOutput::init(); + + /* Effects */ + Internal::BlurEdge::init(); + Internal::GimpGrad::init(); + Internal::Grid::init(); + + /* Load search path for extensions */ + if (Inkscape::Extension::Extension::search_path.size() == 0) + { + Inkscape::Extension::Extension::search_path.push_back(profile_path("extensions")); + Inkscape::Extension::Extension::search_path.push_back(g_strdup(INKSCAPE_EXTENSIONDIR)); + } + + for (unsigned int i=0; ideactivated() && !in_plug->check()) { + in_plug->deactivate(); + (*count)++; + } +} + +static void +check_extensions() +{ + int count = 1; + bool anyfail = false; + // int pass = 0; + + Inkscape::Extension::Extension::error_file_open(); + while (count != 0) { + // printf("Check extensions pass %d\n", pass++); + count = 0; + db.foreach(check_extensions_internal, (gpointer)&count); + if (count != 0) anyfail = true; + } + Inkscape::Extension::Extension::error_file_close(); +} + +} } /* namespace Inkscape::Extension */ + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/init.h b/src/extension/init.h new file mode 100644 index 000000000..6ccc85aea --- /dev/null +++ b/src/extension/init.h @@ -0,0 +1,36 @@ +/* + * This is what gets executed to initialize all of the modules. For + * the internal modules this invovles executing their initialization + * functions, for external ones it involves reading their .spmodule + * files and bringing them into Sodipodi. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_INIT_H__ +#define INKSCAPE_EXTENSION_INIT_H__ + +namespace Inkscape { +namespace Extension { + +void init (void); + +} } /* namespace Inkscape::Extension */ + +#endif /* INKSCAPE_EXTENSION_INIT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/input.cpp b/src/extension/input.cpp new file mode 100644 index 000000000..081cc4fb2 --- /dev/null +++ b/src/extension/input.cpp @@ -0,0 +1,261 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "implementation/implementation.h" +#include "timer.h" +#include "input.h" +#include "io/sys.h" +#include "prefdialog.h" + +/* Inkscape::Extension::Input */ + +namespace Inkscape { +namespace Extension { + +/** + \return None + \brief Builds a SPModuleInput object from a XML description + \param module The module to be initialized + \param repr The XML description in a Inkscape::XML::Node tree + + Okay, so you want to build a SPModuleInput object. + + This function first takes and does the build of the parent class, + which is SPModule. Then, it looks for the section of the + XML description. Under there should be several fields which + describe the input module to excruciating detail. Those are parsed, + copied, and put into the structure that is passed in as module. + Overall, there are many levels of indentation, just to handle the + levels of indentation in the XML file. +*/ +Input::Input (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : Extension(in_repr, in_imp) +{ + mimetype = NULL; + extension = NULL; + filetypename = NULL; + filetypetooltip = NULL; + output_extension = NULL; + + if (repr != NULL) { + Inkscape::XML::Node * child_repr; + + child_repr = sp_repr_children(repr); + + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "input")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + char const * chname = child_repr->name(); + if (chname[0] == '_') /* Allow _ for translation of tags */ + chname++; + if (!strcmp(chname, "extension")) { + g_free (extension); + extension = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "mimetype")) { + g_free (mimetype); + mimetype = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "filetypename")) { + g_free (filetypename); + filetypename = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "filetypetooltip")) { + g_free (filetypetooltip); + filetypetooltip = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "output_extension")) { + g_free (output_extension); + output_extension = g_strdup(sp_repr_children(child_repr)->content()); + } + + child_repr = sp_repr_next(child_repr); + } + + break; + } + + child_repr = sp_repr_next(child_repr); + } + + } + + return; +} + +/** + \return None + \brief Destroys an Input extension +*/ +Input::~Input (void) +{ + g_free(mimetype); + g_free(extension); + g_free(filetypename); + g_free(filetypetooltip); + g_free(output_extension); + return; +} + +/** + \return Whether this extension checks out + \brief Validate this extension + + This function checks to make sure that the input extension has + a filename extension and a MIME type. Then it calls the parent + class' check function which also checks out the implmentation. +*/ +bool +Input::check (void) +{ + if (extension == NULL) + return FALSE; + if (mimetype == NULL) + return FALSE; + + return Extension::check(); +} + +/** + \return A new document + \brief This function creates a document from a file + \param uri The filename to create the document from + + This function acts as the first step in creating a new document + from a file. The first thing that this does is make sure that the + file actually exists. If it doesn't, a NULL is returned. If the + file exits, then it is opened using the implmentation of this extension. + + After opening the document the output_extension is set. What this + accomplishes is that save can try to use an extension that supports + the same fileformat. So something like opening and saveing an + Adobe Illustrator file can be transparent (not recommended, but + transparent). This is all done with undo being turned off. +*/ +SPDocument * +Input::open (const gchar *uri) +{ + if (!loaded()) { + set_state(Extension::STATE_LOADED); + } + if (!loaded()) { + return NULL; + } + timer->touch(); + + SPDocument * doc = NULL; + +#ifdef WITH_GNOME_VFS + doc = imp->open(this, uri); +#else + if (Inkscape::IO::file_test(uri, G_FILE_TEST_EXISTS)) { + doc = imp->open(this, uri); + } +#endif + + if (doc != NULL) { + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + gboolean saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive (doc, FALSE); + repr->setAttribute("inkscape:output_extension", output_extension); + sp_document_set_undo_sensitive (doc, saved); + } + + return doc; +} + +/** + \return IETF mime-type for the extension + \brief Get the mime-type that describes this extension +*/ +gchar * +Input::get_mimetype(void) +{ + return mimetype; +} + +/** + \return Filename extension for the extension + \brief Get the filename extension for this extension +*/ +gchar * +Input::get_extension(void) +{ + return extension; +} + +/** + \return The name of the filetype supported + \brief Get the name of the filetype supported +*/ +gchar * +Input::get_filetypename(void) +{ + if (filetypename != NULL) + return filetypename; + else + return get_name(); +} + +/** + \return Tooltip giving more information on the filetype + \brief Get the tooltip for more information on the filetype +*/ +gchar * +Input::get_filetypetooltip(void) +{ + return filetypetooltip; +} + +/** + \return A dialog to get settings for this extension + \brief Create a dialog for preference for this extension + + Calls the implementation to get the preferences. +*/ +bool +Input::prefs (const gchar *uri) +{ + if (!loaded()) + set_state(Extension::STATE_LOADED); + if (!loaded()) return false; + + Gtk::Widget * controls; + controls = imp->prefs_input(this, uri); + if (controls == NULL) { + // std::cout << "No preferences for Input" << std::endl; + return true; + } + + PrefDialog * dialog = new PrefDialog(this->get_name(), controls); + int response = dialog->run(); + dialog->hide(); + + delete dialog; + + if (response == Gtk::RESPONSE_OK) return true; + return false; +} + +} } /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/input.h b/src/extension/input.h new file mode 100644 index 000000000..55d807ce2 --- /dev/null +++ b/src/extension/input.h @@ -0,0 +1,60 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#ifndef INKSCAPE_EXTENSION_INPUT_H__ +#define INKSCAPE_EXTENSION_INPUT_H__ + +#include +#include "extension.h" +#include "xml/repr.h" +#include "document.h" +#include + +namespace Inkscape { +namespace Extension { + +class Input : public Extension { + gchar *mimetype; /**< What is the mime type this inputs? */ + gchar *extension; /**< The extension of the input files */ + gchar *filetypename; /**< A userfriendly name for the file type */ + gchar *filetypetooltip; /**< A more detailed description of the filetype */ + +public: /* this is a hack for this release, this will be private shortly */ + gchar *output_extension; /**< Setting of what output extension should be used */ + +public: + class open_failed {}; /**< Generic failure for an undescribed reason */ + class no_extension_found {}; /**< Failed because we couldn't find an extension to match the filename */ + + Input (Inkscape::XML::Node * in_repr, + Implementation::Implementation * in_imp); + virtual ~Input (void); + virtual bool check (void); + SPDocument * open (gchar const *uri); + gchar * get_mimetype (void); + gchar * get_extension (void); + gchar * get_filetypename (void); + gchar * get_filetypetooltip (void); + bool prefs (gchar const *uri); +}; + +} } /* namespace Inkscape, Extension */ +#endif /* INKSCAPE_EXTENSION_INPUT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/.cvsignore b/src/extension/internal/.cvsignore new file mode 100644 index 000000000..e8014d011 --- /dev/null +++ b/src/extension/internal/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +makefile +.dirstamp diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert new file mode 100644 index 000000000..c6c7aefe2 --- /dev/null +++ b/src/extension/internal/Makefile_insert @@ -0,0 +1,42 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +extension/internal/all: extension/internal/libinternal.a + +extension/internal/clean: + rm -f extension/internal/libinternal.a $(extension_internal_libinternal_OBJECTS) + +if USE_GNOME_PRINT +extension_internal_gnome_print_sources = \ + extension/internal/gnome.cpp \ + extension/internal/gnome.h +endif + +extension_internal_libinternal_a_SOURCES = \ + extension/internal/bluredge.h \ + extension/internal/bluredge.cpp \ + extension/internal/grid.h \ + extension/internal/grid.cpp \ +\ + extension/internal/gimpgrad.h \ + extension/internal/gimpgrad.cpp \ + extension/internal/svg.h \ + extension/internal/svg.cpp \ + extension/internal/svgz.h \ + extension/internal/svgz.cpp \ + extension/internal/ps.h \ + extension/internal/ps.cpp \ + extension/internal/ps-out.h \ + extension/internal/ps-out.cpp \ + extension/internal/eps-out.h \ + extension/internal/eps-out.cpp \ + extension/internal/gdkpixbuf-input.h \ + extension/internal/gdkpixbuf-input.cpp \ + extension/internal/pov-out.cpp \ + extension/internal/pov-out.h \ + extension/internal/latex-pstricks.cpp \ + extension/internal/latex-pstricks.h \ + extension/internal/latex-pstricks-out.cpp \ + extension/internal/latex-pstricks-out.h \ +\ + $(extension_internal_gnome_print_sources) + diff --git a/src/extension/internal/bluredge.cpp b/src/extension/internal/bluredge.cpp new file mode 100644 index 000000000..3f230439a --- /dev/null +++ b/src/extension/internal/bluredge.cpp @@ -0,0 +1,157 @@ +/** + \file bluredge.cpp + + A plug-in to add an effect to blur the edges of an object. +*/ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "desktop.h" +#include "selection.h" +#include "helper/action.h" +#include "prefs-utils.h" +#include "path-chemistry.h" +#include "sp-item.h" + +#include "util/glib-list-iterators.h" + +#include "extension/effect.h" +#include "extension/system.h" + + +#include "bluredge.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + + +/** + \brief A function to allocated anything -- just an example here + \param module Unused + \return Whether the load was sucessful +*/ +bool +BlurEdge::load (Inkscape::Extension::Extension *module) +{ + // std::cout << "Hey, I'm Blur Edge, I'm loading!" << std::endl; + return TRUE; +} + +/** + \brief This actually blurs the edge. + \param module The effect that was called (unused) + \param document What should be edited. +*/ +void +BlurEdge::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document) +{ + Inkscape::Selection * selection = ((SPDesktop *)document)->selection; + + float width = module->get_param_float("blur-width"); + int steps = module->get_param_int("num-steps"); + + double old_offset = prefs_get_double_attribute("options.defaultoffsetwidth", "value", 1.0); + + using Inkscape::Util::GSListConstIterator; + // TODO need to properly refcount the items, at least + std::list items; + items.insert >(items.end(), selection->itemList(), NULL); + selection->clear(); + + std::list new_items; + for(std::list::iterator item = items.begin(); + item != items.end(); item++) { + SPItem * spitem = *item; + + Inkscape::XML::Node * new_items[steps]; + Inkscape::XML::Node * new_group = sp_repr_new("svg:g"); + (SP_OBJECT_REPR(spitem)->parent())->appendChild(new_group); + /** \todo Need to figure out how to get from XML::Node to SPItem */ + /* new_items.push_back(); */ + + double orig_opacity = sp_repr_css_double_property(sp_repr_css_attr(SP_OBJECT_REPR(spitem), "style"), "opacity", 1.0); + char opacity_string[64]; + g_ascii_formatd(opacity_string, sizeof(opacity_string), "%f", + orig_opacity / (steps)); + + for (int i = 0; i < steps; i++) { + double offset = (width / (float)(steps - 1) * (float)i) - (width / 2.0); + + new_items[i] = (SP_OBJECT_REPR(spitem))->duplicate(); + + SPCSSAttr * css = sp_repr_css_attr(new_items[i], "style"); + sp_repr_css_set_property(css, "opacity", opacity_string); + sp_repr_css_change(new_items[i], css, "style"); + + new_group->appendChild(new_items[i]); + selection->add(new_items[i]); + sp_selected_path_to_curves(); + + if (offset < 0.0) { + /* Doing an inset here folks */ + offset *= -1.0; + prefs_set_double_attribute("options.defaultoffsetwidth", "value", offset); + sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_INSET)->get_action(document), NULL); + } else if (offset == 0.0) { + } else { + prefs_set_double_attribute("options.defaultoffsetwidth", "value", offset); + sp_action_perform(Inkscape::Verb::get(SP_VERB_SELECTION_OFFSET)->get_action(document), NULL); + } + + selection->clear(); + } + + } + + prefs_set_double_attribute("options.defaultoffsetwidth", "value", old_offset); + + selection->clear(); + selection->add(items.begin(), items.end()); + selection->add(new_items.begin(), new_items.end()); + + return; +} + +Gtk::Widget * +BlurEdge::prefs_effect(Inkscape::Extension::Effect * module, Inkscape::UI::View::View * view) +{ + return module->autogui(); +} + +void +BlurEdge::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "Blur Edge\n" + "org.inkscape.effect.bluredge\n" + "1.0\n" + "11\n" + "\n" + "all\n" + "\n" + "\n" , new BlurEdge()); + return; +} + +}; /* namespace Internal */ +}; /* namespace Extension */ +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/bluredge.h b/src/extension/internal/bluredge.h new file mode 100644 index 000000000..a442f570e --- /dev/null +++ b/src/extension/internal/bluredge.h @@ -0,0 +1,43 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "extension/extension-forward.h" +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** \brief Implementation class of the GIMP gradient plugin. This mostly + just creates a namespace for the GIMP gradient plugin today. +*/ +class BlurEdge : public Inkscape::Extension::Implementation::Implementation { + +public: + bool load(Inkscape::Extension::Extension *module); + void effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document); + Gtk::Widget * prefs_effect(Inkscape::Extension::Effect * module, Inkscape::UI::View::View * view); + + static void init (void); +}; + +}; /* namespace Internal */ +}; /* namespace Extension */ +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/eps-out.cpp b/src/extension/internal/eps-out.cpp new file mode 100644 index 000000000..849f9cfa4 --- /dev/null +++ b/src/extension/internal/eps-out.cpp @@ -0,0 +1,108 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "eps-out.h" +#include +#include "extension/system.h" +#include "extension/db.h" +#include "extension/output.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +bool +EpsOutput::check (Inkscape::Extension::Extension * module) +{ + if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_PRINT_PS)) + return FALSE; + + return TRUE; +} + +/** + \brief This function calls the print system with the filename + \param mod unused + \param doc Document to be saved + \param uri Filename to save to (probably will end in .eps) + + The most interesting thing that this function does is just attach + an '>' on the front of the filename. This is the syntax used to + tell the printing system to save to file. +*/ +void +EpsOutput::save (Inkscape::Extension::Output *mod, SPDocument *doc, const gchar *uri) +{ + gchar * final_name; + Inkscape::Extension::Extension * ext; + + ext = Inkscape::Extension::db.get(SP_MODULE_KEY_PRINT_PS); + if (ext == NULL) + return; + + bool old_pageBoundingBox = ext->get_param_bool("pageBoundingBox"); + bool new_val = mod->get_param_bool("pageBoundingBox"); + ext->set_param_bool("pageBoundingBox", new_val); + + bool old_textToPath = ext->get_param_bool("textToPath"); + new_val = mod->get_param_bool("textToPath"); + ext->set_param_bool("textToPath", new_val); + + final_name = g_strdup_printf("> %s", uri); + sp_print_document_to_file(doc, final_name); + g_free(final_name); + + ext->set_param_bool("pageBoundingBox", old_pageBoundingBox); + ext->set_param_bool("textToPath", old_textToPath); + + return; +} + +/** + \brief A function allocate a copy of this function. + + This is the definition of postscript out. This function just + calls the extension system with the memory allocated XML that + describes the data. +*/ +void +EpsOutput::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "Encapsulated Postscript Output\n" + "org.inkscape.output.eps\n" + "FALSE\n" + "TRUE\n" + "\n" + ".eps\n" + "image/x-e-postscript\n" + "Encapsulated Postscript (*.eps)\n" + "Encapsulated Postscript File\n" + "\n" + "", new EpsOutput()); + + return; +} + +} } } /* namespace Inkscape, Extension, Implementation */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/eps-out.h b/src/extension/internal/eps-out.h new file mode 100644 index 000000000..41a6c1f5c --- /dev/null +++ b/src/extension/internal/eps-out.h @@ -0,0 +1,47 @@ +/* + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef EXTENSION_INTERNAL_EPS_OUT_H +#define EXTENSION_INTERNAL_EPS_OUT_H + +#include +#include + +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class EpsOutput : Inkscape::Extension::Implementation::Implementation { +public: + bool check(Inkscape::Extension::Extension *module); + + void save(Inkscape::Extension::Output *mod, + SPDocument *doc, + gchar const *uri); + + static void init(void); +}; + +} } } /* namespace Inkscape, Extension, Implementation */ + +#endif /* EXTENSION_INTERNAL_EPS_OUT_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp new file mode 100644 index 000000000..ee9fc4086 --- /dev/null +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -0,0 +1,176 @@ +#ifdef HAVE_CONFIG_H +# include +#endif +#include "document-private.h" +#include +#include "prefs-utils.h" +#include "extension/system.h" +#include "gdkpixbuf-input.h" + +namespace Inkscape { + +namespace IO { +GdkPixbuf* pixbuf_new_from_file( char const *utf8name, GError **error ); +} + +namespace Extension { +namespace Internal { + +SPDocument * +GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri) +{ + SPDocument *doc = sp_document_new(NULL, TRUE, TRUE); + sp_document_set_undo_sensitive(doc, FALSE); // no need to undo in this temporary document + GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file( uri, NULL ); + Inkscape::XML::Node *rdoc = sp_document_repr_root(doc); + gchar const *docbase = rdoc->attribute("sodipodi:docbase"); + gchar const *relname = sp_relative_path_from_path(uri, docbase); + + if (pb) { /* We are readable */ + Inkscape::XML::Node *repr = NULL; + + double width = gdk_pixbuf_get_width(pb); + double height = gdk_pixbuf_get_height(pb); + gchar const *str = gdk_pixbuf_get_option( pb, "Inkscape::DpiX" ); + if ( str ) + { + gint dpi = atoi(str); + if ( dpi > 0 && dpi != 72 ) + { + double scale = 72.0 / (double)dpi; + width *= scale; + } + } + str = gdk_pixbuf_get_option( pb, "Inkscape::DpiY" ); + if ( str ) + { + gint dpi = atoi(str); + if ( dpi > 0 && dpi != 72 ) + { + double scale = 72.0 / (double)dpi; + height *= scale; + } + } + + if (prefs_get_int_attribute("options.importbitmapsasimages", "value", 1) == 1) { + // import as + repr = sp_repr_new("svg:image"); + repr->setAttribute("xlink:href", relname); + repr->setAttribute("sodipodi:absref", uri); + + sp_repr_set_svg_double(repr, "width", width); + sp_repr_set_svg_double(repr, "height", height); + + } else { + // import as pattern-filled rect + Inkscape::XML::Node *pat = sp_repr_new("svg:pattern"); + pat->setAttribute("inkscape:collect", "always"); + pat->setAttribute("patternUnits", "userSpaceOnUse"); + sp_repr_set_svg_double(pat, "width", width); + sp_repr_set_svg_double(pat, "height", height); + SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->appendChild(pat); + gchar const *pat_id = pat->attribute("id"); + SPObject *pat_object = doc->getObjectById(pat_id); + + Inkscape::XML::Node *im = sp_repr_new("svg:image"); + im->setAttribute("xlink:href", relname); + im->setAttribute("sodipodi:absref", uri); + sp_repr_set_svg_double(im, "width", width); + sp_repr_set_svg_double(im, "height", height); + SP_OBJECT_REPR(pat_object)->addChild(im, NULL); + + repr = sp_repr_new("svg:rect"); + repr->setAttribute("style", g_strdup_printf("stroke:none;fill:url(#%s)", pat_id)); + sp_repr_set_svg_double(repr, "width", width); + sp_repr_set_svg_double(repr, "height", height); + } + + SP_DOCUMENT_ROOT(doc)->appendChildRepr(repr); + Inkscape::GC::release(repr); + gdk_pixbuf_unref(pb); + // restore undo, as now this document may be shown to the user if a bitmap was opened + sp_document_set_undo_sensitive(doc, TRUE); + } else { + printf("GdkPixbuf loader failed\n"); + } + + return doc; +} + + +void +GdkpixbufInput::init(void) +{ + GSList * formatlist, * formatlisthead; + + /* \todo I'm not sure if I need to free this list */ + for (formatlist = formatlisthead = gdk_pixbuf_get_formats(); + formatlist != NULL; + formatlist = g_slist_next(formatlist)) { + + GdkPixbufFormat *pixformat = (GdkPixbufFormat *)formatlist->data; + + gchar *name = gdk_pixbuf_format_get_name(pixformat); + gchar *description = gdk_pixbuf_format_get_description(pixformat); + gchar **extensions = gdk_pixbuf_format_get_extensions(pixformat); + gchar **mimetypes = gdk_pixbuf_format_get_mime_types(pixformat); + + for (int i = 0; extensions[i] != NULL; i++) { + for (int j = 0; mimetypes[j] != NULL; j++) { + + /* thanks but no thanks, we'll handle SVG extensions... */ + if (strcmp(extensions[i], "svg") == 0) { + continue; + } + if (strcmp(extensions[i], "svgz") == 0) { + continue; + } + if (strcmp(extensions[i], "svg.gz") == 0) { + continue; + } + + gchar *xmlString = g_strdup_printf( + "\n" + "%s GDK pixbuf Input\n" + "org.inkscape.input.gdkpixbuf.%s\n" + "\n" + ".%s\n" + "%s\n" + "%s (*.%s)\n" + "%s\n" + "\n" + "", + name, + extensions[i], + extensions[i], + mimetypes[j], + name, + extensions[i], + description + ); + + Inkscape::Extension::build_from_mem(xmlString, new GdkpixbufInput()); + g_free(xmlString); + }} + + g_free(name); + g_free(description); + g_strfreev(mimetypes); + g_strfreev(extensions); + } + + g_slist_free(formatlisthead); +} + +} } } /* namespace Inkscape, Extension, Implementation */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/gdkpixbuf-input.h b/src/extension/internal/gdkpixbuf-input.h new file mode 100644 index 000000000..9d5e6ccf7 --- /dev/null +++ b/src/extension/internal/gdkpixbuf-input.h @@ -0,0 +1,31 @@ +#ifndef INKSCAPE_EXTENSION_INTERNAL_GDKPIXBUF_INPUT_H +#define INKSCAPE_EXTENSION_INTERNAL_GDKPIXBUF_INPUT_H + +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class GdkpixbufInput : Inkscape::Extension::Implementation::Implementation { +public: + SPDocument *open(Inkscape::Extension::Input *mod, + gchar const *uri); + static void init(); +}; + +} } } /* namespace Inkscape, Extension, Implementation */ + + +#endif /* INKSCAPE_EXTENSION_INTERNAL_GDKPIXBUF_INPUT_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/gimpgrad.cpp b/src/extension/internal/gimpgrad.cpp new file mode 100644 index 000000000..9560c5df6 --- /dev/null +++ b/src/extension/internal/gimpgrad.cpp @@ -0,0 +1,233 @@ +/** \file + * Inkscape::Extension::Internal::GimpGrad implementation + */ + +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2004-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "io/sys.h" +#include "extension/system.h" + +#include "gimpgrad.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** + \brief A function to allocated anything -- just an example here + \param module Unused + \return Whether the load was sucessful +*/ +bool +GimpGrad::load (Inkscape::Extension::Extension *module) +{ + // std::cout << "Hey, I'm loading!\n" << std::endl; + return TRUE; +} + +/** + \brief A function to remove what was allocated + \param module Unused + \return None +*/ +void +GimpGrad::unload (Inkscape::Extension::Extension *module) +{ + // std::cout << "Nooo! I'm being unloaded!" << std::endl; + return; +} + +/** + \brief A function to turn a color into a gradient stop + \param in_color The color for the stop + \param location Where the stop is placed in the gradient + \return The text that is the stop. Full SVG containing the element. + + This function encapsulates all of the translation of the ColorRGBA + and the location into the gradient. It is really pretty simple except + that the ColorRGBA is in floats that are 0 to 1 and the SVG wants + hex values from 0 to 255 for color. Otherwise mostly this is just + turning the values into strings and returning it. +*/ +Glib::ustring +GimpGrad::new_stop (ColorRGBA in_color, float location) +{ + char temp_string[25]; + Glib::ustring mystring("\n"; + return mystring; +} + +/** + \brief Actually open the gradient and turn it into an SPDocument + \param module The input module being used + \param filename The filename of the gradient to be opened + \return A Document with the gradient in it. + + GIMP gradients are pretty simple (atleast the newer format, this + function does not handle the old one yet). They start out with + the like "GIMP Gradient", then name it, and tell how many entries + there are. This function currently ignores the name and the number + of entries just reading until it fails. + + The other small piece of trickery here is that GIMP gradients define + a left possition, right possition and middle possition. SVG gradients + have no middle possition in them. In order to handle this case the + left and right colors are averaged in a linear manner and the middle + possition is used for that color. + + That is another point, the GIMP gradients support many different types + of gradients -- linear being the most simple. This plugin assumes + that they are all linear. Most GIMP gradients are done this way, + but it is possible to encounter more complex ones -- which won't be + handled correctly. + + The one optimization that this plugin makes that if the right side + of the previous segment is the same color as the left side of the + current segment, then the second one is dropped. This is often + done in GIMP gradients and they are not necissary in SVG. + + What this function does is build up an SVG document with a single + linear gradient in it with all the stops of the colors in the GIMP + gradient that is passed in. This document is then turned into a + document using the \c sp_document_from_mem. That is then returned + to Inkscape. +*/ +SPDocument * +GimpGrad::open (Inkscape::Extension::Input *module, gchar const *filename) +{ + FILE * gradient; + // std::cout << "Open filename: " << filename << std::endl; + + Inkscape::IO::dump_fopen_call(filename, "I"); + gradient = Inkscape::IO::fopen_utf8name(filename, "r"); + if (gradient == NULL) return NULL; + + char tempstr[1024]; + if (fgets(tempstr, 1024, gradient) == 0) { + // std::cout << "Seems that the read failed" << std::endl; + fclose(gradient); + return NULL; + } + + if (!strcmp(tempstr, "GIMP Gradient")) { + // std::cout << "This doesn't appear to be a GIMP gradient" << std::endl; + fclose(gradient); + return NULL; + } + + if (fgets(tempstr, 1024, gradient) == 0) { + // std::cout << "Seems that the second read failed" << std::endl; + fclose(gradient); + return NULL; + } + + if (fgets(tempstr, 1024, gradient) == 0) { + // std::cout << "Seems that the third read failed" << std::endl; + fclose(gradient); + return NULL; + } + + ColorRGBA last_color(-1.0, -1.0, -1.0, -1.0); + float lastlocation = -1.0; + Glib::ustring outsvg("\n"); + while (fgets(tempstr, 1024, gradient) != 0) { + float left, middle, right; + float temp_color[4]; + int type; + int color; + gchar * end; + + left = g_ascii_strtod(tempstr, &end); + middle = g_ascii_strtod(end, &end); + right = g_ascii_strtod(end, &end); + + for (int i = 0; i < 4; i++) { + temp_color[i] = g_ascii_strtod(end, &end); + } + ColorRGBA leftcolor(temp_color[0], temp_color[1], temp_color[2], temp_color[3]); + + for (int i = 0; i < 4; i++) { + temp_color[i] = g_ascii_strtod(end, &end); + } + ColorRGBA rightcolor(temp_color[0], temp_color[1], temp_color[2], temp_color[3]); + + sscanf(end, "%d %d", &type, &color); + + if (!(last_color == leftcolor) || left != lastlocation) { + outsvg += new_stop(leftcolor, left); + } + outsvg += new_stop(leftcolor.average(rightcolor), middle); + outsvg += new_stop(rightcolor, right); + + last_color = rightcolor; + lastlocation = right; + } + + outsvg += ""; + + // std::cout << "SVG Output: " << outsvg << std::endl; + + fclose(gradient); + + return sp_document_new_from_mem(outsvg.c_str(), outsvg.length(), TRUE); +} + +void +GimpGrad::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "GIMP Gradients\n" + "org.inkscape.input.gimpgrad\n" + "gimpgrad\n" + "\n" + ".ggr\n" + "application/x-gimp-gradient\n" + "GIMP Gradient (*.ggr)\n" + "Gradients used in GIMP\n" + "\n" + "\n", new GimpGrad()); + return; +} + +} } } /* namespace Internal; Extension; Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/gimpgrad.h b/src/extension/internal/gimpgrad.h new file mode 100644 index 000000000..0d0698e67 --- /dev/null +++ b/src/extension/internal/gimpgrad.h @@ -0,0 +1,49 @@ +/** \file + * + * Implementation class of the GIMP gradient plugin. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include "extension/implementation/implementation.h" +#include "extension/extension-forward.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** \brief Implementation class of the GIMP gradient plugin. This mostly + just creates a namespace for the GIMP gradient plugin today. +*/ +class GimpGrad : public Inkscape::Extension::Implementation::Implementation { +private: + Glib::ustring new_stop (ColorRGBA in_color, float location); + +public: + bool load(Inkscape::Extension::Extension *module); + void unload(Inkscape::Extension::Extension *module); + SPDocument *open(Inkscape::Extension::Input *module, gchar const *filename); + + static void init (void); +}; + + +} } } /* namespace Internal; Extension; Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/gnome.cpp b/src/extension/internal/gnome.cpp new file mode 100644 index 000000000..d81dfa95e --- /dev/null +++ b/src/extension/internal/gnome.cpp @@ -0,0 +1,443 @@ +#define __SP_GNOME_C__ + +/* + * Gnome stuff + * + * Author: + * Lauris Kaplinski + * Ted Gould + * + * + * Copyright (C) 2005 Authors + * + * Lauris: This code is in public domain + * Ted: This code is under the GNU GPL + */ + +/* Gnome Print */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "libnr/n-art-bpath.h" +#include "libnr/nr-rect.h" +#include "libnr/nr-matrix.h" +#include "libnr/nr-matrix-fns.h" +#include "libnr/nr-path.h" +#include "libnr/nr-pixblock.h" +#include "display/canvas-bpath.h" + +#include +#include +#include +#include + +#if OLDGnome +#include +#include +#endif + +#include +#include "enums.h" +#include "document.h" +#include "style.h" +#include "sp-paint-server.h" + +#include "gnome.h" + +#include "extension/extension.h" +#include "extension/system.h" + +static ArtBpath *nr_artpath_to_art_bpath(NArtBpath const *s); + +namespace Inkscape { +namespace Extension { +namespace Internal { + +PrintGNOME::PrintGNOME (void) +{ + /* Nothing here */ +} + +PrintGNOME::~PrintGNOME (void) +{ + return; +} + +unsigned int +PrintGNOME::setup (Inkscape::Extension::Print *mod) +{ + GnomePrintConfig *config; +#if OLDGnome + GtkWidget *dlg, *vbox, *sel; +#endif + + config = gnome_print_config_default (); +#if OLDGnome + dlg = gtk_dialog_new_with_buttons (_("Select printer"), NULL, + GTK_DIALOG_MODAL, + GTK_STOCK_PRINT, + GTK_RESPONSE_OK, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NULL); + + vbox = GTK_DIALOG (dlg)->vbox; + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + + sel = gnome_printer_selector_new (config); + gtk_widget_show (sel); + gtk_box_pack_start (GTK_BOX (vbox), sel, TRUE, TRUE, 0); + + btn = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + if (btn != GTK_RESPONSE_OK) return FALSE; +#endif + _gpc = gnome_print_context_new (config); + gnome_print_config_unref (config); + + return TRUE; +} + +unsigned int +PrintGNOME::set_preview (Inkscape::Extension::Print *mod) +{ +#if OLDGnome + SPPrintContext ctx; + GnomePrintContext *gpc; + GnomePrintMaster *gpm; + GtkWidget *gpmp; + gchar *title; + + + gpm = gnome_print_master_new(); + _gpc = gnome_print_master_get_context (gpm); + + g_return_if_fail (gpm != NULL); + g_return_if_fail (gpc != NULL); + + /* Print document */ + gnome_print_beginpage (gpc, SP_DOCUMENT_NAME (doc)); + gnome_print_translate (gpc, 0.0, sp_document_height (doc)); + /* From desktop points to document pixels */ + gnome_print_scale (gpc, 0.8, -0.8); + sp_item_invoke_print (SP_ITEM (sp_document_root (doc)), &ctx); + gnome_print_showpage (gpc); + gnome_print_context_close (gpc); + + title = g_strdup_printf (_("Inkscape: Print Preview")); + gpmp = gnome_print_master_preview_new (gpm, title); + + gtk_widget_show (GTK_WIDGET(gpmp)); + + gnome_print_master_close (gpm); + + g_free (title); +#endif + return 0; +} + +unsigned int +PrintGNOME::begin (Inkscape::Extension::Print *mod, SPDocument *doc) +{ + gnome_print_beginpage (_gpc, (const guchar *)SP_DOCUMENT_NAME (doc)); + gnome_print_translate (_gpc, 0.0, sp_document_height (doc)); + /* From desktop points to document pixels */ + gnome_print_scale (_gpc, 0.8, -0.8); + + return 0; +} + +unsigned int +PrintGNOME::finish (Inkscape::Extension::Print *mod) +{ + gnome_print_showpage (_gpc); + gnome_print_context_close (_gpc); + + return 0; +} + +unsigned int +PrintGNOME::bind (Inkscape::Extension::Print *mod, const NRMatrix *transform, float opacity) +{ + gdouble t[6]; + + gnome_print_gsave(_gpc); + + t[0] = transform->c[0]; + t[1] = transform->c[1]; + t[2] = transform->c[2]; + t[3] = transform->c[3]; + t[4] = transform->c[4]; + t[5] = transform->c[5]; + + gnome_print_concat (_gpc, t); + + /* fixme: Opacity? (lauris) */ + + return 0; +} + +unsigned int +PrintGNOME::release (Inkscape::Extension::Print *mod) +{ + gnome_print_grestore (_gpc); + return 0; +} + +unsigned int PrintGNOME::comment (Inkscape::Extension::Print * module, + const char * comment) +{ + // ignore comment + return 0; +} + +unsigned int +PrintGNOME::fill(Inkscape::Extension::Print *mod, + NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + gdouble t[6]; + + /* CTM is for information purposes only */ + /* We expect user coordinate system to be set up already */ + + t[0] = ctm->c[0]; + t[1] = ctm->c[1]; + t[2] = ctm->c[2]; + t[3] = ctm->c[3]; + t[4] = ctm->c[4]; + t[5] = ctm->c[5]; + + if (style->fill.type == SP_PAINT_TYPE_COLOR) { + float rgb[3], opacity; + sp_color_get_rgb_floatv (&style->fill.value.color, rgb); + gnome_print_setrgbcolor (_gpc, rgb[0], rgb[1], rgb[2]); + + /* fixme: */ + opacity = SP_SCALE24_TO_FLOAT (style->fill_opacity.value) * SP_SCALE24_TO_FLOAT (style->opacity.value); + gnome_print_setopacity (_gpc, opacity); + + ArtBpath * apath = nr_artpath_to_art_bpath(bpath->path); + gnome_print_bpath (_gpc, apath, FALSE); + g_free(apath); + + if (style->fill_rule.value == SP_WIND_RULE_EVENODD) { + gnome_print_eofill (_gpc); + } else { + gnome_print_fill (_gpc); + } + } else if (style->fill.type == SP_PAINT_TYPE_PAINTSERVER) { + SPPainter *painter; + NRRect dpbox; + + /* fixme: */ + dpbox.x0 = pbox->x0; + dpbox.y0 = pbox->y0; + dpbox.x1 = pbox->x1; + dpbox.y1 = pbox->y1; + painter = sp_paint_server_painter_new(SP_STYLE_FILL_SERVER(style), + // FIXME: the second matrix below must be the parent (context) transform. + // I don't know what it must be for gnome-print. --bb + ctm, NR::identity(), + &dpbox); + if (painter) { + NRRect cbox; + NRRectL ibox; + NRMatrix d2i; + double dd2i[6]; + int x, y; + + nr_rect_d_intersect (&cbox, dbox, bbox); + ibox.x0 = (long) cbox.x0; + ibox.y0 = (long) cbox.y0; + ibox.x1 = (long) (cbox.x1 + 0.9999); + ibox.y1 = (long) (cbox.y1 + 0.9999); + + nr_matrix_invert (&d2i, ctm); + + gnome_print_gsave (_gpc); + + ArtBpath * apath = nr_artpath_to_art_bpath(bpath->path); + gnome_print_bpath (_gpc, apath, FALSE); + g_free(apath); + + if (style->fill_rule.value == SP_WIND_RULE_EVENODD) { + gnome_print_eoclip (_gpc); + } else { + gnome_print_clip (_gpc); + } + dd2i[0] = d2i.c[0]; + dd2i[1] = d2i.c[1]; + dd2i[2] = d2i.c[2]; + dd2i[3] = d2i.c[3]; + dd2i[4] = d2i.c[4]; + dd2i[5] = d2i.c[5]; + gnome_print_concat (_gpc, dd2i); + /* Now we are in desktop coordinates */ + for (y = ibox.y0; y < ibox.y1; y+= 64) { + for (x = ibox.x0; x < ibox.x1; x+= 64) { + NRPixBlock pb; + nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, x, y, x + 64, y + 64, TRUE); + painter->fill (painter, &pb); + gnome_print_gsave (_gpc); + gnome_print_translate (_gpc, x, y + 64); + gnome_print_scale (_gpc, 64, -64); + gnome_print_rgbaimage (_gpc, NR_PIXBLOCK_PX (&pb), 64, 64, pb.rs); + gnome_print_grestore (_gpc); + nr_pixblock_release (&pb); + } + } + gnome_print_grestore (_gpc); + sp_painter_free (painter); + } + } + + return 0; +} + +unsigned int +PrintGNOME::stroke (Inkscape::Extension::Print *mod, const NRBPath *bpath, const NRMatrix *ctm, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox) +{ + gdouble t[6]; + + /* CTM is for information purposes only */ + /* We expect user coordinate system to be set up already */ + + t[0] = ctm->c[0]; + t[1] = ctm->c[1]; + t[2] = ctm->c[2]; + t[3] = ctm->c[3]; + t[4] = ctm->c[4]; + t[5] = ctm->c[5]; + + if (style->stroke.type == SP_PAINT_TYPE_COLOR) { + float rgb[3], opacity; + sp_color_get_rgb_floatv (&style->stroke.value.color, rgb); + gnome_print_setrgbcolor (_gpc, rgb[0], rgb[1], rgb[2]); + + /* fixme: */ + opacity = SP_SCALE24_TO_FLOAT (style->stroke_opacity.value) * SP_SCALE24_TO_FLOAT (style->opacity.value); + gnome_print_setopacity (_gpc, opacity); + + if (style->stroke_dash.n_dash > 0) { + gnome_print_setdash (_gpc, style->stroke_dash.n_dash, style->stroke_dash.dash, style->stroke_dash.offset); + } else { + gnome_print_setdash (_gpc, 0, NULL, 0.0); + } + + gnome_print_setlinewidth (_gpc, style->stroke_width.computed); + gnome_print_setlinejoin (_gpc, style->stroke_linejoin.computed); + gnome_print_setlinecap (_gpc, style->stroke_linecap.computed); + + ArtBpath * apath = nr_artpath_to_art_bpath(bpath->path); + gnome_print_bpath (_gpc, apath, FALSE); + g_free(apath); + + gnome_print_stroke (_gpc); + } + + return 0; +} + +unsigned int +PrintGNOME::image (Inkscape::Extension::Print *mod, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + const NRMatrix *transform, const SPStyle *style) +{ + gdouble t[6]; + + t[0] = transform->c[0]; + t[1] = transform->c[1]; + t[2] = transform->c[2]; + t[3] = transform->c[3]; + t[4] = transform->c[4]; + t[5] = transform->c[5]; + + gnome_print_gsave (_gpc); + + gnome_print_concat (_gpc, t); + + if (style->opacity.value != SP_SCALE24_MAX) { + guchar *dpx, *d, *s; + guint x, y; + guint32 alpha; + alpha = (guint32) floor (SP_SCALE24_TO_FLOAT (style->opacity.value) * 255.9999); + dpx = g_new (guchar, w * h * 4); + for (y = 0; y < h; y++) { + s = px + y * rs; + d = dpx + y * w * 4; + memcpy (d, s, w * 4); + for (x = 0; x < w; x++) { + d[3] = (s[3] * alpha) / 255; + s += 4; + d += 4; + } + } + gnome_print_rgbaimage (_gpc, dpx, w, h, w * 4); + g_free (dpx); + } else { + gnome_print_rgbaimage (_gpc, px, w, h, rs); + } + + gnome_print_grestore (_gpc); + + return 0; +} + +void +PrintGNOME::init (void) +{ + Inkscape::Extension::Extension * ext; + + /* SVG in */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "GNOME Print\n" + "" SP_MODULE_KEY_PRINT_GNOME "\n" + "\n" + "", new PrintGNOME()); + + return; +} + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + + +// Remember to free the result! +static ArtBpath * +nr_artpath_to_art_bpath(NArtBpath const *s) +{ + int i; + if (!s) { + return NULL; + } + + i = 0; + while (s[i].code != NR_END) i += 1; + + ArtBpath* d = nr_new (ArtBpath, i + 1); + + i = 0; + while (s[i].code != NR_END) { + d[i].code = (ArtPathcode)s[i].code; + if (s[i].code == NR_CURVETO) { + d[i].x1 = s[i].x1; + d[i].y1 = s[i].y1; + d[i].x2 = s[i].x2; + d[i].y2 = s[i].y2; + } + d[i].x3 = s[i].x3; + d[i].y3 = s[i].y3; + i += 1; + } + d[i].code = ART_END; + + return d; +} + diff --git a/src/extension/internal/gnome.h b/src/extension/internal/gnome.h new file mode 100644 index 000000000..1a1ff55f2 --- /dev/null +++ b/src/extension/internal/gnome.h @@ -0,0 +1,58 @@ +#ifndef __INKSCAPE_EXTENSION_INTERNAL_PRINT_GNOME_H__ +#define __INKSCAPE_EXTENSION_INTERNAL_PRINT_GNOME_H__ + +/* + * Gnome stuff + * + * Author: + * Lauris Kaplinski + * Ted Gould + * + * Lauris: This code is in public domain + * Ted: This code is under the GNU GPL + */ + +#include + +#include + +#include "extension/implementation/implementation.h" +#include "extension/extension.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PrintGNOME : public Inkscape::Extension::Implementation::Implementation { + GnomePrintContext * _gpc; + +public: + PrintGNOME (void); + virtual ~PrintGNOME (void); + + /* Print functions */ + virtual unsigned int setup (Inkscape::Extension::Print * module); + virtual unsigned int set_preview (Inkscape::Extension::Print * module); + + virtual unsigned int begin (Inkscape::Extension::Print * module, SPDocument *doc); + virtual unsigned int finish (Inkscape::Extension::Print * module); + + /* Rendering methods */ + virtual unsigned int bind (Inkscape::Extension::Print * module, const NRMatrix *transform, float opacity); + virtual unsigned int release (Inkscape::Extension::Print * module); + virtual unsigned int fill (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *ctm, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int stroke (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int image (Inkscape::Extension::Print * module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + const NRMatrix *transform, const SPStyle *style); + virtual unsigned int comment(Inkscape::Extension::Print *module, const char * comment); + + static void init (void); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_GNOME_H__ */ diff --git a/src/extension/internal/grid.cpp b/src/extension/internal/grid.cpp new file mode 100644 index 000000000..f74c652aa --- /dev/null +++ b/src/extension/internal/grid.cpp @@ -0,0 +1,272 @@ +/** + \file grid.cpp + + A plug-in to add a grid creation effect into Inkscape. +*/ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2004-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include + +#include "desktop.h" +#include "selection.h" +#include "sp-object.h" + +#include "extension/effect.h" +#include "extension/system.h" + + +#include "grid.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** + \brief A function to allocated anything -- just an example here + \param module Unused + \return Whether the load was sucessful +*/ +bool +Grid::load (Inkscape::Extension::Extension *module) +{ + // std::cout << "Hey, I'm Grid, I'm loading!" << std::endl; + return TRUE; +} + +/** + \brief This actually draws the grid. + \param module The effect that was called (unused) + \param document What should be edited. +*/ +void +Grid::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document) +{ + Inkscape::Selection * selection = ((SPDesktop *)document)->selection; + + NR::Rect bounding_area = NR::Rect(NR::Point(0,0), NR::Point(100,100)); + if (selection->isEmpty()) { + /* get page size */ + SPDocument * doc = document->doc(); + bounding_area = NR::Rect(NR::Point(0,0), + NR::Point(sp_document_width(doc), + sp_document_height(doc))); + } else { + bounding_area = selection->bounds(); + + gdouble doc_height = sp_document_height(document->doc()); + NR::Rect temprec = NR::Rect(NR::Point(bounding_area.min()[NR::X], doc_height - bounding_area.min()[NR::Y]), + NR::Point(bounding_area.max()[NR::X], doc_height - bounding_area.max()[NR::Y])); + + bounding_area = temprec; + } + + + float xspacing = module->get_param_float("xspacing"); + float yspacing = module->get_param_float("yspacing"); + float line_width = module->get_param_float("lineWidth"); + float xoffset = module->get_param_float("xoffset"); + float yoffset = module->get_param_float("yoffset"); + + // std::cout << "Spacing: " << spacing; + // std::cout << " Line Width: " << line_width; + // std::cout << " Offset: " << offset << std::endl; + + Glib::ustring path_data; + + for (NR::Point start_point = bounding_area.min(); + start_point[NR::X] + xoffset <= (bounding_area.max())[NR::X]; + start_point[NR::X] += xspacing) { + NR::Point end_point = start_point; + end_point[NR::Y] = (bounding_area.max())[NR::Y]; + gchar floatstring[64]; + + path_data += "M "; + sprintf(floatstring, "%f", start_point[NR::X] + xoffset); + path_data += floatstring; + path_data += " "; + sprintf(floatstring, "%f", start_point[NR::Y]); + path_data += floatstring; + path_data += " L "; + sprintf(floatstring, "%f", end_point[NR::X] + xoffset); + path_data += floatstring; + path_data += " "; + sprintf(floatstring, "%f", end_point[NR::Y]); + path_data += floatstring; + path_data += " "; + } + + for (NR::Point start_point = bounding_area.min(); + start_point[NR::Y] + yoffset <= (bounding_area.max())[NR::Y]; + start_point[NR::Y] += yspacing) { + NR::Point end_point = start_point; + end_point[NR::X] = (bounding_area.max())[NR::X]; + gchar floatstring[64]; + + path_data += "M "; + sprintf(floatstring, "%f", start_point[NR::X]); + path_data += floatstring; + path_data += " "; + sprintf(floatstring, "%f", start_point[NR::Y] + yoffset); + path_data += floatstring; + path_data += " L "; + sprintf(floatstring, "%f", end_point[NR::X]); + path_data += floatstring; + path_data += " "; + sprintf(floatstring, "%f", end_point[NR::Y] + yoffset); + path_data += floatstring; + path_data += " "; + } + + // std::cout << "Path Data: " << path_data << std::endl; + + Inkscape::XML::Node * current_layer = ((SPDesktop *)document)->currentLayer()->repr; + Inkscape::XML::Node * path = sp_repr_new("svg:path"); + + path->setAttribute("d", path_data.c_str()); + + Glib::ustring style("fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"); + style += ";stroke-width:"; + gchar floatstring[64]; + sprintf(floatstring, "%f", line_width); + style += floatstring; + style += "pt"; + path->setAttribute("style", style.c_str()); + + // Glib::ustring transform("scale(1.25 1.25)"); + // path->setAttribute("transform", transform.c_str()); + + current_layer->appendChild(path); + + return; +} + +/** \brief A class to make an adjustment that uses Extension params */ +class PrefAdjustment : public Gtk::Adjustment { + /** Extension that this relates to */ + Inkscape::Extension::Extension * _ext; + /** The string which represents the parameter */ + char * _pref; +public: + /** \brief Make the adjustment using an extension and the string + describing the parameter. */ + PrefAdjustment(Inkscape::Extension::Extension * ext, char * pref) : + Gtk::Adjustment(0.0, 0.0, 10.0, 0.1), _ext(ext), _pref(pref) { + this->set_value(_ext->get_param_float(_pref)); + this->signal_value_changed().connect(sigc::mem_fun(this, &PrefAdjustment::val_changed)); + return; + }; + + void val_changed (void); +}; /* class PrefAdjustment */ + +/** \brief A function to respond to the value_changed signal from the + adjustment. + + This function just grabs the value from the adjustment and writes + it to the parameter. Very simple, but yet beautiful. +*/ +void +PrefAdjustment::val_changed (void) +{ + // std::cout << "Value Changed to: " << this->get_value() << std::endl; + _ext->set_param_float(_pref, this->get_value()); + return; +} + +/** \brief A function to get the prefences for the grid + \param moudule Module which holds the params + \param view Unused today - may get style information in the future. + + This function builds a VBox, and puts it into a Gtk::Plug. This way + the native window pointer can be pulled out and returned up to be + stuck in a Gtk::Socket further up the call stack. In the Vbox there + are several Hboxes, each one being a spin button to adjust a particular + parameter. The names of the parameters and the labels are all + stored in the arrays in the middle of the function. This makes + the code very generic. This will probably have to change if someone + wants to make this dialog look nicer. +*/ +Gtk::Widget * +Grid::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view) +{ + Gtk::VBox * vbox; + vbox = new Gtk::VBox(); + +#define NUM_PREFERENCES 5 + char * labels[NUM_PREFERENCES] = {N_("Line Width"), + N_("Horizontal Spacing"), + N_("Vertical Spacing"), + N_("Horizontal Offset"), + N_("Vertical Offset")}; + char * prefs[NUM_PREFERENCES] = {"lineWidth", + "xspacing", + "yspacing", + "xoffset", + "yoffset"}; + + for (int i = 0; i < NUM_PREFERENCES; i++) { + Gtk::HBox * hbox = new Gtk::HBox(); + + Gtk::Label * label = new Gtk::Label(_(labels[i]), Gtk::ALIGN_LEFT); + label->show(); + hbox->pack_start(*label, true, true); + + PrefAdjustment * pref = new PrefAdjustment(module, prefs[i]); + + Gtk::SpinButton * spin = new Gtk::SpinButton(*pref, 0.1, 1); + spin->show(); + hbox->pack_start(*spin, false, false); + + hbox->show(); + + vbox->pack_start(*hbox, true, true); + } +#undef NUM_PREFERENCES + + vbox->show(); + + return vbox; +} + +void +Grid::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "Grid\n" + "org.inkscape.effect.grid\n" + "1.0\n" + "10.0\n" + "10.0\n" + "5.0\n" + "5.0\n" + "\n" + "all\n" + "\n" + "\n", new Grid()); + return; +} + +}; /* namespace Internal */ +}; /* namespace Extension */ +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/grid.h b/src/extension/internal/grid.h new file mode 100644 index 000000000..41ac56356 --- /dev/null +++ b/src/extension/internal/grid.h @@ -0,0 +1,43 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2004-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "extension/implementation/implementation.h" +#include "extension/extension-forward.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** \brief Implementation class of the GIMP gradient plugin. This mostly + just creates a namespace for the GIMP gradient plugin today. +*/ +class Grid : public Inkscape::Extension::Implementation::Implementation { + +public: + bool load(Inkscape::Extension::Extension *module); + void effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document); + Gtk::Widget * prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view); + + static void init (void); +}; + +}; /* namespace Internal */ +}; /* namespace Extension */ +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/latex-pstricks-out.cpp b/src/extension/internal/latex-pstricks-out.cpp new file mode 100644 index 000000000..d9db73fe6 --- /dev/null +++ b/src/extension/internal/latex-pstricks-out.cpp @@ -0,0 +1,128 @@ +/* + * Authors: + * Michael Forbes + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "latex-pstricks-out.h" +#include "sp-path.h" +#include +#include "extension/system.h" +#include "extension/print.h" +#include "extension/db.h" +#include "display/nr-arena.h" +#include "display/nr-arena-item.h" + + + + + +namespace Inkscape { +namespace Extension { +namespace Internal { + +LatexOutput::LatexOutput (void) // The null constructor +{ + return; +} + +LatexOutput::~LatexOutput (void) //The destructor +{ + return; +} + +bool +LatexOutput::check (Inkscape::Extension::Extension * module) +{ + if (NULL == Inkscape::Extension::db.get("org.inkscape.print.latex")) + return FALSE; + return TRUE; +} + + +void +LatexOutput::save (Inkscape::Extension::Output *mod2, SPDocument *doc, const gchar *uri) +{ + Inkscape::Extension::Print *mod; + SPPrintContext context; + const gchar * oldconst; + gchar * oldoutput; + unsigned int ret; + + sp_document_ensure_up_to_date (doc); + + mod = Inkscape::Extension::get_print(SP_MODULE_KEY_PRINT_LATEX); + oldconst = mod->get_param_string("destination"); + oldoutput = g_strdup(oldconst); + mod->set_param_string("destination", (gchar *)uri); + + /* Start */ + context.module = mod; + /* fixme: This has to go into module constructor somehow */ + /* Create new arena */ + mod->base = SP_ITEM (sp_document_root (doc)); + mod->arena = NRArena::create(); + mod->dkey = sp_item_display_key_new (1); + mod->root = sp_item_invoke_show (mod->base, mod->arena, mod->dkey, SP_ITEM_SHOW_DISPLAY); + /* Print document */ + ret = mod->begin (doc); + sp_item_invoke_print (mod->base, &context); + ret = mod->finish (); + /* Release arena */ + sp_item_invoke_hide (mod->base, mod->dkey); + mod->base = NULL; + nr_arena_item_unref (mod->root); + mod->root = NULL; + nr_object_unref ((NRObject *) mod->arena); + mod->arena = NULL; + /* end */ + + mod->set_param_string("destination", oldoutput); + g_free(oldoutput); + + return; +} + +/** + \brief A function allocate a copy of this function. + + This is the definition of postscript out. This function just + calls the extension system with the memory allocated XML that + describes the data. +*/ +void +LatexOutput::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "LaTeX Output\n" + "org.inkscape.output.latex\n" + "\n" + ".tex\n" + "text/plain\n" + "LaTeX With PSTricks macros (*.tex)\n" + "LaTeX PSTricks File\n" + "\n" + "", new LatexOutput()); + + return; +} + +} } } /* namespace Inkscape, Extension, Implementation */ + +/* + Local Variables: + mode:cpp + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/latex-pstricks-out.h b/src/extension/internal/latex-pstricks-out.h new file mode 100644 index 000000000..8e544d5b4 --- /dev/null +++ b/src/extension/internal/latex-pstricks-out.h @@ -0,0 +1,49 @@ +/* + * + * Authors: + * Michael Forbes + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef EXTENSION_INTERNAL_LATEX_OUT_H +#define EXTENSION_INTERNAL_LATEX_OUT_H + +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class LatexOutput : Inkscape::Extension::Implementation::Implementation { //This is a derived class + +public: + LatexOutput(); // Empty constructor + + virtual ~LatexOutput();//Destructor + + bool check(Inkscape::Extension::Extension *module); //Can this module load (always yes for now) + + void save(Inkscape::Extension::Output *mod, // Save the given document to the given filename + SPDocument *doc, + gchar const *uri); + + static void init(void);//Initialize the class +}; + +} } } /* namespace Inkscape, Extension, Implementation */ + +#endif /* EXTENSION_INTERNAL_LATEX_OUT_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/latex-pstricks.cpp b/src/extension/internal/latex-pstricks.cpp new file mode 100644 index 000000000..90a2128b6 --- /dev/null +++ b/src/extension/internal/latex-pstricks.cpp @@ -0,0 +1,362 @@ +#define __SP_LATEX_C__ + +/* + * LaTeX Printing + * + * Author: + * Michael Forbes + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include +#include + +#include "libnr/n-art-bpath.h" +#include "sp-item.h" + + +#include "style.h" + +#include "latex-pstricks.h" + +#include + +#include "extension/system.h" +#include "extension/print.h" + +#include "io/sys.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +PrintLatex::PrintLatex (void): _stream(NULL) +{ +} + +PrintLatex::~PrintLatex (void) +{ + if (_stream) fclose(_stream); + + /* restore default signal handling for SIGPIPE */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_DFL); +#endif + return; +} + +unsigned int +PrintLatex::setup (Inkscape::Extension::Print *mod) +{ + return TRUE; +} + +unsigned int +PrintLatex::begin (Inkscape::Extension::Print *mod, SPDocument *doc) +{ + Inkscape::SVGOStringStream os; + int res; + FILE *osf, *osp; + const gchar * fn; + + fn = mod->get_param_string("destination"); + + osf = NULL; + osp = NULL; + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError* error = NULL; + gchar* local_fn = g_filename_from_utf8( fn, + -1, &bytesRead, &bytesWritten, &error); + fn = local_fn; + + /* TODO: Replace the below fprintf's with something that does the right thing whether in + * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of + * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the + * return code. + */ + if (fn != NULL) { + while (isspace(*fn)) fn += 1; + Inkscape::IO::dump_fopen_call(fn, "K"); + osf = Inkscape::IO::fopen_utf8name(fn, "w+"); + if (!osf) { + fprintf(stderr, "inkscape: fopen(%s): %s\n", + fn, strerror(errno)); + return 0; + } + _stream = osf; + } + + g_free(local_fn); + + if (_stream) { + /* fixme: this is kinda icky */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_IGN); +#endif + } + + res = fprintf(_stream, "%%LaTeX with PSTricks extensions\n"); + /* flush this to test output stream as early as possible */ + if (fflush(_stream)) { + /*g_print("caught error in sp_module_print_plain_begin\n");*/ + if (ferror(_stream)) { + g_print("Error %d on output stream: %s\n", errno, + g_strerror(errno)); + } + g_print("Printing failed\n"); + /* fixme: should use pclose() for pipes */ + fclose(_stream); + _stream = NULL; + fflush(stdout); + return 0; + } + + // width and height in pt + _width = sp_document_width(doc) * PT_PER_PX; + _height = sp_document_height(doc) * PT_PER_PX; + + NRRect d; + bool pageLandscape; + // printf("Page Bounding Box: %s\n", pageBoundingBox ? "TRUE" : "FALSE"); +/* if (pageBoundingBox) { + d.x0 = d.y0 = 0; + d.x1 = ceil(_width); + d.y1 = ceil(_height); + } else */{ // no bounding boxes for now + SPItem* doc_item = SP_ITEM(sp_document_root(doc)); + sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE); + // convert from px to pt + d.x0 *= PT_PER_PX; + d.x1 *= PT_PER_PX; + d.y0 *= PT_PER_PX; + d.y1 *= PT_PER_PX; + } + + + if (res >= 0) { + + os << "%%Creator: " << PACKAGE_STRING << "\n"; + os << "%%Please note this file requires PSTricks extensions\n"; + + // 2004 Dec 10, BFC: + // The point of the following code is (1) to do the thing that's expected by users + // who have done File>New>A4_landscape or ...letter_landscape (i.e., rotate + // the output), while (2) not messing up users who simply want their output wider + // than it is tall (e.g., small figures for inclusion in LaTeX). + // The original patch by WQ only had the w>h condition. + { + double w = (d.x1 - d.x0); // width and height of bounding box, in pt + double h = (d.y1 - d.y0); + pageLandscape = ( + (w > 0. && h > 0.) // empty documents fail this sanity check, have w<0, h<0 + && (w > h) // implies, but does not prove, the user wanted landscape + && (w > 600) // approximate maximum printable width of an A4 + ) + ? true : false; + } + + if (pageLandscape) { + os << "\\rotate{90}\n"; + } + + os << "\\psset{xunit=.5pt,yunit=.5pt,runit=.5pt}\n"; + // from now on we can output px, but they will be treated as pt + + os << "\\begin{pspicture}(" << sp_document_width(doc) << "," << sp_document_height(doc) << ")\n"; + } + + return fprintf(_stream, "%s", os.str().c_str()); +} + +unsigned int +PrintLatex::finish (Inkscape::Extension::Print *mod) +{ + int res; + + if (!_stream) return 0; + + res = fprintf(_stream, "\\end{pspicture}\n"); + + /* Flush stream to be sure. */ + (void) fflush(_stream); + + fclose(_stream); + _stream = NULL; + return 0; +} + +unsigned int PrintLatex::comment (Inkscape::Extension::Print * module, + const char * comment) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + + return fprintf(_stream, "%%! %s\n",comment); +} + +unsigned int +PrintLatex::fill(Inkscape::Extension::Print *mod, + NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + + if (style->fill.type == SP_PAINT_TYPE_COLOR) { + Inkscape::SVGOStringStream os; + float rgb[3]; + + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + os << "{\n\\newrgbcolor{curcolor}{" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "}\n"; + + os << "\\pscustom[fillstyle=solid,fillcolor=curcolor]\n{\n"; + + print_bpath(os, bpath->path, transform); + + os << "}\n}\n"; + + fprintf(_stream, "%s", os.str().c_str()); + } + + return 0; +} + +unsigned int +PrintLatex::stroke (Inkscape::Extension::Print *mod, const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + + if (style->stroke.type == SP_PAINT_TYPE_COLOR) { + Inkscape::SVGOStringStream os; + float rgb[3]; + + sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); + os << "{\n\\newrgbcolor{curcolor}{" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "}\n"; + + os << "\\pscustom[linewidth=" << style->stroke_width.computed<< ",linecolor=curcolor"; + + if (style->stroke_dasharray_set && + style->stroke_dash.n_dash && + style->stroke_dash.dash) { + int i; + os << ",linestyle=dashed,dash="; + for (i = 0; i < style->stroke_dash.n_dash; i++) { + if ((i)) { + os << " "; + } + os << style->stroke_dash.dash[i]; + } + } + + os <<"]\n{\n"; + + print_bpath(os, bpath->path, transform); + + os << "}\n}\n"; + + fprintf(_stream, "%s", os.str().c_str()); + } + + return 0; +} + +void +PrintLatex::print_bpath(SVGOStringStream &os, const NArtBpath *bp, const NRMatrix *transform) +{ + unsigned int closed; + NR::Matrix tf=*transform; + + + os << "\\newpath\n"; + closed = FALSE; + while (bp->code != NR_END) { + using NR::X; + using NR::Y; + NR::Point const p1(bp->c(1) * tf); + NR::Point const p2(bp->c(2) * tf); + NR::Point const p3(bp->c(3) * tf); + double const x1 = p1[X], y1 = p1[Y]; + double const x2 = p2[X], y2 = p2[Y]; + double const x3 = p3[X], y3 = p3[Y]; + + switch (bp->code) { + case NR_MOVETO: + if (closed) { + os << "\\closepath\n"; + } + closed = TRUE; + os << "\\moveto(" << x3 << "," << y3 << ")\n"; + break; + case NR_MOVETO_OPEN: + if (closed) { + os << "\\closepath\n"; + } + closed = FALSE; + os << "\\moveto(" << x3 << "," << y3 << ")\n"; + break; + case NR_LINETO: + os << "\\lineto(" << x3 << "," << y3 << ")\n"; + break; + case NR_CURVETO: + os << "\\curveto(" << x1 << "," << y1 << ")(" + << x2 << "," << y2 << ")(" + << x3 << "," << y3 << ")\n"; + break; + default: + break; + } + bp += 1; + } + if (closed) { + os << "\\closepath\n"; + } +} + +bool +PrintLatex::textToPath(Inkscape::Extension::Print * ext) +{ + return ext->get_param_bool("textToPath"); +} + +void +PrintLatex::init (void) +{ + Inkscape::Extension::Extension * ext; + + /* SVG in */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "LaTeX Print\n" + "" SP_MODULE_KEY_PRINT_LATEX "\n" + "\n" + "TRUE\n" + "\n" + "", new PrintLatex()); + + return; +} + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : + diff --git a/src/extension/internal/latex-pstricks.h b/src/extension/internal/latex-pstricks.h new file mode 100644 index 000000000..9238fe606 --- /dev/null +++ b/src/extension/internal/latex-pstricks.h @@ -0,0 +1,70 @@ +#ifndef __INKSCAPE_EXTENSION_INTERNAL_PRINT_LATEX_H__ +#define __INKSCAPE_EXTENSION_INTERNAL_PRINT_LATEX_H__ + +/* + * LaTeX Printing + * + * Author: + * Michael Forbes + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include "extension/implementation/implementation.h" +#include "extension/extension.h" + +#include "svg/stringstream.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PrintLatex : public Inkscape::Extension::Implementation::Implementation { + + float _width; + float _height; + FILE * _stream; + + void print_bpath (SVGOStringStream &os, const NArtBpath *bp, const NRMatrix *transform); + +public: + PrintLatex (void); + virtual ~PrintLatex (void); + + /* Print functions */ + virtual unsigned int setup (Inkscape::Extension::Print * module); + + virtual unsigned int begin (Inkscape::Extension::Print * module, SPDocument *doc); + virtual unsigned int finish (Inkscape::Extension::Print * module); + + /* Rendering methods */ + virtual unsigned int fill (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *ctm, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int stroke (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int comment(Inkscape::Extension::Print *module, const char * comment); + bool textToPath (Inkscape::Extension::Print * ext); + + static void init (void); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_LATEX */ + +/* + Local Variables: + mode:cpp + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/makefile.in b/src/extension/internal/makefile.in new file mode 100644 index 000000000..e727234ed --- /dev/null +++ b/src/extension/internal/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd ../.. && $(MAKE) extension/internal/all + +clean %.a %.o: + cd ../.. && $(MAKE) extension/internal/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp new file mode 100644 index 000000000..29d6d48d5 --- /dev/null +++ b/src/extension/internal/pov-out.cpp @@ -0,0 +1,482 @@ +/* + * A simple utility for exporting Inkscape svg Shapes as PovRay bezier + * prisms. Note that this is output-only, and would thus seem to be + * better placed as an 'export' rather than 'output'. However, Export + * handles all or partial documents, while this outputs ALL shapes in + * the current SVG document. + * + * For information on the PovRay file format, see: + * http://www.povray.org + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "pov-out.h" +#include "inkscape.h" +#include "sp-path.h" +#include +#include "display/curve.h" +#include "libnr/n-art-bpath.h" +#include "extension/system.h" + + + +#include "io/sys.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + + + +static const char * +dstr(gchar *sbuffer, double d) +{ + return (const char *)g_ascii_formatd(sbuffer, + G_ASCII_DTOSTR_BUF_SIZE, "%.8g", (gdouble)d); + +} + + +/** + * Make sure that we are in the database + */ +bool +PovOutput::check (Inkscape::Extension::Extension *module) +{ + /* We don't need a Key + if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV)) + return FALSE; + */ + + return TRUE; +} + + + + +/** + * This function searches the Repr tree recursively from the given node, + * and adds refs to all nodes with the given name, to the result vector + */ +static void +findElementsByTagName(std::vector &results, + Inkscape::XML::Node *node, + char const *name) +{ + if ( !name + || strcmp(node->name(), name) == 0 ) { + results.push_back(node); + } + + for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next()) + findElementsByTagName( results, child, name ); + +} + + +/** + * used for saving information about shapes + */ +class PovShapeInfo +{ +public: + PovShapeInfo() + {} + virtual ~PovShapeInfo() + {} + std::string id; + std::string color; +}; + + + +static double +effective_opacity(SPItem const *item) +{ + double ret = 1.0; + for (SPObject const *obj = item; obj; obj = obj->parent) { + SPStyle const *const style = SP_OBJECT_STYLE(obj); + g_return_val_if_fail(style, ret); + ret *= SP_SCALE24_TO_FLOAT(style->opacity.value); + } + return ret; +} + + +/** + * Saves the of an Inkscape SVG file as PovRay spline definitions +*/ +void +PovOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *uri) +{ + std::vectorresults; + //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, "path"); + findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);//Check all nodes + if (results.size() == 0) + return; + Inkscape::IO::dump_fopen_call(uri, "L"); + FILE *f = Inkscape::IO::fopen_utf8name(uri, "w"); + if (!f) + return; + + time_t tim = time(NULL); + fprintf(f, "/*#################################################\n"); + fprintf(f, "### This PovRay document was generated by Inkscape\n"); + fprintf(f, "### http://www.inkscape.org\n"); + fprintf(f, "### Created: %s", ctime(&tim)); + fprintf(f, "##################################################*/\n\n\n"); + + std::vectorpovShapes; //A list for saving information about the shapes + + //we only need 8 for printf() calls that call dstr() 8 times + gchar s1[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s2[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s3[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s4[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s5[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s6[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s7[G_ASCII_DTOSTR_BUF_SIZE + 1]; + gchar s8[G_ASCII_DTOSTR_BUF_SIZE + 1]; + + double bignum = 1000000.0; + double minx = bignum; + double maxx = -bignum; + double miny = bignum; + double maxy = -bignum; + + + + unsigned indx; + for (indx = 0; indx < results.size() ; indx++) + { + //### Fetch the object from the repr info + Inkscape::XML::Node *rpath = results[indx]; + gchar *id = (gchar *)rpath->attribute("id"); + SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath); + if (!reprobj) + continue; + + //### Get the transform of the item + if (!SP_IS_ITEM(reprobj)) + { + continue; + } + SPItem *item = SP_ITEM(reprobj); + NR::Matrix tf = sp_item_i2d_affine(item); + + //### Get the Shape + if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion. Allow all shapes + { + continue; + } + SPShape *shape = SP_SHAPE(reprobj); + SPCurve *curve = shape->curve; + if (sp_curve_empty(curve)) + continue; + + PovShapeInfo shapeInfo; + + shapeInfo.id = id; + shapeInfo.color = ""; + + //Try to get the fill color of the shape + SPStyle *style = SP_OBJECT_STYLE(shape); + /* fixme: Handle other fill types, even if this means translating gradients to a single + flat colour. */ + if (style && (style->fill.type == SP_PAINT_TYPE_COLOR)) { + // see color.h for how to parse SPColor + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + double const dopacity = ( SP_SCALE24_TO_FLOAT(style->fill_opacity.value) + * effective_opacity(shape) ); + //gchar *str = g_strdup_printf("rgbf < %1.3f, %1.3f, %1.3f %1.3f>", + // rgb[0], rgb[1], rgb[2], 1.0 - dopacity); + gchar *str = g_strdup_printf("rgbf < %s, %s, %s %s>", + dstr(s1, rgb[0]), dstr(s2, rgb[1]), dstr(s3, rgb[2]), dstr(s4, 1.0 - dopacity)); + + shapeInfo.color += str; + g_free(str); + } + + povShapes.push_back(shapeInfo); //passed all tests. save the info + + int curveNr; + + //Count the NR_CURVETOs/LINETOs + int segmentCount=0; + NArtBpath *bp = curve->bpath; + for (curveNr=0 ; curveNrlength ; curveNr++, bp++) + if (bp->code == NR_CURVETO || bp->code == NR_LINETO) + segmentCount++; + + bp = curve->bpath; + double cminx = bignum; + double cmaxx = -bignum; + double cminy = bignum; + double cmaxy = -bignum; + double lastx = 0.0; + double lasty = 0.0; + + fprintf(f, "/*##############################################\n"); + fprintf(f, "### PRISM: %s\n", id); + fprintf(f, "##############################################*/\n"); + fprintf(f, "#declare %s = prism {\n", id); + fprintf(f, " linear_sweep\n"); + fprintf(f, " bezier_spline\n"); + fprintf(f, " 1.0, //top\n"); + fprintf(f, " 0.0, //bottom\n"); + fprintf(f, " %d, //nr points\n", segmentCount * 4); + int segmentNr = 0; + for (bp = curve->bpath, curveNr=0 ; curveNrlength ; curveNr++, bp++) { + using NR::X; + using NR::Y; + NR::Point const p1(bp->c(1) * tf); + NR::Point const p2(bp->c(2) * tf); + NR::Point const p3(bp->c(3) * tf); + double const x1 = p1[X], y1 = p1[Y]; + double const x2 = p2[X], y2 = p2[Y]; + double const x3 = p3[X], y3 = p3[Y]; + + switch (bp->code) { + case NR_MOVETO: + case NR_MOVETO_OPEN: + //fprintf(f, "moveto: %f %f\n", bp->x3, bp->y3); + break; + case NR_CURVETO: + + //fprintf(f, " /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>", + // segmentNr++, lastx, lasty, x1, y1, x2, y2, x3, y3); + fprintf(f, " /*%4d*/ <%s, %s>, <%s, %s>, <%s,%s>, <%s,%s>", + segmentNr++, + dstr(s1, lastx), dstr(s2, lasty), + dstr(s3, x1), dstr(s4, y1), + dstr(s5, x2), dstr(s6, y2), + dstr(s7, x3), dstr(s8, y3)); + + if (segmentNr < segmentCount) + fprintf(f, ",\n"); + else + fprintf(f, "\n"); + + if (lastx < cminx) + cminx = lastx; + if (lastx > cmaxx) + cmaxx = lastx; + if (lasty < cminy) + cminy = lasty; + if (lasty > cmaxy) + cmaxy = lasty; + break; + case NR_LINETO: + + //fprintf(f, " /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>", + // segmentNr++, lastx, lasty, lastx, lasty, x3, y3, x3, y3); + fprintf(f, " /*%4d*/ <%s, %s>, <%s, %s>, <%s,%s>, <%s,%s>", + segmentNr++, + dstr(s1, lastx), dstr(s2, lasty), + dstr(s3, lastx), dstr(s4, lasty), + dstr(s5, x3), dstr(s6, y3), + dstr(s7, x3), dstr(s8, y3)); + + if (segmentNr < segmentCount) + fprintf(f, ",\n"); + else + fprintf(f, "\n"); + + //fprintf(f, "lineto\n"); + if (lastx < cminx) + cminx = lastx; + if (lastx > cmaxx) + cmaxx = lastx; + if (lasty < cminy) + cminy = lasty; + if (lasty > cmaxy) + cmaxy = lasty; + break; + case NR_END: + //fprintf(f, "end\n"); + break; + } + lastx = x3; + lasty = y3; + } + fprintf(f, "}\n"); + /* + fprintf(f, "#declare %s_MIN_X = %4.3f;\n", id, cminx); + fprintf(f, "#declare %s_CENTER_X = %4.3f;\n", id, (cmaxx+cminx)/2.0); + fprintf(f, "#declare %s_MAX_X = %4.3f;\n", id, cmaxx); + fprintf(f, "#declare %s_WIDTH = %4.3f;\n", id, cmaxx-cminx); + fprintf(f, "#declare %s_MIN_Y = %4.3f;\n", id, cminy); + fprintf(f, "#declare %s_CENTER_Y = %4.3f;\n", id, (cmaxy+cminy)/2.0); + fprintf(f, "#declare %s_MAX_Y = %4.3f;\n", id, cmaxy); + fprintf(f, "#declare %s_HEIGHT = %4.3f;\n", id, cmaxy-cminy); + */ + fprintf(f, "#declare %s_MIN_X = %s;\n", id, dstr(s1, cminx)); + fprintf(f, "#declare %s_CENTER_X = %s;\n", id, dstr(s1, (cmaxx+cminx)/2.0)); + fprintf(f, "#declare %s_MAX_X = %s;\n", id, dstr(s1, cmaxx)); + fprintf(f, "#declare %s_WIDTH = %s;\n", id, dstr(s1, cmaxx-cminx)); + fprintf(f, "#declare %s_MIN_Y = %s;\n", id, dstr(s1, cminy)); + fprintf(f, "#declare %s_CENTER_Y = %s;\n", id, dstr(s1, (cmaxy+cminy)/2.0)); + fprintf(f, "#declare %s_MAX_Y = %s;\n", id, dstr(s1, cmaxy)); + fprintf(f, "#declare %s_HEIGHT = %s;\n", id, dstr(s1, cmaxy-cminy)); + if (shapeInfo.color.length()>0) + fprintf(f, "#declare %s_COLOR = %s;\n", + id, shapeInfo.color.c_str()); + fprintf(f, "/*##############################################\n"); + fprintf(f, "### end %s\n", id); + fprintf(f, "##############################################*/\n\n\n\n"); + if (cminx < minx) + minx = cminx; + if (cmaxx > maxx) + maxx = cmaxx; + if (cminy < miny) + miny = cminy; + if (cmaxy > maxy) + maxy = cmaxy; + + + }//for + + + + //## Let's make a union of all of the Shapes + if (!povShapes.empty()) { + char const *id = "AllShapes"; + fprintf(f, "/*##############################################\n"); + fprintf(f, "### UNION OF ALL SHAPES IN DOCUMENT\n"); + fprintf(f, "##############################################*/\n"); + fprintf(f, "\n\n"); + fprintf(f, "/**\n"); + fprintf(f, " * Allow the user to redefine the finish{}\n"); + fprintf(f, " * by declaring it before #including this file\n"); + fprintf(f, " */\n"); + fprintf(f, "#ifndef (%s_Finish)\n", id); + fprintf(f, "#declare %s_Finish = finish {\n", id); + fprintf(f, " phong 0.5\n"); + fprintf(f, " reflection 0.3\n"); + fprintf(f, " specular 0.5\n"); + fprintf(f, "}\n"); + fprintf(f, "#end\n"); + fprintf(f, "\n\n"); + fprintf(f, "#declare %s = union {\n", id); + for (unsigned i = 0 ; i < povShapes.size() ; i++) { + fprintf(f, " object { %s\n", povShapes[i].id.c_str()); + fprintf(f, " texture { \n"); + if (povShapes[i].color.length()>0) + fprintf(f, " pigment { %s }\n", povShapes[i].color.c_str()); + else + fprintf(f, " pigment { rgb <0,0,0> }\n"); + fprintf(f, " finish { %s_Finish }\n", id); + fprintf(f, " } \n"); + fprintf(f, " } \n"); + } + fprintf(f, "}\n\n\n\n"); + + + double zinc = 0.2 / (double)povShapes.size(); + fprintf(f, "/*#### Same union, but with Z-diffs (actually Y in pov) ####*/\n"); + fprintf(f, "\n\n"); + fprintf(f, "/**\n"); + fprintf(f, " * Allow the user to redefine the Z-Increment\n"); + fprintf(f, " */\n"); + fprintf(f, "#ifndef (AllShapes_Z_Increment)\n"); + fprintf(f, "#declare AllShapes_Z_Increment = %s;\n", dstr(s1, zinc)); + fprintf(f, "#end\n"); + fprintf(f, "\n"); + fprintf(f, "#declare AllShapes_Z_Scale = 1.0;\n"); + fprintf(f, "\n\n"); + fprintf(f, "#declare %s_Z = union {\n", id); + for (unsigned i = 0 ; i < povShapes.size() ; i++) { + fprintf(f, " object { %s\n", povShapes[i].id.c_str()); + fprintf(f, " texture { \n"); + if (povShapes[i].color.length()>0) + fprintf(f, " pigment { %s }\n", povShapes[i].color.c_str()); + else + fprintf(f, " pigment { rgb <0,0,0> }\n"); + fprintf(f, " finish { %s_Finish }\n", id); + fprintf(f, " } \n"); + fprintf(f, " scale <1, %s_Z_Scale, 1>\n", id); + fprintf(f, " } \n"); + fprintf(f, "#declare %s_Z_Scale = %s_Z_Scale + %s_Z_Increment;\n\n", + id, id, id); + } + + fprintf(f, "}\n"); + /* + fprintf(f, "#declare %s_MIN_X = %4.3f;\n", id, minx); + fprintf(f, "#declare %s_CENTER_X = %4.3f;\n", id, (maxx+minx)/2.0); + fprintf(f, "#declare %s_MAX_X = %4.3f;\n", id, maxx); + fprintf(f, "#declare %s_WIDTH = %4.3f;\n", id, maxx-minx); + fprintf(f, "#declare %s_MIN_Y = %4.3f;\n", id, miny); + fprintf(f, "#declare %s_CENTER_Y = %4.3f;\n", id, (maxy+miny)/2.0); + fprintf(f, "#declare %s_MAX_Y = %4.3f;\n", id, maxy); + fprintf(f, "#declare %s_HEIGHT = %4.3f;\n", id, maxy-miny); + */ + fprintf(f, "#declare %s_MIN_X = %s;\n", id, dstr(s1, minx)); + fprintf(f, "#declare %s_CENTER_X = %s;\n", id, dstr(s1, (maxx+minx)/2.0)); + fprintf(f, "#declare %s_MAX_X = %s;\n", id, dstr(s1, maxx)); + fprintf(f, "#declare %s_WIDTH = %s;\n", id, dstr(s1, maxx-minx)); + fprintf(f, "#declare %s_MIN_Y = %s;\n", id, dstr(s1, miny)); + fprintf(f, "#declare %s_CENTER_Y = %s;\n", id, dstr(s1, (maxy+miny)/2.0)); + fprintf(f, "#declare %s_MAX_Y = %s;\n", id, dstr(s1, maxy)); + fprintf(f, "#declare %s_HEIGHT = %s;\n", id, dstr(s1, maxy-miny)); + fprintf(f, "/*##############################################\n"); + fprintf(f, "### end %s\n", id); + fprintf(f, "##############################################*/\n\n\n\n"); + } + + //All done + fclose(f); +} + +/** + * This is the definition of PovRay output. This function just + * calls the extension system with the memory allocated XML that + * describes the data. +*/ +void +PovOutput::init() +{ + Inkscape::Extension::build_from_mem( + "\n" + "PovRay Output\n" + "org.inkscape.output.pov\n" + "\n" + ".pov\n" + "text/x-povray-script\n" + "PovRay (*.pov) (export splines)\n" + "PovRay Raytracer File\n" + "\n" + "", + new PovOutput()); +} + + + + + +} //namespace Internal +} //namespace Extension +} //namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/pov-out.h b/src/extension/internal/pov-out.h new file mode 100644 index 000000000..7e71d121c --- /dev/null +++ b/src/extension/internal/pov-out.h @@ -0,0 +1,52 @@ +/* + * A simple utility for exporting Inkscape svg Shapes as PovRay bezier + * prisms. Note that this is output-only, and would thus seem to be + * better placed as an 'export' rather than 'output'. However, Export + * handles all or partial documents, while this outputs ALL shapes in + * the current SVG document. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef EXTENSION_INTERNAL_POV_OUT_H +#define EXTENSION_INTERNAL_POV_OUT_H + +#include +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PovOutput : public Inkscape::Extension::Implementation::Implementation +{ + + public: + + bool check (Inkscape::Extension::Extension * module); + + void save (Inkscape::Extension::Output *mod, + SPDocument *doc, + const gchar *uri); + + static void init (void); + + +}; + + + + +} //namespace Internal +} //namespace Extension +} //namespace Inkscape + + + +#endif /* EXTENSION_INTERNAL_POV_OUT_H */ + diff --git a/src/extension/internal/ps-out.cpp b/src/extension/internal/ps-out.cpp new file mode 100644 index 000000000..a41b90007 --- /dev/null +++ b/src/extension/internal/ps-out.cpp @@ -0,0 +1,94 @@ +/* + * A quick hack to use the print output to write out a file. This + * then makes 'save as...' Postscript. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "ps-out.h" +#include +#include "extension/system.h" +#include "extension/db.h" +#include "extension/output.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +bool +PsOutput::check (Inkscape::Extension::Extension * module) +{ + if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_PRINT_PS)) + return FALSE; + + return TRUE; +} + +/** + \brief This function calls the print system with the filename + \param mod unused + \param doc Document to be saved + \param uri Filename to save to (probably will end in .ps) + + The most interesting thing that this function does is just attach + an '>' on the front of the filename. This is the syntax used to + tell the printing system to save to file. +*/ +void +PsOutput::save (Inkscape::Extension::Output *mod, SPDocument *doc, const gchar *uri) +{ + Inkscape::Extension::Extension * ext; + + ext = Inkscape::Extension::db.get(SP_MODULE_KEY_PRINT_PS); + if (ext == NULL) + return; + + bool old_textToPath = ext->get_param_bool("textToPath"); + bool new_val = mod->get_param_bool("textToPath"); + ext->set_param_bool("textToPath", new_val); + + gchar * final_name; + final_name = g_strdup_printf("> %s", uri); + sp_print_document_to_file(doc, final_name); + g_free(final_name); + + ext->set_param_bool("textToPath", old_textToPath); + + return; +} + +/** + \brief A function allocate a copy of this function. + + This is the definition of postscript out. This function just + calls the extension system with the memory allocated XML that + describes the data. +*/ +void +PsOutput::init (void) +{ + Inkscape::Extension::build_from_mem( + "\n" + "Postscript Output\n" + "org.inkscape.output.ps\n" + "true\n" + "\n" + ".ps\n" + "image/x-postscript\n" + "Postscript (*.ps)\n" + "Postscript File\n" + "\n" + "", new PsOutput()); + + return; +} + +} } } /* namespace Inkscape, Extension, Implementation */ diff --git a/src/extension/internal/ps-out.h b/src/extension/internal/ps-out.h new file mode 100644 index 000000000..592f3d70e --- /dev/null +++ b/src/extension/internal/ps-out.h @@ -0,0 +1,45 @@ +/* + * A quick hack to use the print output to write out a file. This + * then makes 'save as...' Postscript. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef EXTENSION_INTERNAL_PS_OUT_H +#define EXTENSION_INTERNAL_PS_OUT_H + +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PsOutput : Inkscape::Extension::Implementation::Implementation { + +public: + bool check(Inkscape::Extension::Extension *module); + void save(Inkscape::Extension::Output *mod, + SPDocument *doc, + gchar const *uri); + static void init(); +}; + +} } } /* namespace Inkscape, Extension, Implementation */ + +#endif /* !EXTENSION_INTERNAL_PS_OUT_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/ps.cpp b/src/extension/internal/ps.cpp new file mode 100644 index 000000000..2b3103f40 --- /dev/null +++ b/src/extension/internal/ps.cpp @@ -0,0 +1,1264 @@ +#define __SP_PS_C__ + +/** \file + * PostScript printing. + */ +/* + * Authors: + * Lauris Kaplinski + * bulia byak + * + * Basic printing code, EXCEPT image and + * ascii85 filter is in public domain + * + * Image printing and Ascii85 filter: + * + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * Copyright (C) 1997-98 Peter Kirchgessner + * George White + * Austin Donnelly + * + * Licensed under GNU GPL + */ + +/* Plain Print */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "display/nr-arena-item.h" +#include "display/canvas-bpath.h" +#include "sp-item.h" +#include "style.h" +#include "sp-linear-gradient.h" +#include "sp-radial-gradient.h" + +#include "libnrtype/font-instance.h" +#include "libnrtype/font-style-to-pos.h" + +#include + +#include "ps.h" +#include "extension/system.h" +#include "extension/print.h" + +#include "io/sys.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +PrintPS::PrintPS() : + _stream(NULL), + _dpi(72), + _bitmap(false) +{ +} + +PrintPS::~PrintPS(void) +{ + /* fixme: should really use pclose for popen'd streams */ + if (_stream) fclose(_stream); + + /* restore default signal handling for SIGPIPE */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_DFL); +#endif + + return; +} + +unsigned int +PrintPS::setup(Inkscape::Extension::Print * mod) +{ + static gchar const *const pdr[] = {"72", "75", "100", "144", "150", "200", "300", "360", "600", "1200", "2400", NULL}; + +#ifdef TED + Inkscape::XML::Node *repr = ((SPModule *) mod)->repr; +#endif + + unsigned int ret = FALSE; + + /* Create dialog */ + GtkTooltips *tt = gtk_tooltips_new(); + g_object_ref((GObject *) tt); + gtk_object_sink((GtkObject *) tt); + + GtkWidget *dlg = gtk_dialog_new_with_buttons(_("Print Destination"), +// SP_DT_WIDGET(SP_ACTIVE_DESKTOP)->window, + NULL, + (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_PRINT, + GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK); + + GtkWidget *vbox = GTK_DIALOG(dlg)->vbox; + gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); + /* Print properties frame */ + GtkWidget *f = gtk_frame_new(_("Print properties")); + gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4); + GtkWidget *vb = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(f), vb); + gtk_container_set_border_width(GTK_CONTAINER(vb), 4); + /* Print type */ + bool const p2bm = mod->get_param_bool("bitmap"); + GtkWidget *rb = gtk_radio_button_new_with_label(NULL, _("Print using PostScript operators")); + gtk_tooltips_set_tip((GtkTooltips *) tt, rb, + _("Use PostScript vector operators. The resulting image is usually smaller " + "in file size and can be arbitrarily scaled, but alpha transparency " + "and patterns will be lost."), NULL); + if (!p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE); + gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0); + rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group((GtkRadioButton *) rb), _("Print as bitmap")); + gtk_tooltips_set_tip((GtkTooltips *) tt, rb, + _("Print everything as bitmap. The resulting image is usually larger " + "in file size and cannot be arbitrarily scaled without quality loss, " + "but all objects will be rendered exactly as displayed."), NULL); + if (p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE); + gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0); + /* Resolution */ + GtkWidget *hb = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 0); + GtkWidget *combo = gtk_combo_new(); + gtk_combo_set_value_in_list(GTK_COMBO(combo), FALSE, FALSE); + gtk_combo_set_use_arrows(GTK_COMBO(combo), TRUE); + gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE); + gtk_widget_set_size_request(combo, 64, -1); + gtk_tooltips_set_tip((GtkTooltips *) tt, GTK_COMBO(combo)->entry, + _("Preferred resolution (dots per inch) of bitmap"), NULL); + /* Setup strings */ + GList *sl = NULL; + for (unsigned i = 0; pdr[i] != NULL; i++) { + sl = g_list_prepend(sl, (gpointer) pdr[i]); + } + sl = g_list_reverse(sl); + gtk_combo_set_popdown_strings(GTK_COMBO(combo), sl); + g_list_free(sl); + if (1) { + gchar const *val = mod->get_param_string("resolution"); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), val); + } + gtk_box_pack_end(GTK_BOX(hb), combo, FALSE, FALSE, 0); + GtkWidget *l = gtk_label_new(_("Resolution:")); + gtk_box_pack_end(GTK_BOX(hb), l, FALSE, FALSE, 0); + + /* Print destination frame */ + f = gtk_frame_new(_("Print destination")); + gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4); + vb = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(f), vb); + gtk_container_set_border_width(GTK_CONTAINER(vb), 4); + + l = gtk_label_new(_("Printer name (as given by lpstat -p);\n" + "leave empty to use the system default printer.\n" + "Use '> filename' to print to file.\n" + "Use '| prog arg...' to pipe to a program.")); + gtk_box_pack_start(GTK_BOX(vb), l, FALSE, FALSE, 0); + + GtkWidget *e = gtk_entry_new(); + if (1) { + gchar const *val = mod->get_param_string("destination"); + gtk_entry_set_text(GTK_ENTRY(e), ( val != NULL + ? val + : "" )); + } + gtk_box_pack_start(GTK_BOX(vb), e, FALSE, FALSE, 0); + + // pressing enter in the destination field is the same as clicking Print: + gtk_entry_set_activates_default(GTK_ENTRY(e), TRUE); + + gtk_widget_show_all(vbox); + + int const response = gtk_dialog_run(GTK_DIALOG(dlg)); + + g_object_unref((GObject *) tt); + + if (response == GTK_RESPONSE_OK) { + gchar const *fn; + char const *sstr; + + _bitmap = gtk_toggle_button_get_active((GtkToggleButton *) rb); + sstr = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry)); + _dpi = (unsigned int) MAX((int)(atof(sstr)), 1); + /* Arrgh, have to do something */ + fn = gtk_entry_get_text(GTK_ENTRY(e)); + /* skip leading whitespace, bug #1068483 */ + while (fn && *fn==' ') { fn++; } + /* g_print("Printing to %s\n", fn); */ + + mod->set_param_bool("bitmap", _bitmap); + mod->set_param_string("resolution", (gchar *)sstr); + mod->set_param_string("destination", (gchar *)fn); + ret = TRUE; + } + + gtk_widget_destroy(dlg); + + return ret; +} + +unsigned int +PrintPS::begin(Inkscape::Extension::Print *mod, SPDocument *doc) +{ + gboolean epsexport = false; + + _latin1_encoded_fonts.clear(); + _newlatin1font_proc_defined = false; + + FILE *osf = NULL; + FILE *osp = NULL; + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError *error = NULL; + gchar const *utf8_fn = mod->get_param_string("destination"); + gchar *local_fn = g_filename_from_utf8( utf8_fn, + -1, &bytesRead, &bytesWritten, &error); + gchar const *fn = local_fn; + + /* TODO: Replace the below fprintf's with something that does the right thing whether in + * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of + * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the + * return code. + */ + if (fn != NULL) { + if (*fn == '|') { + fn += 1; + while (isspace(*fn)) fn += 1; +#ifndef WIN32 + osp = popen(fn, "w"); +#else + osp = _popen(fn, "w"); +#endif + if (!osp) { + fprintf(stderr, "inkscape: popen(%s): %s\n", + fn, strerror(errno)); + return 0; + } + _stream = osp; + } else if (*fn == '>') { + fn += 1; + epsexport = g_str_has_suffix(fn,".eps"); + while (isspace(*fn)) fn += 1; + Inkscape::IO::dump_fopen_call(fn, "K"); + osf = Inkscape::IO::fopen_utf8name(fn, "w+"); + if (!osf) { + fprintf(stderr, "inkscape: fopen(%s): %s\n", + fn, strerror(errno)); + return 0; + } + _stream = osf; + } else { + /* put cwd stuff in here */ + gchar *qn = ( *fn + ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */ + : g_strdup("lpr") ); +#ifndef WIN32 + osp = popen(qn, "w"); +#else + osp = _popen(qn, "w"); +#endif + if (!osp) { + fprintf(stderr, "inkscape: popen(%s): %s\n", + qn, strerror(errno)); + return 0; + } + g_free(qn); + _stream = osp; + } + } + + g_free(local_fn); + + if (_stream) { + /* fixme: this is kinda icky */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_IGN); +#endif + } + + int const res = fprintf(_stream, ( epsexport + ? "%%!PS-Adobe-3.0 EPSF-3.0\n" + : "%%!PS-Adobe-3.0\n" )); + /* flush this to test output stream as early as possible */ + if (fflush(_stream)) { + /*g_print("caught error in sp_module_print_plain_begin\n");*/ + if (ferror(_stream)) { + g_print("Error %d on output stream: %s\n", errno, + g_strerror(errno)); + } + g_print("Printing failed\n"); + /* fixme: should use pclose() for pipes */ + fclose(_stream); + _stream = NULL; + fflush(stdout); + return 0; + } + + // width and height in pt + _width = sp_document_width(doc) * PT_PER_PX; + _height = sp_document_height(doc) * PT_PER_PX; + + NRRect d; + bool pageBoundingBox; + bool pageLandscape; + pageBoundingBox = mod->get_param_bool("pageBoundingBox"); + // printf("Page Bounding Box: %s\n", pageBoundingBox ? "TRUE" : "FALSE"); + if (pageBoundingBox) { + d.x0 = d.y0 = 0; + d.x1 = ceil(_width); + d.y1 = ceil(_height); + } else { + SPItem* doc_item = SP_ITEM(sp_document_root(doc)); + sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE); + // convert from px to pt + d.x0 *= PT_PER_PX; + d.x1 *= PT_PER_PX; + d.y0 *= PT_PER_PX; + d.y1 *= PT_PER_PX; + } + + Inkscape::SVGOStringStream os; + if (res >= 0) { + + os << "%%Creator: " << PACKAGE_STRING << "\n"; + // This will become problematic if inkscape gains the + // ability to handle multi paged documents. If this is + // the case the %%Orientation: comments should be + // renamed to %%PageOrientation: and moved to the + // respective pages. + os << "%%Pages: 1\n"; + + // 2004 Dec 10, BFC: + // The point of the following code is (1) to do the thing that's expected by users + // who have done File>New>A4_landscape or ...letter_landscape (i.e., rotate + // the output), while (2) not messing up users who simply want their output wider + // than it is tall (e.g., small figures for inclusion in LaTeX). + // The original patch by WQ only had the w>h condition. + { + double w = (d.x1 - d.x0); // width and height of bounding box, in pt + double h = (d.y1 - d.y0); + pageLandscape = ( + (w > 0. && h > 0.) // empty documents fail this sanity check, have w<0, h<0 + && (w > h) // implies, but does not prove, the user wanted landscape + && (w > 600) // approximate maximum printable width of an A4 + && (!epsexport) // eps should always be portrait + ) + ? true : false; + } + + if (pageLandscape) { + os << "%%Orientation: Landscape\n"; + os << "%%BoundingBox: " << (int) (_height - d.y1) << " " + << (int) d.x0 << " " + << (int) ceil(_height - d.y0) << " " + << (int) ceil(d.x1) << "\n"; + // According to Mike Sweet (the author of CUPS) + // HiResBoundingBox is only appropriate + // for EPS files. This means that we should + // distinguish if we export to ps or eps here. + // FIXME: I couldn't find HiResBoundingBox in the PS + // reference manual, so I guess we should skip + // it. + os << "%%HiResBoundingBox: " << (_height - d.y1) << " " + << d.x0 << " " + << (_height - d.y0) << " " + << d.x1 << "\n"; + } else { + os << "%%Orientation: Portrait\n"; + os << "%%BoundingBox: " << (int) d.x0 << " " + << (int) d.y0 << " " + << (int) ceil(d.x1) << " " + << (int) ceil(d.y1) << "\n"; + os << "%%HiResBoundingBox: " << d.x0 << " " + << d.y0 << " " + << d.x1 << " " + << d.y1 << "\n"; + } + + os << "%%EndComments\n"; + // This will become problematic if we print multi paged documents: + os << "%%Page: 1 1\n"; + + if (pageLandscape) { + os << "90 rotate\n"; + if (_bitmap) { + os << "0 " << (int) -ceil(_height) << " translate\n"; + } + } else { + if (!_bitmap) { + os << "0 " << (int) ceil(_height) << " translate\n"; + } + } + + if (!_bitmap) { + os << PT_PER_PX << " " << -PT_PER_PX << " scale\n"; + // from now on we can output px, but they will be treated as pt + } + } + + /* FIXME: This function is declared to return unsigned, whereas fprintf returns a signed int * + * that can be zero if the first fprintf failed (os is empty) or "negative" (i.e. very positive + * in unsigned int interpretation) if the first fprintf failed but this one succeeds, or + * positive if both succeed. */ + return fprintf(_stream, "%s", os.str().c_str()); +} + +unsigned int +PrintPS::finish(Inkscape::Extension::Print *mod) +{ + if (!_stream) return 0; + + if (_bitmap) { + double const dots_per_pt = _dpi / PT_PER_IN; + + double const x0 = 0.0; + double const y0 = 0.0; + double const x1 = x0 + _width; + double const y1 = y0 + _height; + + /* Bitmap width/height in bitmap dots. */ + int const width = (int) (_width * dots_per_pt + 0.5); + int const height = (int) (_height * dots_per_pt + 0.5); + + NRMatrix affine; + affine.c[0] = width / ((x1 - x0) * PX_PER_PT); + affine.c[1] = 0.0; + affine.c[2] = 0.0; + affine.c[3] = height / ((y1 - y0) * PX_PER_PT); + affine.c[4] = -affine.c[0] * x0; + affine.c[5] = -affine.c[3] * y0; + + nr_arena_item_set_transform(mod->root, &affine); + + guchar *const px = nr_new(guchar, 4 * width * 64); + + for (int y = 0; y < height; y += 64) { + /* Set area of interest. */ + NRRectL bbox; + bbox.x0 = 0; + bbox.y0 = y; + bbox.x1 = width; + bbox.y1 = MIN(height, y + 64); + + /* Update to renderable state. */ + NRGC gc(NULL); + nr_matrix_set_identity(&gc.transform); + nr_arena_item_invoke_update(mod->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE); + /* Render */ + /* This should take guchar* instead of unsigned char*) */ + NRPixBlock pb; + nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, + bbox.x0, bbox.y0, bbox.x1, bbox.y1, + (guchar*)px, 4 * width, FALSE, FALSE); + memset(px, 0xff, 4 * width * 64); + nr_arena_item_invoke_render(mod->root, &bbox, &pb, 0); + /* Blitter goes here */ + NRMatrix imgt; + imgt.c[0] = (bbox.x1 - bbox.x0) / dots_per_pt; + imgt.c[1] = 0.0; + imgt.c[2] = 0.0; + imgt.c[3] = (bbox.y1 - bbox.y0) / dots_per_pt; + imgt.c[4] = 0.0; + imgt.c[5] = _height - y / dots_per_pt - (bbox.y1 - bbox.y0) / dots_per_pt; + + print_image(_stream, px, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, 4 * width, &imgt); + } + + nr_free(px); + } + + int const res = fprintf(_stream, "showpage\n"); + + /* Flush stream to be sure. */ + (void) fflush(_stream); + + /* fixme: should really use pclose for popen'd streams */ + fclose(_stream); + _stream = 0; + _latin1_encoded_fonts.clear(); + + return res; +} + +unsigned int +PrintPS::bind(Inkscape::Extension::Print *mod, NRMatrix const *transform, float opacity) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + Inkscape::SVGOStringStream os; + os << "gsave [" << transform->c[0] << " " + << transform->c[1] << " " + << transform->c[2] << " " + << transform->c[3] << " " + << transform->c[4] << " " + << transform->c[5] << "] concat\n"; + + return fprintf(_stream, "%s", os.str().c_str()); +} + +unsigned int +PrintPS::release(Inkscape::Extension::Print *mod) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + return fprintf(_stream, "grestore\n"); +} + +unsigned int +PrintPS::comment(Inkscape::Extension::Print *mod, char const *comment) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + return fprintf(_stream, "%%! %s\n",comment); +} + +void +PrintPS::print_fill_style(SVGOStringStream &os, SPStyle const *const style, NRRect const *pbox) +{ + g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR + || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) ); + + if (style->fill.type == SP_PAINT_TYPE_COLOR) { + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + + os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " setrgbcolor\n"; + + } else { + g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ); + + if (SP_IS_LINEARGRADIENT(SP_STYLE_FILL_SERVER(style))) { + + SPLinearGradient *lg = SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER(style)); + NR::Point p1 (lg->x1.computed, lg->y1.computed); + NR::Point p2 (lg->x2.computed, lg->y2.computed); + if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + // convert to userspace + NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0); + p1 *= bbox2user; + p2 *= bbox2user; + } + + os << "<<\n/ShadingType 2\n/ColorSpace /DeviceRGB\n"; + os << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n"; + os << "/Extend [true true]\n"; + os << "/Domain [0 1]\n"; + os << "/Function <<\n/FunctionType 3\n/Functions\n[\n"; + + sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built + for (unsigned i = 0; i + 1 < lg->vector.stops.size(); i++) { + float rgb[3]; + sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb); + os << "<<\n/FunctionType 2\n/Domain [0 1]\n"; + os << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n"; + sp_color_get_rgb_floatv(&lg->vector.stops[i+1].color, rgb); + os << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n"; + os << "/N 1\n>>\n"; + } + os << "]\n/Domain [0 1]\n"; + os << "/Bounds [ "; + for (unsigned i = 0; i + 2 < lg->vector.stops.size(); i++) { + os << lg->vector.stops[i+1].offset <<" "; + } + os << "]\n"; + os << "/Encode [ "; + for (unsigned i = 0; i + 1 < lg->vector.stops.size(); i++) { + os << "0 1 "; + } + os << "]\n"; + os << ">>\n>>\n"; + + } else if (SP_IS_RADIALGRADIENT(SP_STYLE_FILL_SERVER(style))) { + + SPRadialGradient *rg = SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER(style)); + NR::Point c(rg->cx.computed, rg->cy.computed); + NR::Point f(rg->fx.computed, rg->fy.computed); + double r = rg->r.computed; + if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + // convert to userspace + NR::Matrix const bbox2user(pbox->x1 - pbox->x0, 0, + 0, pbox->y1 - pbox->y0, + pbox->x0, pbox->y0); + c *= bbox2user; + f *= bbox2user; + r *= bbox2user.expansion(); + } + + os << "<<\n/ShadingType 3\n/ColorSpace /DeviceRGB\n"; + os << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n"; + os << "/Extend [true true]\n"; + os << "/Domain [0 1]\n"; + os << "/Function <<\n/FunctionType 3\n/Functions\n[\n"; + + sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built + for (unsigned i = 0; i + 1 < rg->vector.stops.size(); i++) { + float rgb[3]; + sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb); + os << "<<\n/FunctionType 2\n/Domain [0 1]\n"; + os << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n"; + sp_color_get_rgb_floatv(&rg->vector.stops[i+1].color, rgb); + os << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n"; + os << "/N 1\n>>\n"; + } + os << "]\n/Domain [0 1]\n"; + os << "/Bounds [ "; + for (unsigned i = 0; i + 2 < rg->vector.stops.size(); i++) { + os << rg->vector.stops[i+1].offset << " "; + } + os << "]\n"; + os << "/Encode [ "; + for (unsigned i = 0; i + 1 < rg->vector.stops.size(); i++) { + os << "0 1 "; + } + os << "]\n"; + os << ">>\n>>\n"; + } + } +} + +void +PrintPS::print_stroke_style(SVGOStringStream &os, SPStyle const *style) +{ + float rgb[3]; + sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); + + os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " setrgbcolor\n"; + + // There are rare cases in which for a solid line stroke_dasharray_set is true. To avoid + // invalid PS-lines such as "[0.0000000 0.0000000] 0.0000000 setdash", which should be "[] 0 setdash", + // we first check if all components of stroke_dash.dash are 0. + bool LineSolid = true; + if (style->stroke_dasharray_set && + style->stroke_dash.n_dash && + style->stroke_dash.dash ) + { + int i = 0; + while (LineSolid && (i < style->stroke_dash.n_dash)) { + i++; + if (style->stroke_dash.dash[i] > 0.00000001) + LineSolid = false; + } + if (!LineSolid) { + os << "["; + for (i = 0; i < style->stroke_dash.n_dash; i++) { + if (i > 0) { + os << " "; + } + os << style->stroke_dash.dash[i]; + } + os << "] " << style->stroke_dash.offset << " setdash\n"; + } else { + os << "[] 0 setdash\n"; + } + } else { + os << "[] 0 setdash\n"; + } + + os << style->stroke_width.computed << " setlinewidth\n"; + os << style->stroke_linejoin.computed << " setlinejoin\n"; + os << style->stroke_linecap.computed << " setlinecap\n"; +} + + +unsigned int +PrintPS::fill(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *const style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + if ( style->fill.type == SP_PAINT_TYPE_COLOR + || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) ) + { + Inkscape::SVGOStringStream os; + + os << "gsave\n"; + + print_fill_style(os, style, pbox); + + print_bpath(os, bpath->path); + + if (style->fill_rule.value == SP_WIND_RULE_EVENODD) { + if (style->fill.type == SP_PAINT_TYPE_COLOR) { + os << "eofill\n"; + } else { + g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ); + SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style)); + os << "eoclip\n"; + if (g->gradientTransform_set) { + os << "gsave [" << g->gradientTransform[0] << " " << g->gradientTransform[1] + << " " << g->gradientTransform[2] << " " << g->gradientTransform[3] + << " " << g->gradientTransform[4] << " " << g->gradientTransform[5] << "] concat\n"; + } + os << "shfill\n"; + if (g->gradientTransform_set) { + os << "grestore\n"; + } + } + } else { + if (style->fill.type == SP_PAINT_TYPE_COLOR) { + os << "fill\n"; + } else { + g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ); + SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style)); + os << "clip\n"; + if (g->gradientTransform_set) { + os << "gsave [" << g->gradientTransform[0] << " " << g->gradientTransform[1] + << " " << g->gradientTransform[2] << " " << g->gradientTransform[3] + << " " << g->gradientTransform[4] << " " << g->gradientTransform[5] << "] concat\n"; + } + os << "shfill\n"; + if (g->gradientTransform_set) { + os << "grestore\n"; + } + } + } + + os << "grestore\n"; + + fprintf(_stream, "%s", os.str().c_str()); + } + + return 0; +} + + +unsigned int +PrintPS::stroke(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + if (style->stroke.type == SP_PAINT_TYPE_COLOR) { + Inkscape::SVGOStringStream os; + + print_stroke_style(os, style); + + print_bpath(os, bpath->path); + + os << "stroke\n"; + + fprintf(_stream, "%s", os.str().c_str()); + } + + return 0; +} + +unsigned int +PrintPS::image(Inkscape::Extension::Print *mod, guchar *px, unsigned int w, unsigned int h, unsigned int rs, + NRMatrix const *transform, SPStyle const *style) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + return print_image(_stream, px, w, h, rs, transform); +#if 0 + fprintf(_stream, "gsave\n"); + fprintf(_stream, "/rowdata %d string def\n", 3 * w); + fprintf(_stream, "[%g %g %g %g %g %g] concat\n", + transform->c[0], + transform->c[1], + transform->c[2], + transform->c[3], + transform->c[4], + transform->c[5]); + fprintf(_stream, "%d %d 8 [%d 0 0 -%d 0 %d]\n", w, h, w, h, h); + fprintf(_stream, "{currentfile rowdata readhexstring pop}\n"); + fprintf(_stream, "false 3 colorimage\n"); + + for (unsigned int r = 0; r < h; r++) { + guchar *s; + unsigned int c0, c1, c; + s = px + r * rs; + for (c0 = 0; c0 < w; c0 += 24) { + c1 = MIN(w, c0 + 24); + for (c = c0; c < c1; c++) { + static char const xtab[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + fputc(xtab[s[0] >> 4], _stream); + fputc(xtab[s[0] & 0xf], _stream); + fputc(xtab[s[1] >> 4], _stream); + fputc(xtab[s[1] & 0xf], _stream); + fputc(xtab[s[2] >> 4], _stream); + fputc(xtab[s[2] & 0xf], _stream); + s += 4; + } + fputs("\n", _stream); + } + } + + fprintf(_stream, "grestore\n"); + + return 0; +#endif +} + +char const * +PrintPS::PSFontName(SPStyle const *style) +{ + font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style)); + + char const *n; + char name_buf[256]; + + if (tf) { + tf->PSName(name_buf, sizeof(name_buf)); + n = name_buf; + tf->Unref(); + } else { + // this system does not have this font, so just use the name from SVG in the hope that PS interpreter will make sense of it + bool i = (style->font_style.value == SP_CSS_FONT_STYLE_ITALIC); + bool o = (style->font_style.value == SP_CSS_FONT_STYLE_OBLIQUE); + bool b = (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) || + (style->font_weight.value >= SP_CSS_FONT_WEIGHT_500 && style->font_weight.value <= SP_CSS_FONT_WEIGHT_900); + + n = g_strdup_printf("%s%s%s%s", + g_strdelimit(style->text->font_family.value, " ", '-'), + (b || i || o) ? "-" : "", + (b) ? "Bold" : "", + (i) ? "Italic" : ((o) ? "Oblique" : "") ); + } + + return g_strdup(n); +} + + +unsigned int +PrintPS::text(Inkscape::Extension::Print *mod, char const *text, NR::Point p, + SPStyle const *const style) +{ + if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned. + if (_bitmap) return 0; + + Inkscape::SVGOStringStream os; + + // Escape chars + Inkscape::SVGOStringStream escaped_text; + escaped_text << std::oct; + for (gchar const *p_text = text ; *p_text ; p_text = g_utf8_next_char(p_text)) { + gunichar const c = g_utf8_get_char(p_text); + if (c == '\\' || c == ')' || c == '(') + escaped_text << '\\' << static_cast(c); + else if (c >= 0x80) + escaped_text << '\\' << c; + else + escaped_text << static_cast(c); + } + + os << "gsave\n"; + + // set font + char const *fn = PSFontName(style); + if (_latin1_encoded_fonts.find(fn) == _latin1_encoded_fonts.end()) { + if (!_newlatin1font_proc_defined) { + // input: newfontname, existingfontname + // output: new font object, also defined to newfontname + os << "/newlatin1font " // name of the proc + "{findfont dup length dict copy " // load the font and create a copy of it + "dup /Encoding ISOLatin1Encoding put " // change the encoding in the copy + "definefont} def\n"; // create the new font and leave it on the stack, define the proc + _newlatin1font_proc_defined = true; + } + os << "/" << fn << "-ISOLatin1 /" << fn << " newlatin1font\n"; + _latin1_encoded_fonts.insert(fn); + } else + os << "/" << fn << "-ISOLatin1 findfont\n"; + os << style->font_size.computed << " scalefont\n"; + os << "setfont\n"; + g_free((void *) fn); + + if ( style->fill.type == SP_PAINT_TYPE_COLOR + || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER + && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) ) + { + // set fill style + print_fill_style(os, style, NULL); + // FIXME: we don't know the pbox of text, so have to pass NULL. This means gradients with + // bbox units won't work with text. However userspace gradients don't work with text either + // (text is black) for some reason. + + os << "newpath\n"; + os << p[NR::X] << " " << p[NR::Y] << " moveto\n"; + os << "(" << escaped_text.str() << ") show\n"; + } + + if (style->stroke.type == SP_PAINT_TYPE_COLOR) { + + // set stroke style + print_stroke_style(os, style); + + // paint stroke + os << "newpath\n"; + os << p[NR::X] << " " << p[NR::Y] << " moveto\n"; + os << "(" << escaped_text.str() << ") false charpath stroke\n"; + } + + os << "grestore\n"; + + fprintf(_stream, "%s", os.str().c_str()); + + return 0; +} + + + +/* PostScript helpers */ + +void +PrintPS::print_bpath(SVGOStringStream &os, NArtBpath const *bp) +{ + os << "newpath\n"; + bool closed = false; + while (bp->code != NR_END) { + switch (bp->code) { + case NR_MOVETO: + if (closed) { + os << "closepath\n"; + } + closed = true; + os << bp->x3 << " " << bp->y3 << " moveto\n"; + break; + case NR_MOVETO_OPEN: + if (closed) { + os << "closepath\n"; + } + closed = false; + os << bp->x3 << " " << bp->y3 << " moveto\n"; + break; + case NR_LINETO: + os << bp->x3 << " " << bp->y3 << " lineto\n"; + break; + case NR_CURVETO: + os << bp->x1 << " " << bp->y1 << " " + << bp->x2 << " " << bp->y2 << " " + << bp->x3 << " " << bp->y3 << " curveto\n"; + break; + default: + break; + } + bp += 1; + } + if (closed) { + os << "closepath\n"; + } +} + +/* The following code is licensed under GNU GPL. +** The packbits, ascii85 and imaging printing code +** is from the gimp's postscript.c. +*/ + +/** +* \param nin Number of bytes of source data. +* \param src Source data. +* \param nout Number of output bytes. +* \param dst Buffer for output. +*/ +void +PrintPS::compress_packbits(int nin, + guchar *src, + int *nout, + guchar *dst) + +{ + register guchar c; + int nrepeat, nliteral; + guchar *run_start; + guchar *start_dst = dst; + guchar *last_literal = NULL; + + for (;;) { + if (nin <= 0) break; + + run_start = src; + c = *run_start; + + /* Search repeat bytes */ + if ((nin > 1) && (c == src[1])) { + nrepeat = 1; + nin -= 2; + src += 2; + while ((nin > 0) && (c == *src)) { + nrepeat++; + src++; + nin--; + if (nrepeat == 127) break; /* Maximum repeat */ + } + + /* Add two-byte repeat to last literal run ? */ + if ( (nrepeat == 1) + && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128) ) + { + *last_literal += 2; + *(dst++) = c; + *(dst++) = c; + continue; + } + + /* Add repeat run */ + *(dst++) = (guchar)((-nrepeat) & 0xff); + *(dst++) = c; + last_literal = NULL; + continue; + } + /* Search literal bytes */ + nliteral = 1; + nin--; + src++; + + for (;;) { + if (nin <= 0) break; + + if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */ + break; + + nliteral++; + nin--; + src++; + if (nliteral == 128) break; /* Maximum literal run */ + } + + /* Could be added to last literal run ? */ + if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128)) { + *last_literal += nliteral; + } else { + last_literal = dst; + *(dst++) = (guchar)(nliteral-1); + } + while (nliteral-- > 0) *(dst++) = *(run_start++); + } + *nout = dst - start_dst; +} + +void +PrintPS::ascii85_init(void) +{ + ascii85_len = 0; + ascii85_linewidth = 0; +} + +void +PrintPS::ascii85_flush(SVGOStringStream &os) +{ + char c[5]; + bool const zero_case = (ascii85_buf == 0); + static int const max_linewidth = 75; + + for (int i = 4; i >= 0; i--) { + c[i] = (ascii85_buf % 85) + '!'; + ascii85_buf /= 85; + } + /* check for special case: "!!!!!" becomes "z", but only if not + * at end of data. */ + if (zero_case && (ascii85_len == 4)) { + if (ascii85_linewidth >= max_linewidth) { + os << '\n'; + ascii85_linewidth = 0; + } + os << 'z'; + ascii85_linewidth++; + } else { + for (int i = 0; i < ascii85_len+1; i++) { + if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%')) { + os << '\n'; + ascii85_linewidth = 0; + } + os << c[i]; + ascii85_linewidth++; + } + } + + ascii85_len = 0; + ascii85_buf = 0; +} + +inline void +PrintPS::ascii85_out(guchar byte, SVGOStringStream &os) +{ + if (ascii85_len == 4) + ascii85_flush(os); + + ascii85_buf <<= 8; + ascii85_buf |= byte; + ascii85_len++; +} + +void +PrintPS::ascii85_nout(int n, guchar *uptr, SVGOStringStream &os) +{ + while (n-- > 0) { + ascii85_out(*uptr, os); + uptr++; + } +} + +void +PrintPS::ascii85_done(SVGOStringStream &os) +{ + if (ascii85_len) { + /* zero any unfilled buffer portion, then flush */ + ascii85_buf <<= (8 * (4-ascii85_len)); + ascii85_flush(os); + } + + os << "~>\n"; +} + +unsigned int +PrintPS::print_image(FILE *ofp, guchar *px, unsigned int width, unsigned int height, unsigned int rs, + NRMatrix const *transform) +{ + Inkscape::SVGOStringStream os; + + os << "gsave\n"; + os << "[" << transform->c[0] << " " + << transform->c[1] << " " + << transform->c[2] << " " + << transform->c[3] << " " + << transform->c[4] << " " + << transform->c[5] << "] concat\n"; + os << width << " " << height << " 8 [" + << width << " 0 0 -" << height << " 0 " << height << "]\n"; + + + /* Write read image procedure */ + os << "% Strings to hold RGB-samples per scanline\n"; + os << "/rstr " << width << " string def\n"; + os << "/gstr " << width << " string def\n"; + os << "/bstr " << width << " string def\n"; + os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}\n"; + os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}\n"; + os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}\n"; + os << "true 3\n"; + + /* Allocate buffer for packbits data. Worst case: Less than 1% increase */ + guchar *const packb = (guchar *)g_malloc((width * 105)/100+2); + guchar *const plane = (guchar *)g_malloc(width); + + /* ps_begin_data(ofp); */ + os << "colorimage\n"; + +/*#define GET_RGB_TILE(begin) \ + * {int scan_lines; \ + * scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \ + * gimp_pixel_rgn_get_rect(&pixel_rgn, begin, 0, i, width, scan_lines); \ + * src = begin; } + */ + + for (unsigned i = 0; i < height; i++) { + /* if ((i % tile_height) == 0) GET_RGB_TILE(data); */ /* Get more data */ + guchar const *const src = px + i * rs; + + /* Iterate over RGB */ + for (int rgb = 0; rgb < 3; rgb++) { + guchar const *src_ptr = src + rgb; + guchar *plane_ptr = plane; + for (unsigned j = 0; j < width; j++) { + *(plane_ptr++) = *src_ptr; + src_ptr += 4; + } + + int nout; + compress_packbits(width, plane, &nout, packb); + + ascii85_init(); + ascii85_nout(nout, packb, os); + ascii85_out(128, os); /* Write EOD of RunLengthDecode filter */ + ascii85_done(os); + } + } + /* ps_end_data(ofp); */ + +#if 0 + fprintf(ofp, "showpage\n"); + g_free(data); +#endif + + g_free(packb); + g_free(plane); + +#if 0 + if (ferror(ofp)) { + g_message(_("write error occurred")); + return (FALSE); + } +#endif + + os << "grestore\n"; + + fprintf(ofp, "%s", os.str().c_str()); + + return 0; +//#undef GET_RGB_TILE +} + +bool +PrintPS::textToPath(Inkscape::Extension::Print * ext) +{ + return ext->get_param_bool("textToPath"); +} + +void +PrintPS::init(void) +{ + /* SVG in */ + (void) Inkscape::Extension::build_from_mem( + "\n" + "Postscript Print\n" + "" SP_MODULE_KEY_PRINT_PS "\n" + "FALSE\n" + "72\n" + "\n" + "TRUE\n" + "TRUE\n" + "\n" + "", new PrintPS()); +} + + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* End of GNU GPL code */ + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/ps.h b/src/extension/internal/ps.h new file mode 100644 index 000000000..eb07dc9bd --- /dev/null +++ b/src/extension/internal/ps.h @@ -0,0 +1,108 @@ +#ifndef EXTENSION_INTERNAL_PS_H_SEEN +#define EXTENSION_INTERNAL_PS_H_SEEN + +/** \file + * Declaration of PrintPS, the internal module used to do Postscript Printing. + */ +/* + * Authors: + * Lauris Kaplinski + * Ted Gould + * + * Lauris' original code is in the public domain. + * Ted's changes are licensed under the GNU GPL. + */ + +#include +#include "extension/extension.h" +#include "extension/implementation/implementation.h" +#include +#include + +#include "libnr/nr-path.h" + +#include "svg/stringstream.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PrintPS : public Inkscape::Extension::Implementation::Implementation { + float _width; + float _height; + FILE *_stream; + unsigned short _dpi; + bool _bitmap; + std::set _latin1_encoded_fonts; + bool _newlatin1font_proc_defined; + + void print_bpath(SVGOStringStream &os, NArtBpath const *bp); + + void print_fill_style(SVGOStringStream &os, SPStyle const *style, NRRect const *pbox); + void print_stroke_style(SVGOStringStream &os, SPStyle const *style); + + char const *PSFontName(SPStyle const *style); + + unsigned int print_image(FILE *ofp, guchar *px, unsigned int width, unsigned int height, unsigned int rs, + NRMatrix const *transform); + void compress_packbits(int nin, guchar *src, int *nout, guchar *dst); + + /* ASCII 85 variables */ + guint32 ascii85_buf; + int ascii85_len; + int ascii85_linewidth; + /* ASCII 85 Functions */ + void ascii85_init(void); + void ascii85_flush(SVGOStringStream &os); + inline void ascii85_out(guchar byte, SVGOStringStream &os); + void ascii85_nout(int n, guchar *uptr, SVGOStringStream &os); + void ascii85_done(SVGOStringStream &os); + + +public: + PrintPS(void); + virtual ~PrintPS(void); + + /* Print functions */ + virtual unsigned int setup(Inkscape::Extension::Print *module); + /* + virtual unsigned int set_preview(Inkscape::Extension::Print *module); + */ + + virtual unsigned int begin(Inkscape::Extension::Print *module, SPDocument *doc); + virtual unsigned int finish(Inkscape::Extension::Print *module); + + /* Rendering methods */ + virtual unsigned int bind(Inkscape::Extension::Print *module, NRMatrix const *transform, float opacity); + virtual unsigned int release(Inkscape::Extension::Print *module); + virtual unsigned int comment(Inkscape::Extension::Print *module, char const *comment); + virtual unsigned int fill(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox); + virtual unsigned int stroke(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style, + NRRect const *pbox, NRRect const *dbox, NRRect const *bbox); + virtual unsigned int image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + NRMatrix const *transform, SPStyle const *style); + virtual unsigned int text(Inkscape::Extension::Print *module, char const *text, + NR::Point p, SPStyle const *style); + + bool textToPath(Inkscape::Extension::Print *ext); + static void init(void); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + + +#endif /* !EXTENSION_INTERNAL_PS_H_SEEN */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/svg.cpp b/src/extension/internal/svg.cpp new file mode 100644 index 000000000..e6d02b62d --- /dev/null +++ b/src/extension/internal/svg.cpp @@ -0,0 +1,248 @@ +/* + * This is the code that moves all of the SVG loading and saving into + * the module format. Really Inkscape is built to handle these formats + * internally, so this is just calling those internal functions. + * + * Authors: + * Lauris Kaplinski + * Ted Gould + * + * Copyright (C) 2002-2003 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "sp-object.h" +#include "svg.h" +#include "file.h" +#include "extension/system.h" +#include "extension/output.h" + +#ifdef WITH_GNOME_VFS +# include +#endif + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** + \return None + \brief What would an SVG editor be without loading/saving SVG + files. This function sets that up. + + For each module there is a call to Inkscape::Extension::build_from_mem + with a rather large XML file passed in. This is a constant string + that describes the module. At the end of this call a module is + returned that is basically filled out. The one thing that it doesn't + have is the key function for the operation. And that is linked at + the end of each call. +*/ +void +Svg::init(void) +{ + Inkscape::Extension::Extension * ext; + + /* SVG in */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVG Input\n" + "" SP_MODULE_KEY_INPUT_SVG "\n" + "\n" + ".svg\n" + "image/x-svg\n" + "Scalable Vector Graphic (*.svg)\n" + "Inkscape native file format and W3C standard\n" + "" SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE "\n" + "\n" + "", new Svg()); + + /* SVG out Inkscape */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVG Output Inkscape\n" + "" SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE "\n" + "\n" + ".svg\n" + "image/x-svg\n" + "Inkscape SVG (*.svg)\n" + "SVG format with Inkscape extensions\n" + "FALSE\n" + "\n" + "", new Svg()); + + /* SVG out */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVG Output\n" + "" SP_MODULE_KEY_OUTPUT_SVG "\n" + "\n" + ".svg\n" + "image/x-svg\n" + "Plain SVG (*.svg)\n" + "Scalable Vector Graphics format as defined by the W3C\n" + "\n" + "", new Svg()); + +#ifdef WITH_GNOME_VFS + gnome_vfs_init(); +#endif + + + return; +} + + +#ifdef WITH_GNOME_VFS +#define BUF_SIZE 8192 + +gchar * +_load_uri (const gchar *uri) +{ + GnomeVFSHandle *handle = NULL; + GnomeVFSFileSize bytes_read; + gchar buffer[BUF_SIZE] = ""; + gchar *doc = NULL; + gchar *new_doc = NULL; + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError* error = NULL; + gchar* uri_local = g_filename_from_utf8( uri, -1, &bytesRead, &bytesWritten, &error); + + if ( uri_local == NULL ) { + g_warning( "Error converting filename to locale encoding."); + } + + GnomeVFSResult result = gnome_vfs_open (&handle, uri_local, GNOME_VFS_OPEN_READ); + + if (result != GNOME_VFS_OK) { + g_warning(gnome_vfs_result_to_string(result)); + } + + while (result == GNOME_VFS_OK) { + result = gnome_vfs_read (handle, buffer, BUF_SIZE, &bytes_read); + buffer[bytes_read] = '\0'; + + if (doc == NULL) { + doc = g_strndup(buffer, bytes_read); + } else { + new_doc = g_strconcat(doc, buffer, NULL); + g_free(doc); + doc = new_doc; + } + } + + return doc; +} +#endif + + +/** + \return A new document just for you! + \brief This function takes in a filename of a SVG document and + turns it into a SPDocument. + \param mod Module to use + \param uri The path to the file (UTF-8) + + This function is really simple, it just calls sp_document_new... +*/ +SPDocument * +Svg::open (Inkscape::Extension::Input *mod, const gchar *uri) +{ +#ifdef WITH_GNOME_VFS + if (gnome_vfs_uri_is_local(gnome_vfs_uri_new(uri))) { + // Use built-in loader instead of VFS for this + return sp_document_new(uri, TRUE); + } + gchar * buffer = _load_uri(uri); + if (buffer == NULL) { + g_warning("Error: Could not open file '%s' with VFS\n", uri); + return NULL; + } + SPDocument * doc = sp_document_new_from_mem(buffer, strlen(buffer), 1); + + g_free(buffer); + return doc; +#else + return sp_document_new (uri, TRUE); +#endif +} + +/** + \return None + \brief This is the function that does all of the SVG saves in + Inkscape. It detects whether it should do a Inkscape + namespace save internally. + \param mod Extension to use. + \param doc Document to save. + \param uri The filename to save the file to. + + This function first checks it's parameters, and makes sure that + we're getting good data. It also checks the module ID of the + incoming module to figure out if this is save should include + the Inkscape namespace stuff or not. The result of that comparison + is stored in the spns variable. + + If there is not to be Inkscape name spaces a new document is created + without. (I think, I'm not sure on this code) + + All of the internally referenced imageins are also set to relative + paths in the file. And the file is saved. + + This really needs to be fleshed out more, but I don't quite understand + all of this code. I just stole it. +*/ +void +Svg::save (Inkscape::Extension::Output *mod, SPDocument *doc, const gchar *uri) +{ + g_return_if_fail(doc != NULL); + g_return_if_fail(uri != NULL); + + gchar *save_path = g_path_get_dirname (uri); + + gboolean const spns = (!mod->get_id() + || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE) + || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE)); + + Inkscape::XML::Document *rdoc = NULL; + Inkscape::XML::Node *repr = NULL; + if (spns) { + repr = sp_document_repr_root (doc); + } else { + rdoc = sp_repr_document_new ("svg:svg"); + repr = sp_repr_document_root (rdoc); + repr = sp_document_root (doc)->updateRepr(repr, SP_OBJECT_WRITE_BUILD); + } + + Inkscape::IO::fixupHrefs( doc, save_path, spns ); + + gboolean const s = sp_repr_save_file (sp_repr_document (repr), uri, SP_SVG_NS_URI); + if (s == FALSE) { + throw Inkscape::Extension::Output::save_failed(); + } + + if (!spns) { + Inkscape::GC::release(rdoc); + } + + g_free(save_path); + + return; +} + +} } } /* namespace inkscape, module, implementation */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/svg.h b/src/extension/internal/svg.h new file mode 100644 index 000000000..de75f0021 --- /dev/null +++ b/src/extension/internal/svg.h @@ -0,0 +1,48 @@ +/* + * This is the code that moves all of the SVG loading and saving into + * the module format. Really Sodipodi is built to handle these formats + * internally, so this is just calling those internal functions. + * + * Authors: + * Lauris Kaplinski + * Ted Gould + * + * Copyright (C) 2002-2003 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef __SVG_H__ +#define __SVG_H__ + +#include "../implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class Svg : public Inkscape::Extension::Implementation::Implementation { + +public: + virtual void save( Inkscape::Extension::Output *mod, + SPDocument *doc, + const gchar *uri ); + virtual SPDocument *open( Inkscape::Extension::Input *mod, + const gchar *uri ); + static void init( void ); + +}; + +} } } /* namespace Inkscape, Extension, Implementation */ +#endif /* __SVG_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/svgz.cpp b/src/extension/internal/svgz.cpp new file mode 100644 index 000000000..d56ac31b2 --- /dev/null +++ b/src/extension/internal/svgz.cpp @@ -0,0 +1,99 @@ +/* + * Code to handle compressed SVG loading and saving. Almost identical to svg + * routines, but separated for simpler extension maintenance. + * + * Authors: + * Lauris Kaplinski + * Ted Gould + * Jon A. Cruz + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "svgz.h" +#include "extension/system.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** + \return None + \brief What would an SVG editor be without loading/saving SVG + files. This function sets that up. + + For each module there is a call to Inkscape::Extension::build_from_mem + with a rather large XML file passed in. This is a constant string + that describes the module. At the end of this call a module is + returned that is basically filled out. The one thing that it doesn't + have is the key function for the operation. And that is linked at + the end of each call. +*/ +void +Svgz::init(void) +{ + Inkscape::Extension::Extension * ext; + + /* SVGZ in */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVGZ Input\n" + "" SP_MODULE_KEY_INPUT_SVGZ "\n" + "" SP_MODULE_KEY_INPUT_SVG "\n" + "\n" + ".svgz\n" + "image/x-svgz\n" + "Compressed Inkscape SVG (*.svgz)\n" + "SVG file format compressed with GZip\n" + "" SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE "\n" + "\n" + "", new Svgz()); + + /* SVGZ out Inkscape */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVGZ Output\n" + "" SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE "\n" + "\n" + ".svgz\n" + "image/x-svgz\n" + "Compressed Inkscape SVG (*.svgz)\n" + "Inkscape's native file format compressed with GZip\n" + "FALSE\n" + "\n" + "", new Svgz()); + + /* SVGZ out */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "SVGZ Output\n" + "" SP_MODULE_KEY_OUTPUT_SVGZ "\n" + "\n" + ".svgz\n" + "image/x-svgz\n" + "Compressed plain SVG (*.svgz)\n" + "Scalable Vector Graphics format compressed with GZip\n" + "\n" + "\n", new Svgz()); + + return; +} + + +} } } // namespace inkscape, module, implementation + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/svgz.h b/src/extension/internal/svgz.h new file mode 100644 index 000000000..ba348f887 --- /dev/null +++ b/src/extension/internal/svgz.h @@ -0,0 +1,41 @@ +/* + * Code to handle compressed SVG loading and saving. Almost identical to svg + * routines, but separated for simpler extension maintenance. + * + * Authors: + * Lauris Kaplinski + * Ted Gould + * Jon A. Cruz + * + * Copyright (C) 2002-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SVGZ_H +#define SEEN_SVGZ_H + +#include "svg.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class Svgz : public Svg { +public: + static void init( void ); +}; + +} } } // namespace Inkscape, Extension, Implementation +#endif // SEEN_SVGZ_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/internal/win32.cpp b/src/extension/internal/win32.cpp new file mode 100644 index 000000000..7f1774d21 --- /dev/null +++ b/src/extension/internal/win32.cpp @@ -0,0 +1,499 @@ +#define __SP_MODULE_WIN32_C__ + +/* + * Windows stuff + * + * Author: + * Lauris Kaplinski + * + * This code is in public domain + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "display/nr-arena-item.h" +#include "display/nr-arena.h" +#include "document.h" + +#include "win32.h" +#include "system.h" +#include "extension/print.h" +#include + +/* Initialization */ + +namespace Inkscape { +namespace Extension { +namespace Internal { + +static unsigned int SPWin32Modal = FALSE; + +/** + * Callback function.. not a method + */ +static void +my_gdk_event_handler (GdkEvent *event) +{ + if (SPWin32Modal) { + /* Win32 widget is modal, filter events */ + switch (event->type) { + case GDK_NOTHING: + case GDK_DELETE: + case GDK_SCROLL: + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + case GDK_DRAG_STATUS: + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DROP_START: + case GDK_DROP_FINISHED: + return; + break; + default: + break; + } + } + gtk_main_do_event (event); +} + +void +PrintWin32::main_init (int argc, char **argv, const char *name) +{ + gdk_event_handler_set ((GdkEventFunc) my_gdk_event_handler, NULL, NULL); +} + +void +PrintWin32::finish (void) +{ +} + +#define SP_FOREIGN_MAX_ITER 10 + + +/** + * Callback function.. not a method + */ +static VOID CALLBACK +my_timer (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + int cdown = 0; + while ((cdown++ < SP_FOREIGN_MAX_ITER) && gdk_events_pending ()) { + gtk_main_iteration_do (FALSE); + } + gtk_main_iteration_do (FALSE); +} + + +/* Platform detection */ + +gboolean +PrintWin32::is_os_wide() +{ + static gboolean initialized = FALSE; + static gboolean is_wide = FALSE; + static OSVERSIONINFOA osver; + + if ( !initialized ) + { + BOOL result; + + initialized = TRUE; + + memset (&osver, 0, sizeof(OSVERSIONINFOA)); + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + result = GetVersionExA (&osver); + if (result) + { + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) + is_wide = TRUE; + } + // If we can't even call to get the version, fall back to ANSI API + } + + return is_wide; +} + + +/* Printing */ + +PrintWin32::PrintWin32 (void) +{ + /* Nothing here */ +} + + +PrintWin32::~PrintWin32 (void) +{ + DeleteDC (_hDC); +} + + +/** + * Callback function.. not a method + */ +static UINT_PTR CALLBACK +print_hook (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) +{ +#if 0 + int cdown = 0; + while ((cdown++ < SP_FOREIGN_MAX_ITER) && gdk_events_pending ()) { + gtk_main_iteration_do (FALSE); + } + gtk_main_iteration_do (FALSE); +#endif + return 0; +} + +unsigned int +PrintWin32::setup (Inkscape::Extension::Print *mod) +{ + HRESULT res; + PRINTDLG pd = { + sizeof (PRINTDLG), + NULL, /* hwndOwner */ + NULL, /* hDevMode */ + NULL, /* hDevNames */ + NULL, /* hDC */ + PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE, /* Flags */ + 1, 1, 1, 1, /* nFromPage, nToPage, nMinPage, nMaxPage */ + 1, /* nCoies */ + NULL, /* hInstance */ + 0, /* lCustData */ + NULL, NULL, NULL, NULL, NULL, NULL + }; + UINT_PTR timer; + + SPWin32Modal = TRUE; + pd.Flags |= PD_ENABLEPRINTHOOK; + pd.lpfnPrintHook = print_hook; + timer = SetTimer (NULL, 0, 40, my_timer); + + res = PrintDlg (&pd); + + KillTimer (NULL, timer); + SPWin32Modal = FALSE; + + if (!res) return FALSE; + + _hDC = pd.hDC; + +#if 0 + caps = GetDeviceCaps (_hDC, RASTERCAPS); + if (caps & RC_BANDING) { + printf ("needs banding\n"); + } + if (caps & RC_BITBLT) { + printf ("does bitblt\n"); + } + if (caps & RC_DIBTODEV) { + printf ("does dibtodev\n"); + } + if (caps & RC_STRETCHDIB) { + printf ("does stretchdib\n"); + } +#endif + if (pd.hDevMode) { + DEVMODE *devmodep; + devmodep = (DEVMODE *)pd.hDevMode; + if (devmodep->dmFields & DM_ORIENTATION) { + _landscape = (devmodep->dmOrientation == DMORIENT_LANDSCAPE); + } + } + + return TRUE; +} + +unsigned int +PrintWin32::begin (Inkscape::Extension::Print *mod, SPDocument *doc) +{ + DOCINFO di = { + sizeof (DOCINFO), + NULL, /* lpszDocName */ + NULL, /* lpszOutput */ + NULL, /* lpszDatatype */ + 0 /* DI_APPBANDING */ /* fwType */ + }; + int res; + + _PageWidth = sp_document_width (doc); + _PageHeight = sp_document_height (doc); + + di.lpszDocName = SP_DOCUMENT_NAME (doc); + + SPWin32Modal = TRUE; + + res = StartDoc (_hDC, &di); + res = StartPage (_hDC); + + SPWin32Modal = FALSE; + + return 0; +} + +unsigned int +PrintWin32::finish (Inkscape::Extension::Print *mod) +{ + int dpiX, dpiY; + int pPhysicalWidth, pPhysicalHeight; + int pPhysicalOffsetX, pPhysicalOffsetY; + int pPrintableWidth, pPrintableHeight; + float scalex, scaley; + int x0, y0, x1, y1; + int width, height; + NRMatrix affine; + unsigned char *px; + int sheight, row; + BITMAPINFO bmInfo = { + { + sizeof (BITMAPINFOHEADER), // bV4Size + 64, // biWidth + 64, // biHeight + 1, // biPlanes + 32, // biBitCount + BI_RGB, // biCompression + 0, // biSizeImage + 2835, // biXPelsPerMeter + 2835, // biYPelsPerMeter + 0, // biClrUsed + 0 // biClrImportant + }, + { { 0, 0, 0, 0 } } // bmiColors + }; + //RECT wrect; + int res; + + SPWin32Modal = TRUE; + + // Number of pixels per logical inch + dpiX = (int) GetDeviceCaps (_hDC, LOGPIXELSX); + dpiY = (int) GetDeviceCaps (_hDC, LOGPIXELSY); + // Size in pixels of the printable area + pPhysicalWidth = GetDeviceCaps (_hDC, PHYSICALWIDTH); + pPhysicalHeight = GetDeviceCaps (_hDC, PHYSICALHEIGHT); + // Top left corner of prontable area + pPhysicalOffsetX = GetDeviceCaps (_hDC, PHYSICALOFFSETX); + pPhysicalOffsetY = GetDeviceCaps (_hDC, PHYSICALOFFSETY); + // Size in pixels of the printable area + pPrintableWidth = GetDeviceCaps (_hDC, HORZRES); + pPrintableHeight = GetDeviceCaps (_hDC, VERTRES); + + // Scaling from document to device + scalex = dpiX / 72.0; + scaley = dpiY / 72.0; + + // We simply map document 0,0 to physical page 0,0 + affine.c[0] = scalex / 1.25; + affine.c[1] = 0.0; + affine.c[2] = 0.0; + affine.c[3] = scaley / 1.25; + affine.c[4] = 0.0; + affine.c[5] = 0.0; + + nr_arena_item_set_transform (mod->root, &affine); + + // Calculate printable area in device coordinates + x0 = pPhysicalOffsetX; + y0 = pPhysicalOffsetY; + x1 = x0 + pPrintableWidth; + y1 = y0 + pPrintableHeight; + x1 = MIN (x1, (int) (_PageWidth * scalex)); + y1 = MIN (y1, (int) (_PageHeight * scaley)); + + width = x1 - x0; + height = y1 - y0; + + px = nr_new (unsigned char, 4 * 64 * width); + sheight = 64; + + /* Printing goes here */ + for (row = 0; row < height; row += 64) { + NRPixBlock pb; + NRRectL bbox; + NRGC gc(NULL); + int num_rows; + int i; + + num_rows = sheight; + if ((row + num_rows) > height) num_rows = height - row; + + /* Set area of interest */ + bbox.x0 = x0; + bbox.y0 = y0 + row; + bbox.x1 = bbox.x0 + width; + bbox.y1 = bbox.y0 + num_rows; + /* Update to renderable state */ + nr_matrix_set_identity (&gc.transform); + nr_arena_item_invoke_update (mod->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE); + + nr_pixblock_setup_extern (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox.x0, bbox.y0, bbox.x1, bbox.y1, px, 4 * (bbox.x1 - bbox.x0), FALSE, FALSE); + + /* Blitter goes here */ + bmInfo.bmiHeader.biWidth = bbox.x1 - bbox.x0; + bmInfo.bmiHeader.biHeight = -(bbox.y1 - bbox.y0); + + memset (px, 0xff, 4 * num_rows * width); + /* Render */ + nr_arena_item_invoke_render (mod->root, &bbox, &pb, 0); + + /* Swap red and blue channels; we use RGBA, whereas + * the Win32 GDI uses BGRx. + */ + for ( i = 0 ; i < num_rows * width ; i++ ) { + unsigned char temp=px[i*4]; + px[i*4] = px[i*4+2]; + px[i*4+2] = temp; + } + + SetStretchBltMode(_hDC, COLORONCOLOR); + res = StretchDIBits (_hDC, + bbox.x0 - x0, bbox.y0 - y0, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, + 0, 0, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, + px, + &bmInfo, + DIB_RGB_COLORS, + SRCCOPY); + + /* Blitter ends here */ + + nr_pixblock_release (&pb); + } + + nr_free (px); + + res = EndPage (_hDC); + res = EndDoc (_hDC); + + SPWin32Modal = FALSE; + + return 0; +} + +/* File dialogs */ + +char * +PrintWin32::get_open_filename (unsigned char *dir, unsigned char *filter, unsigned char *title) +{ + char fnbuf[4096] = {0}; + OPENFILENAME ofn = { + sizeof (OPENFILENAME), + NULL, /* hwndOwner */ + NULL, /* hInstance */ + (const CHAR *)filter, /* lpstrFilter */ + NULL, /* lpstrCustomFilter */ + 0, /* nMaxCustFilter */ + 1, /* nFilterIndex */ + fnbuf, /* lpstrFile */ + sizeof (fnbuf), /* nMaxFile */ + NULL, /* lpstrFileTitle */ + 0, /* nMaxFileTitle */ + (const CHAR *)dir, /* lpstrInitialDir */ + (const CHAR *)title, /* lpstrTitle */ + OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, /* Flags */ + 0, /* nFileOffset */ + 0, /* nFileExtension */ + NULL, /* lpstrDefExt */ + 0, /* lCustData */ + NULL, /* lpfnHook */ + NULL /* lpTemplateName */ + }; + int retval; + UINT_PTR timer; + + SPWin32Modal = TRUE; + timer = SetTimer (NULL, 0, 40, my_timer); + + retval = GetOpenFileName (&ofn); + + KillTimer (NULL, timer); + SPWin32Modal = FALSE; + + if (!retval) { + int errcode; + errcode = CommDlgExtendedError(); + return NULL; + } + return g_strdup (fnbuf); +} + +char * +PrintWin32::get_write_filename (unsigned char *dir, unsigned char *filter, unsigned char *title) +{ + return NULL; +} + +char * +PrintWin32::get_save_filename (unsigned char *dir, unsigned int *spns) +{ + char fnbuf[4096] = {0}; + OPENFILENAME ofn = { + sizeof (OPENFILENAME), + NULL, /* hwndOwner */ + NULL, /* hInstance */ + "Inkscape SVG (*.svg)\0*\0Plain SVG (*.svg)\0*\0", /* lpstrFilter */ + NULL, /* lpstrCustomFilter */ + 0, /* nMaxCustFilter */ + 1, /* nFilterIndex */ + fnbuf, /* lpstrFile */ + sizeof (fnbuf), /* nMaxFile */ + NULL, /* lpstrFileTitle */ + 0, /* nMaxFileTitle */ + (const CHAR *)dir, /* lpstrInitialDir */ + "Save document to file", /* lpstrTitle */ + OFN_HIDEREADONLY, /* Flags */ + 0, /* nFileOffset */ + 0, /* nFileExtension */ + NULL, /* lpstrDefExt */ + 0, /* lCustData */ + NULL, /* lpfnHook */ + NULL /* lpTemplateName */ + }; + int retval; + UINT_PTR timer; + + SPWin32Modal = TRUE; + timer = SetTimer (NULL, 0, 40, my_timer); + + retval = GetSaveFileName (&ofn); + + KillTimer (NULL, timer); + SPWin32Modal = FALSE; + + if (!retval) { + int errcode; + errcode = CommDlgExtendedError(); + return NULL; + } + *spns = (ofn.nFilterIndex != 2); + return g_strdup (fnbuf); +} + +void +PrintWin32::init (void) +{ + Inkscape::Extension::Extension * ext; + + /* SVG in */ + ext = Inkscape::Extension::build_from_mem( + "\n" + "Windows 32-bit Print\n" + "" SP_MODULE_KEY_PRINT_WIN32 "\n" + "TRUE\n" + "\n" + "", new PrintWin32()); + + return; +} + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ diff --git a/src/extension/internal/win32.h b/src/extension/internal/win32.h new file mode 100644 index 000000000..d46b5b2fb --- /dev/null +++ b/src/extension/internal/win32.h @@ -0,0 +1,91 @@ +#ifndef __INKSCAPE_EXTENSION_INTERNAL_PRINT_WIN32_H__ +#define __INKSCAPE_EXTENSION_INTERNAL_PRINT_WIN32_H__ + +/* + * Windows stuff + * + * Author: + * Lauris Kaplinski + * Ted Gould + * + * Lauris: This code is in public domain + * Ted: This code is released under the GNU GPL + */ + +#include + +#ifndef WIN32 +#error "This file is only usable for Windows" +#endif + +#ifdef DATADIR +#undef DATADIR +#endif +#include + +#include "extension/extension.h" +#include "extension/implementation/implementation.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/* Initialization */ + +class PrintWin32 : public Inkscape::Extension::Implementation::Implementation { + /* Document dimensions */ + float _PageWidth; + float _PageHeight; + + HDC _hDC; + + unsigned int _landscape; + + void main_init (int argc, char **argv, const char *name); + void finish (void); + + /* File dialogs */ + char *get_open_filename (unsigned char *dir, unsigned char *filter, unsigned char *title); + char *get_write_filename (unsigned char *dir, unsigned char *filter, unsigned char *title); + char *get_save_filename (unsigned char *dir, unsigned int *spns); + + VOID CALLBACK timer (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); + + +public: + PrintWin32 (void); + virtual ~PrintWin32 (void); + + /* Tell modules about me */ + static void init (void); + + /* Platform detection */ + static gboolean is_os_wide(); + + /* Print functions */ + virtual unsigned int setup (Inkscape::Extension::Print * module); + //virtual unsigned int set_preview (Inkscape::Extension::Print * module); + + virtual unsigned int begin (Inkscape::Extension::Print * module, SPDocument *doc); + virtual unsigned int finish (Inkscape::Extension::Print * module); + + /* Rendering methods */ + /* + virtual unsigned int bind (Inkscape::Extension::Print * module, const NRMatrix *transform, float opacity); + virtual unsigned int release (Inkscape::Extension::Print * module); + virtual unsigned int comment (Inkscape::Extension::Print * module, const char * comment); + virtual unsigned int fill (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *ctm, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int stroke (Inkscape::Extension::Print * module, const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox); + virtual unsigned int image (Inkscape::Extension::Print * module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + const NRMatrix *transform, const SPStyle *style); + */ + +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_WIN32_H__ */ diff --git a/src/extension/makefile.in b/src/extension/makefile.in new file mode 100644 index 000000000..97df8bfe3 --- /dev/null +++ b/src/extension/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd .. && $(MAKE) extension/all + +clean %.a %.o: + cd .. && $(MAKE) extension/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/extension/output.cpp b/src/extension/output.cpp new file mode 100644 index 000000000..5f740bbff --- /dev/null +++ b/src/extension/output.cpp @@ -0,0 +1,262 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "document.h" +#include "implementation/implementation.h" +#include "output.h" + +#include "prefdialog.h" + +/* Inkscape::Extension::Output */ + +namespace Inkscape { +namespace Extension { + +/** + \return None + \brief Builds a SPModuleOutput object from a XML description + \param module The module to be initialized + \param repr The XML description in a Inkscape::XML::Node tree + + Okay, so you want to build a SPModuleOutput object. + + This function first takes and does the build of the parent class, + which is SPModule. Then, it looks for the section of the + XML description. Under there should be several fields which + describe the output module to excruciating detail. Those are parsed, + copied, and put into the structure that is passed in as module. + Overall, there are many levels of indentation, just to handle the + levels of indentation in the XML file. +*/ +Output::Output (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : Extension(in_repr, in_imp) +{ + mimetype = NULL; + extension = NULL; + filetypename = NULL; + filetypetooltip = NULL; + dataloss = TRUE; + + if (repr != NULL) { + Inkscape::XML::Node * child_repr; + + child_repr = sp_repr_children(repr); + + while (child_repr != NULL) { + if (!strcmp(child_repr->name(), "output")) { + child_repr = sp_repr_children(child_repr); + while (child_repr != NULL) { + char const * chname = child_repr->name(); + if (chname[0] == '_') /* Allow _ for translation of tags */ + chname++; + if (!strcmp(chname, "extension")) { + g_free (extension); + extension = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "mimetype")) { + g_free (mimetype); + mimetype = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "filetypename")) { + g_free (filetypename); + filetypename = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "filetypetooltip")) { + g_free (filetypetooltip); + filetypetooltip = g_strdup(sp_repr_children(child_repr)->content()); + } + if (!strcmp(chname, "dataloss")) { + if (!strcmp(sp_repr_children(child_repr)->content(), "FALSE")) { + dataloss = FALSE; + } + } + + child_repr = sp_repr_next(child_repr); + } + + break; + } + + child_repr = sp_repr_next(child_repr); + } + + } +} + +/** + \brief Destroy an output extension +*/ +Output::~Output (void) +{ + g_free(mimetype); + g_free(extension); + g_free(filetypename); + g_free(filetypetooltip); + return; +} + +/** + \return Whether this extension checks out + \brief Validate this extension + + This function checks to make sure that the output extension has + a filename extension and a MIME type. Then it calls the parent + class' check function which also checks out the implmentation. +*/ +bool +Output::check (void) +{ + if (extension == NULL) + return FALSE; + if (mimetype == NULL) + return FALSE; + + return Extension::check(); +} + +/** + \return IETF mime-type for the extension + \brief Get the mime-type that describes this extension +*/ +gchar * +Output::get_mimetype(void) +{ + return mimetype; +} + +/** + \return Filename extension for the extension + \brief Get the filename extension for this extension +*/ +gchar * +Output::get_extension(void) +{ + return extension; +} + +/** + \return The name of the filetype supported + \brief Get the name of the filetype supported +*/ +gchar * +Output::get_filetypename(void) +{ + if (filetypename != NULL) + return filetypename; + else + return get_name(); +} + +/** + \return Tooltip giving more information on the filetype + \brief Get the tooltip for more information on the filetype +*/ +gchar * +Output::get_filetypetooltip(void) +{ + return filetypetooltip; +} + +/** + \return A dialog to get settings for this extension + \brief Create a dialog for preference for this extension + + Calls the implementation to get the preferences. +*/ +bool +Output::prefs (void) +{ + if (!loaded()) + set_state(Extension::STATE_LOADED); + if (!loaded()) return false; + + Gtk::Widget * controls; + controls = imp->prefs_output(this); + if (controls == NULL) { + // std::cout << "No preferences for Output" << std::endl; + return true; + } + + PrefDialog * dialog = new PrefDialog(this->get_name(), controls); + int response = dialog->run(); + dialog->hide(); + + delete dialog; + + if (response == Gtk::RESPONSE_OK) return true; + return false; +} + +/** + \return None + \brief Save a document as a file + \param doc Document to save + \param uri File to save the document as + + This function does a little of the dirty work involved in saving + a document so that the implementation only has to worry about geting + bits on the disk. + + The big thing that it does is remove and readd the fields that are + only used at runtime and shouldn't be saved. One that may surprise + people is the output extension. This is not saved so that the IDs + could be changed, and old files will still work properly. + + After the file is saved by the implmentation the output_extension + and dataloss variables are recreated. The output_extension is set + to this extension so that future saves use this extension. Dataloss + is set so that a warning will occur on closing the document that + there may be some dataloss from this extension. +*/ +void +Output::save (SPDocument * doc, const gchar * uri) +{ + bool modified = false; + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + + gboolean saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive (doc, FALSE); + repr->setAttribute("inkscape:output_extension", NULL); + repr->setAttribute("inkscape:dataloss", NULL); + if (repr->attribute("sodipodi:modified") != NULL) + modified = true; + repr->setAttribute("sodipodi:modified", NULL); + sp_document_set_undo_sensitive (doc, saved); + + try { + imp->save(this, doc, uri); + } + catch (...) { + if (modified) + repr->setAttribute("sodipodi:modified", "true"); + throw; + } + + saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive (doc, FALSE); + repr->setAttribute("inkscape:output_extension", get_id()); + if (dataloss) { + repr->setAttribute("inkscape:dataloss", "true"); + } + sp_document_set_undo_sensitive (doc, saved); + + return; +} + +} } /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/output.h b/src/extension/output.h new file mode 100644 index 000000000..41bca6a7b --- /dev/null +++ b/src/extension/output.h @@ -0,0 +1,58 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + + +#ifndef INKSCAPE_EXTENSION_OUTPUT_H__ +#define INKSCAPE_EXTENSION_OUTPUT_H__ + +#include +#include "extension.h" +struct SPDocument; + +namespace Inkscape { +namespace Extension { + +class Output : public Extension { + gchar *mimetype; /**< What is the mime type this inputs? */ + gchar *extension; /**< The extension of the input files */ + gchar *filetypename; /**< A userfriendly name for the file type */ + gchar *filetypetooltip; /**< A more detailed description of the filetype */ + bool dataloss; /**< The extension causes data loss on save */ + +public: + class save_failed {}; /**< Generic failure for an undescribed reason */ + class no_extension_found {}; /**< Failed because we couldn't find an extension to match the filename */ + + Output (Inkscape::XML::Node * in_repr, + Implementation::Implementation * in_imp); + virtual ~Output (void); + virtual bool check (void); + void save (SPDocument *doc, + gchar const *uri); + bool prefs (void); + gchar * get_mimetype(void); + gchar * get_extension(void); + gchar * get_filetypename(void); + gchar * get_filetypetooltip(void); +}; + +} } /* namespace Inkscape, Extension */ +#endif /* INKSCAPE_EXTENSION_OUTPUT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/parameter.cpp b/src/extension/parameter.cpp new file mode 100644 index 000000000..76163ac49 --- /dev/null +++ b/src/extension/parameter.cpp @@ -0,0 +1,778 @@ +/** \file + * Parameters for extensions. + */ + +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include +#include +#include + +#include + +#include "extension.h" +#include "prefs-utils.h" + +#include "parameter.h" + +/** \brief The root directory in the preferences database for extension + related parameters. */ +#define PREF_DIR "extensions" + +namespace Inkscape { +namespace Extension { + +/** \brief A boolean parameter */ +class ParamBool : public Parameter { +private: + /** \brief Internal value. */ + bool _value; +public: + ParamBool(const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + /** \brief Returns \c _value */ + bool get (const Inkscape::XML::Document * doc) { return _value; } + bool set (bool in, Inkscape::XML::Document * doc); + Gtk::Widget * get_widget(void); + Glib::ustring * string (void); +}; + +/** \brief Use the superclass' allocator and set the \c _value */ +ParamBool::ParamBool (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : + Parameter(name, guitext, ext), _value(false) +{ + const char * defaultval = NULL; + if (sp_repr_children(xml) != NULL) + defaultval = sp_repr_children(xml)->content(); + + if (defaultval != NULL && (!strcmp(defaultval, "TRUE") || !strcmp(defaultval, "true") || !strcmp(defaultval, "1"))) { + _value = true; + } else { + _value = false; + } + + gchar * pref_name = this->pref_name(); + _value = (bool)prefs_get_int_attribute(PREF_DIR, pref_name, _value); + g_free(pref_name); + + return; +} + +class ParamInt : public Parameter { +private: + /** \brief Internal value. */ + int _value; + int _min; + int _max; +public: + ParamInt (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + /** \brief Returns \c _value */ + int get (const Inkscape::XML::Document * doc) { return _value; } + int set (int in, Inkscape::XML::Document * doc); + int max (void) { return _max; } + int min (void) { return _min; } + Gtk::Widget * get_widget(void); + Glib::ustring * string (void); +}; + +/** \brief Use the superclass' allocator and set the \c _value */ +ParamInt::ParamInt (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : + Parameter(name, guitext, ext), _value(0), _min(0), _max(10) +{ + const char * defaultval = NULL; + if (sp_repr_children(xml) != NULL) + defaultval = sp_repr_children(xml)->content(); + if (defaultval != NULL) { + _value = atoi(defaultval); + } + + const char * maxval = xml->attribute("max"); + if (maxval != NULL) + _max = atoi(maxval); + + const char * minval = xml->attribute("min"); + if (minval != NULL) + _min = atoi(minval); + + /* We're handling this by just killing both values */ + if (_max < _min) { + _max = 10; + _min = 0; + } + + gchar * pref_name = this->pref_name(); + _value = prefs_get_int_attribute(PREF_DIR, pref_name, _value); + g_free(pref_name); + + // std::cout << "New Int:: value: " << _value << " max: " << _max << " min: " << _min << std::endl; + + if (_value > _max) _value = _max; + if (_value < _min) _value = _min; + + return; +} + +class ParamFloat : public Parameter { +private: + /** \brief Internal value. */ + float _value; + float _min; + float _max; +public: + ParamFloat (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + /** \brief Returns \c _value */ + float get (const Inkscape::XML::Document * doc) { return _value; } + float set (float in, Inkscape::XML::Document * doc); + float max (void) { return _max; } + float min (void) { return _min; } + Gtk::Widget * get_widget(void); + Glib::ustring * string (void); +}; + +/** \brief Use the superclass' allocator and set the \c _value */ +ParamFloat::ParamFloat (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : + Parameter(name, guitext, ext), _value(0.0), _min(0.0), _max(10.0) +{ + const char * defaultval = NULL; + if (sp_repr_children(xml) != NULL) + defaultval = sp_repr_children(xml)->content(); + if (defaultval != NULL) { + _value = atof(defaultval); + } + + const char * maxval = xml->attribute("max"); + if (maxval != NULL) + _max = atof(maxval); + + const char * minval = xml->attribute("min"); + if (minval != NULL) + _min = atof(minval); + + /* We're handling this by just killing both values */ + if (_max < _min) { + _max = 10.0; + _min = 0.0; + } + + gchar * pref_name = this->pref_name(); + _value = prefs_get_double_attribute(PREF_DIR, pref_name, _value); + g_free(pref_name); + + // std::cout << "New Float:: value: " << _value << " max: " << _max << " min: " << _min << std::endl; + + if (_value > _max) _value = _max; + if (_value < _min) _value = _min; + + return; +} + +class ParamString : public Parameter { +private: + /** \brief Internal value. This should point to a string that has + been allocated in memory. And should be free'd. */ + gchar * _value; +public: + ParamString(const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ~ParamString(void); + /** \brief Returns \c _value, with a \i const to protect it. */ + const gchar * get (const Inkscape::XML::Document * doc) { return _value; } + const gchar * set (const gchar * in, Inkscape::XML::Document * doc); + Gtk::Widget * get_widget(void); + Glib::ustring * string (void); +}; + +/** + \return None + \brief This function creates a parameter that can be used later. This + is typically done in the creation of the extension and defined + in the XML file describing the extension (it's private so people + have to use the system) :) + \param in_repr The XML describing the parameter + + This function first grabs all of the data out of the Repr and puts + it into local variables. Actually, these are just pointers, and the + data is not duplicated so we need to be careful with it. If there + isn't a name or a type in the XML, then no parameter is created as + the function just returns. + + From this point on, we're pretty committed as we've allocated an + object and we're starting to fill it. The name is set first, and + is created with a strdup to actually allocate memory for it. Then + there is a case statement (roughly because strcmp requires 'ifs') + based on what type of parameter this is. Depending which type it + is, the value is interpreted differently, but they are relatively + straight forward. In all cases the value is set to the default + value from the XML and the type is set to the interpreted type. +*/ +Parameter * +Parameter::make (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext) +{ + const char * name; + const char * type; + const char * guitext; + + name = in_repr->attribute("name"); + type = in_repr->attribute("type"); + guitext = in_repr->attribute("gui-text"); + if (guitext == NULL) + guitext = in_repr->attribute("_gui-text"); + + /* In this case we just don't have enough information */ + if (name == NULL || type == NULL) { + return NULL; + } + + Parameter * param = NULL; + if (!strcmp(type, "boolean")) { + param = new ParamBool(name, guitext, in_ext, in_repr); + } else if (!strcmp(type, "int")) { + param = new ParamInt(name, guitext, in_ext, in_repr); + } else if (!strcmp(type, "float")) { + param = new ParamFloat(name, guitext, in_ext, in_repr); + } else if (!strcmp(type, "string")) { + param = new ParamString(name, guitext, in_ext, in_repr); + } + + /* Note: param could equal NULL */ + return param; +} + +/** \brief A function to set the \c _value + \param in The value to set to + \param doc A document that should be used to set the value. + + This function sets the internal value, but it also sets the value + in the preferences structure. To put it in the right place, \c PREF_DIR + and \c pref_name() are used. +*/ +bool +ParamBool::set (bool in, Inkscape::XML::Document * doc) +{ + _value = in; + + gchar * prefname = this->pref_name(); + prefs_set_int_attribute(PREF_DIR, prefname, _value == true ? 1 : 0); + g_free(prefname); + + return _value; +} + +/** \brief A function to set the \c _value + \param in The value to set to + \param doc A document that should be used to set the value. + + This function sets the internal value, but it also sets the value + in the preferences structure. To put it in the right place, \c PREF_DIR + and \c pref_name() are used. +*/ +int +ParamInt::set (int in, Inkscape::XML::Document * doc) +{ + _value = in; + if (_value > _max) _value = _max; + if (_value < _min) _value = _min; + + gchar * prefname = this->pref_name(); + prefs_set_int_attribute(PREF_DIR, prefname, _value); + g_free(prefname); + + return _value; +} + +/** \brief A function to set the \c _value + \param in The value to set to + \param doc A document that should be used to set the value. + + This function sets the internal value, but it also sets the value + in the preferences structure. To put it in the right place, \c PREF_DIR + and \c pref_name() are used. +*/ +float +ParamFloat::set (float in, Inkscape::XML::Document * doc) +{ + _value = in; + if (_value > _max) _value = _max; + if (_value < _min) _value = _min; + + gchar * prefname = this->pref_name(); + prefs_set_double_attribute(PREF_DIR, prefname, _value); + g_free(prefname); + + return _value; +} + +/** \brief A function to set the \c _value + \param in The value to set to + \param doc A document that should be used to set the value. + + This function sets the internal value, but it also sets the value + in the preferences structure. To put it in the right place, \c PREF_DIR + and \c pref_name() are used. + + To copy the data into _value the old memory must be free'd first. + It is important to note that \c g_free handles \c NULL just fine. Then + the passed in value is duplicated using \c g_strdup(). +*/ +const gchar * +ParamString::set (const gchar * in, Inkscape::XML::Document * doc) +{ + if (in == NULL) return NULL; /* Can't have NULL string */ + + if (_value != NULL) + g_free(_value); + _value = g_strdup(in); + + gchar * prefname = this->pref_name(); + prefs_set_string_attribute(PREF_DIR, prefname, _value); + g_free(prefname); + + return _value; +} + +/** \brief Wrapper to cast to the object and use it's function. */ +bool +Parameter::get_bool (const Inkscape::XML::Document * doc) +{ + ParamBool * boolpntr; + boolpntr = dynamic_cast(this); + if (boolpntr == NULL) + throw Extension::param_wrong_type(); + return boolpntr->get(doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +int +Parameter::get_int (const Inkscape::XML::Document * doc) +{ + ParamInt * intpntr; + intpntr = dynamic_cast(this); + if (intpntr == NULL) + throw Extension::param_wrong_type(); + return intpntr->get(doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +float +Parameter::get_float (const Inkscape::XML::Document * doc) +{ + ParamFloat * floatpntr; + floatpntr = dynamic_cast(this); + if (floatpntr == NULL) + throw Extension::param_wrong_type(); + return floatpntr->get(doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +const gchar * +Parameter::get_string (const Inkscape::XML::Document * doc) +{ + ParamString * stringpntr; + stringpntr = dynamic_cast(this); + if (stringpntr == NULL) + throw Extension::param_wrong_type(); + return stringpntr->get(doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +bool +Parameter::set_bool (bool in, Inkscape::XML::Document * doc) +{ + ParamBool * boolpntr; + boolpntr = dynamic_cast(this); + if (boolpntr == NULL) + throw Extension::param_wrong_type(); + return boolpntr->set(in, doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +int +Parameter::set_int (int in, Inkscape::XML::Document * doc) +{ + ParamInt * intpntr; + intpntr = dynamic_cast(this); + if (intpntr == NULL) + throw Extension::param_wrong_type(); + return intpntr->set(in, doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +float +Parameter::set_float (float in, Inkscape::XML::Document * doc) +{ + ParamFloat * floatpntr; + floatpntr = dynamic_cast(this); + if (floatpntr == NULL) + throw Extension::param_wrong_type(); + return floatpntr->set(in, doc); +} + +/** \brief Wrapper to cast to the object and use it's function. */ +const gchar * +Parameter::set_string (const gchar * in, Inkscape::XML::Document * doc) +{ + ParamString * stringpntr; + stringpntr = dynamic_cast(this); + if (stringpntr == NULL) + throw Extension::param_wrong_type(); + return stringpntr->set(in, doc); +} + +/** \brief Initialize the object, to do that, copy the data. */ +ParamString::ParamString (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : + Parameter(name, guitext, ext), _value(NULL) +{ + const char * defaultval = NULL; + if (sp_repr_children(xml) != NULL) + defaultval = sp_repr_children(xml)->content(); + + gchar * pref_name = this->pref_name(); + const gchar * paramval = prefs_get_string_attribute(PREF_DIR, pref_name); + g_free(pref_name); + + if (paramval != NULL) + defaultval = paramval; + if (defaultval != NULL) + _value = g_strdup(defaultval); + + return; +} + +/** \brief Free the allocated data. */ +ParamString::~ParamString(void) +{ + g_free(_value); +} + +/** \brief Oop, now that we need a parameter, we need it's name. */ +Parameter::Parameter (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext) : + extension(ext), _name(NULL), _text(NULL) +{ + _name = g_strdup(name); + if (guitext != NULL) + _text = g_strdup(guitext); + else + _text = g_strdup(name); +} + +/** \brief Just free the allocated name. */ +Parameter::~Parameter (void) +{ + g_free(_name); + g_free(_text); +} + +/** \brief Build the name to write the parameter from the extension's + ID and the name of this parameter. */ +gchar * +Parameter::pref_name (void) +{ + return g_strdup_printf("%s.%s", extension->get_id(), _name); +} + +/** \brief Basically, if there is no widget pass a NULL. */ +Gtk::Widget * +Parameter::get_widget (void) +{ + return NULL; +} + +/** \brief If I'm not sure which it is, just don't return a value. */ +Glib::ustring * +Parameter::string (void) +{ + Glib::ustring * mystring = new Glib::ustring(""); + return mystring; +} + +/** \brief A class to make an adjustment that uses Extension params */ +class ParamFloatAdjustment : public Gtk::Adjustment { + /** The parameter to adjust */ + ParamFloat * _pref; +public: + /** \brief Make the adjustment using an extension and the string + describing the parameter. */ + ParamFloatAdjustment (ParamFloat * param) : + Gtk::Adjustment(0.0, param->min(), param->max(), 0.1), _pref(param) { + this->set_value(_pref->get(NULL) /* \todo fix */); + this->signal_value_changed().connect(sigc::mem_fun(this, &ParamFloatAdjustment::val_changed)); + return; + }; + + void val_changed (void); +}; /* class ParamFloatAdjustment */ + +/** \brief A function to respond to the value_changed signal from the + adjustment. + + This function just grabs the value from the adjustment and writes + it to the parameter. Very simple, but yet beautiful. +*/ +void +ParamFloatAdjustment::val_changed (void) +{ + // std::cout << "Value Changed to: " << this->get_value() << std::endl; + _pref->set(this->get_value(), NULL /* \todo fix */); + return; +} + +/** \brief A class to make an adjustment that uses Extension params */ +class ParamIntAdjustment : public Gtk::Adjustment { + /** The parameter to adjust */ + ParamInt * _pref; +public: + /** \brief Make the adjustment using an extension and the string + describing the parameter. */ + ParamIntAdjustment (ParamInt * param) : + Gtk::Adjustment(0.0, param->min(), param->max(), 1.0), _pref(param) { + this->set_value(_pref->get(NULL) /* \todo fix */); + this->signal_value_changed().connect(sigc::mem_fun(this, &ParamIntAdjustment::val_changed)); + return; + }; + + void val_changed (void); +}; /* class ParamIntAdjustment */ + +/** \brief A function to respond to the value_changed signal from the + adjustment. + + This function just grabs the value from the adjustment and writes + it to the parameter. Very simple, but yet beautiful. +*/ +void +ParamIntAdjustment::val_changed (void) +{ + // std::cout << "Value Changed to: " << this->get_value() << std::endl; + _pref->set((int)this->get_value(), NULL /* \todo fix */); + return; +} + +/** + \brief Creates a Float Adjustment for a float parameter + + Builds a hbox with a label and a float adjustment in it. +*/ +Gtk::Widget * +ParamFloat::get_widget (void) +{ + Gtk::HBox * hbox = new Gtk::HBox(); + + Gtk::Label * label = new Gtk::Label(_(_text), Gtk::ALIGN_LEFT); + label->show(); + hbox->pack_start(*label, true, true); + + ParamFloatAdjustment * fadjust = new ParamFloatAdjustment(this); + Gtk::SpinButton * spin = new Gtk::SpinButton(*fadjust, 0.1, 1); + spin->show(); + hbox->pack_start(*spin, false, false); + + hbox->show(); + + return dynamic_cast(hbox); +} + +/** + \brief Creates a Int Adjustment for a int parameter + + Builds a hbox with a label and a int adjustment in it. +*/ +Gtk::Widget * +ParamInt::get_widget (void) +{ + Gtk::HBox * hbox = new Gtk::HBox(); + + Gtk::Label * label = new Gtk::Label(_(_text), Gtk::ALIGN_LEFT); + label->show(); + hbox->pack_start(*label, true, true); + + ParamIntAdjustment * fadjust = new ParamIntAdjustment(this); + Gtk::SpinButton * spin = new Gtk::SpinButton(*fadjust, 1.0, 0); + spin->show(); + hbox->pack_start(*spin, false, false); + + hbox->show(); + + return dynamic_cast(hbox); +} + +/** \brief A check button which is Param aware. It works with the + parameter to change it's value as the check button changes + value. */ +class ParamBoolCheckButton : public Gtk::CheckButton { +private: + /** \brief Param to change */ + ParamBool * _pref; +public: + /** \brief Initialize the check button + \param param Which parameter to adjust on changing the check button + + This function sets the value of the checkbox to be that of the + parameter, and then sets up a callback to \c on_toggle. + */ + ParamBoolCheckButton (ParamBool * param) : + Gtk::CheckButton(), _pref(param) { + this->set_active(_pref->get(NULL) /**\todo fix */); + this->signal_toggled().connect(sigc::mem_fun(this, &ParamBoolCheckButton::on_toggle)); + return; + } + void on_toggle (void); +}; + +/** + \brief A function to respond to the check box changing + + Adjusts the value of the preference to match that in the check box. +*/ +void +ParamBoolCheckButton::on_toggle (void) +{ + _pref->set(this->get_active(), NULL /**\todo fix this */); + return; +} + +/** + \brief Creates a bool check button for a bool parameter + + Builds a hbox with a label and a check button in it. +*/ +Gtk::Widget * +ParamBool::get_widget (void) +{ + Gtk::HBox * hbox = new Gtk::HBox(); + + Gtk::Label * label = new Gtk::Label(_(_text), Gtk::ALIGN_LEFT); + label->show(); + hbox->pack_start(*label, true, true); + + ParamBoolCheckButton * checkbox = new ParamBoolCheckButton(this); + checkbox->show(); + hbox->pack_start(*checkbox, false, false); + + hbox->show(); + + return dynamic_cast(hbox); +} + +/** \brief A special category of Gtk::Entry to handle string parameteres */ +class ParamStringEntry : public Gtk::Entry { +private: + ParamString * _pref; +public: + /** \brief Build a string preference for the given parameter + \param pref Where to get the string from, and where to put it + when it changes. + */ + ParamStringEntry (ParamString * pref) : + Gtk::Entry(), _pref(pref) { + if (_pref->get(NULL) != NULL) + this->set_text(Glib::ustring(_pref->get(NULL))); + this->signal_changed().connect(sigc::mem_fun(this, &ParamStringEntry::changed_text)); + }; + void changed_text (void); +}; + +/** \brief Respond to the text box changing + + This function responds to the box changing by grabbing the value + from the text box and putting it in the parameter. +*/ +void +ParamStringEntry::changed_text (void) +{ + Glib::ustring data = this->get_text(); + _pref->set(data.c_str(), NULL); + return; +} + +/** + \brief Creates a text box for the string parameter + + Builds a hbox with a label and a text box in it. +*/ +Gtk::Widget * +ParamString::get_widget (void) +{ + Gtk::HBox * hbox = new Gtk::HBox(); + + Gtk::Label * label = new Gtk::Label(_(_text), Gtk::ALIGN_LEFT); + label->show(); + hbox->pack_start(*label, true, true); + + ParamStringEntry * textbox = new ParamStringEntry(this); + textbox->show(); + hbox->pack_start(*textbox, false, false); + + hbox->show(); + + return dynamic_cast(hbox); +} + +/** \brief Return 'true' or 'false' */ +Glib::ustring * +ParamBool::string (void) +{ + Glib::ustring * mystring; + + if (_value) + mystring = new Glib::ustring("true"); + else + mystring = new Glib::ustring("false"); + + return mystring; +} + +/** \brief Return the value as a string */ +Glib::ustring * +ParamInt::string (void) +{ + char startstring[32]; + sprintf(startstring, "%d", _value); + Glib::ustring * mystring = new Glib::ustring(startstring); + return mystring; +} + +/** \brief Return the value as a string */ +Glib::ustring * +ParamFloat::string (void) +{ + char startstring[G_ASCII_DTOSTR_BUF_SIZE]; + g_ascii_dtostr(startstring, G_ASCII_DTOSTR_BUF_SIZE, _value); + Glib::ustring * mystring = new Glib::ustring(startstring); + return mystring; +} + +/** \brief Return the value as a string */ +Glib::ustring * +ParamString::string (void) +{ + Glib::ustring * mystring = new Glib::ustring(""); + *mystring += "\""; + *mystring += _value; + *mystring += "\""; + return mystring; +} + + +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/parameter.h b/src/extension/parameter.h new file mode 100644 index 000000000..afaecbdc4 --- /dev/null +++ b/src/extension/parameter.h @@ -0,0 +1,71 @@ +#ifndef __INK_EXTENSION_PARAM_H__ +#define __INK_EXTENSION_PARAM_H__ + +/** \file + * Parameters for extensions. + */ + +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#include + +#include "xml/document.h" +#include "extension-forward.h" + +namespace Inkscape { +namespace Extension { + +class Parameter { +private: + /** \brief Which extension is this parameter attached to? */ + Inkscape::Extension::Extension * extension; + /** \brief The name of this parameter. */ + gchar * _name; + +protected: + /** \brief Text for the GUI selection of this. */ + gchar * _text; + gchar * pref_name (void); + +public: + Parameter (const gchar * name, const gchar * guitext, Inkscape::Extension::Extension * ext); + virtual ~Parameter(void); + bool get_bool (const Inkscape::XML::Document * doc); + int get_int (const Inkscape::XML::Document * doc); + float get_float (const Inkscape::XML::Document * doc); + const gchar * get_string (const Inkscape::XML::Document * doc); + + bool set_bool (bool in, Inkscape::XML::Document * doc); + int set_int (int in, Inkscape::XML::Document * doc); + float set_float (float in, Inkscape::XML::Document * doc); + const gchar * set_string (const gchar * in, Inkscape::XML::Document * doc); + + const gchar * name (void) {return _name;} + + static Parameter * make (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext); + virtual Gtk::Widget * get_widget (void); + virtual Glib::ustring * string (void); +}; + + +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* __INK_EXTENSION_PARAM_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/plugin/.cvsignore b/src/extension/plugin/.cvsignore new file mode 100644 index 000000000..70c7c16b2 --- /dev/null +++ b/src/extension/plugin/.cvsignore @@ -0,0 +1,7 @@ +*.dll +*.la +*.lo +.deps +.dirstamp +.libs +makefile diff --git a/src/extension/plugin/Makefile_insert b/src/extension/plugin/Makefile_insert new file mode 100644 index 000000000..a36b52d99 --- /dev/null +++ b/src/extension/plugin/Makefile_insert @@ -0,0 +1,30 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +plugindir = $(libdir)/inkscape/plugins + +plugin_LTLIBRARIES = \ + extension/plugin/libgimpgrad.la \ + extension/plugin/libgrid.la \ + extension/plugin/libbluredge.la \ + extension/plugin/librandompnt.la \ + extension/plugin/librandompos.la + +extension/plugin/clean: + rm -f $(plugin_LTLIBRARIES) + +extension_plugin_libgimpgrad_la_SOURCES = \ + extension/plugin/gimpgrad.cpp + +extension_plugin_libgrid_la_SOURCES = \ + extension/plugin/grid.cpp + +extension_plugin_libbluredge_la_SOURCES = \ + extension/plugin/bluredge.cpp + +extension_plugin_librandompnt_la_SOURCES = \ + extension/plugin/randompnt.cpp + +extension_plugin_librandompos_la_SOURCES = \ + extension/plugin/randompos.cpp + +## extension_plugin_libtest_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS) diff --git a/src/extension/plugin/makefile.in b/src/extension/plugin/makefile.in new file mode 100644 index 000000000..9de8caf05 --- /dev/null +++ b/src/extension/plugin/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd ../.. && $(MAKE) extension/plugin/all + +clean %.a %.o: + cd ../.. && $(MAKE) extension/plugin/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/extension/prefdialog.cpp b/src/extension/prefdialog.cpp new file mode 100644 index 000000000..40eb037b6 --- /dev/null +++ b/src/extension/prefdialog.cpp @@ -0,0 +1,48 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include + +#include "../dialogs/dialog-events.h" + +#include "prefdialog.h" + +namespace Inkscape { +namespace Extension { + +PrefDialog::PrefDialog (Glib::ustring name, Gtk::Widget * controls) : + Gtk::Dialog::Dialog(name + _(" Preferences"), true, true) +{ + this->get_vbox()->pack_start(*controls, true, true, 5); + + add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + + Gtk::Button * ok = add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK); + set_default_response(Gtk::RESPONSE_OK); + ok->grab_focus(); + + GtkWidget *dlg = GTK_WIDGET(gobj()); + sp_transientize(dlg); + + return; +} + +}; }; /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/prefdialog.h b/src/extension/prefdialog.h new file mode 100644 index 000000000..02b3b6a49 --- /dev/null +++ b/src/extension/prefdialog.h @@ -0,0 +1,44 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_DIALOG_H__ +#define INKSCAPE_EXTENSION_DIALOG_H__ + +#include + +#include + +#include +#include + +namespace Inkscape { +namespace Extension { + +class PrefDialog : public Gtk::Dialog { + Gtk::Socket * _socket; + +public: + PrefDialog (Glib::ustring name, Gtk::Widget * controls); +}; + + +};}; /* namespace Inkscape, Extension */ + +#endif /* INKSCAPE_EXTENSION_DIALOG_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/print.cpp b/src/extension/print.cpp new file mode 100644 index 000000000..c967bd86e --- /dev/null +++ b/src/extension/print.cpp @@ -0,0 +1,125 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "implementation/implementation.h" +#include "print.h" + +/* Inkscape::Extension::Print */ + +namespace Inkscape { +namespace Extension { + +Print::Print (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : Extension(in_repr, in_imp) +{ + base = NULL; + arena = NULL; + root = NULL; + dkey = 0; + + return; +} + +Print::~Print (void) +{ + return; +} + +bool +Print::check (void) +{ + return Extension::check(); +} + +unsigned int +Print::setup (void) +{ + return imp->setup(this); +} + +unsigned int +Print::set_preview (void) +{ + return imp->set_preview(this); +} + +unsigned int +Print::begin (SPDocument *doc) +{ + return imp->begin(this, doc); +} + +unsigned int +Print::finish (void) +{ + return imp->finish(this); +} + +unsigned int +Print::bind (const NRMatrix *transform, float opacity) +{ + return imp->bind (this, transform, opacity); +} + +unsigned int +Print::release (void) +{ + return imp->release(this); +} + +unsigned int +Print::comment (const char * comment) +{ + return imp->comment(this,comment); +} + +unsigned int +Print::fill (const NRBPath *bpath, const NRMatrix *ctm, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox) +{ + return imp->fill (this, bpath, ctm, style, pbox, dbox, bbox); +} + +unsigned int +Print::stroke (const NRBPath *bpath, const NRMatrix *transform, const SPStyle *style, + const NRRect *pbox, const NRRect *dbox, const NRRect *bbox) +{ + return imp->stroke (this, bpath, transform, style, pbox, dbox, bbox); +} + +unsigned int +Print::image (unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, + const NRMatrix *transform, const SPStyle *style) +{ + return imp->image (this, px, w, h, rs, transform, style); +} + +unsigned int +Print::text (const char* text, NR::Point p, const SPStyle* style) +{ + return imp->text (this, text, p, style); +} + +bool +Print::textToPath (void) +{ + return imp->textToPath(this); +} + +} } /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/print.h b/src/extension/print.h new file mode 100644 index 000000000..40cafef1d --- /dev/null +++ b/src/extension/print.h @@ -0,0 +1,84 @@ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_PRINT_H__ +#define INKSCAPE_EXTENSION_PRINT_H__ + +#include "extension.h" + +#include "libnr/nr-path.h" +#include "display/nr-arena-forward.h" +#include "forward.h" + +namespace Inkscape { +namespace Extension { + +class Print : public Extension { + +public: /* TODO: These are public for the short term, but this should be fixed */ + SPItem *base; /**< TODO: Document these */ + NRArena *arena; /**< TODO: Document these */ + NRArenaItem *root; /**< TODO: Document these */ + unsigned int dkey; /**< TODO: Document these */ + +public: + Print (Inkscape::XML::Node * in_repr, + Implementation::Implementation * in_imp); + ~Print (void); + virtual bool check (void); + + /* FALSE means user hit cancel */ + unsigned int setup (void); + unsigned int set_preview (void); + + unsigned int begin (SPDocument *doc); + unsigned int finish (void); + + /* Rendering methods */ + unsigned int bind (NRMatrix const *transform, + float opacity); + unsigned int release (void); + unsigned int comment (const char * comment); + unsigned int fill (NRBPath const *bpath, + NRMatrix const *ctm, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + unsigned int stroke (NRBPath const *bpath, + NRMatrix const *transform, + SPStyle const *style, + NRRect const *pbox, + NRRect const *dbox, + NRRect const *bbox); + unsigned int image (unsigned char *px, + unsigned int w, + unsigned int h, + unsigned int rs, + NRMatrix const *transform, + SPStyle const *style); + unsigned int text (char const *text, + NR::Point p, + SPStyle const *style); + bool textToPath (void); +}; + +} } /* namespace Inkscape, Extension */ +#endif /* INKSCAPE_EXTENSION_PRINT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/script/.cvsignore b/src/extension/script/.cvsignore new file mode 100644 index 000000000..38efca7bc --- /dev/null +++ b/src/extension/script/.cvsignore @@ -0,0 +1,3 @@ +.deps +.dirstamp +makefile diff --git a/src/extension/script/InkscapeBinding.cpp b/src/extension/script/InkscapeBinding.cpp new file mode 100644 index 000000000..10cde0774 --- /dev/null +++ b/src/extension/script/InkscapeBinding.cpp @@ -0,0 +1,199 @@ + + +#include "InkscapeBinding.h" + +#include "help.h" + +namespace Inkscape { +namespace Extension { +namespace Script { + + +class InkscapeImpl; +class DesktopImpl; +class DocumentImpl; + + +//######################################################################### +//# D O C U M E N T +//######################################################################### +class DocumentImpl : public Document +{ +public: + DocumentImpl(); + + virtual ~DocumentImpl(); + + virtual void hello(); + +private: + + +}; + + +DocumentImpl::DocumentImpl() +{ + + +} + +DocumentImpl::~DocumentImpl() +{ + + +} + +void DocumentImpl::hello() +{ + printf("######## HELLO, WORLD! #######\n"); +} + + + +//######################################################################### +//# D E S K T O P +//######################################################################### +class DesktopImpl : public Desktop +{ +public: + DesktopImpl(); + + virtual ~DesktopImpl(); + + virtual Document *getDocument(); + +private: + + DocumentImpl document; + +}; + + +DesktopImpl::DesktopImpl() +{ + + +} + +DesktopImpl::~DesktopImpl() +{ + + +} + + +Document *DesktopImpl::getDocument() +{ + return &document; +} + + + +//######################################################################### +//# D I A L O G M A N A G E R +//######################################################################### + +class DialogManagerImpl : public DialogManager +{ +public: + DialogManagerImpl(); + + virtual ~DialogManagerImpl(); + + virtual void showAbout(); + +private: + + +}; + +DialogManagerImpl::DialogManagerImpl() +{ + +} + + +DialogManagerImpl::~DialogManagerImpl() +{ + +} + + +void DialogManagerImpl::showAbout() +{ + sp_help_about(); + +} + + + +//######################################################################### +//# I N K S C A P E +//######################################################################### + +class InkscapeImpl : public Inkscape +{ +public: + InkscapeImpl(); + + virtual ~InkscapeImpl(); + + virtual Desktop *getDesktop(); + + virtual DialogManager *getDialogManager(); + +private: + + DesktopImpl desktop; + + DialogManagerImpl dialogManager; + +}; + +Inkscape *getInkscape() +{ + Inkscape *inkscape = new InkscapeImpl(); + return inkscape; +} + + +InkscapeImpl::InkscapeImpl() +{ + +} + + +InkscapeImpl::~InkscapeImpl() +{ + +} + + +Desktop *InkscapeImpl::getDesktop() +{ + return &desktop; +} + +DialogManager *InkscapeImpl::getDialogManager() +{ + return &dialogManager; +} + + + +}//namespace Script +}//namespace Extension +}//namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapeBinding.h b/src/extension/script/InkscapeBinding.h new file mode 100644 index 000000000..6bbd82fc6 --- /dev/null +++ b/src/extension/script/InkscapeBinding.h @@ -0,0 +1,168 @@ +#ifndef __INKSCAPE_BINDING_H__ +#define __INKSCAPE_BINDING_H__ +/** + * This file is an attempt to provide a hierarchical design + * to wrap Inkscape in an OO model. This file is parsed by Swig + * to produce scripting extension modules for such interpreters + * as Python or Perl + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +/* + * Note: we are doc-ing this file instead of the .cpp since + * the .cpp file has impl's, not def's of these classes. + * Also note that you need to understand Swig before you edit this file. + */ + + +namespace Inkscape { +namespace Extension { +namespace Script { + +class Inkscape; +class DialogManager; +class Desktop; +class Document; + + + +/** + * Get the root Inkscape object. The module wrapper should + * always call this first so that there will be an 'inkscape' + * object available to the user. + */ +Inkscape *getInkscape(); + + +/** + * Root inkscape object. Owner of everything + */ +class Inkscape +{ +public: + + /** + * + */ + Inkscape(){} + + /** + * + */ + virtual ~Inkscape(){}; + + /** + * + */ + virtual Desktop *getDesktop() = 0; + + /** + * + */ + virtual DialogManager *getDialogManager() = 0; + +}; + +/** + * Controller for the various Inkscape dialogs + */ +class DialogManager +{ +public: + + /** + * + */ + DialogManager(){} + + /** + * + */ + virtual ~DialogManager(){}; + + /** + * + */ + virtual void showAbout() = 0; + +}; + + +/** + * + */ +class Desktop +{ + +public: + + /** + * + */ + Desktop() {} + + /** + * + */ + virtual ~Desktop(){}; + + /** + * + */ + virtual Document *getDocument() = 0; + +}; + + + +/** + * + */ +class Document +{ + +public: + + /** + * + */ + Document() {}; + + /** + * + */ + virtual ~Document(){}; + + /** + * + */ + virtual void hello() = 0; + + +}; + + +}//namespace Script +}//namespace Extension +}//namespace Inkscape + + + +#endif /*__INKSCAPE_BINDING_H__*/ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapeInterpreter.cpp b/src/extension/script/InkscapeInterpreter.cpp new file mode 100644 index 000000000..4342922b1 --- /dev/null +++ b/src/extension/script/InkscapeInterpreter.cpp @@ -0,0 +1,93 @@ +/** + * Python Interpreter wrapper for Inkscape + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "InkscapeInterpreter.h" + +#include + +namespace Inkscape { +namespace Extension { +namespace Script { + +/* + * + */ +InkscapeInterpreter::InkscapeInterpreter() +{ +} + + + +/* + * + */ +InkscapeInterpreter::~InkscapeInterpreter() +{ + +} + + + + +/* + * Interpret an in-memory string + */ +bool InkscapeInterpreter::interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error) +{ + //do nothing. let the subclasses implement this + return true; +} + + + + +/* + * Interpret a named file + */ +bool InkscapeInterpreter::interpretUri(Glib::ustring &uri, + Glib::ustring &output, + Glib::ustring &error) +{ + char *curi = (char *)uri.raw().c_str(); + std::ifstream ins(curi); + if (!ins.good()) + { + printf("interpretUri: Could not open %s for reading\n", curi); + return false; + } + + Glib::ustring buf; + + while (!ins.eof()) + { + gunichar ch = (gunichar) ins.get(); + buf.push_back(ch); + } + + ins.close(); + + bool ret = interpretScript(buf, output, error); + + return ret; + +} + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/extension/script/InkscapeInterpreter.h b/src/extension/script/InkscapeInterpreter.h new file mode 100644 index 000000000..9dd585d0d --- /dev/null +++ b/src/extension/script/InkscapeInterpreter.h @@ -0,0 +1,68 @@ +#ifndef __INKSCAPE_INTERPRETER_H__ +#define __INKSCAPE_INTERPRETER_H__ + +/** + * Base class for interpreter implementations, (InkscapePython, etc) + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +namespace Inkscape { +namespace Extension { +namespace Script { + + +class InkscapeInterpreter +{ +public: + + /** + * + */ + InkscapeInterpreter(); + + /** + * + */ + virtual ~InkscapeInterpreter(); + + /** + * + */ + virtual bool interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error); + + /** + * + */ + virtual bool interpretUri(Glib::ustring &uri, + Glib::ustring &output, + Glib::ustring &error); + + + +}; //class InkscapeInterpreter + + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + + + +#endif /* __INKSCAPE_INTERPRETER_H__ */ +//######################################################################### +//# E N D O F F I L E +//######################################################################### + + diff --git a/src/extension/script/InkscapePerl.cpp b/src/extension/script/InkscapePerl.cpp new file mode 100644 index 000000000..7b3c3c1a5 --- /dev/null +++ b/src/extension/script/InkscapePerl.cpp @@ -0,0 +1,80 @@ +/** + * Perl Interpreter wrapper for Inkscape + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + + +#include "InkscapePerl.h" + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + + +#include + +#include "inkscape_perl.pm.h" + +/* + * Generated by SWIG + */ +extern "C" int +InkscapePerlParseBuf(char *startupCodeBuf, char *codeBuf); + +namespace Inkscape { +namespace Extension { +namespace Script { + + +/* + * + */ +InkscapePerl::InkscapePerl() +{ +} + + + +/* + * + */ +InkscapePerl::~InkscapePerl() +{ + +} + + + + + +bool InkscapePerl::interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error) +{ + char *codeBuf = (char *)script.raw().c_str(); + int ret = InkscapePerlParseBuf(inkscape_module_script, codeBuf); + if (!ret) + { + return false; + } + return true; +} + + + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/extension/script/InkscapePerl.h b/src/extension/script/InkscapePerl.h new file mode 100644 index 000000000..0ec79a9cd --- /dev/null +++ b/src/extension/script/InkscapePerl.h @@ -0,0 +1,73 @@ +#ifndef __INKSCAPE_PERL_H__ +#define __INKSCAPE_PERL_H__ + +/** + * Perl Interpreter wrapper for Inkscape + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "InkscapeInterpreter.h" +#include + +namespace Inkscape { +namespace Extension { +namespace Script { + + +class InkscapePerl : public InkscapeInterpreter +{ +public: + + /* + * + */ + InkscapePerl(); + + + /* + * + */ + virtual ~InkscapePerl(); + + + + /* + * + */ + bool interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error); + + + + + +private: + + +}; + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + + + +#endif /*__INKSCAPE_PERL_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapePython.cpp b/src/extension/script/InkscapePython.cpp new file mode 100644 index 000000000..eca09951a --- /dev/null +++ b/src/extension/script/InkscapePython.cpp @@ -0,0 +1,120 @@ +/** + * Python Interpreter wrapper for Inkscape + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include + +#include "InkscapePython.h" + + +#include + +#include "inkscape_py.py.h" + + +/* + * Generated by SWIG + */ +extern "C" void init_inkscape_py(void); + + +namespace Inkscape { +namespace Extension { +namespace Script { + +/* + * + */ +InkscapePython::InkscapePython() +{ +} + + + +/* + * + */ +InkscapePython::~InkscapePython() +{ + +} + + + +static bool initialized = false; +/* + * Interpret an in-memory string + */ +bool InkscapePython::interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error) +{ + if (!initialized) + { + Py_Initialize(); + init_inkscape_py(); + initialized = true; + } + char *codeStr = (char *)script.raw().c_str(); + PyRun_SimpleString(inkscape_module_script); + PyRun_SimpleString("inkscape = _inkscape_py.getInkscape()\n"); + PyRun_SimpleString(codeStr); + + //## Check for errors + if (PyErr_Occurred()) + { + PyObject *errobj = NULL; + PyObject *errdata = NULL; + PyObject *errtraceback = NULL; + + PyErr_Fetch(&errobj, &errdata, &errtraceback); + //PyErr_Clear(); + + if (errobj && PyString_Check(errobj)) + { + PyObject *pystring = PyObject_Str(errobj); + char *errStr = PyString_AsString(pystring); + error = errStr; + Py_XDECREF(pystring); + } + else + { + error = "Error occurred"; + } + Py_XDECREF(errobj); + Py_XDECREF(errdata); + Py_XDECREF(errtraceback); + return false; + } + //Py_Finalize(); + return true; +} + + + + + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapePython.h b/src/extension/script/InkscapePython.h new file mode 100644 index 000000000..fdcd4906f --- /dev/null +++ b/src/extension/script/InkscapePython.h @@ -0,0 +1,74 @@ +#ifndef __INKSCAPE_PYTHON_H__ +#define __INKSCAPE_PYTHON_H__ + +/** + * Python Interpreter wrapper for Inkscape + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "InkscapeInterpreter.h" +#include + +namespace Inkscape { +namespace Extension { +namespace Script { + + + +class InkscapePython : public InkscapeInterpreter +{ +public: + + /* + * + */ + InkscapePython(); + + + /* + * + */ + virtual ~InkscapePython(); + + + + /* + * + */ + virtual bool interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error); + + + + +private: + + +}; + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + + + +#endif /*__INKSCAPE_PYTHON_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapeScript.cpp b/src/extension/script/InkscapeScript.cpp new file mode 100644 index 000000000..cc9271caa --- /dev/null +++ b/src/extension/script/InkscapeScript.cpp @@ -0,0 +1,177 @@ +/** + * Inkscape Scripting container + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "InkscapeScript.h" + +#include "InkscapeInterpreter.h" + +#ifdef WITH_PERL +# include "InkscapePerl.h" +#endif + +#ifdef WITH_PYTHON +# include "InkscapePython.h" +#endif + + +namespace Inkscape { +namespace Extension { +namespace Script { + + +/** + * + */ +InkscapeScript::InkscapeScript() +{ + + + +} + + + + +/** + * + */ +InkscapeScript::~InkscapeScript() +{ + + +} + + + + +/** + * + */ +bool InkscapeScript::interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error, + ScriptLanguage language) +{ +#ifndef __GNUC__ + static char const __FUNCTION__[] = "interpretScript"; +#endif + char * langname=NULL; + InkscapeInterpreter *interp = NULL; + //if() instead of switch() lets us scope vars + if (language == InkscapeScript::PERL) + { +#ifdef WITH_PERL + langname="Perl"; + interp = new InkscapePerl(); +#endif + } + else if (language == InkscapeScript::PYTHON) + { +#ifdef WITH_PYTHON + langname="Python"; + interp = new InkscapePython(); +#endif + } + else + { + //replace with g_error + fprintf(stderr, "%s: Unknown Script Language type: %d\n", + __FUNCTION__, language); + return false; + } + + if (!interp) + { + fprintf(stderr, "%s: error starting Language '%s'\n", + __FUNCTION__, langname); + return false; + } + + if (!interp->interpretScript(script, output, error)) + { + fprintf(stderr, "%s: error in executing %s script\n", + __FUNCTION__, langname); + return false; + } + + delete interp; + + return true; +} + +/** + * + */ +bool InkscapeScript::interpretUri(Glib::ustring &uri, + Glib::ustring &output, + Glib::ustring &error, + ScriptLanguage language) +{ + + InkscapeInterpreter *interp = NULL; + //if() instead of switch() lets us scope vars + if (language == InkscapeScript::PERL) + { +#ifdef WITH_PERL + interp = new InkscapePerl(); +#endif + } + else if (language == InkscapeScript::PYTHON) + { +#ifdef WITH_PYTHON + interp = new InkscapePython(); +#endif + } + else + { + //replace with g_error + fprintf(stderr, "Unknown Script Language type:%d\n", language); + return false; + } + + if (!interp) + return false; + + if (!interp->interpretUri(uri, output, error)) + { + fprintf(stderr, "error in executing script '%s'\n", uri.raw().c_str()); + return false; + } + + return true; +} + + + + + + + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + +//######################################################################### +//# E N D O F F I L E +//######################################################################### + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/InkscapeScript.h b/src/extension/script/InkscapeScript.h new file mode 100644 index 000000000..88df2c82a --- /dev/null +++ b/src/extension/script/InkscapeScript.h @@ -0,0 +1,87 @@ +#ifndef __INKSCAPE_SCRIPT_H__ +#define __INKSCAPE_SCRIPT_H__ + +/** + * Inkscape Scripting container + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "config.h" + +#include + +namespace Inkscape { +namespace Extension { +namespace Script { + + +class InkscapeScript +{ +public: + + /** + * Which type of language? + */ + typedef enum + { + PYTHON, + PERL + } ScriptLanguage; + + /** + * + */ + InkscapeScript(); + + /** + * + */ + ~InkscapeScript(); + + /** + * + */ + bool interpretScript(Glib::ustring &script, + Glib::ustring &output, + Glib::ustring &error, + ScriptLanguage language); + + /** + * + */ + bool interpretUri(Glib::ustring &uri, + Glib::ustring &output, + Glib::ustring &error, + ScriptLanguage language); + + + +}; //class InkscapeScript + + + + +} // namespace Script +} // namespace Extension +} // namespace Inkscape + + + +#endif /* __INKSCAPE_SCRIPT_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/script/Makefile.tmp b/src/extension/script/Makefile.tmp new file mode 100644 index 000000000..a952f2ba3 --- /dev/null +++ b/src/extension/script/Makefile.tmp @@ -0,0 +1,89 @@ +############################################# +# Makefile for testing embedding +# +# This temporary makefile is for designing +# and testing a generic structure for embedding +# interpreters in Inkscape, and binding back +# from them into the Inkscape internals. This +# would allow users to automate some Inkscape +# functionality. +############################################# + +CXX = g++ + +WRAPS = \ +inkscape_py_wrap.o \ +inkscape_perl_wrap.o + +OBJ = \ +InkscapeScript.o \ +InkscapeInterpreter.o \ +InkscapeBinding.o \ +InkscapePython.o \ +InkscapePerl.o \ +$(WRAPS) + +######################################################### +# NOTE that we are using the interpreters themselves, to +# discover the settings for compiling and linking +######################################################### + +PERL_CFLAGS := $(shell perl -MExtUtils::Embed -e ccopts ) +PERL_LDFLAGS := $(shell perl -MExtUtils::Embed -e ldopts ) + +#INCLUDEPY, LIBPL, LIBRARY +PYTHON_CFLAGS := -I$(shell python -c "import distutils.sysconfig ; print distutils.sysconfig.get_config_var('INCLUDEPY')" ) +PYTHON_LDPATH := $(shell python -c "import distutils.sysconfig ; print distutils.sysconfig.get_config_var('LIBPL')" ) +PYTHON_LIB := $(shell python -c "import distutils.sysconfig ; print distutils.sysconfig.get_config_var('LIBRARY')" ) +PYTHON_LDFLAGS = $(PYTHON_LDPATH)/$(PYTHON_LIB) +#PYTHON_LDFLAGS = $(PYTHON_LDPATH) + +GLIB_INC := $(shell pkg-config --cflags glib-2.0) +GLIB_LIB := $(shell pkg-config --libs glib-2.0) + +INC = -I. -I../.. $(GLIB_INC) + +CFLAGS = -g $(PYTHON_CFLAGS) $(PERL_CFLAGS) +CXXFLAGS = $(CFLAGS) + +LIBS = $(PYTHON_LDFLAGS) $(PERL_LDFLAGS) $(GLIB_LIB) + +all: bindtest cpptest + +wraps: $(WRAPS) + +bindtest: bindtest.o $(OBJ) + $(CXX) -o $@ bindtest.o $(OBJ) $(LIBS) + +cpptest: cpptest.o $(OBJ) + $(CXX) -o $@ cpptest.o $(OBJ) $(LIBS) + + +#_inkscape_py.so : inkscape_py_wrap.o $(OBJ) +# $(CXX) -shared -o _inkscape_py.so $(OBJ) + +inkscape_py_wrap.cpp: InkscapeBinding.h inkscape_py.i + swig -c++ -python -o inkscape_py_wrap.cpp inkscape_py.i + perl quotefile.pl inkscape_py.py inkscape_py.py.h + +InkscapePython.o: InkscapePython.cpp InkscapePython.h inkscape_py_wrap.o + $(CXX) $(CXXFLAGS) $(INC) $(PYINC) -o $@ -c InkscapePython.cpp + +inkscape_perl_wrap.cpp: InkscapeBinding.h inkscape_perl.i + swig -c++ -perl5 -static -o inkscape_perl_wrap.cpp inkscape_perl.i + perl quotefile.pl inkscape_perl.pm inkscape_perl.pm.h + +InkscapePerl.o: InkscapePerl.cpp InkscapePerl.h inkscape_perl_wrap.o + $(CXX) $(CXXFLAGS) $(INC) $(PERLINC) -o $@ -c InkscapePerl.cpp + +.cpp.o: + $(CXX) $(CXXFLAGS) $(INC) -o $@ -c $< + +clean: + -$(RM) bindtest + -$(RM) cpptest + -$(RM) *.o + -$(RM) *.so + -$(RM) *.pyc + + diff --git a/src/extension/script/Makefile_insert b/src/extension/script/Makefile_insert new file mode 100644 index 000000000..1dcb30042 --- /dev/null +++ b/src/extension/script/Makefile_insert @@ -0,0 +1,36 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +extension/script/all: extension/script/libscript.a + +extension/script/clean: + rm -f extension/script/libscript.a + rm -f $(extension_script_libscript_a_OBJECTS) + +if WITH_PERL +perl_sources = \ + extension/script/InkscapePerl.h \ + extension/script/InkscapePerl.cpp \ + extension/script/inkscape_perl_wrap.cpp \ + extension/script/inkscape_perl.pm \ + extension/script/inkscape_perl.pm.h +endif + +if WITH_PYTHON +python_sources = \ + extension/script/InkscapePython.h \ + extension/script/InkscapePython.cpp \ + extension/script/inkscape_py_wrap.cpp \ + extension/script/inkscape_py.py \ + extension/script/inkscape_py.py.h +endif + +extension_script_libscript_a_SOURCES = \ + extension/script/InkscapeBinding.h \ + extension/script/InkscapeBinding.cpp \ + extension/script/InkscapeInterpreter.h \ + extension/script/InkscapeInterpreter.cpp \ + extension/script/InkscapeScript.h \ + extension/script/InkscapeScript.cpp \ + $(perl_sources) \ + $(python_sources) + diff --git a/src/extension/script/README.txt b/src/extension/script/README.txt new file mode 100644 index 000000000..796c52cae --- /dev/null +++ b/src/extension/script/README.txt @@ -0,0 +1,41 @@ +SWIG Scripting Notes +==================== +by Ishmal + +The code in this directory is an initial start +at providing application-level scripting to Inkscape +via SWIG and interpreter embedding. Please do not +modify these files until you have become well +acquainted with SWIG and the various methods of +embedding scripting languages in a C/C++ program. + + +The classes defined in InkscapeBinding.h and +implemented in InkscapeBinding.cpp are destined to +be a thin shell for scripting Inkscape. Since +Inkscape currently is not organized in a heirarchical +tree, nor is it threadsafe, this binding tree will merely +mimic such an arrangement. + +Note that this -NOT- the same as ECMAScript binding on an +SVG page. That is another task, coupled with XPath. + +Currently, the way to update InkscapeBinding is to: + +1. Modify InkscapeBinding.h and InkscapeBinding.cpp +2. Run 'make -f Makefile.tmp wraps' +3. cd to the src or toplevel directory, and build +4. when this works well, commit the files in this directory + +#### SWIG is available here: + +http://www.swig.org + +#### Information on embedding Python is here + +http://docs.python.org/ext/ext.html + + +#### Information on embedding PERL is available here: + +http://perldoc.com/perl5.8.4/pod/perlembed.html diff --git a/src/extension/script/bindtest.cpp b/src/extension/script/bindtest.cpp new file mode 100644 index 000000000..97970381d --- /dev/null +++ b/src/extension/script/bindtest.cpp @@ -0,0 +1,86 @@ + +#include + +#include "InkscapeScript.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +static char *pythonCodeStr = +//"\n" +//"inkscape = _inkscape_py.getInkscape()\n" +"desktop = inkscape.getDesktop()\n" +"document = desktop.getDocument()\n" +"document.hello()\n" +""; + +int testPython() +{ + Inkscape::Extension::Script::InkscapeScript scriptEngine; + printf("##### Python Test #####\n"); + printf("===== CODE ====\n%s\n==============\n", pythonCodeStr); + scriptEngine.interpretScript(pythonCodeStr, + Inkscape::Extension::Script::InkscapeScript::PYTHON); + printf("##### End Python #####\n\n"); + return TRUE; +} + +static char *perlCodeStr = +//"\n" +//"$inkscape = inkscape_perlc::getInkscape();\n" +"print \"inkscape: '$inkscape'\\n\"; \n" +"$desktop = $inkscape->getDesktop();\n" +"$document = $desktop->getDocument();\n" +"$document->hello()\n" +//"reverse 'rekcaH lreP rehtonA tsuJ'\n" +""; + +int testPerl() +{ + Inkscape::Extension::Script::InkscapeScript scriptEngine; + printf("##### Perl Test #####\n"); + printf("===== CODE ====\n%s\n==============\n", perlCodeStr); + scriptEngine.interpretScript(perlCodeStr, + Inkscape::Extension::Script::InkscapeScript::PERL); + printf("##### End Perl #####\n\n"); + return TRUE; +} + + + +int doTest() +{ + if (!testPython()) + { + printf("Failed Python test\n"); + return FALSE; + } + if (!testPerl()) + { + printf("Failed Perl test\n"); + return FALSE; + } + return TRUE; +} + + + +int main(int argc, char **argv) +{ + + if (doTest()) + printf("Tests succeeded\n"); + else + printf("Tests failed\n"); + return 0; +} + + + + + + diff --git a/src/extension/script/cpptest.cpp b/src/extension/script/cpptest.cpp new file mode 100644 index 000000000..e5c9d34de --- /dev/null +++ b/src/extension/script/cpptest.cpp @@ -0,0 +1,22 @@ + +#include + +#include "InkscapeBinding.h" + +void doTest() +{ + Inkscape::Extension::Script::Inkscape *inkscape = + Inkscape::Extension::Script::getInkscape(); + Inkscape::Extension::Script::Desktop *desktop = inkscape->getDesktop(); + Inkscape::Extension::Script::Document *document = desktop->getDocument(); + document->hello(); +} + +int main(int argc, char **argv) +{ + + doTest(); + +} + + diff --git a/src/extension/script/inkscape_perl.i b/src/extension/script/inkscape_perl.i new file mode 100644 index 000000000..ccb31c117 --- /dev/null +++ b/src/extension/script/inkscape_perl.i @@ -0,0 +1,77 @@ +%module inkscape_perl +%{ +#include "InkscapeBinding.h" + + +static void xs_init _((pTHX)); +static PerlInterpreter *my_perl; + +int perl_eval(char *string) { + char *argv[2]; + argv[0] = string; + argv[1] = (char *) 0; + return perl_call_argv("eval",0,argv); +} + +extern "C" int +InkscapePerlParseBuf(char *startupCodeBuf, char *codeBuf) +{ + STRLEN n_a; + int exitstatus; + char *embedding[] = { "", "-e", "0" }; + + my_perl = perl_alloc(); + if (!my_perl) + return 0; + perl_construct( my_perl ); + + exitstatus = perl_parse( my_perl, xs_init, 3, + embedding, (char **) NULL ); + if (exitstatus) + return 0; + + /* Initialize all of the module variables */ + + exitstatus = perl_run( my_perl ); + + SV *retSV = eval_pv(startupCodeBuf, TRUE); + char *ret = SvPV(retSV, n_a); + //printf("## module ret:%s\n", ret); + + retSV = eval_pv("$inkscape = inkscape_perlc::getInkscape();\n", TRUE); + ret = SvPV(retSV, n_a); + //printf("## inkscape ret:%s\n", ret); + + retSV = eval_pv(codeBuf, TRUE); + ret = SvPV(retSV, n_a); + //printf("## code ret:%s\n", ret); + + perl_destruct( my_perl ); + perl_free( my_perl ); + + return 1; +} + +/* Register any extra external extensions */ + +/* Do not delete this line--writemain depends on it */ +/* EXTERN_C void boot_DynaLoader _((CV* cv)); */ + +static void +xs_init(pTHX) +{ +/* dXSUB_SYS; */ + char *file = __FILE__; + { + /* newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); */ + newXS(SWIG_name, SWIG_init, file); +#ifdef SWIGMODINIT + SWIGMODINIT +#endif + } +} + + +%} + +%include "InkscapeBinding.h" diff --git a/src/extension/script/inkscape_perl.pm b/src/extension/script/inkscape_perl.pm new file mode 100644 index 000000000..86f8a5e02 --- /dev/null +++ b/src/extension/script/inkscape_perl.pm @@ -0,0 +1,178 @@ +# This file was automatically generated by SWIG +package inkscape_perl; +require Exporter; +@ISA = qw(Exporter); +package inkscape_perlc; +boot_inkscape_perl(); +package inkscape_perl; +@EXPORT = qw( ); + +# ---------- BASE METHODS ------------- + +package inkscape_perl; + +sub TIEHASH { + my ($classname,$obj) = @_; + return bless $obj, $classname; +} + +sub CLEAR { } + +sub FIRSTKEY { } + +sub NEXTKEY { } + +sub FETCH { + my ($self,$field) = @_; + my $member_func = "swig_${field}_get"; + $self->$member_func(); +} + +sub STORE { + my ($self,$field,$newval) = @_; + my $member_func = "swig_${field}_set"; + $self->$member_func($newval); +} + +sub this { + my $ptr = shift; + return tied(%$ptr); +} + + +# ------- FUNCTION WRAPPERS -------- + +package inkscape_perl; + +*getInkscape = *inkscape_perlc::getInkscape; + +############# Class : inkscape_perl::Inkscape ############## + +package inkscape_perl::Inkscape; +@ISA = qw( inkscape_perl ); +%OWNER = (); +%ITERATORS = (); +sub DESTROY { + return unless $_[0]->isa('HASH'); + my $self = tied(%{$_[0]}); + return unless defined $self; + delete $ITERATORS{$self}; + if (exists $OWNER{$self}) { + inkscape_perlc::delete_Inkscape($self); + delete $OWNER{$self}; + } +} + +*getDesktop = *inkscape_perlc::Inkscape_getDesktop; +*getDialogManager = *inkscape_perlc::Inkscape_getDialogManager; +sub DISOWN { + my $self = shift; + my $ptr = tied(%$self); + delete $OWNER{$ptr}; +} + +sub ACQUIRE { + my $self = shift; + my $ptr = tied(%$self); + $OWNER{$ptr} = 1; +} + + +############# Class : inkscape_perl::DialogManager ############## + +package inkscape_perl::DialogManager; +@ISA = qw( inkscape_perl ); +%OWNER = (); +%ITERATORS = (); +sub DESTROY { + return unless $_[0]->isa('HASH'); + my $self = tied(%{$_[0]}); + return unless defined $self; + delete $ITERATORS{$self}; + if (exists $OWNER{$self}) { + inkscape_perlc::delete_DialogManager($self); + delete $OWNER{$self}; + } +} + +*showAbout = *inkscape_perlc::DialogManager_showAbout; +sub DISOWN { + my $self = shift; + my $ptr = tied(%$self); + delete $OWNER{$ptr}; +} + +sub ACQUIRE { + my $self = shift; + my $ptr = tied(%$self); + $OWNER{$ptr} = 1; +} + + +############# Class : inkscape_perl::Desktop ############## + +package inkscape_perl::Desktop; +@ISA = qw( inkscape_perl ); +%OWNER = (); +%ITERATORS = (); +sub DESTROY { + return unless $_[0]->isa('HASH'); + my $self = tied(%{$_[0]}); + return unless defined $self; + delete $ITERATORS{$self}; + if (exists $OWNER{$self}) { + inkscape_perlc::delete_Desktop($self); + delete $OWNER{$self}; + } +} + +*getDocument = *inkscape_perlc::Desktop_getDocument; +sub DISOWN { + my $self = shift; + my $ptr = tied(%$self); + delete $OWNER{$ptr}; +} + +sub ACQUIRE { + my $self = shift; + my $ptr = tied(%$self); + $OWNER{$ptr} = 1; +} + + +############# Class : inkscape_perl::Document ############## + +package inkscape_perl::Document; +@ISA = qw( inkscape_perl ); +%OWNER = (); +%ITERATORS = (); +sub DESTROY { + return unless $_[0]->isa('HASH'); + my $self = tied(%{$_[0]}); + return unless defined $self; + delete $ITERATORS{$self}; + if (exists $OWNER{$self}) { + inkscape_perlc::delete_Document($self); + delete $OWNER{$self}; + } +} + +*hello = *inkscape_perlc::Document_hello; +sub DISOWN { + my $self = shift; + my $ptr = tied(%$self); + delete $OWNER{$ptr}; +} + +sub ACQUIRE { + my $self = shift; + my $ptr = tied(%$self); + $OWNER{$ptr} = 1; +} + + +# ------- VARIABLE STUBS -------- + +package inkscape_perl; + +1; diff --git a/src/extension/script/inkscape_perl.pm.h b/src/extension/script/inkscape_perl.pm.h new file mode 100644 index 000000000..b33b16045 --- /dev/null +++ b/src/extension/script/inkscape_perl.pm.h @@ -0,0 +1,187 @@ + +/* ################################################### +## This file generated by quotefile.pl from +## inkscape_perl.pm on Thu Dec 16 15:35:48 2004 +## DO NOT EDIT +################################################### */ + +static char *inkscape_module_script = +"# This file was automatically generated by SWIG\n" +"package inkscape_perl;\n" +"require Exporter;\n" +"@ISA = qw(Exporter);\n" +"package inkscape_perlc;\n" +"boot_inkscape_perl();\n" +"package inkscape_perl;\n" +"@EXPORT = qw( );\n" +"\n" +"# ---------- BASE METHODS -------------\n" +"\n" +"package inkscape_perl;\n" +"\n" +"sub TIEHASH {\n" +" my ($classname,$obj) = @_;\n" +" return bless $obj, $classname;\n" +"}\n" +"\n" +"sub CLEAR { }\n" +"\n" +"sub FIRSTKEY { }\n" +"\n" +"sub NEXTKEY { }\n" +"\n" +"sub FETCH {\n" +" my ($self,$field) = @_;\n" +" my $member_func = \"swig_${field}_get\";\n" +" $self->$member_func();\n" +"}\n" +"\n" +"sub STORE {\n" +" my ($self,$field,$newval) = @_;\n" +" my $member_func = \"swig_${field}_set\";\n" +" $self->$member_func($newval);\n" +"}\n" +"\n" +"sub this {\n" +" my $ptr = shift;\n" +" return tied(%$ptr);\n" +"}\n" +"\n" +"\n" +"# ------- FUNCTION WRAPPERS --------\n" +"\n" +"package inkscape_perl;\n" +"\n" +"*getInkscape = *inkscape_perlc::getInkscape;\n" +"\n" +"############# Class : inkscape_perl::Inkscape ##############\n" +"\n" +"package inkscape_perl::Inkscape;\n" +"@ISA = qw( inkscape_perl );\n" +"%OWNER = ();\n" +"%ITERATORS = ();\n" +"sub DESTROY {\n" +" return unless $_[0]->isa('HASH');\n" +" my $self = tied(%{$_[0]});\n" +" return unless defined $self;\n" +" delete $ITERATORS{$self};\n" +" if (exists $OWNER{$self}) {\n" +" inkscape_perlc::delete_Inkscape($self);\n" +" delete $OWNER{$self};\n" +" }\n" +"}\n" +"\n" +"*getDesktop = *inkscape_perlc::Inkscape_getDesktop;\n" +"*getDialogManager = *inkscape_perlc::Inkscape_getDialogManager;\n" +"sub DISOWN {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" delete $OWNER{$ptr};\n" +"}\n" +"\n" +"sub ACQUIRE {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" $OWNER{$ptr} = 1;\n" +"}\n" +"\n" +"\n" +"############# Class : inkscape_perl::DialogManager ##############\n" +"\n" +"package inkscape_perl::DialogManager;\n" +"@ISA = qw( inkscape_perl );\n" +"%OWNER = ();\n" +"%ITERATORS = ();\n" +"sub DESTROY {\n" +" return unless $_[0]->isa('HASH');\n" +" my $self = tied(%{$_[0]});\n" +" return unless defined $self;\n" +" delete $ITERATORS{$self};\n" +" if (exists $OWNER{$self}) {\n" +" inkscape_perlc::delete_DialogManager($self);\n" +" delete $OWNER{$self};\n" +" }\n" +"}\n" +"\n" +"*showAbout = *inkscape_perlc::DialogManager_showAbout;\n" +"sub DISOWN {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" delete $OWNER{$ptr};\n" +"}\n" +"\n" +"sub ACQUIRE {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" $OWNER{$ptr} = 1;\n" +"}\n" +"\n" +"\n" +"############# Class : inkscape_perl::Desktop ##############\n" +"\n" +"package inkscape_perl::Desktop;\n" +"@ISA = qw( inkscape_perl );\n" +"%OWNER = ();\n" +"%ITERATORS = ();\n" +"sub DESTROY {\n" +" return unless $_[0]->isa('HASH');\n" +" my $self = tied(%{$_[0]});\n" +" return unless defined $self;\n" +" delete $ITERATORS{$self};\n" +" if (exists $OWNER{$self}) {\n" +" inkscape_perlc::delete_Desktop($self);\n" +" delete $OWNER{$self};\n" +" }\n" +"}\n" +"\n" +"*getDocument = *inkscape_perlc::Desktop_getDocument;\n" +"sub DISOWN {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" delete $OWNER{$ptr};\n" +"}\n" +"\n" +"sub ACQUIRE {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" $OWNER{$ptr} = 1;\n" +"}\n" +"\n" +"\n" +"############# Class : inkscape_perl::Document ##############\n" +"\n" +"package inkscape_perl::Document;\n" +"@ISA = qw( inkscape_perl );\n" +"%OWNER = ();\n" +"%ITERATORS = ();\n" +"sub DESTROY {\n" +" return unless $_[0]->isa('HASH');\n" +" my $self = tied(%{$_[0]});\n" +" return unless defined $self;\n" +" delete $ITERATORS{$self};\n" +" if (exists $OWNER{$self}) {\n" +" inkscape_perlc::delete_Document($self);\n" +" delete $OWNER{$self};\n" +" }\n" +"}\n" +"\n" +"*hello = *inkscape_perlc::Document_hello;\n" +"sub DISOWN {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" delete $OWNER{$ptr};\n" +"}\n" +"\n" +"sub ACQUIRE {\n" +" my $self = shift;\n" +" my $ptr = tied(%$self);\n" +" $OWNER{$ptr} = 1;\n" +"}\n" +"\n" +"\n" +"# ------- VARIABLE STUBS --------\n" +"\n" +"package inkscape_perl;\n" +"\n" +"1;\n" +""; diff --git a/src/extension/script/inkscape_perl_wrap.cpp b/src/extension/script/inkscape_perl_wrap.cpp new file mode 100644 index 000000000..2f674d49f --- /dev/null +++ b/src/extension/script/inkscape_perl_wrap.cpp @@ -0,0 +1,1334 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 1.3.23 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + + +#ifdef __cplusplus +template class SwigValueWrapper { + T *tt; +public: + SwigValueWrapper() : tt(0) { } + SwigValueWrapper(const SwigValueWrapper& rhs) : tt(new T(*rhs.tt)) { } + SwigValueWrapper(const T& t) : tt(new T(t)) { } + ~SwigValueWrapper() { delete tt; } + SwigValueWrapper& operator=(const T& t) { delete tt; tt = new T(t); return *this; } + operator T&() const { return *tt; } + T *operator&() { return tt; } +private: + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); +}; +#endif + + +#ifndef SWIG_TEMPLATE_DISAMBIGUATOR +# if defined(__SUNPRO_CC) +# define SWIG_TEMPLATE_DISAMBIGUATOR template +# else +# define SWIG_TEMPLATE_DISAMBIGUATOR +# endif +#endif + +/*********************************************************************** + * common.swg + * + * This file contains generic SWIG runtime support for pointer + * type checking as well as a few commonly used macros to control + * external linkage. + * + * Author : David Beazley (beazley@cs.uchicago.edu) + * + * Copyright (c) 1999-2000, The University of Chicago + * + * This file may be freely redistributed without license or fee provided + * this copyright message remains intact. + ************************************************************************/ + +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if !defined(STATIC_LINKED) +# define SWIGEXPORT(a) __declspec(dllexport) a +# else +# define SWIGEXPORT(a) a +# endif +#else +# define SWIGEXPORT(a) a +#endif + +#define SWIGRUNTIME(x) static x + +#ifndef SWIGINLINE +#if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +#else +# define SWIGINLINE +#endif +#endif + + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "1" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +#define SWIG_QUOTE_STRING(x) #x +#define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +#define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +#define SWIG_TYPE_TABLE_NAME +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +typedef struct swig_type_info { + const char *name; + swig_converter_func converter; + const char *str; + void *clientdata; + swig_dycast_func dcast; + struct swig_type_info *next; + struct swig_type_info *prev; +} swig_type_info; + +static swig_type_info *swig_type_list = 0; +static swig_type_info **swig_type_list_handle = &swig_type_list; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +static int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return *f1 - *f2; + } + return (l1 - f1) - (l2 - f2); +} + +/* + Check type equivalence in a name list like ||... +*/ +static int +SWIG_TypeEquiv(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = SWIG_TypeNameComp(nb, ne, tb, te) == 0; + if (*ne) ++ne; + } + return equiv; +} + + +/* Register a type mapping with the type-checking */ +static swig_type_info * +SWIG_TypeRegister(swig_type_info *ti) { + swig_type_info *tc, *head, *ret, *next; + /* Check to see if this type has already been registered */ + tc = *swig_type_list_handle; + while (tc) { + /* check simple type equivalence */ + int typeequiv = (strcmp(tc->name, ti->name) == 0); + /* check full type equivalence, resolving typedefs */ + if (!typeequiv) { + /* only if tc is not a typedef (no '|' on it) */ + if (tc->str && ti->str && !strstr(tc->str,"|")) { + typeequiv = SWIG_TypeEquiv(ti->str,tc->str); + } + } + if (typeequiv) { + /* Already exists in the table. Just add additional types to the list */ + if (ti->clientdata) tc->clientdata = ti->clientdata; + head = tc; + next = tc->next; + goto l1; + } + tc = tc->prev; + } + head = ti; + next = 0; + + /* Place in list */ + ti->prev = *swig_type_list_handle; + *swig_type_list_handle = ti; + + /* Build linked lists */ + l1: + ret = head; + tc = ti + 1; + /* Patch up the rest of the links */ + while (tc->name) { + head->next = tc; + tc->prev = head; + head = tc; + tc++; + } + if (next) next->prev = head; + head->next = next; + + return ret; +} + +/* Check the typename */ +static swig_type_info * +SWIG_TypeCheck(char *c, swig_type_info *ty) { + swig_type_info *s; + if (!ty) return 0; /* Void pointer */ + s = ty->next; /* First element always just a name */ + do { + if (strcmp(s->name,c) == 0) { + if (s == ty->next) return s; + /* Move s to the top of the linked list */ + s->prev->next = s->next; + if (s->next) { + s->next->prev = s->prev; + } + /* Insert s as second element in the list */ + s->next = ty->next; + if (ty->next) ty->next->prev = s; + ty->next = s; + s->prev = ty; + return s; + } + s = s->next; + } while (s && (s != ty->next)); + return 0; +} + +/* Cast a pointer up an inheritance hierarchy */ +static SWIGINLINE void * +SWIG_TypeCast(swig_type_info *ty, void *ptr) { + if ((!ty) || (!ty->converter)) return ptr; + return (*ty->converter)(ptr); +} + +/* Dynamic pointer casting. Down an inheritance hierarchy */ +static swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* Return the name associated with this type */ +static SWIGINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +static const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* Search for a swig_type_info structure */ +static swig_type_info * +SWIG_TypeQuery(const char *name) { + swig_type_info *ty = *swig_type_list_handle; + while (ty) { + if (ty->str && (SWIG_TypeEquiv(ty->str,name))) return ty; + if (ty->name && (strcmp(name,ty->name) == 0)) return ty; + ty = ty->prev; + } + return 0; +} + +/* Set the clientdata field for a type */ +static void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_type_info *tc, *equiv; + if (ti->clientdata) return; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + equiv = ti->next; + while (equiv) { + if (!equiv->converter) { + tc = *swig_type_list_handle; + while (tc) { + if ((strcmp(tc->name, equiv->name) == 0)) + SWIG_TypeClientData(tc,clientdata); + tc = tc->prev; + } + } + equiv = equiv->next; + } +} + +/* Pack binary data into a string */ +static char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static char hex[17] = "0123456789abcdef"; + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + register unsigned char uu; + for (; u != eu; ++u) { + uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* Unpack binary data from a string */ +static char * +SWIG_UnpackData(char *c, void *ptr, size_t sz) { + register unsigned char uu = 0; + register int d; + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + *u = uu; + } + return c; +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +static void +SWIG_PropagateClientData(swig_type_info *type) { + swig_type_info *equiv = type->next; + swig_type_info *tc; + if (!type->clientdata) return; + while (equiv) { + if (!equiv->converter) { + tc = *swig_type_list_handle; + while (tc) { + if ((strcmp(tc->name, equiv->name) == 0) && !tc->clientdata) + SWIG_TypeClientData(tc, type->clientdata); + tc = tc->prev; + } + } + equiv = equiv->next; + } +} + +#ifdef __cplusplus +} +#endif + +/* ---------------------------------------------------------------------- -*- c -*- + * perl5.swg + * + * Perl5 runtime library + * $Header$ + * ----------------------------------------------------------------------------- */ + +#define SWIGPERL +#define SWIGPERL5 +#ifdef __cplusplus +/* Needed on some windows machines---since MS plays funny games with the header files under C++ */ +#include +#include +extern "C" { +#endif +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +/* Get rid of free and malloc defined by perl */ +#undef free +#undef malloc + +#ifndef pTHX_ +#define pTHX_ +#endif + +#include +#ifdef __cplusplus +} +#endif + +/* Macro to call an XS function */ + +#ifdef PERL_OBJECT +# define SWIG_CALLXS(_name) _name(cv,pPerl) +#else +# ifndef MULTIPLICITY +# define SWIG_CALLXS(_name) _name(cv) +# else +# define SWIG_CALLXS(_name) _name(PERL_GET_THX, cv) +# endif +#endif + +/* Contract support */ + +#define SWIG_contract_assert(expr,msg) if (!(expr)) { SWIG_croak(msg); } else + +/* Note: SwigMagicFuncHack is a typedef used to get the C++ compiler to just shut up already */ + +#ifdef PERL_OBJECT +#define MAGIC_PPERL CPerlObj *pPerl = (CPerlObj *) this; +typedef int (CPerlObj::*SwigMagicFunc)(SV *, MAGIC *); + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (CPerlObj::*SwigMagicFuncHack)(SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + +#define SWIG_MAGIC(a,b) (SV *a, MAGIC *b) +#define SWIGCLASS_STATIC +#else +#define MAGIC_PPERL +#define SWIGCLASS_STATIC static +#ifndef MULTIPLICITY +#define SWIG_MAGIC(a,b) (SV *a, MAGIC *b) +typedef int (*SwigMagicFunc)(SV *, MAGIC *); + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (*SwigMagicFuncHack)(SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + + +#else +#define SWIG_MAGIC(a,b) (struct interpreter *interp, SV *a, MAGIC *b) +typedef int (*SwigMagicFunc)(struct interpreter *, SV *, MAGIC *); +#ifdef __cplusplus +extern "C" { +#endif +typedef int (*SwigMagicFuncHack)(struct interpreter *, SV *, MAGIC *); +#ifdef __cplusplus +} +#endif + +#endif +#endif + +#if defined(WIN32) && defined(PERL_OBJECT) && !defined(PerlIO_exportFILE) +#define PerlIO_exportFILE(fh,fl) (FILE*)(fh) +#endif + +/* Modifications for newer Perl 5.005 releases */ + +#if !defined(PERL_REVISION) || ((PERL_REVISION >= 5) && ((PERL_VERSION < 5) || ((PERL_VERSION == 5) && (PERL_SUBVERSION < 50)))) +# ifndef PL_sv_yes +# define PL_sv_yes sv_yes +# endif +# ifndef PL_sv_undef +# define PL_sv_undef sv_undef +# endif +# ifndef PL_na +# define PL_na na +# endif +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SWIG_OWNER 1 +#define SWIG_SHADOW 2 + +/* Common SWIG API */ + +#ifdef PERL_OBJECT +# define SWIG_ConvertPtr(obj, pp, type, flags) \ + SWIG_Perl_ConvertPtr(pPerl, obj, pp, type, flags) +# define SWIG_NewPointerObj(p, type, flags) \ + SWIG_Perl_NewPointerObj(pPerl, p, type, flags) +# define SWIG_MakePackedObj(sv, p, s, type) \ + SWIG_Perl_MakePackedObj(pPerl, sv, p, s, type) +# define SWIG_ConvertPacked(obj, p, s, type, flags) \ + SWIG_Perl_ConvertPacked(pPerl, obj, p, s, type, flags) + +#else +# define SWIG_ConvertPtr(obj, pp, type, flags) \ + SWIG_Perl_ConvertPtr(obj, pp, type, flags) +# define SWIG_NewPointerObj(p, type, flags) \ + SWIG_Perl_NewPointerObj(p, type, flags) +# define SWIG_MakePackedObj(sv, p, s, type) \ + SWIG_Perl_MakePackedObj(sv, p, s, type ) +# define SWIG_ConvertPacked(obj, p, s, type, flags) \ + SWIG_Perl_ConvertPacked(obj, p, s, type, flags) +#endif + +/* Perl-specific API */ +#ifdef PERL_OBJECT +# define SWIG_MakePtr(sv, ptr, type, flags) \ + SWIG_Perl_MakePtr(pPerl, sv, ptr, type, flags) +# define SWIG_SetError(str) \ + SWIG_Perl_SetError(pPerl, str) +#else +# define SWIG_MakePtr(sv, ptr, type, flags) \ + SWIG_Perl_MakePtr(sv, ptr, type, flags) +# define SWIG_SetError(str) \ + SWIG_Perl_SetError(str) +# define SWIG_SetErrorSV(str) \ + SWIG_Perl_SetErrorSV(str) +#endif + +#define SWIG_SetErrorf SWIG_Perl_SetErrorf + + +#ifdef PERL_OBJECT +# define SWIG_MAYBE_PERL_OBJECT CPerlObj *pPerl, +#else +# define SWIG_MAYBE_PERL_OBJECT +#endif + +/* load the swig_runtime_list_handle variable from the interpreter */ +static void SWIG_Perl_LookupTypePointer() { + SV *pointer; + + /* first check if pointer already created */ + pointer = get_sv("swig_runtime_data::type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, FALSE); + if (pointer && SvOK(pointer)) { + swig_type_list_handle = INT2PTR(swig_type_info **, SvIV(pointer)); + } else { + /* create a new pointer */ + pointer = get_sv("swig_runtime_data::type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, TRUE); + sv_setiv(pointer, PTR2IV(swig_type_list_handle)); + } +} + +static swig_type_info * +SWIG_Perl_TypeCheckRV(SWIG_MAYBE_PERL_OBJECT SV *rv, swig_type_info *ty) { + swig_type_info *s; + if (!ty) return 0; /* Void pointer */ + s = ty->next; /* First element always just a name */ + do { + if (sv_derived_from(rv, (char *) s->name)) { + if (s == ty->next) return s; + /* Move s to the top of the linked list */ + s->prev->next = s->next; + if (s->next) { + s->next->prev = s->prev; + } + /* Insert s as second element in the list */ + s->next = ty->next; + if (ty->next) ty->next->prev = s; + ty->next = s; + s->prev = ty; + return s; + } + s = s->next; + } while (s && (s != ty->next)); + return 0; +} + +/* Function for getting a pointer value */ + +static int +SWIG_Perl_ConvertPtr(SWIG_MAYBE_PERL_OBJECT SV *sv, void **ptr, swig_type_info *_t, int flags) { + swig_type_info *tc; + void *voidptr = (void *)0; + + /* If magical, apply more magic */ + if (SvGMAGICAL(sv)) + mg_get(sv); + + /* Check to see if this is an object */ + if (sv_isobject(sv)) { + SV *tsv = (SV*) SvRV(sv); + IV tmp = 0; + if ((SvTYPE(tsv) == SVt_PVHV)) { + MAGIC *mg; + if (SvMAGICAL(tsv)) { + mg = mg_find(tsv,'P'); + if (mg) { + sv = mg->mg_obj; + if (sv_isobject(sv)) { + tmp = SvIV((SV*)SvRV(sv)); + } + } + } else { + return -1; + } + } else { + tmp = SvIV((SV*)SvRV(sv)); + } + voidptr = (void *)tmp; + if (!_t) { + *(ptr) = voidptr; + return 0; + } + } else if (! SvOK(sv)) { /* Check for undef */ + *(ptr) = (void *) 0; + return 0; + } else if (SvTYPE(sv) == SVt_RV) { /* Check for NULL pointer */ + *(ptr) = (void *) 0; + if (!SvROK(sv)) + return 0; + else + return -1; + } else { /* Don't know what it is */ + *(ptr) = (void *) 0; + return -1; + } + if (_t) { + /* Now see if the types match */ + char *_c = HvNAME(SvSTASH(SvRV(sv))); + tc = SWIG_TypeCheck(_c,_t); + if (!tc) { + *ptr = voidptr; + return -1; + } + *ptr = SWIG_TypeCast(tc,voidptr); + return 0; + } + *ptr = voidptr; + return 0; +} + +static void +SWIG_Perl_MakePtr(SWIG_MAYBE_PERL_OBJECT SV *sv, void *ptr, swig_type_info *t, int flags) { + if (ptr && (flags & SWIG_SHADOW)) { + SV *self; + SV *obj=newSV(0); + HV *hash=newHV(); + HV *stash; + sv_setref_pv(obj, (char *) t->name, ptr); + stash=SvSTASH(SvRV(obj)); + if (flags & SWIG_OWNER) { + HV *hv; + GV *gv=*(GV**)hv_fetch(stash, "OWNER", 5, TRUE); + if (!isGV(gv)) + gv_init(gv, stash, "OWNER", 5, FALSE); + hv=GvHVn(gv); + hv_store_ent(hv, obj, newSViv(1), 0); + } + sv_magic((SV *)hash, (SV *)obj, 'P', Nullch, 0); + SvREFCNT_dec(obj); + self=newRV_noinc((SV *)hash); + sv_setsv(sv, self); + SvREFCNT_dec((SV *)self); + sv_bless(sv, stash); + } + else { + sv_setref_pv(sv, (char *) t->name, ptr); + } +} + +static SWIGINLINE SV * +SWIG_Perl_NewPointerObj(SWIG_MAYBE_PERL_OBJECT void *ptr, swig_type_info *t, int flags) { + SV *result = sv_newmortal(); + SWIG_MakePtr(result, ptr, t, flags); + return result; +} + +static void + SWIG_Perl_MakePackedObj(SWIG_MAYBE_PERL_OBJECT SV *sv, void *ptr, int sz, swig_type_info *type) { + char result[1024]; + char *r = result; + if ((2*sz + 1 + strlen(type->name)) > 1000) return; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + strcpy(r,type->name); + sv_setpv(sv, result); +} + +/* Convert a packed value value */ +static int +SWIG_Perl_ConvertPacked(SWIG_MAYBE_PERL_OBJECT SV *obj, void *ptr, int sz, swig_type_info *ty, int flags) { + swig_type_info *tc; + char *c = 0; + + if ((!obj) || (!SvOK(obj))) return -1; + c = SvPV(obj, PL_na); + /* Pointer values must start with leading underscore */ + if (*c != '_') return -1; + c++; + c = SWIG_UnpackData(c,ptr,sz); + if (ty) { + tc = SWIG_TypeCheck(c,ty); + if (!tc) return -1; + } + return 0; +} + +static SWIGINLINE void +SWIG_Perl_SetError(SWIG_MAYBE_PERL_OBJECT const char *error) { + if (error) sv_setpv(perl_get_sv("@", TRUE), error); +} + +static SWIGINLINE void +SWIG_Perl_SetErrorSV(SWIG_MAYBE_PERL_OBJECT SV *error) { + if (error) sv_setsv(perl_get_sv("@", TRUE), error); +} + +static void +SWIG_Perl_SetErrorf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + sv_vsetpvfn(perl_get_sv("@", TRUE), fmt, strlen(fmt), &args, Null(SV**), 0, Null(bool*)); + va_end(args); +} + +/* Macros for low-level exception handling */ +#define SWIG_fail goto fail +#define SWIG_croak(x) { SWIG_SetError(x); goto fail; } +#define SWIG_croakSV(x) { SWIG_SetErrorSV(x); goto fail; } +/* most preprocessors do not support vararg macros :-( */ +/* #define SWIG_croakf(x...) { SWIG_SetErrorf(x); goto fail; } */ + + +typedef XS(SwigPerlWrapper); +typedef SwigPerlWrapper *SwigPerlWrapperPtr; + +/* Structure for command table */ +typedef struct { + const char *name; + SwigPerlWrapperPtr wrapper; +} swig_command_info; + +/* Information for constant table */ + +#define SWIG_INT 1 +#define SWIG_FLOAT 2 +#define SWIG_STRING 3 +#define SWIG_POINTER 4 +#define SWIG_BINARY 5 + +/* Constant information structure */ +typedef struct swig_constant_info { + int type; + const char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_constant_info; + +#ifdef __cplusplus +} +#endif + +/* Structure for variable table */ +typedef struct { + const char *name; + SwigMagicFunc set; + SwigMagicFunc get; + swig_type_info **type; +} swig_variable_info; + +/* Magic variable code */ +#ifndef PERL_OBJECT +#define swig_create_magic(s,a,b,c) _swig_create_magic(s,a,b,c) + #ifndef MULTIPLICITY + static void _swig_create_magic(SV *sv, char *name, int (*set)(SV *, MAGIC *), int (*get)(SV *,MAGIC *)) { + #else + static void _swig_create_magic(SV *sv, char *name, int (*set)(struct interpreter*, SV *, MAGIC *), int (*get)(struct interpreter*, SV *,MAGIC *)) { + #endif +#else +# define swig_create_magic(s,a,b,c) _swig_create_magic(pPerl,s,a,b,c) +static void _swig_create_magic(CPerlObj *pPerl, SV *sv, const char *name, int (CPerlObj::*set)(SV *, MAGIC *), int (CPerlObj::*get)(SV *, MAGIC *)) { +#endif + MAGIC *mg; + sv_magic(sv,sv,'U',(char *) name,strlen(name)); + mg = mg_find(sv,'U'); + mg->mg_virtual = (MGVTBL *) malloc(sizeof(MGVTBL)); + mg->mg_virtual->svt_get = (SwigMagicFuncHack) get; + mg->mg_virtual->svt_set = (SwigMagicFuncHack) set; + mg->mg_virtual->svt_len = 0; + mg->mg_virtual->svt_clear = 0; + mg->mg_virtual->svt_free = 0; +} + + + + + + +#ifdef do_open + #undef do_open +#endif +#ifdef do_close + #undef do_close +#endif +#ifdef scalar + #undef scalar +#endif +#ifdef list + #undef list +#endif +#ifdef apply + #undef apply +#endif +#ifdef convert + #undef convert +#endif +#ifdef Error + #undef Error +#endif +#ifdef form + #undef form +#endif +#ifdef vform + #undef vform +#endif +#ifdef LABEL + #undef LABEL +#endif +#ifdef METHOD + #undef METHOD +#endif +#ifdef Move + #undef Move +#endif +#ifdef yylex + #undef yylex +#endif +#ifdef yyparse + #undef yyparse +#endif +#ifdef yyerror + #undef yyerror +#endif +#ifdef invert + #undef invert +#endif +#ifdef ref + #undef ref +#endif +#ifdef ENTER + #undef ENTER +#endif + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_Inkscape__Extension__Script__Desktop swig_types[0] +#define SWIGTYPE_p_Inkscape__Extension__Script__Inkscape swig_types[1] +#define SWIGTYPE_p_Inkscape__Extension__Script__DialogManager swig_types[2] +#define SWIGTYPE_p_Inkscape__Extension__Script__Document swig_types[3] +static swig_type_info *swig_types[5]; + +/* -------- TYPES TABLE (END) -------- */ + +#define SWIG_init boot_inkscape_perl + +#define SWIG_name "inkscape_perlc::boot_inkscape_perl" +#define SWIG_prefix "inkscape_perlc::" + +#ifdef __cplusplus +extern "C" +#endif +#ifndef PERL_OBJECT +#ifndef MULTIPLICITY +SWIGEXPORT(void) SWIG_init (CV* cv); +#else +SWIGEXPORT(void) SWIG_init (pTHXo_ CV* cv); +#endif +#else +SWIGEXPORT(void) SWIG_init (CV *cv, CPerlObj *); +#endif + + +#include "InkscapeBinding.h" + + +static void xs_init _((pTHX)); +static PerlInterpreter *my_perl; + +int perl_eval(char *string) { + char *argv[2]; + argv[0] = string; + argv[1] = (char *) 0; + return perl_call_argv("eval",0,argv); +} + +extern "C" int +InkscapePerlParseBuf(char *startupCodeBuf, char *codeBuf) +{ + STRLEN n_a; + int exitstatus; + char *embedding[] = { "", "-e", "0" }; + + my_perl = perl_alloc(); + if (!my_perl) + return 0; + perl_construct( my_perl ); + + exitstatus = perl_parse( my_perl, xs_init, 3, + embedding, (char **) NULL ); + if (exitstatus) + return 0; + + /* Initialize all of the module variables */ + + exitstatus = perl_run( my_perl ); + + SV *retSV = eval_pv(startupCodeBuf, TRUE); + char *ret = SvPV(retSV, n_a); + //printf("## module ret:%s\n", ret); + + retSV = eval_pv("$inkscape = inkscape_perlc::getInkscape();\n", TRUE); + ret = SvPV(retSV, n_a); + //printf("## inkscape ret:%s\n", ret); + + retSV = eval_pv(codeBuf, TRUE); + ret = SvPV(retSV, n_a); + //printf("## code ret:%s\n", ret); + + perl_destruct( my_perl ); + perl_free( my_perl ); + + return 1; +} + +/* Register any extra external extensions */ + +/* Do not delete this line--writemain depends on it */ +/* EXTERN_C void boot_DynaLoader _((CV* cv)); */ + +static void +xs_init(pTHX) +{ +/* dXSUB_SYS; */ + char *file = __FILE__; + { + /* newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); */ + newXS(SWIG_name, SWIG_init, file); +#ifdef SWIGMODINIT + SWIGMODINIT +#endif + } +} + + + +#ifdef PERL_OBJECT +#define MAGIC_CLASS _wrap_inkscape_perl_var:: +class _wrap_inkscape_perl_var : public CPerlObj { +public: +#else +#define MAGIC_CLASS +#endif +SWIGCLASS_STATIC int swig_magic_readonly(pTHX_ SV *sv, MAGIC *mg) { + MAGIC_PPERL + sv = sv; mg = mg; + croak("Value is read-only."); + return 0; +} + + +#ifdef PERL_OBJECT +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif +XS(_wrap_getInkscape) { + { + Inkscape::Extension::Script::Inkscape *result; + int argvi = 0; + dXSARGS; + + if ((items < 0) || (items > 0)) { + SWIG_croak("Usage: getInkscape();"); + } + result = (Inkscape::Extension::Script::Inkscape *)Inkscape::Extension::Script::getInkscape(); + + ST(argvi) = sv_newmortal(); + SWIG_MakePtr(ST(argvi++), (void *) result, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, SWIG_SHADOW|0); + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_delete_Inkscape) { + { + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: delete_Inkscape(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape,0) < 0) { + SWIG_croak("Type error in argument 1 of delete_Inkscape. Expected _p_Inkscape__Extension__Script__Inkscape"); + } + } + delete arg1; + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_Inkscape_getDesktop) { + { + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + Inkscape::Extension::Script::Desktop *result; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: Inkscape_getDesktop(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape,0) < 0) { + SWIG_croak("Type error in argument 1 of Inkscape_getDesktop. Expected _p_Inkscape__Extension__Script__Inkscape"); + } + } + result = (Inkscape::Extension::Script::Desktop *)(arg1)->getDesktop(); + + ST(argvi) = sv_newmortal(); + SWIG_MakePtr(ST(argvi++), (void *) result, SWIGTYPE_p_Inkscape__Extension__Script__Desktop, SWIG_SHADOW|0); + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_Inkscape_getDialogManager) { + { + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + Inkscape::Extension::Script::DialogManager *result; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: Inkscape_getDialogManager(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape,0) < 0) { + SWIG_croak("Type error in argument 1 of Inkscape_getDialogManager. Expected _p_Inkscape__Extension__Script__Inkscape"); + } + } + result = (Inkscape::Extension::Script::DialogManager *)(arg1)->getDialogManager(); + + ST(argvi) = sv_newmortal(); + SWIG_MakePtr(ST(argvi++), (void *) result, SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, SWIG_SHADOW|0); + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_delete_DialogManager) { + { + Inkscape::Extension::Script::DialogManager *arg1 = (Inkscape::Extension::Script::DialogManager *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: delete_DialogManager(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__DialogManager,0) < 0) { + SWIG_croak("Type error in argument 1 of delete_DialogManager. Expected _p_Inkscape__Extension__Script__DialogManager"); + } + } + delete arg1; + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_DialogManager_showAbout) { + { + Inkscape::Extension::Script::DialogManager *arg1 = (Inkscape::Extension::Script::DialogManager *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: DialogManager_showAbout(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__DialogManager,0) < 0) { + SWIG_croak("Type error in argument 1 of DialogManager_showAbout. Expected _p_Inkscape__Extension__Script__DialogManager"); + } + } + (arg1)->showAbout(); + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_delete_Desktop) { + { + Inkscape::Extension::Script::Desktop *arg1 = (Inkscape::Extension::Script::Desktop *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: delete_Desktop(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Desktop,0) < 0) { + SWIG_croak("Type error in argument 1 of delete_Desktop. Expected _p_Inkscape__Extension__Script__Desktop"); + } + } + delete arg1; + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_Desktop_getDocument) { + { + Inkscape::Extension::Script::Desktop *arg1 = (Inkscape::Extension::Script::Desktop *) 0 ; + Inkscape::Extension::Script::Document *result; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: Desktop_getDocument(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Desktop,0) < 0) { + SWIG_croak("Type error in argument 1 of Desktop_getDocument. Expected _p_Inkscape__Extension__Script__Desktop"); + } + } + result = (Inkscape::Extension::Script::Document *)(arg1)->getDocument(); + + ST(argvi) = sv_newmortal(); + SWIG_MakePtr(ST(argvi++), (void *) result, SWIGTYPE_p_Inkscape__Extension__Script__Document, SWIG_SHADOW|0); + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_delete_Document) { + { + Inkscape::Extension::Script::Document *arg1 = (Inkscape::Extension::Script::Document *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: delete_Document(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Document,0) < 0) { + SWIG_croak("Type error in argument 1 of delete_Document. Expected _p_Inkscape__Extension__Script__Document"); + } + } + delete arg1; + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + +XS(_wrap_Document_hello) { + { + Inkscape::Extension::Script::Document *arg1 = (Inkscape::Extension::Script::Document *) 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 1) || (items > 1)) { + SWIG_croak("Usage: Document_hello(self);"); + } + { + if (SWIG_ConvertPtr(ST(0), (void **) &arg1, SWIGTYPE_p_Inkscape__Extension__Script__Document,0) < 0) { + SWIG_croak("Type error in argument 1 of Document_hello. Expected _p_Inkscape__Extension__Script__Document"); + } + } + (arg1)->hello(); + + + XSRETURN(argvi); + fail: + ; + } + croak(Nullch); +} + + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_Inkscape__Extension__Script__Desktop[] = {{"inkscape_perl::Desktop", 0, "Inkscape::Extension::Script::Desktop *", 0, 0, 0, 0},{"inkscape_perl::Desktop", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__Inkscape[] = {{"inkscape_perl::Inkscape", 0, "Inkscape::Extension::Script::Inkscape *", 0, 0, 0, 0},{"inkscape_perl::Inkscape", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__DialogManager[] = {{"inkscape_perl::DialogManager", 0, "Inkscape::Extension::Script::DialogManager *", 0, 0, 0, 0},{"inkscape_perl::DialogManager", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__Document[] = {{"inkscape_perl::Document", 0, "Inkscape::Extension::Script::Document *", 0, 0, 0, 0},{"inkscape_perl::Document", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; + +static swig_type_info *swig_types_initial[] = { +_swigt__p_Inkscape__Extension__Script__Desktop, +_swigt__p_Inkscape__Extension__Script__Inkscape, +_swigt__p_Inkscape__Extension__Script__DialogManager, +_swigt__p_Inkscape__Extension__Script__Document, +0 +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_constant_info swig_constants[] = { +{0,0,0,0,0,0} +}; +#ifdef __cplusplus +} +#endif +static swig_variable_info swig_variables[] = { +{0,0,0,0} +}; +static swig_command_info swig_commands[] = { +{"inkscape_perlc::getInkscape", _wrap_getInkscape}, +{"inkscape_perlc::delete_Inkscape", _wrap_delete_Inkscape}, +{"inkscape_perlc::Inkscape_getDesktop", _wrap_Inkscape_getDesktop}, +{"inkscape_perlc::Inkscape_getDialogManager", _wrap_Inkscape_getDialogManager}, +{"inkscape_perlc::delete_DialogManager", _wrap_delete_DialogManager}, +{"inkscape_perlc::DialogManager_showAbout", _wrap_DialogManager_showAbout}, +{"inkscape_perlc::delete_Desktop", _wrap_delete_Desktop}, +{"inkscape_perlc::Desktop_getDocument", _wrap_Desktop_getDocument}, +{"inkscape_perlc::delete_Document", _wrap_delete_Document}, +{"inkscape_perlc::Document_hello", _wrap_Document_hello}, +{0,0} +}; + +#ifdef __cplusplus +extern "C" +#endif + +XS(SWIG_init) { + dXSARGS; + int i; + static int _init = 0; + if (!_init) { + SWIG_Perl_LookupTypePointer(); + for (i = 0; swig_types_initial[i]; i++) { + swig_types[i] = SWIG_TypeRegister(swig_types_initial[i]); + } + _init = 1; + } + + /* Install commands */ + for (i = 0; swig_commands[i].name; i++) { + newXS((char*) swig_commands[i].name,swig_commands[i].wrapper, (char*)__FILE__); + } + + /* Install variables */ + for (i = 0; swig_variables[i].name; i++) { + SV *sv; + sv = perl_get_sv((char*) swig_variables[i].name, TRUE | 0x2); + if (swig_variables[i].type) { + SWIG_MakePtr(sv,(void *)1, *swig_variables[i].type,0); + } else { + sv_setiv(sv,(IV) 0); + } + swig_create_magic(sv, (char *) swig_variables[i].name, swig_variables[i].set, swig_variables[i].get); + } + + /* Install constant */ + for (i = 0; swig_constants[i].type; i++) { + SV *sv; + sv = perl_get_sv((char*)swig_constants[i].name, TRUE | 0x2); + switch(swig_constants[i].type) { + case SWIG_INT: + sv_setiv(sv, (IV) swig_constants[i].lvalue); + break; + case SWIG_FLOAT: + sv_setnv(sv, (double) swig_constants[i].dvalue); + break; + case SWIG_STRING: + sv_setpv(sv, (char *) swig_constants[i].pvalue); + break; + case SWIG_POINTER: + SWIG_MakePtr(sv, swig_constants[i].pvalue, *(swig_constants[i].ptype),0); + break; + case SWIG_BINARY: + SWIG_MakePackedObj(sv, swig_constants[i].pvalue, swig_constants[i].lvalue, *(swig_constants[i].ptype)); + break; + default: + break; + } + SvREADONLY_on(sv); + } + + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, (void*) "inkscape_perl::Inkscape"); + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, (void*) "inkscape_perl::DialogManager"); + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Desktop, (void*) "inkscape_perl::Desktop"); + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Document, (void*) "inkscape_perl::Document"); + ST(0) = &PL_sv_yes; + XSRETURN(1); +} + diff --git a/src/extension/script/inkscape_py.i b/src/extension/script/inkscape_py.i new file mode 100644 index 000000000..32f3199a9 --- /dev/null +++ b/src/extension/script/inkscape_py.i @@ -0,0 +1,6 @@ +%module inkscape_py +%{ +#include "InkscapeBinding.h" +%} + +%include "InkscapeBinding.h" diff --git a/src/extension/script/inkscape_py.py b/src/extension/script/inkscape_py.py new file mode 100644 index 000000000..81eabccbf --- /dev/null +++ b/src/extension/script/inkscape_py.py @@ -0,0 +1,130 @@ +# This file was created automatically by SWIG. +# Don't modify this file, modify the SWIG interface instead. +# This file is compatible with both classic and new-style classes. + +import _inkscape_py + +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "this"): + if isinstance(value, class_type): + self.__dict__[name] = value.this + if hasattr(value,"thisown"): self.__dict__["thisown"] = value.thisown + del value.thisown + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static) or hasattr(self,name) or (name == "thisown"): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError,name + +import types +try: + _object = types.ObjectType + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 +del types + + + +getInkscape = _inkscape_py.getInkscape +class Inkscape(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, Inkscape, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, Inkscape, name) + def __init__(self): raise RuntimeError, "No constructor defined" + def __repr__(self): + return "<%s.%s; proxy of C++ Inkscape::Extension::Script::Inkscape instance at %s>" % (self.__class__.__module__, self.__class__.__name__, self.this,) + def __del__(self, destroy=_inkscape_py.delete_Inkscape): + try: + if self.thisown: destroy(self) + except: pass + + def getDesktop(*args): return _inkscape_py.Inkscape_getDesktop(*args) + def getDialogManager(*args): return _inkscape_py.Inkscape_getDialogManager(*args) + +class InkscapePtr(Inkscape): + def __init__(self, this): + _swig_setattr(self, Inkscape, 'this', this) + if not hasattr(self,"thisown"): _swig_setattr(self, Inkscape, 'thisown', 0) + _swig_setattr(self, Inkscape,self.__class__,Inkscape) +_inkscape_py.Inkscape_swigregister(InkscapePtr) + +class DialogManager(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, DialogManager, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, DialogManager, name) + def __init__(self): raise RuntimeError, "No constructor defined" + def __repr__(self): + return "<%s.%s; proxy of C++ Inkscape::Extension::Script::DialogManager instance at %s>" % (self.__class__.__module__, self.__class__.__name__, self.this,) + def __del__(self, destroy=_inkscape_py.delete_DialogManager): + try: + if self.thisown: destroy(self) + except: pass + + def showAbout(*args): return _inkscape_py.DialogManager_showAbout(*args) + +class DialogManagerPtr(DialogManager): + def __init__(self, this): + _swig_setattr(self, DialogManager, 'this', this) + if not hasattr(self,"thisown"): _swig_setattr(self, DialogManager, 'thisown', 0) + _swig_setattr(self, DialogManager,self.__class__,DialogManager) +_inkscape_py.DialogManager_swigregister(DialogManagerPtr) + +class Desktop(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, Desktop, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, Desktop, name) + def __init__(self): raise RuntimeError, "No constructor defined" + def __repr__(self): + return "<%s.%s; proxy of C++ Inkscape::Extension::Script::Desktop instance at %s>" % (self.__class__.__module__, self.__class__.__name__, self.this,) + def __del__(self, destroy=_inkscape_py.delete_Desktop): + try: + if self.thisown: destroy(self) + except: pass + + def getDocument(*args): return _inkscape_py.Desktop_getDocument(*args) + +class DesktopPtr(Desktop): + def __init__(self, this): + _swig_setattr(self, Desktop, 'this', this) + if not hasattr(self,"thisown"): _swig_setattr(self, Desktop, 'thisown', 0) + _swig_setattr(self, Desktop,self.__class__,Desktop) +_inkscape_py.Desktop_swigregister(DesktopPtr) + +class Document(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, Document, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, Document, name) + def __init__(self): raise RuntimeError, "No constructor defined" + def __repr__(self): + return "<%s.%s; proxy of C++ Inkscape::Extension::Script::Document instance at %s>" % (self.__class__.__module__, self.__class__.__name__, self.this,) + def __del__(self, destroy=_inkscape_py.delete_Document): + try: + if self.thisown: destroy(self) + except: pass + + def hello(*args): return _inkscape_py.Document_hello(*args) + +class DocumentPtr(Document): + def __init__(self, this): + _swig_setattr(self, Document, 'this', this) + if not hasattr(self,"thisown"): _swig_setattr(self, Document, 'thisown', 0) + _swig_setattr(self, Document,self.__class__,Document) +_inkscape_py.Document_swigregister(DocumentPtr) + + diff --git a/src/extension/script/inkscape_py.py.h b/src/extension/script/inkscape_py.py.h new file mode 100644 index 000000000..50d432a1b --- /dev/null +++ b/src/extension/script/inkscape_py.py.h @@ -0,0 +1,139 @@ + +/* ################################################### +## This file generated by quotefile.pl from +## inkscape_py.py on Thu Dec 16 15:35:46 2004 +## DO NOT EDIT +################################################### */ + +static char *inkscape_module_script = +"# This file was created automatically by SWIG.\n" +"# Don't modify this file, modify the SWIG interface instead.\n" +"# This file is compatible with both classic and new-style classes.\n" +"\n" +"import _inkscape_py\n" +"\n" +"def _swig_setattr_nondynamic(self,class_type,name,value,static=1):\n" +" if (name == \"this\"):\n" +" if isinstance(value, class_type):\n" +" self.__dict__[name] = value.this\n" +" if hasattr(value,\"thisown\"): self.__dict__[\"thisown\"] = value.thisown\n" +" del value.thisown\n" +" return\n" +" method = class_type.__swig_setmethods__.get(name,None)\n" +" if method: return method(self,value)\n" +" if (not static) or hasattr(self,name) or (name == \"thisown\"):\n" +" self.__dict__[name] = value\n" +" else:\n" +" raise AttributeError(\"You cannot add attributes to %s\" % self)\n" +"\n" +"def _swig_setattr(self,class_type,name,value):\n" +" return _swig_setattr_nondynamic(self,class_type,name,value,0)\n" +"\n" +"def _swig_getattr(self,class_type,name):\n" +" method = class_type.__swig_getmethods__.get(name,None)\n" +" if method: return method(self)\n" +" raise AttributeError,name\n" +"\n" +"import types\n" +"try:\n" +" _object = types.ObjectType\n" +" _newclass = 1\n" +"except AttributeError:\n" +" class _object : pass\n" +" _newclass = 0\n" +"del types\n" +"\n" +"\n" +"\n" +"getInkscape = _inkscape_py.getInkscape\n" +"class Inkscape(_object):\n" +" __swig_setmethods__ = {}\n" +" __setattr__ = lambda self, name, value: _swig_setattr(self, Inkscape, name, value)\n" +" __swig_getmethods__ = {}\n" +" __getattr__ = lambda self, name: _swig_getattr(self, Inkscape, name)\n" +" def __init__(self): raise RuntimeError, \"No constructor defined\"\n" +" def __repr__(self):\n" +" return \"<%s.%s; proxy of C++ Inkscape::Extension::Script::Inkscape instance at %s>\" % (self.__class__.__module__, self.__class__.__name__, self.this,)\n" +" def __del__(self, destroy=_inkscape_py.delete_Inkscape):\n" +" try:\n" +" if self.thisown: destroy(self)\n" +" except: pass\n" +"\n" +" def getDesktop(*args): return _inkscape_py.Inkscape_getDesktop(*args)\n" +" def getDialogManager(*args): return _inkscape_py.Inkscape_getDialogManager(*args)\n" +"\n" +"class InkscapePtr(Inkscape):\n" +" def __init__(self, this):\n" +" _swig_setattr(self, Inkscape, 'this', this)\n" +" if not hasattr(self,\"thisown\"): _swig_setattr(self, Inkscape, 'thisown', 0)\n" +" _swig_setattr(self, Inkscape,self.__class__,Inkscape)\n" +"_inkscape_py.Inkscape_swigregister(InkscapePtr)\n" +"\n" +"class DialogManager(_object):\n" +" __swig_setmethods__ = {}\n" +" __setattr__ = lambda self, name, value: _swig_setattr(self, DialogManager, name, value)\n" +" __swig_getmethods__ = {}\n" +" __getattr__ = lambda self, name: _swig_getattr(self, DialogManager, name)\n" +" def __init__(self): raise RuntimeError, \"No constructor defined\"\n" +" def __repr__(self):\n" +" return \"<%s.%s; proxy of C++ Inkscape::Extension::Script::DialogManager instance at %s>\" % (self.__class__.__module__, self.__class__.__name__, self.this,)\n" +" def __del__(self, destroy=_inkscape_py.delete_DialogManager):\n" +" try:\n" +" if self.thisown: destroy(self)\n" +" except: pass\n" +"\n" +" def showAbout(*args): return _inkscape_py.DialogManager_showAbout(*args)\n" +"\n" +"class DialogManagerPtr(DialogManager):\n" +" def __init__(self, this):\n" +" _swig_setattr(self, DialogManager, 'this', this)\n" +" if not hasattr(self,\"thisown\"): _swig_setattr(self, DialogManager, 'thisown', 0)\n" +" _swig_setattr(self, DialogManager,self.__class__,DialogManager)\n" +"_inkscape_py.DialogManager_swigregister(DialogManagerPtr)\n" +"\n" +"class Desktop(_object):\n" +" __swig_setmethods__ = {}\n" +" __setattr__ = lambda self, name, value: _swig_setattr(self, Desktop, name, value)\n" +" __swig_getmethods__ = {}\n" +" __getattr__ = lambda self, name: _swig_getattr(self, Desktop, name)\n" +" def __init__(self): raise RuntimeError, \"No constructor defined\"\n" +" def __repr__(self):\n" +" return \"<%s.%s; proxy of C++ Inkscape::Extension::Script::Desktop instance at %s>\" % (self.__class__.__module__, self.__class__.__name__, self.this,)\n" +" def __del__(self, destroy=_inkscape_py.delete_Desktop):\n" +" try:\n" +" if self.thisown: destroy(self)\n" +" except: pass\n" +"\n" +" def getDocument(*args): return _inkscape_py.Desktop_getDocument(*args)\n" +"\n" +"class DesktopPtr(Desktop):\n" +" def __init__(self, this):\n" +" _swig_setattr(self, Desktop, 'this', this)\n" +" if not hasattr(self,\"thisown\"): _swig_setattr(self, Desktop, 'thisown', 0)\n" +" _swig_setattr(self, Desktop,self.__class__,Desktop)\n" +"_inkscape_py.Desktop_swigregister(DesktopPtr)\n" +"\n" +"class Document(_object):\n" +" __swig_setmethods__ = {}\n" +" __setattr__ = lambda self, name, value: _swig_setattr(self, Document, name, value)\n" +" __swig_getmethods__ = {}\n" +" __getattr__ = lambda self, name: _swig_getattr(self, Document, name)\n" +" def __init__(self): raise RuntimeError, \"No constructor defined\"\n" +" def __repr__(self):\n" +" return \"<%s.%s; proxy of C++ Inkscape::Extension::Script::Document instance at %s>\" % (self.__class__.__module__, self.__class__.__name__, self.this,)\n" +" def __del__(self, destroy=_inkscape_py.delete_Document):\n" +" try:\n" +" if self.thisown: destroy(self)\n" +" except: pass\n" +"\n" +" def hello(*args): return _inkscape_py.Document_hello(*args)\n" +"\n" +"class DocumentPtr(Document):\n" +" def __init__(self, this):\n" +" _swig_setattr(self, Document, 'this', this)\n" +" if not hasattr(self,\"thisown\"): _swig_setattr(self, Document, 'thisown', 0)\n" +" _swig_setattr(self, Document,self.__class__,Document)\n" +"_inkscape_py.Document_swigregister(DocumentPtr)\n" +"\n" +"\n" +""; diff --git a/src/extension/script/inkscape_py_wrap.cpp b/src/extension/script/inkscape_py_wrap.cpp new file mode 100644 index 000000000..28eeccb80 --- /dev/null +++ b/src/extension/script/inkscape_py_wrap.cpp @@ -0,0 +1,1408 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 1.3.23 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +#define SWIGPYTHON + +#ifdef __cplusplus +template class SwigValueWrapper { + T *tt; +public: + SwigValueWrapper() : tt(0) { } + SwigValueWrapper(const SwigValueWrapper& rhs) : tt(new T(*rhs.tt)) { } + SwigValueWrapper(const T& t) : tt(new T(t)) { } + ~SwigValueWrapper() { delete tt; } + SwigValueWrapper& operator=(const T& t) { delete tt; tt = new T(t); return *this; } + operator T&() const { return *tt; } + T *operator&() { return tt; } +private: + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); +}; +#endif + + +#ifndef SWIG_TEMPLATE_DISAMBIGUATOR +# if defined(__SUNPRO_CC) +# define SWIG_TEMPLATE_DISAMBIGUATOR template +# else +# define SWIG_TEMPLATE_DISAMBIGUATOR +# endif +#endif + + +#include + +/*********************************************************************** + * common.swg + * + * This file contains generic SWIG runtime support for pointer + * type checking as well as a few commonly used macros to control + * external linkage. + * + * Author : David Beazley (beazley@cs.uchicago.edu) + * + * Copyright (c) 1999-2000, The University of Chicago + * + * This file may be freely redistributed without license or fee provided + * this copyright message remains intact. + ************************************************************************/ + +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if !defined(STATIC_LINKED) +# define SWIGEXPORT(a) __declspec(dllexport) a +# else +# define SWIGEXPORT(a) a +# endif +#else +# define SWIGEXPORT(a) a +#endif + +#define SWIGRUNTIME(x) static x + +#ifndef SWIGINLINE +#if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +#else +# define SWIGINLINE +#endif +#endif + + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "1" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +#define SWIG_QUOTE_STRING(x) #x +#define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +#define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +#define SWIG_TYPE_TABLE_NAME +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +typedef struct swig_type_info { + const char *name; + swig_converter_func converter; + const char *str; + void *clientdata; + swig_dycast_func dcast; + struct swig_type_info *next; + struct swig_type_info *prev; +} swig_type_info; + +static swig_type_info *swig_type_list = 0; +static swig_type_info **swig_type_list_handle = &swig_type_list; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +static int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return *f1 - *f2; + } + return (l1 - f1) - (l2 - f2); +} + +/* + Check type equivalence in a name list like ||... +*/ +static int +SWIG_TypeEquiv(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = SWIG_TypeNameComp(nb, ne, tb, te) == 0; + if (*ne) ++ne; + } + return equiv; +} + + +/* Register a type mapping with the type-checking */ +static swig_type_info * +SWIG_TypeRegister(swig_type_info *ti) { + swig_type_info *tc, *head, *ret, *next; + /* Check to see if this type has already been registered */ + tc = *swig_type_list_handle; + while (tc) { + /* check simple type equivalence */ + int typeequiv = (strcmp(tc->name, ti->name) == 0); + /* check full type equivalence, resolving typedefs */ + if (!typeequiv) { + /* only if tc is not a typedef (no '|' on it) */ + if (tc->str && ti->str && !strstr(tc->str,"|")) { + typeequiv = SWIG_TypeEquiv(ti->str,tc->str); + } + } + if (typeequiv) { + /* Already exists in the table. Just add additional types to the list */ + if (ti->clientdata) tc->clientdata = ti->clientdata; + head = tc; + next = tc->next; + goto l1; + } + tc = tc->prev; + } + head = ti; + next = 0; + + /* Place in list */ + ti->prev = *swig_type_list_handle; + *swig_type_list_handle = ti; + + /* Build linked lists */ + l1: + ret = head; + tc = ti + 1; + /* Patch up the rest of the links */ + while (tc->name) { + head->next = tc; + tc->prev = head; + head = tc; + tc++; + } + if (next) next->prev = head; + head->next = next; + + return ret; +} + +/* Check the typename */ +static swig_type_info * +SWIG_TypeCheck(char *c, swig_type_info *ty) { + swig_type_info *s; + if (!ty) return 0; /* Void pointer */ + s = ty->next; /* First element always just a name */ + do { + if (strcmp(s->name,c) == 0) { + if (s == ty->next) return s; + /* Move s to the top of the linked list */ + s->prev->next = s->next; + if (s->next) { + s->next->prev = s->prev; + } + /* Insert s as second element in the list */ + s->next = ty->next; + if (ty->next) ty->next->prev = s; + ty->next = s; + s->prev = ty; + return s; + } + s = s->next; + } while (s && (s != ty->next)); + return 0; +} + +/* Cast a pointer up an inheritance hierarchy */ +static SWIGINLINE void * +SWIG_TypeCast(swig_type_info *ty, void *ptr) { + if ((!ty) || (!ty->converter)) return ptr; + return (*ty->converter)(ptr); +} + +/* Dynamic pointer casting. Down an inheritance hierarchy */ +static swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* Return the name associated with this type */ +static SWIGINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +static const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* Search for a swig_type_info structure */ +static swig_type_info * +SWIG_TypeQuery(const char *name) { + swig_type_info *ty = *swig_type_list_handle; + while (ty) { + if (ty->str && (SWIG_TypeEquiv(ty->str,name))) return ty; + if (ty->name && (strcmp(name,ty->name) == 0)) return ty; + ty = ty->prev; + } + return 0; +} + +/* Set the clientdata field for a type */ +static void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_type_info *tc, *equiv; + if (ti->clientdata) return; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + equiv = ti->next; + while (equiv) { + if (!equiv->converter) { + tc = *swig_type_list_handle; + while (tc) { + if ((strcmp(tc->name, equiv->name) == 0)) + SWIG_TypeClientData(tc,clientdata); + tc = tc->prev; + } + } + equiv = equiv->next; + } +} + +/* Pack binary data into a string */ +static char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static char hex[17] = "0123456789abcdef"; + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + register unsigned char uu; + for (; u != eu; ++u) { + uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* Unpack binary data from a string */ +static char * +SWIG_UnpackData(char *c, void *ptr, size_t sz) { + register unsigned char uu = 0; + register int d; + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + *u = uu; + } + return c; +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +static void +SWIG_PropagateClientData(swig_type_info *type) { + swig_type_info *equiv = type->next; + swig_type_info *tc; + if (!type->clientdata) return; + while (equiv) { + if (!equiv->converter) { + tc = *swig_type_list_handle; + while (tc) { + if ((strcmp(tc->name, equiv->name) == 0) && !tc->clientdata) + SWIG_TypeClientData(tc, type->clientdata); + tc = tc->prev; + } + } + equiv = equiv->next; + } +} + +#ifdef __cplusplus +} +#endif + +/* ----------------------------------------------------------------------------- + * SWIG API. Portion that goes into the runtime + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * for internal method declarations + * ----------------------------------------------------------------------------- */ + +#ifndef SWIGINTERN +#define SWIGINTERN static +#endif + +#ifndef SWIGINTERNSHORT +#ifdef __cplusplus +#define SWIGINTERNSHORT static inline +#else /* C case */ +#define SWIGINTERNSHORT static +#endif /* __cplusplus */ +#endif + + +/* Common SWIG API */ +#define SWIG_ConvertPtr(obj, pp, type, flags) SWIG_Python_ConvertPtr(obj, pp, type, flags) +#define SWIG_NewPointerObj(p, type, flags) SWIG_Python_NewPointerObj(p, type, flags) +#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) + +/* Python-specific SWIG API */ +#define SWIG_newvarlink() SWIG_Python_newvarlink() +#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) +#define SWIG_ConvertPacked(obj, ptr, sz, ty, flags) SWIG_Python_ConvertPacked(obj, ptr, sz, ty, flags) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) +#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) + +/* + Exception handling in wrappers +*/ +#define SWIG_fail goto fail +#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) +#define SWIG_append_errmsg(msg) SWIG_Python_AddErrMesg(msg,0) +#define SWIG_preppend_errmsg(msg) SWIG_Python_AddErrMesg(msg,1) +#define SWIG_type_error(type,obj) SWIG_Python_TypeError(type,obj) +#define SWIG_null_ref(type) SWIG_Python_NullRef(type) + +/* + Contract support +*/ +#define SWIG_contract_assert(expr, msg) \ + if (!(expr)) { PyErr_SetString(PyExc_RuntimeError, (char *) msg ); goto fail; } else + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_PY_INT 1 +#define SWIG_PY_FLOAT 2 +#define SWIG_PY_STRING 3 +#define SWIG_PY_POINTER 4 +#define SWIG_PY_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + +/* ----------------------------------------------------------------------------- + * Pointer declarations + * ----------------------------------------------------------------------------- */ +/* + Use SWIG_NO_COBJECT_TYPES to force the use of strings to represent + C/C++ pointers in the python side. Very useful for debugging, but + not always safe. +*/ +#if !defined(SWIG_NO_COBJECT_TYPES) && !defined(SWIG_COBJECT_TYPES) +# define SWIG_COBJECT_TYPES +#endif + +/* Flags for pointer conversion */ +#define SWIG_POINTER_EXCEPTION 0x1 +#define SWIG_POINTER_DISOWN 0x2 + +/* ----------------------------------------------------------------------------- + * Alloc. memory flags + * ----------------------------------------------------------------------------- */ +#define SWIG_OLDOBJ 1 +#define SWIG_NEWOBJ SWIG_OLDOBJ + 1 +#define SWIG_PYSTR SWIG_NEWOBJ + 1 + +#ifdef __cplusplus +} +#endif + + +/*********************************************************************** + * pyrun.swg + * + * This file contains the runtime support for Python modules + * and includes code for managing global variables and pointer + * type checking. + * + * Author : David Beazley (beazley@cs.uchicago.edu) + ************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * global variable support code. + * ----------------------------------------------------------------------------- */ + +typedef struct swig_globalvar { + char *name; /* Name of global variable */ + PyObject *(*get_attr)(); /* Return the current value */ + int (*set_attr)(PyObject *); /* Set the value */ + struct swig_globalvar *next; +} swig_globalvar; + +typedef struct swig_varlinkobject { + PyObject_HEAD + swig_globalvar *vars; +} swig_varlinkobject; + +static PyObject * +swig_varlink_repr(swig_varlinkobject *v) { + v = v; + return PyString_FromString(""); +} + +static int +swig_varlink_print(swig_varlinkobject *v, FILE *fp, int flags) { + swig_globalvar *var; + flags = flags; + fprintf(fp,"Global variables { "); + for (var = v->vars; var; var=var->next) { + fprintf(fp,"%s", var->name); + if (var->next) fprintf(fp,", "); + } + fprintf(fp," }\n"); + return 0; +} + +static PyObject * +swig_varlink_getattr(swig_varlinkobject *v, char *n) { + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + return (*var->get_attr)(); + } + var = var->next; + } + PyErr_SetString(PyExc_NameError,"Unknown C global variable"); + return NULL; +} + +static int +swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + return (*var->set_attr)(p); + } + var = var->next; + } + PyErr_SetString(PyExc_NameError,"Unknown C global variable"); + return 1; +} + +static PyTypeObject varlinktype = { + PyObject_HEAD_INIT(0) + 0, /* Number of items in variable part (ob_size) */ + (char *)"swigvarlink", /* Type name (tp_name) */ + sizeof(swig_varlinkobject), /* Basic size (tp_basicsize) */ + 0, /* Itemsize (tp_itemsize) */ + 0, /* Deallocator (tp_dealloc) */ + (printfunc) swig_varlink_print, /* Print (tp_print) */ + (getattrfunc) swig_varlink_getattr, /* get attr (tp_getattr) */ + (setattrfunc) swig_varlink_setattr, /* Set attr (tp_setattr) */ + 0, /* tp_compare */ + (reprfunc) swig_varlink_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ +#if PY_VERSION_HEX >= 0x02020000 + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#endif +#if PY_VERSION_HEX >= 0x02030000 + 0, /* tp_del */ +#endif +#ifdef COUNT_ALLOCS + /* these must be last */ + 0, /* tp_alloc */ + 0, /* tp_free */ + 0, /* tp_maxalloc */ + 0, /* tp_next */ +#endif +}; + +/* Create a variable linking object for use later */ +static PyObject * +SWIG_Python_newvarlink(void) { + swig_varlinkobject *result = 0; + result = PyMem_NEW(swig_varlinkobject,1); + varlinktype.ob_type = &PyType_Type; /* Patch varlinktype into a PyType */ + result->ob_type = &varlinktype; + result->vars = 0; + result->ob_refcnt = 0; + Py_XINCREF((PyObject *) result); + return ((PyObject*) result); +} + +static void +SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { + swig_varlinkobject *v; + swig_globalvar *gv; + v= (swig_varlinkobject *) p; + gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); + gv->name = (char *) malloc(strlen(name)+1); + strcpy(gv->name,name); + gv->get_attr = get_attr; + gv->set_attr = set_attr; + gv->next = v->vars; + v->vars = gv; +} + +/* ----------------------------------------------------------------------------- + * errors manipulation + * ----------------------------------------------------------------------------- */ + +static void +SWIG_Python_TypeError(const char *type, PyObject *obj) +{ + if (type) { + if (!PyCObject_Check(obj)) { + const char *otype = (obj ? obj->ob_type->tp_name : 0); + if (otype) { + PyObject *str = PyObject_Str(obj); + const char *cstr = str ? PyString_AsString(str) : 0; + if (cstr) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", + type, otype, cstr); + } else { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", + type, otype); + } + Py_DECREF(str); + return; + } + } else { + const char *otype = (char *) PyCObject_GetDesc(obj); + if (otype) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'PyCObject(%s)' is received", + type, otype); + return; + } + } + PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); + + } else { + PyErr_Format(PyExc_TypeError, "unexpected type is received"); + } +} + +static SWIGINLINE void +SWIG_Python_NullRef(const char *type) +{ + if (type) { + PyErr_Format(PyExc_TypeError, "null reference of type '%s' was received",type); + } else { + PyErr_Format(PyExc_TypeError, "null reference was received"); + } +} + +static int +SWIG_Python_AddErrMesg(const char* mesg, int infront) +{ + if (PyErr_Occurred()) { + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + PyErr_Fetch(&type, &value, &traceback); + if (value) { + PyObject *old_str = PyObject_Str(value); + Py_XINCREF(type); + PyErr_Clear(); + if (infront) { + PyErr_Format(type, "%s %s", mesg, PyString_AsString(old_str)); + } else { + PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg); + } + Py_DECREF(old_str); + } + return 1; + } else { + return 0; + } +} + +static int +SWIG_Python_ArgFail(int argnum) +{ + if (PyErr_Occurred()) { + /* add information about failing argument */ + char mesg[256]; + sprintf(mesg, "argument number %d:", argnum); + return SWIG_Python_AddErrMesg(mesg, 1); + } else { + return 0; + } +} + + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +/* Convert a pointer value */ +static int +SWIG_Python_ConvertPtr(PyObject *obj, void **ptr, swig_type_info *ty, int flags) { + swig_type_info *tc; + char *c = 0; + static PyObject *SWIG_this = 0; + int newref = 0; + PyObject *pyobj = 0; + void *vptr; + + if (!obj) return 0; + if (obj == Py_None) { + *ptr = 0; + return 0; + } + +#ifdef SWIG_COBJECT_TYPES + if (!(PyCObject_Check(obj))) { + if (!SWIG_this) + SWIG_this = PyString_FromString("this"); + pyobj = obj; + obj = PyObject_GetAttr(obj,SWIG_this); + newref = 1; + if (!obj) goto type_error; + if (!PyCObject_Check(obj)) { + Py_DECREF(obj); + goto type_error; + } + } + vptr = PyCObject_AsVoidPtr(obj); + c = (char *) PyCObject_GetDesc(obj); + if (newref) Py_DECREF(obj); + goto type_check; +#else + if (!(PyString_Check(obj))) { + if (!SWIG_this) + SWIG_this = PyString_FromString("this"); + pyobj = obj; + obj = PyObject_GetAttr(obj,SWIG_this); + newref = 1; + if (!obj) goto type_error; + if (!PyString_Check(obj)) { + Py_DECREF(obj); + goto type_error; + } + } + c = PyString_AS_STRING(obj); + /* Pointer values must start with leading underscore */ + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + if (newref) { Py_DECREF(obj); } + *ptr = (void *) 0; + return 0; + } else { + if (newref) { Py_DECREF(obj); } + goto type_error; + } + } + c++; + c = SWIG_UnpackData(c,&vptr,sizeof(void *)); + if (newref) { Py_DECREF(obj); } +#endif + +type_check: + + if (ty) { + tc = SWIG_TypeCheck(c,ty); + if (!tc) goto type_error; + *ptr = SWIG_TypeCast(tc,vptr); + } + + if ((pyobj) && (flags & SWIG_POINTER_DISOWN)) { + PyObject_SetAttrString(pyobj,(char*)"thisown",Py_False); + } + return 0; + +type_error: + PyErr_Clear(); + if (pyobj && !obj) { + obj = pyobj; + if (PyCFunction_Check(obj)) { + /* here we get the method pointer for callbacks */ + char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); + c = doc ? strstr(doc, "swig_ptr: ") : 0; + if (c) { + c += 10; + if (*c == '_') { + c++; + c = SWIG_UnpackData(c,&vptr,sizeof(void *)); + goto type_check; + } + } + } + } + if (flags & SWIG_POINTER_EXCEPTION) { + if (ty) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + } else { + SWIG_Python_TypeError("C/C++ pointer", obj); + } + } + return -1; +} + +/* Convert a pointer value, signal an exception on a type mismatch */ +static void * +SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int argnum, int flags) { + void *result; + if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { + PyErr_Clear(); + if (flags & SWIG_POINTER_EXCEPTION) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + SWIG_Python_ArgFail(argnum); + } + } + return result; +} + +/* Convert a packed value value */ +static int +SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty, int flags) { + swig_type_info *tc; + char *c = 0; + + if ((!obj) || (!PyString_Check(obj))) goto type_error; + c = PyString_AS_STRING(obj); + /* Pointer values must start with leading underscore */ + if (*c != '_') goto type_error; + c++; + c = SWIG_UnpackData(c,ptr,sz); + if (ty) { + tc = SWIG_TypeCheck(c,ty); + if (!tc) goto type_error; + } + return 0; + +type_error: + PyErr_Clear(); + if (flags & SWIG_POINTER_EXCEPTION) { + if (ty) { + SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); + } else { + SWIG_Python_TypeError("C/C++ packed data", obj); + } + } + return -1; +} + +/* Create a new pointer string */ +static char * +SWIG_Python_PointerStr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + + +/* Create a new pointer object */ +static PyObject * +SWIG_Python_NewPointerObj(void *ptr, swig_type_info *type, int own) { + PyObject *robj; + if (!ptr) { + Py_INCREF(Py_None); + return Py_None; + } +#ifdef SWIG_COBJECT_TYPES + robj = PyCObject_FromVoidPtrAndDesc((void *) ptr, (char *) type->name, NULL); +#else + { + char result[1024]; + SWIG_Python_PointerStr(result, ptr, type->name, 1024); + robj = PyString_FromString(result); + } +#endif + if (!robj || (robj == Py_None)) return robj; + if (type->clientdata) { + PyObject *inst; + PyObject *args = Py_BuildValue((char*)"(O)", robj); + Py_DECREF(robj); + inst = PyObject_CallObject((PyObject *) type->clientdata, args); + Py_DECREF(args); + if (inst) { + if (own) { + PyObject_SetAttrString(inst,(char*)"thisown",Py_True); + } + robj = inst; + } + } + return robj; +} + +static PyObject * +SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { + char result[1024]; + char *r = result; + if ((2*sz + 2 + strlen(type->name)) > 1024) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + strcpy(r,type->name); + return PyString_FromString(result); +} + + +/* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + +/* Install Constants */ +static void +SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { + int i; + PyObject *obj; + for (i = 0; constants[i].type; i++) { + switch(constants[i].type) { + case SWIG_PY_INT: + obj = PyInt_FromLong(constants[i].lvalue); + break; + case SWIG_PY_FLOAT: + obj = PyFloat_FromDouble(constants[i].dvalue); + break; + case SWIG_PY_STRING: + if (constants[i].pvalue) { + obj = PyString_FromString((char *) constants[i].pvalue); + } else { + Py_INCREF(Py_None); + obj = Py_None; + } + break; + case SWIG_PY_POINTER: + obj = SWIG_NewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_PY_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + PyDict_SetItemString(d,constants[i].name,obj); + Py_DECREF(obj); + } + } +} + +/* Fix SwigMethods to carry the callback ptrs when needed */ +static void +SWIG_Python_FixMethods(PyMethodDef *methods, + swig_const_info *const_table, + swig_type_info **types, + swig_type_info **types_initial) { + int i; + for (i = 0; methods[i].ml_name; ++i) { + char *c = methods[i].ml_doc; + if (c && (c = strstr(c, "swig_ptr: "))) { + int j; + swig_const_info *ci = 0; + char *name = c + 10; + for (j = 0; const_table[j].type; j++) { + if (strncmp(const_table[j].name, name, + strlen(const_table[j].name)) == 0) { + ci = &(const_table[j]); + break; + } + } + if (ci) { + size_t shift = (ci->ptype) - types; + swig_type_info *ty = types_initial[shift]; + size_t ldoc = (c - methods[i].ml_doc); + size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; + char *ndoc = (char*)malloc(ldoc + lptr + 10); + char *buff = ndoc; + void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue: (void *)(ci->lvalue); + strncpy(buff, methods[i].ml_doc, ldoc); + buff += ldoc; + strncpy(buff, "swig_ptr: ", 10); + buff += 10; + SWIG_Python_PointerStr(buff, ptr, ty->name, lptr); + methods[i].ml_doc = ndoc; + } + } + } +} + + +/* ----------------------------------------------------------------------------- + * Lookup type pointer + * ----------------------------------------------------------------------------- */ +#if PY_MAJOR_VERSION < 2 +/* PyModule_AddObject function was introduced in Python 2.0. The following function +is copied out of Python/modsupport.c in python version 2.3.4 */ +static int +PyModule_AddObject(PyObject *m, char *name, PyObject *o) +{ + PyObject *dict; + if (!PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs module as first arg"); + return -1; + } + if (!o) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs non-NULL value"); + return -1; + } + + dict = PyModule_GetDict(m); + if (dict == NULL) { + /* Internal error -- modules must have a dict! */ + PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", + PyModule_GetName(m)); + return -1; + } + if (PyDict_SetItemString(dict, name, o)) + return -1; + Py_DECREF(o); + return 0; +} +#endif +static PyMethodDef swig_empty_runtime_method_table[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +static void +SWIG_Python_LookupTypePointer(swig_type_info ***type_list_handle) { + PyObject *module, *pointer; + void *type_pointer; + + /* first check if module already created */ + type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); + if (type_pointer) { + *type_list_handle = (swig_type_info **) type_pointer; + } else { + PyErr_Clear(); + /* create a new module and variable */ + module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); + pointer = PyCObject_FromVoidPtr((void *) (*type_list_handle), NULL); + if (pointer && module) { + PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); + } + } +} + +#ifdef __cplusplus +} +#endif + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_Inkscape__Extension__Script__Desktop swig_types[0] +#define SWIGTYPE_p_Inkscape__Extension__Script__Inkscape swig_types[1] +#define SWIGTYPE_p_char swig_types[2] +#define SWIGTYPE_p_Inkscape__Extension__Script__DialogManager swig_types[3] +#define SWIGTYPE_size_t swig_types[4] +#define SWIGTYPE_std__size_t swig_types[5] +#define SWIGTYPE_p_Inkscape__Extension__Script__Document swig_types[6] +#define SWIGTYPE_ptrdiff_t swig_types[7] +#define SWIGTYPE_std__ptrdiff_t swig_types[8] +static swig_type_info *swig_types[10]; + +/* -------- TYPES TABLE (END) -------- */ + + +/*----------------------------------------------- + @(target):= _inkscape_py.so + ------------------------------------------------*/ +#define SWIG_init init_inkscape_py + +#define SWIG_name "_inkscape_py" + +#include "InkscapeBinding.h" + +#ifdef __cplusplus +extern "C" { +#endif +static PyObject *_wrap_getInkscape(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Inkscape *result; + + if(!PyArg_ParseTuple(args,(char *)":getInkscape")) goto fail; + result = (Inkscape::Extension::Script::Inkscape *)Inkscape::Extension::Script::getInkscape(); + + resultobj = SWIG_NewPointerObj((void*)(result), SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, 0); + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_delete_Inkscape(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:delete_Inkscape",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + delete arg1; + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_Inkscape_getDesktop(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + Inkscape::Extension::Script::Desktop *result; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:Inkscape_getDesktop",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + result = (Inkscape::Extension::Script::Desktop *)(arg1)->getDesktop(); + + resultobj = SWIG_NewPointerObj((void*)(result), SWIGTYPE_p_Inkscape__Extension__Script__Desktop, 0); + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_Inkscape_getDialogManager(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Inkscape *arg1 = (Inkscape::Extension::Script::Inkscape *) 0 ; + Inkscape::Extension::Script::DialogManager *result; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:Inkscape_getDialogManager",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + result = (Inkscape::Extension::Script::DialogManager *)(arg1)->getDialogManager(); + + resultobj = SWIG_NewPointerObj((void*)(result), SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, 0); + return resultobj; + fail: + return NULL; +} + + +static PyObject * Inkscape_swigregister(PyObject *, PyObject *args) { + PyObject *obj; + if (!PyArg_ParseTuple(args,(char*)"O", &obj)) return NULL; + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Inkscape, obj); + Py_INCREF(obj); + return Py_BuildValue((char *)""); +} +static PyObject *_wrap_delete_DialogManager(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::DialogManager *arg1 = (Inkscape::Extension::Script::DialogManager *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:delete_DialogManager",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + delete arg1; + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_DialogManager_showAbout(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::DialogManager *arg1 = (Inkscape::Extension::Script::DialogManager *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:DialogManager_showAbout",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + (arg1)->showAbout(); + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject * DialogManager_swigregister(PyObject *, PyObject *args) { + PyObject *obj; + if (!PyArg_ParseTuple(args,(char*)"O", &obj)) return NULL; + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__DialogManager, obj); + Py_INCREF(obj); + return Py_BuildValue((char *)""); +} +static PyObject *_wrap_delete_Desktop(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Desktop *arg1 = (Inkscape::Extension::Script::Desktop *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:delete_Desktop",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Desktop, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + delete arg1; + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_Desktop_getDocument(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Desktop *arg1 = (Inkscape::Extension::Script::Desktop *) 0 ; + Inkscape::Extension::Script::Document *result; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:Desktop_getDocument",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Desktop, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + result = (Inkscape::Extension::Script::Document *)(arg1)->getDocument(); + + resultobj = SWIG_NewPointerObj((void*)(result), SWIGTYPE_p_Inkscape__Extension__Script__Document, 0); + return resultobj; + fail: + return NULL; +} + + +static PyObject * Desktop_swigregister(PyObject *, PyObject *args) { + PyObject *obj; + if (!PyArg_ParseTuple(args,(char*)"O", &obj)) return NULL; + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Desktop, obj); + Py_INCREF(obj); + return Py_BuildValue((char *)""); +} +static PyObject *_wrap_delete_Document(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Document *arg1 = (Inkscape::Extension::Script::Document *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:delete_Document",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Document, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + delete arg1; + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_Document_hello(PyObject *, PyObject *args) { + PyObject *resultobj; + Inkscape::Extension::Script::Document *arg1 = (Inkscape::Extension::Script::Document *) 0 ; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:Document_hello",&obj0)) goto fail; + SWIG_Python_ConvertPtr(obj0, (void **)&arg1, SWIGTYPE_p_Inkscape__Extension__Script__Document, SWIG_POINTER_EXCEPTION | 0); + if (SWIG_arg_fail(1)) SWIG_fail; + (arg1)->hello(); + + Py_INCREF(Py_None); resultobj = Py_None; + return resultobj; + fail: + return NULL; +} + + +static PyObject * Document_swigregister(PyObject *, PyObject *args) { + PyObject *obj; + if (!PyArg_ParseTuple(args,(char*)"O", &obj)) return NULL; + SWIG_TypeClientData(SWIGTYPE_p_Inkscape__Extension__Script__Document, obj); + Py_INCREF(obj); + return Py_BuildValue((char *)""); +} +static PyMethodDef SwigMethods[] = { + { (char *)"getInkscape", _wrap_getInkscape, METH_VARARGS, NULL}, + { (char *)"delete_Inkscape", _wrap_delete_Inkscape, METH_VARARGS, NULL}, + { (char *)"Inkscape_getDesktop", _wrap_Inkscape_getDesktop, METH_VARARGS, NULL}, + { (char *)"Inkscape_getDialogManager", _wrap_Inkscape_getDialogManager, METH_VARARGS, NULL}, + { (char *)"Inkscape_swigregister", Inkscape_swigregister, METH_VARARGS, NULL}, + { (char *)"delete_DialogManager", _wrap_delete_DialogManager, METH_VARARGS, NULL}, + { (char *)"DialogManager_showAbout", _wrap_DialogManager_showAbout, METH_VARARGS, NULL}, + { (char *)"DialogManager_swigregister", DialogManager_swigregister, METH_VARARGS, NULL}, + { (char *)"delete_Desktop", _wrap_delete_Desktop, METH_VARARGS, NULL}, + { (char *)"Desktop_getDocument", _wrap_Desktop_getDocument, METH_VARARGS, NULL}, + { (char *)"Desktop_swigregister", Desktop_swigregister, METH_VARARGS, NULL}, + { (char *)"delete_Document", _wrap_delete_Document, METH_VARARGS, NULL}, + { (char *)"Document_hello", _wrap_Document_hello, METH_VARARGS, NULL}, + { (char *)"Document_swigregister", Document_swigregister, METH_VARARGS, NULL}, + { NULL, NULL, 0, NULL } +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_Inkscape__Extension__Script__Desktop[] = {{"_p_Inkscape__Extension__Script__Desktop", 0, "Inkscape::Extension::Script::Desktop *", 0, 0, 0, 0},{"_p_Inkscape__Extension__Script__Desktop", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__Inkscape[] = {{"_p_Inkscape__Extension__Script__Inkscape", 0, "Inkscape::Extension::Script::Inkscape *", 0, 0, 0, 0},{"_p_Inkscape__Extension__Script__Inkscape", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_char[] = {{"_p_char", 0, "char *", 0, 0, 0, 0},{"_p_char", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__DialogManager[] = {{"_p_Inkscape__Extension__Script__DialogManager", 0, "Inkscape::Extension::Script::DialogManager *", 0, 0, 0, 0},{"_p_Inkscape__Extension__Script__DialogManager", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__size_t[] = {{"_size_t", 0, "size_t", 0, 0, 0, 0},{"_size_t", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__std__size_t[] = {{"_std__size_t", 0, "std::size_t", 0, 0, 0, 0},{"_std__size_t", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__p_Inkscape__Extension__Script__Document[] = {{"_p_Inkscape__Extension__Script__Document", 0, "Inkscape::Extension::Script::Document *", 0, 0, 0, 0},{"_p_Inkscape__Extension__Script__Document", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__ptrdiff_t[] = {{"_ptrdiff_t", 0, "ptrdiff_t", 0, 0, 0, 0},{"_ptrdiff_t", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; +static swig_type_info _swigt__std__ptrdiff_t[] = {{"_std__ptrdiff_t", 0, "std::ptrdiff_t", 0, 0, 0, 0},{"_std__ptrdiff_t", 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 0, 0}}; + +static swig_type_info *swig_types_initial[] = { +_swigt__p_Inkscape__Extension__Script__Desktop, +_swigt__p_Inkscape__Extension__Script__Inkscape, +_swigt__p_char, +_swigt__p_Inkscape__Extension__Script__DialogManager, +_swigt__size_t, +_swigt__std__size_t, +_swigt__p_Inkscape__Extension__Script__Document, +_swigt__ptrdiff_t, +_swigt__std__ptrdiff_t, +0 +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_const_info swig_const_table[] = { +{0, 0, 0, 0.0, 0, 0}}; + +#ifdef __cplusplus +} +#endif + + +#ifdef SWIG_LINK_RUNTIME +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(_MSC_VER) || defined(__GNUC__) +# define SWIGIMPORT(a) extern a +# else +# if defined(__BORLANDC__) +# define SWIGIMPORT(a) a _export +# else +# define SWIGIMPORT(a) a +# endif +# endif +#else +# define SWIGIMPORT(a) a +#endif +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT(void *) SWIG_ReturnGlobalTypeList(void *); +#endif + +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT(void) SWIG_init(void) { + static PyObject *SWIG_globals = 0; + static int typeinit = 0; + PyObject *m, *d; + int i; + if (!SWIG_globals) SWIG_globals = SWIG_newvarlink(); + + /* Fix SwigMethods to carry the callback ptrs when needed */ + SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_types_initial); + + m = Py_InitModule((char *) SWIG_name, SwigMethods); + d = PyModule_GetDict(m); + + if (!typeinit) { +#ifdef SWIG_LINK_RUNTIME + swig_type_list_handle = (swig_type_info **) SWIG_ReturnGlobalTypeList(swig_type_list_handle); +#else +# ifndef SWIG_STATIC_RUNTIME + SWIG_Python_LookupTypePointer(&swig_type_list_handle); +# endif +#endif + for (i = 0; swig_types_initial[i]; i++) { + swig_types[i] = SWIG_TypeRegister(swig_types_initial[i]); + } + typeinit = 1; + } + SWIG_InstallConstants(d,swig_const_table); + +} + diff --git a/src/extension/script/js/Makefile b/src/extension/script/js/Makefile new file mode 100644 index 000000000..bd7ee7360 --- /dev/null +++ b/src/extension/script/js/Makefile @@ -0,0 +1,1471 @@ +# ################################# +# Unix/Linux makefile for libjs.a +# ################################# + + +CC=gcc +CFLAGS= -O2 -DXP_UNIX + +all: js + +INC += -Ifdlibm + +OBJ = \ + fdlibm/e_acos.o \ + fdlibm/e_acosh.o \ + fdlibm/e_asin.o \ + fdlibm/e_atan2.o \ + fdlibm/e_atanh.o \ + fdlibm/e_cosh.o \ + fdlibm/e_exp.o \ + fdlibm/e_fmod.o \ + fdlibm/e_gamma.o \ + fdlibm/e_gamma_r.o \ + fdlibm/e_hypot.o \ + fdlibm/e_j0.o \ + fdlibm/e_j1.o \ + fdlibm/e_jn.o \ + fdlibm/e_lgamma.o \ + fdlibm/e_lgamma_r.o \ + fdlibm/e_log.o \ + fdlibm/e_log10.o \ + fdlibm/e_pow.o \ + fdlibm/e_rem_pio2.o \ + fdlibm/e_remainder.o \ + fdlibm/e_scalb.o \ + fdlibm/e_sinh.o \ + fdlibm/e_sqrt.o \ + fdlibm/k_cos.o \ + fdlibm/k_rem_pio2.o \ + fdlibm/k_sin.o \ + fdlibm/k_standard.o \ + fdlibm/k_tan.o \ + fdlibm/s_asinh.o \ + fdlibm/s_atan.o \ + fdlibm/s_cbrt.o \ + fdlibm/s_ceil.o \ + fdlibm/s_copysign.o \ + fdlibm/s_cos.o \ + fdlibm/s_erf.o \ + fdlibm/s_expm1.o \ + fdlibm/s_fabs.o \ + fdlibm/s_finite.o \ + fdlibm/s_floor.o \ + fdlibm/s_frexp.o \ + fdlibm/s_ilogb.o \ + fdlibm/s_isnan.o \ + fdlibm/s_ldexp.o \ + fdlibm/s_lib_version.o \ + fdlibm/s_log1p.o \ + fdlibm/s_logb.o \ + fdlibm/s_matherr.o \ + fdlibm/s_modf.o \ + fdlibm/s_nextafter.o \ + fdlibm/s_rint.o \ + fdlibm/s_scalbn.o \ + fdlibm/s_signgam.o \ + fdlibm/s_significand.o \ + fdlibm/s_sin.o \ + fdlibm/s_tan.o \ + fdlibm/s_tanh.o \ + fdlibm/w_acos.o \ + fdlibm/w_acosh.o \ + fdlibm/w_asin.o \ + fdlibm/w_atan2.o \ + fdlibm/w_atanh.o \ + fdlibm/w_cosh.o \ + fdlibm/w_exp.o \ + fdlibm/w_fmod.o \ + fdlibm/w_gamma.o \ + fdlibm/w_gamma_r.o \ + fdlibm/w_hypot.o \ + fdlibm/w_j0.o \ + fdlibm/w_j1.o \ + fdlibm/w_jn.o \ + fdlibm/w_lgamma.o \ + fdlibm/w_lgamma_r.o \ + fdlibm/w_log.o \ + fdlibm/w_log10.o \ + fdlibm/w_pow.o \ + fdlibm/w_remainder.o \ + fdlibm/w_scalb.o \ + fdlibm/w_sinh.o \ + fdlibm/w_sqrt.o \ + jsapi.o \ + jsarena.o \ + jsarray.o \ + jsatom.o \ + jsbool.o \ + jscntxt.o \ + jscpucfg.o \ + jsdate.o \ + jsdbgapi.o \ + jsdhash.o \ + jsdtoa.o \ + jsemit.o \ + jsexn.o \ + jsfile.o \ + jsfun.o \ + jsgc.o \ + jshash.o \ + jsinterp.o \ + jslock.o \ + jslog2.o \ + jslong.o \ + jsmath.o \ + jsnum.o \ + jsobj.o \ + jsopcode.o \ + jsparse.o \ + jsprf.o \ + jsregexp.o \ + jsscan.o \ + jsscope.o \ + jsscript.o \ + jsstr.o \ + jsutil.o \ + jsxdrapi.o \ + prmjtime.o + +js: js.o libjs.a + $(CC) -o js js.o libjs.a -lm + +libjs.a: $(OBJ) + ar crv libjs.a $(OBJ) + ranlib libjs.a + +clean: + $(RM) js libjs.a $(OBJ) + +# ########################### +# DEPENDENCIES +# ########################### + + +fdlibm/e_acos.o: \ + fdlibm/e_acos.c \ + fdlibm/fdlibm.h + +fdlibm/e_acosh.o: \ + fdlibm/e_acosh.c \ + fdlibm/fdlibm.h + +fdlibm/e_asin.o: \ + fdlibm/e_asin.c \ + fdlibm/fdlibm.h + +fdlibm/e_atan2.o: \ + fdlibm/e_atan2.c \ + fdlibm/fdlibm.h + +fdlibm/e_atanh.o: \ + fdlibm/e_atanh.c \ + fdlibm/fdlibm.h + +fdlibm/e_cosh.o: \ + fdlibm/e_cosh.c \ + fdlibm/fdlibm.h + +fdlibm/e_exp.o: \ + fdlibm/e_exp.c \ + fdlibm/fdlibm.h + +fdlibm/e_fmod.o: \ + fdlibm/e_fmod.c \ + fdlibm/fdlibm.h + +fdlibm/e_gamma.o: \ + fdlibm/e_gamma.c \ + fdlibm/fdlibm.h + +fdlibm/e_gamma_r.o: \ + fdlibm/e_gamma_r.c \ + fdlibm/fdlibm.h + +fdlibm/e_hypot.o: \ + fdlibm/e_hypot.c \ + fdlibm/fdlibm.h + +fdlibm/e_j0.o: \ + fdlibm/e_j0.c \ + fdlibm/fdlibm.h + +fdlibm/e_j1.o: \ + fdlibm/e_j1.c \ + fdlibm/fdlibm.h + +fdlibm/e_jn.o: \ + fdlibm/e_jn.c \ + fdlibm/fdlibm.h + +fdlibm/e_lgamma.o: \ + fdlibm/e_lgamma.c \ + fdlibm/fdlibm.h + +fdlibm/e_lgamma_r.o: \ + fdlibm/e_lgamma_r.c \ + fdlibm/fdlibm.h + +fdlibm/e_log.o: \ + fdlibm/e_log.c \ + fdlibm/fdlibm.h + +fdlibm/e_log10.o: \ + fdlibm/e_log10.c \ + fdlibm/fdlibm.h + +fdlibm/e_pow.o: \ + fdlibm/e_pow.c \ + fdlibm/fdlibm.h + +fdlibm/e_rem_pio2.o: \ + fdlibm/e_rem_pio2.c \ + fdlibm/fdlibm.h + +fdlibm/e_remainder.o: \ + fdlibm/e_remainder.c \ + fdlibm/fdlibm.h + +fdlibm/e_scalb.o: \ + fdlibm/e_scalb.c \ + fdlibm/fdlibm.h + +fdlibm/e_sinh.o: \ + fdlibm/e_sinh.c \ + fdlibm/fdlibm.h + +fdlibm/e_sqrt.o: \ + fdlibm/e_sqrt.c \ + fdlibm/fdlibm.h + +fdlibm/k_cos.o: \ + fdlibm/k_cos.c \ + fdlibm/fdlibm.h + +fdlibm/k_rem_pio2.o: \ + fdlibm/k_rem_pio2.c \ + fdlibm/fdlibm.h + +fdlibm/k_sin.o: \ + fdlibm/k_sin.c \ + fdlibm/fdlibm.h + +fdlibm/k_standard.o: \ + fdlibm/k_standard.c \ + fdlibm/fdlibm.h + +fdlibm/k_tan.o: \ + fdlibm/k_tan.c \ + fdlibm/fdlibm.h + +fdlibm/s_asinh.o: \ + fdlibm/s_asinh.c \ + fdlibm/fdlibm.h + +fdlibm/s_atan.o: \ + fdlibm/s_atan.c \ + fdlibm/fdlibm.h + +fdlibm/s_cbrt.o: \ + fdlibm/s_cbrt.c \ + fdlibm/fdlibm.h + +fdlibm/s_ceil.o: \ + fdlibm/s_ceil.c \ + fdlibm/fdlibm.h + +fdlibm/s_copysign.o: \ + fdlibm/s_copysign.c \ + fdlibm/fdlibm.h + +fdlibm/s_cos.o: \ + fdlibm/s_cos.c \ + fdlibm/fdlibm.h + +fdlibm/s_erf.o: \ + fdlibm/s_erf.c \ + fdlibm/fdlibm.h + +fdlibm/s_expm1.o: \ + fdlibm/s_expm1.c \ + fdlibm/fdlibm.h + +fdlibm/s_fabs.o: \ + fdlibm/s_fabs.c \ + fdlibm/fdlibm.h + +fdlibm/s_finite.o: \ + fdlibm/s_finite.c \ + fdlibm/fdlibm.h + +fdlibm/s_floor.o: \ + fdlibm/s_floor.c \ + fdlibm/fdlibm.h + +fdlibm/s_frexp.o: \ + fdlibm/s_frexp.c \ + fdlibm/fdlibm.h + +fdlibm/s_ilogb.o: \ + fdlibm/s_ilogb.c \ + fdlibm/fdlibm.h + +fdlibm/s_isnan.o: \ + fdlibm/s_isnan.c \ + fdlibm/fdlibm.h + +fdlibm/s_ldexp.o: \ + fdlibm/s_ldexp.c \ + fdlibm/fdlibm.h + +fdlibm/s_lib_version.o: \ + fdlibm/s_lib_version.c \ + fdlibm/fdlibm.h + +fdlibm/s_log1p.o: \ + fdlibm/s_log1p.c \ + fdlibm/fdlibm.h + +fdlibm/s_logb.o: \ + fdlibm/s_logb.c \ + fdlibm/fdlibm.h + +fdlibm/s_matherr.o: \ + fdlibm/s_matherr.c \ + fdlibm/fdlibm.h + +fdlibm/s_modf.o: \ + fdlibm/s_modf.c \ + fdlibm/fdlibm.h + +fdlibm/s_nextafter.o: \ + fdlibm/s_nextafter.c \ + fdlibm/fdlibm.h + +fdlibm/s_rint.o: \ + fdlibm/s_rint.c \ + fdlibm/fdlibm.h + +fdlibm/s_scalbn.o: \ + fdlibm/s_scalbn.c \ + fdlibm/fdlibm.h + +fdlibm/s_signgam.o: \ + fdlibm/s_signgam.c \ + fdlibm/fdlibm.h + +fdlibm/s_significand.o: \ + fdlibm/s_significand.c \ + fdlibm/fdlibm.h + +fdlibm/s_sin.o: \ + fdlibm/s_sin.c \ + fdlibm/fdlibm.h + +fdlibm/s_tan.o: \ + fdlibm/s_tan.c \ + fdlibm/fdlibm.h + +fdlibm/s_tanh.o: \ + fdlibm/s_tanh.c \ + fdlibm/fdlibm.h + +fdlibm/w_acos.o: \ + fdlibm/w_acos.c \ + fdlibm/fdlibm.h + +fdlibm/w_acosh.o: \ + fdlibm/w_acosh.c \ + fdlibm/fdlibm.h + +fdlibm/w_asin.o: \ + fdlibm/w_asin.c \ + fdlibm/fdlibm.h + +fdlibm/w_atan2.o: \ + fdlibm/w_atan2.c \ + fdlibm/fdlibm.h + +fdlibm/w_atanh.o: \ + fdlibm/w_atanh.c \ + fdlibm/fdlibm.h + +fdlibm/w_cosh.o: \ + fdlibm/w_cosh.c \ + fdlibm/fdlibm.h + +fdlibm/w_exp.o: \ + fdlibm/w_exp.c \ + fdlibm/fdlibm.h + +fdlibm/w_fmod.o: \ + fdlibm/w_fmod.c \ + fdlibm/fdlibm.h + +fdlibm/w_gamma.o: \ + fdlibm/w_gamma.c \ + fdlibm/fdlibm.h + +fdlibm/w_gamma_r.o: \ + fdlibm/w_gamma_r.c \ + fdlibm/fdlibm.h + +fdlibm/w_hypot.o: \ + fdlibm/w_hypot.c \ + fdlibm/fdlibm.h + +fdlibm/w_j0.o: \ + fdlibm/w_j0.c \ + fdlibm/fdlibm.h + +fdlibm/w_j1.o: \ + fdlibm/w_j1.c \ + fdlibm/fdlibm.h + +fdlibm/w_jn.o: \ + fdlibm/w_jn.c \ + fdlibm/fdlibm.h + +fdlibm/w_lgamma.o: \ + fdlibm/w_lgamma.c \ + fdlibm/fdlibm.h + +fdlibm/w_lgamma_r.o: \ + fdlibm/w_lgamma_r.c \ + fdlibm/fdlibm.h + +fdlibm/w_log.o: \ + fdlibm/w_log.c \ + fdlibm/fdlibm.h + +fdlibm/w_log10.o: \ + fdlibm/w_log10.c \ + fdlibm/fdlibm.h + +fdlibm/w_pow.o: \ + fdlibm/w_pow.c \ + fdlibm/fdlibm.h + +fdlibm/w_remainder.o: \ + fdlibm/w_remainder.c \ + fdlibm/fdlibm.h + +fdlibm/w_scalb.o: \ + fdlibm/w_scalb.c \ + fdlibm/fdlibm.h + +fdlibm/w_sinh.o: \ + fdlibm/w_sinh.c \ + fdlibm/fdlibm.h + +fdlibm/w_sqrt.o: \ + fdlibm/w_sqrt.c \ + fdlibm/fdlibm.h + +js.o: \ + js.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsapi.o: \ + jsapi.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdate.h \ + jsdhash.h \ + jsdtoa.h \ + jsemit.h \ + jsexn.h \ + jsfile.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsmath.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + prmjtime.h + +jsarena.o: \ + jsarena.c \ + jsarena.h \ + jsbit.h \ + jscompat.h \ + jscpucfg.h \ + jshash.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsscope.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h + +jsarray.o: \ + jsarray.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsatom.o: \ + jsatom.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsbool.o: \ + jsbool.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jscntxt.o: \ + jscntxt.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsexn.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jscpucfg.o: \ + jscpucfg.c + +jsdate.o: \ + jsdate.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdate.h \ + jsdhash.h \ + jsdtoa.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + prmjtime.h + +jsdbgapi.o: \ + jsdbgapi.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsdhash.o: \ + jsdhash.c \ + jsbit.h \ + jscpucfg.h \ + jsdhash.h \ + jsosdep.h \ + jsotypes.h \ + jstypes.h \ + jsutil.h + +jsdtoa.o: \ + jsdtoa.c \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdtoa.h \ + jslibmath.h \ + jslong.h \ + jsnum.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jspubtd.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h + +jsemit.o: \ + jsemit.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsbit.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsexn.o: \ + jsexn.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsbit.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsexn.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsfile.o: \ + jsfile.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdate.h \ + jsdbgapi.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsfun.o: \ + jsfun.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsbit.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsexn.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + jsxdrapi.h + +jsgc.o: \ + jsgc.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jshash.o: \ + jshash.c \ + jsbit.h \ + jscompat.h \ + jscpucfg.h \ + jshash.h \ + jslong.h \ + jsosdep.h \ + jsotypes.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h + +jsinterp.o: \ + jsinterp.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jslock.o: \ + jslock.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsbit.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsdtoa.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jslog2.o: \ + jslog2.c \ + jsbit.h \ + jscpucfg.h \ + jsosdep.h \ + jsotypes.h \ + jsstddef.h \ + jstypes.h + +jslong.o: \ + jslong.c \ + jscpucfg.h \ + jslong.h \ + jsosdep.h \ + jsotypes.h \ + jsstddef.h \ + jstypes.h + +jsmath.o: \ + jsmath.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslibmath.h \ + jslock.h \ + jslong.h \ + jsmath.h \ + jsnum.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + prmjtime.h + +jsnum.o: \ + jsnum.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsdtoa.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsobj.o: \ + jsobj.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + jsxdrapi.h + +jsopcode.o: \ + jsopcode.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsdtoa.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsparse.o: \ + jsparse.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsparse.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsprf.o: \ + jsprf.c \ + jscpucfg.h \ + jslong.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h + +jsregexp.o: \ + jsregexp.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + jsxdrapi.h + +jsscan.o: \ + jsscan.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsdtoa.h \ + jsemit.h \ + jsexn.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscan.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsscope.o: \ + jsscope.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsbit.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsscript.o: \ + jsscript.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdbgapi.h \ + jsdhash.h \ + jsemit.h \ + jsfun.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + jsxdrapi.h + +jsstr.o: \ + jsstr.c \ + jsapi.h \ + jsarena.h \ + jsarray.h \ + jsatom.h \ + jsbool.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsnum.h \ + jsobj.h \ + jsopcode.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h + +jsutil.o: \ + jsutil.c \ + jscpucfg.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h + +jsxdrapi.o: \ + jsxdrapi.c \ + jsapi.h \ + jsarena.h \ + jsatom.h \ + jsclist.h \ + jscntxt.h \ + jscompat.h \ + jsconfig.h \ + jscpucfg.h \ + jsdhash.h \ + jsgc.h \ + jshash.h \ + jsinterp.h \ + jslock.h \ + jslong.h \ + jsobj.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsprvtd.h \ + jspubtd.h \ + jsregexp.h \ + jsscope.h \ + jsscript.h \ + jsstddef.h \ + jsstr.h \ + jstypes.h \ + jsutil.h \ + jsxdrapi.h + +prmjtime.o: \ + prmjtime.c \ + jscompat.h \ + jscpucfg.h \ + jslong.h \ + jsosdep.h \ + jsotypes.h \ + jsprf.h \ + jsstddef.h \ + jstypes.h \ + jsutil.h \ + prmjtime.h + diff --git a/src/extension/script/js/README b/src/extension/script/js/README new file mode 100644 index 000000000..cca453245 --- /dev/null +++ b/src/extension/script/js/README @@ -0,0 +1,20 @@ +//######################################################################### +//# $Id$ +//######################################################################### +See: + +$PROJECT/src/ecma/README + +for more information. + +The source is unchanged from Mozilla.org's distribution with +the minor difference: + +Line 142 in jstypes.h: +#if defined(_WIN32) && !defined(__MWERKS__) +is modified to +#if defined(_WIN32) && !defined(__MWERKS__) && !defined(__GNUC__) +to allow for static linking in Mingw on Win32. + + + diff --git a/src/extension/script/js/fdlibm/e_acos.c b/src/extension/script/js/fdlibm/e_acos.c new file mode 100644 index 000000000..a07c1eebc --- /dev/null +++ b/src/extension/script/js/fdlibm/e_acos.c @@ -0,0 +1,147 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_acos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_acos(x) + * Method : + * acos(x) = pi/2 - asin(x) + * acos(-x) = pi/2 + asin(x) + * For |x|<=0.5 + * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c) + * For x>0.5 + * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) + * = 2asin(sqrt((1-x)/2)) + * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) + * = 2f + (2c + 2s*z*R(z)) + * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term + * for f so that f+c ~ sqrt(z). + * For x<-0.5 + * acos(x) = pi - 2asin(sqrt((1-|x|)/2)) + * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + * Function needed: sqrt + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one= 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ +pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +#ifdef __STDC__ + double __ieee754_acos(double x) +#else + double __ieee754_acos(x) + double x; +#endif +{ + fd_twoints u; + double df; + double z,p,q,r,w,s,c; + int hx,ix; + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + if(ix>=0x3ff00000) { /* |x| >= 1 */ + if(((ix-0x3ff00000)|__LO(u))==0) { /* |x|==1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3fe00000) { /* |x| < 0.5 */ + if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ + z = x*x; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + return pio2_hi - (x - (pio2_lo-x*r)); + } else if (hx<0) { /* x < -0.5 */ + z = (one+x)*0.5; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + s = fd_sqrt(z); + r = p/q; + w = r*s-pio2_lo; + return pi - 2.0*(s+w); + } else { /* x > 0.5 */ + z = (one-x)*0.5; + s = fd_sqrt(z); + u.d = s; + __LO(u) = 0; + df = u.d; + c = (z-df*df)/(s+df); + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + w = r*s+c; + return 2.0*(df+w); + } +} diff --git a/src/extension/script/js/fdlibm/e_acosh.c b/src/extension/script/js/fdlibm/e_acosh.c new file mode 100644 index 000000000..725cceefb --- /dev/null +++ b/src/extension/script/js/fdlibm/e_acosh.c @@ -0,0 +1,105 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_acosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_acosh(x) + * Method : + * Based on + * acosh(x) = log [ x + sqrt(x*x-1) ] + * we have + * acosh(x) := log(x)+ln2, if x is large; else + * acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else + * acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1. + * + * Special cases: + * acosh(x) is NaN with signal if x<1. + * acosh(NaN) is NaN without signal. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.0, +ln2 = 6.93147180559945286227e-01; /* 0x3FE62E42, 0xFEFA39EF */ + +#ifdef __STDC__ + double __ieee754_acosh(double x) +#else + double __ieee754_acosh(x) + double x; +#endif +{ + fd_twoints u; + double t; + int hx; + u.d = x; + hx = __HI(u); + if(hx<0x3ff00000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x41b00000) { /* x > 2**28 */ + if(hx >=0x7ff00000) { /* x is inf of NaN */ + return x+x; + } else + return __ieee754_log(x)+ln2; /* acosh(huge)=log(2x) */ + } else if(((hx-0x3ff00000)|__LO(u))==0) { + return 0.0; /* acosh(1) = 0 */ + } else if (hx > 0x40000000) { /* 2**28 > x > 2 */ + t=x*x; + return __ieee754_log(2.0*x-one/(x+fd_sqrt(t-one))); + } else { /* 10.98 + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + * For x<=0.98, let pio4_hi = pio2_hi/2, then + * f = hi part of s; + * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + * and + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + */ + + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +really_big = 1.000e+300, +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ +pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ +pio4_hi = 7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */ + /* coefficient for R(x^2) */ +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +#ifdef __STDC__ + double __ieee754_asin(double x) +#else + double __ieee754_asin(x) + double x; +#endif +{ + fd_twoints u; + double w,t,p,q,c,r,s; + int hx,ix; + u.d = x; + hx = __HI(u); + x = u.d; + ix = hx&0x7fffffff; + if(ix>= 0x3ff00000) { /* |x|>= 1 */ + if(((ix-0x3ff00000)|__LO(u))==0) + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3fe00000) { /* |x|<0.5 */ + if(ix<0x3e400000) { /* if |x| < 2**-27 */ + if(really_big+x>one) return x;/* return x with inexact if x!=0*/ + } else + t = x*x; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fd_fabs(x); + t = w*0.5; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + s = fd_sqrt(t); + if(ix>=0x3FEF3333) { /* if |x| > 0.975 */ + w = p/q; + t = pio2_hi-(2.0*(s+s*w)-pio2_lo); + } else { + u.d = s; + __LO(u) = 0; + w = u.d; + c = (t-w*w)/(s+w); + r = p/q; + p = 2.0*s*r-(pio2_lo-2.0*c); + q = pio4_hi-2.0*w; + t = pio4_hi-(p-q); + } + if(hx>0) return t; else return -t; +} diff --git a/src/extension/script/js/fdlibm/e_atan2.c b/src/extension/script/js/fdlibm/e_atan2.c new file mode 100644 index 000000000..9c9a2c01f --- /dev/null +++ b/src/extension/script/js/fdlibm/e_atan2.c @@ -0,0 +1,165 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_atan2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_atan2(y,x) + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +tiny = 1.0e-300, +zero = 0.0, +pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ +pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ +pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */ +pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + +#ifdef __STDC__ + double __ieee754_atan2(double y, double x) +#else + double __ieee754_atan2(y,x) + double y,x; +#endif +{ + fd_twoints ux, uy, uz; + double z; + int k,m,hx,hy,ix,iy; + unsigned lx,ly; + + ux.d = x; uy.d = y; + hx = __HI(ux); ix = hx&0x7fffffff; + lx = __LO(ux); + hy = __HI(uy); iy = hy&0x7fffffff; + ly = __LO(uy); + if(((ix|((lx|-(int)lx)>>31))>0x7ff00000)|| + ((iy|((ly|-(int)ly)>>31))>0x7ff00000)) /* x or y is NaN */ + return x+y; + if(((hx-0x3ff00000)|lx)==0) return fd_atan(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if((iy|ly)==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7ff00000) { + if(iy==0x7ff00000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return 3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return -3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>20; + if(k > 60) z=pi_o_2+0.5*pi_lo; /* |y/x| > 2**60 */ + else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */ + else z=fd_atan(fd_fabs(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: uz.d = z; + __HI(uz) ^= 0x80000000; + z = uz.d; + return z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} diff --git a/src/extension/script/js/fdlibm/e_atanh.c b/src/extension/script/js/fdlibm/e_atanh.c new file mode 100644 index 000000000..dc4a90c8e --- /dev/null +++ b/src/extension/script/js/fdlibm/e_atanh.c @@ -0,0 +1,110 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_atanh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_atanh(x) + * Method : + * 1.Reduced x to positive by atanh(-x) = -atanh(x) + * 2.For x>=0.5 + * 1 2x x + * atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) + * + * Special cases: + * atanh(x) is NaN if |x| > 1 with signal; + * atanh(NaN) is that NaN with no signal; + * atanh(+-1) is +-INF with signal. + * + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double one = 1.0, really_big = 1e300; +#else +static double one = 1.0, really_big = 1e300; +#endif + +static double zero = 0.0; + +#ifdef __STDC__ + double __ieee754_atanh(double x) +#else + double __ieee754_atanh(x) + double x; +#endif +{ + double t; + int hx,ix; + unsigned lx; + fd_twoints u; + u.d = x; + hx = __HI(u); /* high word */ + lx = __LO(u); /* low word */ + ix = hx&0x7fffffff; + if ((ix|((lx|(-(int)lx))>>31))>0x3ff00000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3ff00000) + return x/zero; + if(ix<0x3e300000&&(really_big+x)>zero) return x; /* x<2**-28 */ + u.d = x; + __HI(u) = ix; /* x <- |x| */ + x = u.d; + if(ix<0x3fe00000) { /* x < 0.5 */ + t = x+x; + t = 0.5*fd_log1p(t+t*x/(one-x)); + } else + t = 0.5*fd_log1p((x+x)/(one-x)); + if(hx>=0) return t; else return -t; +} diff --git a/src/extension/script/js/fdlibm/e_cosh.c b/src/extension/script/js/fdlibm/e_cosh.c new file mode 100644 index 000000000..4f8d4f769 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_cosh.c @@ -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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_cosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_cosh(x) + * Method : + * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (cosh(x) = cosh(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : cosh(x) := ------------------- + * 2 + * 22 <= x <= lnovft : cosh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : cosh(x) := huge*huge (overflow) + * + * Special cases: + * cosh(x) is |x| if x is +INF, -INF, or NaN. + * only cosh(0)=1 is exact for finite x. + */ + +#include "fdlibm.h" + +#ifdef _WIN32 +#define huge myhuge +#endif + +#ifdef __STDC__ +static const double one = 1.0, half=0.5, really_big = 1.0e300; +#else +static double one = 1.0, half=0.5, really_big = 1.0e300; +#endif + +#ifdef __STDC__ + double __ieee754_cosh(double x) +#else + double __ieee754_cosh(x) + double x; +#endif +{ + fd_twoints u; + double t,w; + int ix; + unsigned lx; + + /* High word of |x|. */ + u.d = x; + ix = __HI(u); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3fd62e43) { + t = fd_expm1(fd_fabs(x)); + w = one+t; + if (ix<0x3c800000) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ix < 0x40360000) { + t = __ieee754_exp(fd_fabs(x)); + return half*t+half/t; + } + + /* |x| in [22, log(maxdouble)] return half*exp(|x|) */ + if (ix < 0x40862E42) return half*__ieee754_exp(fd_fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x); + if (ix<0x408633CE || + (ix==0x408633ce)&&(lx<=(unsigned)0x8fb9f87d)) { + w = __ieee754_exp(half*fd_fabs(x)); + t = half*w; + return t*w; + } + + /* |x| > overflowthresold, cosh(x) overflow */ + return really_big*really_big; +} diff --git a/src/extension/script/js/fdlibm/e_exp.c b/src/extension/script/js/fdlibm/e_exp.c new file mode 100644 index 000000000..ad9cec124 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_exp.c @@ -0,0 +1,202 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_exp.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_exp(x) + * Returns the exponential of x. + * + * Method + * 1. Argument reduction: + * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2. + * + * Here r will be represented as r = hi-lo for better + * accuracy. + * + * 2. Approximation of exp(r) by a special rational function on + * the interval [0,0.34658]: + * Write + * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... + * We use a special Reme algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In + * other words, + * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 + * (where z=r*r, and the values of P1 to P5 are listed below) + * and + * | 5 | -59 + * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | | + * The computation of exp(r) thus becomes + * 2*r + * exp(r) = 1 + ------- + * R - r + * r*R1(r) + * = 1 + r + ----------- (for better accuracy) + * 2 - R1(r) + * where + * 2 4 10 + * R1(r) = r - (P1*r + P2*r + ... + P5*r ). + * + * 3. Scale back to obtain exp(x): + * From step 1, we have + * exp(x) = 2^k * exp(r) + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF) is 0, and + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow + * if x < -7.45133219101941108420e+02 then exp(x) underflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.0, +halF[2] = {0.5,-0.5,}, +really_big = 1.0e+300, +twom1000= 9.33263618503218878990e-302, /* 2**-1000=0x01700000,0*/ +o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ +u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */ +ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ + -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */ +ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ + -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ + + +#ifdef __STDC__ + double __ieee754_exp(double x) /* default IEEE double exp */ +#else + double __ieee754_exp(x) /* default IEEE double exp */ + double x; +#endif +{ + fd_twoints u; + double y,hi,lo,c,t; + int k, xsb; + unsigned hx; + + u.d = x; + hx = __HI(u); /* high word of x */ + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + u.d = x; + if(((hx&0xfffff)|__LO(u))!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + } + if(x > o_threshold) return really_big*really_big; /* overflow */ + if(x < u_threshold) return twom1000*twom1000; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = (int)(invln2*x+halF[xsb]); + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + x = hi - lo; + } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */ + if(really_big+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + c = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + if(k==0) return one-((x*c)/(c-2.0)-x); + else y = one-((lo-(x*c)/(2.0-c))-hi); + if(k >= -1021) { + u.d = y; + __HI(u) += (k<<20); /* add k to y's exponent */ + y = u.d; + return y; + } else { + u.d = y; + __HI(u) += ((k+1000)<<20);/* add k to y's exponent */ + y = u.d; + return y*twom1000; + } +} diff --git a/src/extension/script/js/fdlibm/e_fmod.c b/src/extension/script/js/fdlibm/e_fmod.c new file mode 100644 index 000000000..7b5ce780f --- /dev/null +++ b/src/extension/script/js/fdlibm/e_fmod.c @@ -0,0 +1,184 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_fmod.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __ieee754_fmod(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double one = 1.0, Zero[] = {0.0, -0.0,}; +#else +static double one = 1.0, Zero[] = {0.0, -0.0,}; +#endif + +#ifdef __STDC__ + double __ieee754_fmod(double x, double y) +#else + double __ieee754_fmod(x,y) + double x,y ; +#endif +{ + fd_twoints ux, uy; + int n,hx,hy,hz,ix,iy,sx,i; + unsigned lx,ly,lz; + + ux.d = x; uy.d = y; + hx = __HI(ux); /* high word of x */ + lx = __LO(ux); /* low word of x */ + hy = __HI(uy); /* high word of y */ + ly = __LO(uy); /* low word of y */ + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7ff00000)|| /* y=0,or x not finite */ + ((hy|((ly|-(int)ly)>>31))>0x7ff00000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx>31]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x00100000) { /* subnormal x */ + if(hx==0) { + for (ix = -1043, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1; + } + } else ix = (hx>>20)-1023; + + /* determine iy = ilogb(y) */ + if(hy<0x00100000) { /* subnormal y */ + if(hy==0) { + for (iy = -1043, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1; + } + } else iy = (hy>>20)-1023; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -1022) + hx = 0x00100000|(0x000fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -1022-ix; + if(n<=31) { + hx = (hx<>(32-n)); + lx <<= n; + } else { + hx = lx<<(n-32); + lx = 0; + } + } + if(iy >= -1022) + hy = 0x00100000|(0x000fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -1022-iy; + if(n<=31) { + hy = (hy<>(32-n)); + ly <<= n; + } else { + hy = ly<<(n-32); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx>31); lx = lx+lx;} + else { + if((hz|lz)==0) /* return sign(x)*0 */ + return Zero[(unsigned)sx>>31]; + hx = hz+hz+(lz>>31); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[(unsigned)sx>>31]; + while(hx<0x00100000) { /* normalize x */ + hx = hx+hx+(lx>>31); lx = lx+lx; + iy -= 1; + } + if(iy>= -1022) { /* normalize output */ + hx = ((hx-0x00100000)|((iy+1023)<<20)); + ux.d = x; + __HI(ux) = hx|sx; + __LO(ux) = lx; + x = ux.d; + } else { /* subnormal output */ + n = -1022 - iy; + if(n<=20) { + lx = (lx>>n)|((unsigned)hx<<(32-n)); + hx >>= n; + } else if (n<=31) { + lx = (hx<<(32-n))|(lx>>n); hx = sx; + } else { + lx = hx>>(n-32); hx = sx; + } + ux.d = x; + __HI(ux) = hx|sx; + __LO(ux) = lx; + x = ux.d; + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/src/extension/script/js/fdlibm/e_gamma.c b/src/extension/script/js/fdlibm/e_gamma.c new file mode 100644 index 000000000..a34faa32c --- /dev/null +++ b/src/extension/script/js/fdlibm/e_gamma.c @@ -0,0 +1,71 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_gamma.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_gamma(x) + * Return the logarithm of the Gamma function of x. + * + * Method: call __ieee754_gamma_r + */ + +#include "fdlibm.h" + +extern int signgam; + +#ifdef __STDC__ + double __ieee754_gamma(double x) +#else + double __ieee754_gamma(x) + double x; +#endif +{ + return __ieee754_gamma_r(x,&signgam); +} diff --git a/src/extension/script/js/fdlibm/e_gamma_r.c b/src/extension/script/js/fdlibm/e_gamma_r.c new file mode 100644 index 000000000..f10e32e36 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_gamma_r.c @@ -0,0 +1,70 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_gamma_r.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_gamma_r(x, signgamp) + * Reentrant version of the logarithm of the Gamma function + * with user provide pointer for the sign of Gamma(x). + * + * Method: See __ieee754_lgamma_r + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double __ieee754_gamma_r(double x, int *signgamp) +#else + double __ieee754_gamma_r(x,signgamp) + double x; int *signgamp; +#endif +{ + return __ieee754_lgamma_r(x,signgamp); +} diff --git a/src/extension/script/js/fdlibm/e_hypot.c b/src/extension/script/js/fdlibm/e_hypot.c new file mode 100644 index 000000000..390023087 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_hypot.c @@ -0,0 +1,173 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_hypot.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_hypot(x,y) + * + * Method : + * If (assume round-to-nearest) z=x*x+y*y + * has error less than sqrt(2)/2 ulp, than + * sqrt(z) has error less than 1 ulp (exercise). + * + * So, compute sqrt(x*x+y*y) with some care as + * follows to get the error below 1 ulp: + * + * Assume x>y>0; + * (if possible, set rounding to round-to-nearest) + * 1. if x > 2y use + * x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y + * where x1 = x with lower 32 bits cleared, x2 = x-x1; else + * 2. if x <= 2y use + * t1*y1+((x-y)*(x-y)+(t1*y2+t2*y)) + * where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1, + * y1= y with lower 32 bits chopped, y2 = y-y1. + * + * NOTE: scaling may be necessary if some argument is too + * large or too tiny + * + * Special cases: + * hypot(x,y) is INF if x or y is +INF or -INF; else + * hypot(x,y) is NAN if x or y is NAN. + * + * Accuracy: + * hypot(x,y) returns sqrt(x^2+y^2) with error less + * than 1 ulps (units in the last place) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double __ieee754_hypot(double x, double y) +#else + double __ieee754_hypot(x,y) + double x, y; +#endif +{ + fd_twoints ux, uy; + double a=x,b=y,t1,t2,y1,y2,w; + int j,k,ha,hb; + + ux.d = x; uy.d = y; + ha = __HI(ux)&0x7fffffff; /* high word of x */ + hb = __HI(uy)&0x7fffffff; /* high word of y */ + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + ux.d = a; uy.d = b; + __HI(ux) = ha; /* a <- |a| */ + __HI(uy) = hb; /* b <- |b| */ + a = ux.d; b = uy.d; + if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ + k=0; + if(ha > 0x5f300000) { /* a>2**500 */ + if(ha >= 0x7ff00000) { /* Inf or NaN */ + w = a+b; /* for sNaN */ + ux.d = a; uy.d = b; + if(((ha&0xfffff)|__LO(ux))==0) w = a; + if(((hb^0x7ff00000)|__LO(uy))==0) w = b; + return w; + } + /* scale a and b by 2**-600 */ + ha -= 0x25800000; hb -= 0x25800000; k += 600; + ux.d = a; uy.d = b; + __HI(ux) = ha; + __HI(uy) = hb; + a = ux.d; b = uy.d; + } + if(hb < 0x20b00000) { /* b < 2**-500 */ + if(hb <= 0x000fffff) { /* subnormal b or 0 */ + uy.d = b; + if((hb|(__LO(uy)))==0) return a; + t1=0; + ux.d = t1; + __HI(ux) = 0x7fd00000; /* t1=2^1022 */ + t1 = ux.d; + b *= t1; + a *= t1; + k -= 1022; + } else { /* scale a and b by 2^600 */ + ha += 0x25800000; /* a *= 2^600 */ + hb += 0x25800000; /* b *= 2^600 */ + k -= 600; + ux.d = a; uy.d = b; + __HI(ux) = ha; + __HI(uy) = hb; + a = ux.d; b = uy.d; + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + t1 = 0; + ux.d = t1; + __HI(ux) = ha; + t1 = ux.d; + t2 = a-t1; + w = fd_sqrt(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + y1 = 0; + ux.d = y1; + __HI(ux) = hb; + y1 = ux.d; + y2 = b - y1; + t1 = 0; + ux.d = t1; + __HI(ux) = ha+0x00100000; + t1 = ux.d; + t2 = a - t1; + w = fd_sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + t1 = 1.0; + ux.d = t1; + __HI(ux) += (k<<20); + t1 = ux.d; + return t1*w; + } else return w; +} diff --git a/src/extension/script/js/fdlibm/e_j0.c b/src/extension/script/js/fdlibm/e_j0.c new file mode 100644 index 000000000..078e09641 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_j0.c @@ -0,0 +1,524 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_j0.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_j0(x), __ieee754_y0(x) + * Bessel function of the first and second kinds of order zero. + * Method -- j0(x): + * 1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ... + * 2. Reduce x to |x| since j0(x)=j0(-x), and + * for x in (0,2) + * j0(x) = 1-z/4+ z^2*R0/S0, where z = x*x; + * (precision: |j0-1+z/4-z^2R0/S0 |<2**-63.67 ) + * for x in (2,inf) + * j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * as follow: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (cos(x) + sin(x)) + * sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j0(nan)= nan + * j0(0) = 1 + * j0(inf) = 0 + * + * Method -- y0(x): + * 1. For x<2. + * Since + * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...) + * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. + * We use the following function to approximate y0, + * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2 + * where + * U(z) = u00 + u01*z + ... + u06*z^6 + * V(z) = 1 + v01*z + ... + v04*z^4 + * with absolute approximation error bounded by 2**-72. + * Note: For tiny x, U/V = u0 and j0(x)~1, hence + * y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27) + * 2. For x>=2. + * y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * by the method mentioned above. + * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static double pzero(double), qzero(double); +#else +static double pzero(), qzero(); +#endif + +#ifdef __STDC__ +static const double +#else +static double +#endif +really_big = 1e300, +one = 1.0, +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ + /* R0/S0 on [0, 2.00] */ +R02 = 1.56249999999999947958e-02, /* 0x3F8FFFFF, 0xFFFFFFFD */ +R03 = -1.89979294238854721751e-04, /* 0xBF28E6A5, 0xB61AC6E9 */ +R04 = 1.82954049532700665670e-06, /* 0x3EBEB1D1, 0x0C503919 */ +R05 = -4.61832688532103189199e-09, /* 0xBE33D5E7, 0x73D63FCE */ +S01 = 1.56191029464890010492e-02, /* 0x3F8FFCE8, 0x82C8C2A4 */ +S02 = 1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */ +S03 = 5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */ +S04 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ + +static double zero = 0.0; + +#ifdef __STDC__ + double __ieee754_j0(double x) +#else + double __ieee754_j0(x) + double x; +#endif +{ + fd_twoints un; + double z, s,c,ss,cc,r,u,v; + int hx,ix; + + un.d = x; + hx = __HI(un); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return one/(x*x); + x = fd_fabs(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = fd_sin(x); + c = fd_cos(x); + ss = s-c; + cc = s+c; + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = -fd_cos(x+x); + if ((s*c)0x48000000) z = (invsqrtpi*cc)/fd_sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*cc-v*ss)/fd_sqrt(x); + } + return z; + } + if(ix<0x3f200000) { /* |x| < 2**-13 */ + if(really_big+x>one) { /* raise inexact if x != 0 */ + if(ix<0x3e400000) return one; /* |x|<2**-27 */ + else return one - 0.25*x*x; + } + } + z = x*x; + r = z*(R02+z*(R03+z*(R04+z*R05))); + s = one+z*(S01+z*(S02+z*(S03+z*S04))); + if(ix < 0x3FF00000) { /* |x| < 1.00 */ + return one + z*(-0.25+(r/s)); + } else { + u = 0.5*x; + return((one+u)*(one-u)+z*(r/s)); + } +} + +#ifdef __STDC__ +static const double +#else +static double +#endif +u00 = -7.38042951086872317523e-02, /* 0xBFB2E4D6, 0x99CBD01F */ +u01 = 1.76666452509181115538e-01, /* 0x3FC69D01, 0x9DE9E3FC */ +u02 = -1.38185671945596898896e-02, /* 0xBF8C4CE8, 0xB16CFA97 */ +u03 = 3.47453432093683650238e-04, /* 0x3F36C54D, 0x20B29B6B */ +u04 = -3.81407053724364161125e-06, /* 0xBECFFEA7, 0x73D25CAD */ +u05 = 1.95590137035022920206e-08, /* 0x3E550057, 0x3B4EABD4 */ +u06 = -3.98205194132103398453e-11, /* 0xBDC5E43D, 0x693FB3C8 */ +v01 = 1.27304834834123699328e-02, /* 0x3F8A1270, 0x91C9C71A */ +v02 = 7.60068627350353253702e-05, /* 0x3F13ECBB, 0xF578C6C1 */ +v03 = 2.59150851840457805467e-07, /* 0x3E91642D, 0x7FF202FD */ +v04 = 4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */ + +#ifdef __STDC__ + double __ieee754_y0(double x) +#else + double __ieee754_y0(x) + double x; +#endif +{ + fd_twoints un; + double z, s,c,ss,cc,u,v; + int hx,ix,lx; + + un.d = x; + hx = __HI(un); + ix = 0x7fffffff&hx; + lx = __LO(un); + /* Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0 */ + if(ix>=0x7ff00000) return one/(x+x*x); + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + /* y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + * where x0 = x-pi/4 + * Better formula: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) + cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + s = fd_sin(x); + c = fd_cos(x); + ss = s-c; + cc = s+c; + /* + * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + */ + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = -fd_cos(x+x); + if ((s*c)0x48000000) z = (invsqrtpi*ss)/fd_sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*ss+v*cc)/fd_sqrt(x); + } + return z; + } + if(ix<=0x3e400000) { /* x < 2**-27 */ + return(u00 + tpi*__ieee754_log(x)); + } + z = x*x; + u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06))))); + v = one+z*(v01+z*(v02+z*(v03+z*v04))); + return(u/v + tpi*(__ieee754_j0(x)*__ieee754_log(x))); +} + +/* The asymptotic expansions of pzero is + * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x. + * For x >= 2, We approximate pzero by + * pzero(x) = 1 + (R/S) + * where R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10 + * S = 1 + pS0*s^2 + ... + pS4*s^10 + * and + * | pzero(x)-1-R/S | <= 2 ** ( -60.26) + */ +#ifdef __STDC__ +static const double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#else +static double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#endif + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + -7.03124999999900357484e-02, /* 0xBFB1FFFF, 0xFFFFFD32 */ + -8.08167041275349795626e+00, /* 0xC02029D0, 0xB44FA779 */ + -2.57063105679704847262e+02, /* 0xC0701102, 0x7B19E863 */ + -2.48521641009428822144e+03, /* 0xC0A36A6E, 0xCD4DCAFC */ + -5.25304380490729545272e+03, /* 0xC0B4850B, 0x36CC643D */ +}; +#ifdef __STDC__ +static const double pS8[5] = { +#else +static double pS8[5] = { +#endif + 1.16534364619668181717e+02, /* 0x405D2233, 0x07A96751 */ + 3.83374475364121826715e+03, /* 0x40ADF37D, 0x50596938 */ + 4.05978572648472545552e+04, /* 0x40E3D2BB, 0x6EB6B05F */ + 1.16752972564375915681e+05, /* 0x40FC810F, 0x8F9FA9BD */ + 4.76277284146730962675e+04, /* 0x40E74177, 0x4F2C49DC */ +}; + +#ifdef __STDC__ +static const double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#else +static double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#endif + -1.14125464691894502584e-11, /* 0xBDA918B1, 0x47E495CC */ + -7.03124940873599280078e-02, /* 0xBFB1FFFF, 0xE69AFBC6 */ + -4.15961064470587782438e+00, /* 0xC010A370, 0xF90C6BBF */ + -6.76747652265167261021e+01, /* 0xC050EB2F, 0x5A7D1783 */ + -3.31231299649172967747e+02, /* 0xC074B3B3, 0x6742CC63 */ + -3.46433388365604912451e+02, /* 0xC075A6EF, 0x28A38BD7 */ +}; +#ifdef __STDC__ +static const double pS5[5] = { +#else +static double pS5[5] = { +#endif + 6.07539382692300335975e+01, /* 0x404E6081, 0x0C98C5DE */ + 1.05125230595704579173e+03, /* 0x40906D02, 0x5C7E2864 */ + 5.97897094333855784498e+03, /* 0x40B75AF8, 0x8FBE1D60 */ + 9.62544514357774460223e+03, /* 0x40C2CCB8, 0xFA76FA38 */ + 2.40605815922939109441e+03, /* 0x40A2CC1D, 0xC70BE864 */ +}; + +#ifdef __STDC__ +static const double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#else +static double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#endif + -2.54704601771951915620e-09, /* 0xBE25E103, 0x6FE1AA86 */ + -7.03119616381481654654e-02, /* 0xBFB1FFF6, 0xF7C0E24B */ + -2.40903221549529611423e+00, /* 0xC00345B2, 0xAEA48074 */ + -2.19659774734883086467e+01, /* 0xC035F74A, 0x4CB94E14 */ + -5.80791704701737572236e+01, /* 0xC04D0A22, 0x420A1A45 */ + -3.14479470594888503854e+01, /* 0xC03F72AC, 0xA892D80F */ +}; +#ifdef __STDC__ +static const double pS3[5] = { +#else +static double pS3[5] = { +#endif + 3.58560338055209726349e+01, /* 0x4041ED92, 0x84077DD3 */ + 3.61513983050303863820e+02, /* 0x40769839, 0x464A7C0E */ + 1.19360783792111533330e+03, /* 0x4092A66E, 0x6D1061D6 */ + 1.12799679856907414432e+03, /* 0x40919FFC, 0xB8C39B7E */ + 1.73580930813335754692e+02, /* 0x4065B296, 0xFC379081 */ +}; + +#ifdef __STDC__ +static const double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#else +static double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#endif + -8.87534333032526411254e-08, /* 0xBE77D316, 0xE927026D */ + -7.03030995483624743247e-02, /* 0xBFB1FF62, 0x495E1E42 */ + -1.45073846780952986357e+00, /* 0xBFF73639, 0x8A24A843 */ + -7.63569613823527770791e+00, /* 0xC01E8AF3, 0xEDAFA7F3 */ + -1.11931668860356747786e+01, /* 0xC02662E6, 0xC5246303 */ + -3.23364579351335335033e+00, /* 0xC009DE81, 0xAF8FE70F */ +}; +#ifdef __STDC__ +static const double pS2[5] = { +#else +static double pS2[5] = { +#endif + 2.22202997532088808441e+01, /* 0x40363865, 0x908B5959 */ + 1.36206794218215208048e+02, /* 0x4061069E, 0x0EE8878F */ + 2.70470278658083486789e+02, /* 0x4070E786, 0x42EA079B */ + 1.53875394208320329881e+02, /* 0x40633C03, 0x3AB6FAFF */ + 1.46576176948256193810e+01, /* 0x402D50B3, 0x44391809 */ +}; + +#ifdef __STDC__ + static double pzero(double x) +#else + static double pzero(x) + double x; +#endif +{ +#ifdef __STDC__ + const double *p,*q; +#else + double *p,*q; +#endif + fd_twoints u; + double z,r,s; + int ix; + u.d = x; + ix = 0x7fffffff&__HI(u); + if(ix>=0x40200000) {p = pR8; q= pS8;} + else if(ix>=0x40122E8B){p = pR5; q= pS5;} + else if(ix>=0x4006DB6D){p = pR3; q= pS3;} + else if(ix>=0x40000000){p = pR2; q= pS2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qzero is + * -1/8 s + 75/1024 s^3 - ..., where s = 1/x. + * We approximate pzero by + * qzero(x) = s*(-1.25 + (R/S)) + * where R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10 + * S = 1 + qS0*s^2 + ... + qS5*s^12 + * and + * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22) + */ +#ifdef __STDC__ +static const double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#else +static double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#endif + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + 7.32421874999935051953e-02, /* 0x3FB2BFFF, 0xFFFFFE2C */ + 1.17682064682252693899e+01, /* 0x40278952, 0x5BB334D6 */ + 5.57673380256401856059e+02, /* 0x40816D63, 0x15301825 */ + 8.85919720756468632317e+03, /* 0x40C14D99, 0x3E18F46D */ + 3.70146267776887834771e+04, /* 0x40E212D4, 0x0E901566 */ +}; +#ifdef __STDC__ +static const double qS8[6] = { +#else +static double qS8[6] = { +#endif + 1.63776026895689824414e+02, /* 0x406478D5, 0x365B39BC */ + 8.09834494656449805916e+03, /* 0x40BFA258, 0x4E6B0563 */ + 1.42538291419120476348e+05, /* 0x41016652, 0x54D38C3F */ + 8.03309257119514397345e+05, /* 0x412883DA, 0x83A52B43 */ + 8.40501579819060512818e+05, /* 0x4129A66B, 0x28DE0B3D */ + -3.43899293537866615225e+05, /* 0xC114FD6D, 0x2C9530C5 */ +}; + +#ifdef __STDC__ +static const double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#else +static double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#endif + 1.84085963594515531381e-11, /* 0x3DB43D8F, 0x29CC8CD9 */ + 7.32421766612684765896e-02, /* 0x3FB2BFFF, 0xD172B04C */ + 5.83563508962056953777e+00, /* 0x401757B0, 0xB9953DD3 */ + 1.35111577286449829671e+02, /* 0x4060E392, 0x0A8788E9 */ + 1.02724376596164097464e+03, /* 0x40900CF9, 0x9DC8C481 */ + 1.98997785864605384631e+03, /* 0x409F17E9, 0x53C6E3A6 */ +}; +#ifdef __STDC__ +static const double qS5[6] = { +#else +static double qS5[6] = { +#endif + 8.27766102236537761883e+01, /* 0x4054B1B3, 0xFB5E1543 */ + 2.07781416421392987104e+03, /* 0x40A03BA0, 0xDA21C0CE */ + 1.88472887785718085070e+04, /* 0x40D267D2, 0x7B591E6D */ + 5.67511122894947329769e+04, /* 0x40EBB5E3, 0x97E02372 */ + 3.59767538425114471465e+04, /* 0x40E19118, 0x1F7A54A0 */ + -5.35434275601944773371e+03, /* 0xC0B4EA57, 0xBEDBC609 */ +}; + +#ifdef __STDC__ +static const double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#else +static double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#endif + 4.37741014089738620906e-09, /* 0x3E32CD03, 0x6ADECB82 */ + 7.32411180042911447163e-02, /* 0x3FB2BFEE, 0x0E8D0842 */ + 3.34423137516170720929e+00, /* 0x400AC0FC, 0x61149CF5 */ + 4.26218440745412650017e+01, /* 0x40454F98, 0x962DAEDD */ + 1.70808091340565596283e+02, /* 0x406559DB, 0xE25EFD1F */ + 1.66733948696651168575e+02, /* 0x4064D77C, 0x81FA21E0 */ +}; +#ifdef __STDC__ +static const double qS3[6] = { +#else +static double qS3[6] = { +#endif + 4.87588729724587182091e+01, /* 0x40486122, 0xBFE343A6 */ + 7.09689221056606015736e+02, /* 0x40862D83, 0x86544EB3 */ + 3.70414822620111362994e+03, /* 0x40ACF04B, 0xE44DFC63 */ + 6.46042516752568917582e+03, /* 0x40B93C6C, 0xD7C76A28 */ + 2.51633368920368957333e+03, /* 0x40A3A8AA, 0xD94FB1C0 */ + -1.49247451836156386662e+02, /* 0xC062A7EB, 0x201CF40F */ +}; + +#ifdef __STDC__ +static const double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#else +static double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#endif + 1.50444444886983272379e-07, /* 0x3E84313B, 0x54F76BDB */ + 7.32234265963079278272e-02, /* 0x3FB2BEC5, 0x3E883E34 */ + 1.99819174093815998816e+00, /* 0x3FFFF897, 0xE727779C */ + 1.44956029347885735348e+01, /* 0x402CFDBF, 0xAAF96FE5 */ + 3.16662317504781540833e+01, /* 0x403FAA8E, 0x29FBDC4A */ + 1.62527075710929267416e+01, /* 0x403040B1, 0x71814BB4 */ +}; +#ifdef __STDC__ +static const double qS2[6] = { +#else +static double qS2[6] = { +#endif + 3.03655848355219184498e+01, /* 0x403E5D96, 0xF7C07AED */ + 2.69348118608049844624e+02, /* 0x4070D591, 0xE4D14B40 */ + 8.44783757595320139444e+02, /* 0x408A6645, 0x22B3BF22 */ + 8.82935845112488550512e+02, /* 0x408B977C, 0x9C5CC214 */ + 2.12666388511798828631e+02, /* 0x406A9553, 0x0E001365 */ + -5.31095493882666946917e+00, /* 0xC0153E6A, 0xF8B32931 */ +}; + +#ifdef __STDC__ + static double qzero(double x) +#else + static double qzero(x) + double x; +#endif +{ +#ifdef __STDC__ + const double *p,*q; +#else + double *p,*q; +#endif + fd_twoints u; + double s,r,z; + int ix; + u.d = x; + ix = 0x7fffffff&__HI(u); + if(ix>=0x40200000) {p = qR8; q= qS8;} + else if(ix>=0x40122E8B){p = qR5; q= qS5;} + else if(ix>=0x4006DB6D){p = qR3; q= qS3;} + else if(ix>=0x40000000){p = qR2; q= qS2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (-.125 + r/s)/x; +} diff --git a/src/extension/script/js/fdlibm/e_j1.c b/src/extension/script/js/fdlibm/e_j1.c new file mode 100644 index 000000000..8982ac86a --- /dev/null +++ b/src/extension/script/js/fdlibm/e_j1.c @@ -0,0 +1,523 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_j1.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_j1(x), __ieee754_y1(x) + * Bessel function of the first and second kinds of order zero. + * Method -- j1(x): + * 1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ... + * 2. Reduce x to |x| since j1(x)=-j1(-x), and + * for x in (0,2) + * j1(x) = x/2 + x*z*R0/S0, where z = x*x; + * (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 ) + * for x in (2,inf) + * j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * as follow: + * cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (sin(x) + cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j1(nan)= nan + * j1(0) = 0 + * j1(inf) = 0 + * + * Method -- y1(x): + * 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN + * 2. For x<2. + * Since + * y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...) + * therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function. + * We use the following function to approximate y1, + * y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2 + * where for x in [0,2] (abs err less than 2**-65.89) + * U(z) = U0[0] + U0[1]*z + ... + U0[4]*z^4 + * V(z) = 1 + v0[0]*z + ... + v0[4]*z^5 + * Note: For tiny x, 1/x dominate y1 and hence + * y1(tiny) = -2/pi/tiny, (choose tiny<2**-54) + * 3. For x>=2. + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * by method mentioned above. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static double pone(double), qone(double); +#else +static double pone(), qone(); +#endif + +#ifdef __STDC__ +static const double +#else +static double +#endif +really_big = 1e300, +one = 1.0, +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ + /* R0/S0 on [0,2] */ +r00 = -6.25000000000000000000e-02, /* 0xBFB00000, 0x00000000 */ +r01 = 1.40705666955189706048e-03, /* 0x3F570D9F, 0x98472C61 */ +r02 = -1.59955631084035597520e-05, /* 0xBEF0C5C6, 0xBA169668 */ +r03 = 4.96727999609584448412e-08, /* 0x3E6AAAFA, 0x46CA0BD9 */ +s01 = 1.91537599538363460805e-02, /* 0x3F939D0B, 0x12637E53 */ +s02 = 1.85946785588630915560e-04, /* 0x3F285F56, 0xB9CDF664 */ +s03 = 1.17718464042623683263e-06, /* 0x3EB3BFF8, 0x333F8498 */ +s04 = 5.04636257076217042715e-09, /* 0x3E35AC88, 0xC97DFF2C */ +s05 = 1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */ + +static double zero = 0.0; + +#ifdef __STDC__ + double __ieee754_j1(double x) +#else + double __ieee754_j1(x) + double x; +#endif +{ + fd_twoints un; + double z, s,c,ss,cc,r,u,v,y; + int hx,ix; + + un.d = x; + hx = __HI(un); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return one/x; + y = fd_fabs(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = fd_sin(y); + c = fd_cos(y); + ss = -s-c; + cc = s-c; + if(ix<0x7fe00000) { /* make sure y+y not overflow */ + z = fd_cos(y+y); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* + * j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x) + * y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x) + */ + if(ix>0x48000000) z = (invsqrtpi*cc)/fd_sqrt(y); + else { + u = pone(y); v = qone(y); + z = invsqrtpi*(u*cc-v*ss)/fd_sqrt(y); + } + if(hx<0) return -z; + else return z; + } + if(ix<0x3e400000) { /* |x|<2**-27 */ + if(really_big+x>one) return 0.5*x;/* inexact if x!=0 necessary */ + } + z = x*x; + r = z*(r00+z*(r01+z*(r02+z*r03))); + s = one+z*(s01+z*(s02+z*(s03+z*(s04+z*s05)))); + r *= x; + return(x*0.5+r/s); +} + +#ifdef __STDC__ +static const double U0[5] = { +#else +static double U0[5] = { +#endif + -1.96057090646238940668e-01, /* 0xBFC91866, 0x143CBC8A */ + 5.04438716639811282616e-02, /* 0x3FA9D3C7, 0x76292CD1 */ + -1.91256895875763547298e-03, /* 0xBF5F55E5, 0x4844F50F */ + 2.35252600561610495928e-05, /* 0x3EF8AB03, 0x8FA6B88E */ + -9.19099158039878874504e-08, /* 0xBE78AC00, 0x569105B8 */ +}; +#ifdef __STDC__ +static const double V0[5] = { +#else +static double V0[5] = { +#endif + 1.99167318236649903973e-02, /* 0x3F94650D, 0x3F4DA9F0 */ + 2.02552581025135171496e-04, /* 0x3F2A8C89, 0x6C257764 */ + 1.35608801097516229404e-06, /* 0x3EB6C05A, 0x894E8CA6 */ + 6.22741452364621501295e-09, /* 0x3E3ABF1D, 0x5BA69A86 */ + 1.66559246207992079114e-11, /* 0x3DB25039, 0xDACA772A */ +}; + +#ifdef __STDC__ + double __ieee754_y1(double x) +#else + double __ieee754_y1(x) + double x; +#endif +{ + fd_twoints un; + double z, s,c,ss,cc,u,v; + int hx,ix,lx; + + un.d = x; + hx = __HI(un); + ix = 0x7fffffff&hx; + lx = __LO(un); + /* if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0 */ + if(ix>=0x7ff00000) return one/(x+x*x); + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = fd_sin(x); + c = fd_cos(x); + ss = -s-c; + cc = s-c; + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = fd_cos(x+x); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + * where x0 = x-3pi/4 + * Better formula: + * cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (cos(x) + sin(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + if(ix>0x48000000) z = (invsqrtpi*ss)/fd_sqrt(x); + else { + u = pone(x); v = qone(x); + z = invsqrtpi*(u*ss+v*cc)/fd_sqrt(x); + } + return z; + } + if(ix<=0x3c900000) { /* x < 2**-54 */ + return(-tpi/x); + } + z = x*x; + u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4]))); + v = one+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4])))); + return(x*(u/v) + tpi*(__ieee754_j1(x)*__ieee754_log(x)-one/x)); +} + +/* For x >= 8, the asymptotic expansions of pone is + * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x. + * We approximate pone by + * pone(x) = 1 + (R/S) + * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10 + * S = 1 + ps0*s^2 + ... + ps4*s^10 + * and + * | pone(x)-1-R/S | <= 2 ** ( -60.06) + */ + +#ifdef __STDC__ +static const double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#else +static double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#endif + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + 1.17187499999988647970e-01, /* 0x3FBDFFFF, 0xFFFFFCCE */ + 1.32394806593073575129e+01, /* 0x402A7A9D, 0x357F7FCE */ + 4.12051854307378562225e+02, /* 0x4079C0D4, 0x652EA590 */ + 3.87474538913960532227e+03, /* 0x40AE457D, 0xA3A532CC */ + 7.91447954031891731574e+03, /* 0x40BEEA7A, 0xC32782DD */ +}; +#ifdef __STDC__ +static const double ps8[5] = { +#else +static double ps8[5] = { +#endif + 1.14207370375678408436e+02, /* 0x405C8D45, 0x8E656CAC */ + 3.65093083420853463394e+03, /* 0x40AC85DC, 0x964D274F */ + 3.69562060269033463555e+04, /* 0x40E20B86, 0x97C5BB7F */ + 9.76027935934950801311e+04, /* 0x40F7D42C, 0xB28F17BB */ + 3.08042720627888811578e+04, /* 0x40DE1511, 0x697A0B2D */ +}; + +#ifdef __STDC__ +static const double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#else +static double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#endif + 1.31990519556243522749e-11, /* 0x3DAD0667, 0xDAE1CA7D */ + 1.17187493190614097638e-01, /* 0x3FBDFFFF, 0xE2C10043 */ + 6.80275127868432871736e+00, /* 0x401B3604, 0x6E6315E3 */ + 1.08308182990189109773e+02, /* 0x405B13B9, 0x452602ED */ + 5.17636139533199752805e+02, /* 0x40802D16, 0xD052D649 */ + 5.28715201363337541807e+02, /* 0x408085B8, 0xBB7E0CB7 */ +}; +#ifdef __STDC__ +static const double ps5[5] = { +#else +static double ps5[5] = { +#endif + 5.92805987221131331921e+01, /* 0x404DA3EA, 0xA8AF633D */ + 9.91401418733614377743e+02, /* 0x408EFB36, 0x1B066701 */ + 5.35326695291487976647e+03, /* 0x40B4E944, 0x5706B6FB */ + 7.84469031749551231769e+03, /* 0x40BEA4B0, 0xB8A5BB15 */ + 1.50404688810361062679e+03, /* 0x40978030, 0x036F5E51 */ +}; + +#ifdef __STDC__ +static const double pr3[6] = { +#else +static double pr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#endif + 3.02503916137373618024e-09, /* 0x3E29FC21, 0xA7AD9EDD */ + 1.17186865567253592491e-01, /* 0x3FBDFFF5, 0x5B21D17B */ + 3.93297750033315640650e+00, /* 0x400F76BC, 0xE85EAD8A */ + 3.51194035591636932736e+01, /* 0x40418F48, 0x9DA6D129 */ + 9.10550110750781271918e+01, /* 0x4056C385, 0x4D2C1837 */ + 4.85590685197364919645e+01, /* 0x4048478F, 0x8EA83EE5 */ +}; +#ifdef __STDC__ +static const double ps3[5] = { +#else +static double ps3[5] = { +#endif + 3.47913095001251519989e+01, /* 0x40416549, 0xA134069C */ + 3.36762458747825746741e+02, /* 0x40750C33, 0x07F1A75F */ + 1.04687139975775130551e+03, /* 0x40905B7C, 0x5037D523 */ + 8.90811346398256432622e+02, /* 0x408BD67D, 0xA32E31E9 */ + 1.03787932439639277504e+02, /* 0x4059F26D, 0x7C2EED53 */ +}; + +#ifdef __STDC__ +static const double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#else +static double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#endif + 1.07710830106873743082e-07, /* 0x3E7CE9D4, 0xF65544F4 */ + 1.17176219462683348094e-01, /* 0x3FBDFF42, 0xBE760D83 */ + 2.36851496667608785174e+00, /* 0x4002F2B7, 0xF98FAEC0 */ + 1.22426109148261232917e+01, /* 0x40287C37, 0x7F71A964 */ + 1.76939711271687727390e+01, /* 0x4031B1A8, 0x177F8EE2 */ + 5.07352312588818499250e+00, /* 0x40144B49, 0xA574C1FE */ +}; +#ifdef __STDC__ +static const double ps2[5] = { +#else +static double ps2[5] = { +#endif + 2.14364859363821409488e+01, /* 0x40356FBD, 0x8AD5ECDC */ + 1.25290227168402751090e+02, /* 0x405F5293, 0x14F92CD5 */ + 2.32276469057162813669e+02, /* 0x406D08D8, 0xD5A2DBD9 */ + 1.17679373287147100768e+02, /* 0x405D6B7A, 0xDA1884A9 */ + 8.36463893371618283368e+00, /* 0x4020BAB1, 0xF44E5192 */ +}; + +#ifdef __STDC__ + static double pone(double x) +#else + static double pone(x) + double x; +#endif +{ +#ifdef __STDC__ + const double *p,*q; +#else + double *p,*q; +#endif + fd_twoints un; + double z,r,s; + int ix; + un.d = x; + ix = 0x7fffffff&__HI(un); + if(ix>=0x40200000) {p = pr8; q= ps8;} + else if(ix>=0x40122E8B){p = pr5; q= ps5;} + else if(ix>=0x4006DB6D){p = pr3; q= ps3;} + else if(ix>=0x40000000){p = pr2; q= ps2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qone is + * 3/8 s - 105/1024 s^3 - ..., where s = 1/x. + * We approximate pone by + * qone(x) = s*(0.375 + (R/S)) + * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10 + * S = 1 + qs1*s^2 + ... + qs6*s^12 + * and + * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13) + */ + +#ifdef __STDC__ +static const double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#else +static double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ +#endif + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + -1.02539062499992714161e-01, /* 0xBFBA3FFF, 0xFFFFFDF3 */ + -1.62717534544589987888e+01, /* 0xC0304591, 0xA26779F7 */ + -7.59601722513950107896e+02, /* 0xC087BCD0, 0x53E4B576 */ + -1.18498066702429587167e+04, /* 0xC0C724E7, 0x40F87415 */ + -4.84385124285750353010e+04, /* 0xC0E7A6D0, 0x65D09C6A */ +}; +#ifdef __STDC__ +static const double qs8[6] = { +#else +static double qs8[6] = { +#endif + 1.61395369700722909556e+02, /* 0x40642CA6, 0xDE5BCDE5 */ + 7.82538599923348465381e+03, /* 0x40BE9162, 0xD0D88419 */ + 1.33875336287249578163e+05, /* 0x4100579A, 0xB0B75E98 */ + 7.19657723683240939863e+05, /* 0x4125F653, 0x72869C19 */ + 6.66601232617776375264e+05, /* 0x412457D2, 0x7719AD5C */ + -2.94490264303834643215e+05, /* 0xC111F969, 0x0EA5AA18 */ +}; + +#ifdef __STDC__ +static const double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#else +static double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ +#endif + -2.08979931141764104297e-11, /* 0xBDB6FA43, 0x1AA1A098 */ + -1.02539050241375426231e-01, /* 0xBFBA3FFF, 0xCB597FEF */ + -8.05644828123936029840e+00, /* 0xC0201CE6, 0xCA03AD4B */ + -1.83669607474888380239e+02, /* 0xC066F56D, 0x6CA7B9B0 */ + -1.37319376065508163265e+03, /* 0xC09574C6, 0x6931734F */ + -2.61244440453215656817e+03, /* 0xC0A468E3, 0x88FDA79D */ +}; +#ifdef __STDC__ +static const double qs5[6] = { +#else +static double qs5[6] = { +#endif + 8.12765501384335777857e+01, /* 0x405451B2, 0xFF5A11B2 */ + 1.99179873460485964642e+03, /* 0x409F1F31, 0xE77BF839 */ + 1.74684851924908907677e+04, /* 0x40D10F1F, 0x0D64CE29 */ + 4.98514270910352279316e+04, /* 0x40E8576D, 0xAABAD197 */ + 2.79480751638918118260e+04, /* 0x40DB4B04, 0xCF7C364B */ + -4.71918354795128470869e+03, /* 0xC0B26F2E, 0xFCFFA004 */ +}; + +#ifdef __STDC__ +static const double qr3[6] = { +#else +static double qr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ +#endif + -5.07831226461766561369e-09, /* 0xBE35CFA9, 0xD38FC84F */ + -1.02537829820837089745e-01, /* 0xBFBA3FEB, 0x51AEED54 */ + -4.61011581139473403113e+00, /* 0xC01270C2, 0x3302D9FF */ + -5.78472216562783643212e+01, /* 0xC04CEC71, 0xC25D16DA */ + -2.28244540737631695038e+02, /* 0xC06C87D3, 0x4718D55F */ + -2.19210128478909325622e+02, /* 0xC06B66B9, 0x5F5C1BF6 */ +}; +#ifdef __STDC__ +static const double qs3[6] = { +#else +static double qs3[6] = { +#endif + 4.76651550323729509273e+01, /* 0x4047D523, 0xCCD367E4 */ + 6.73865112676699709482e+02, /* 0x40850EEB, 0xC031EE3E */ + 3.38015286679526343505e+03, /* 0x40AA684E, 0x448E7C9A */ + 5.54772909720722782367e+03, /* 0x40B5ABBA, 0xA61D54A6 */ + 1.90311919338810798763e+03, /* 0x409DBC7A, 0x0DD4DF4B */ + -1.35201191444307340817e+02, /* 0xC060E670, 0x290A311F */ +}; + +#ifdef __STDC__ +static const double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#else +static double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ +#endif + -1.78381727510958865572e-07, /* 0xBE87F126, 0x44C626D2 */ + -1.02517042607985553460e-01, /* 0xBFBA3E8E, 0x9148B010 */ + -2.75220568278187460720e+00, /* 0xC0060484, 0x69BB4EDA */ + -1.96636162643703720221e+01, /* 0xC033A9E2, 0xC168907F */ + -4.23253133372830490089e+01, /* 0xC04529A3, 0xDE104AAA */ + -2.13719211703704061733e+01, /* 0xC0355F36, 0x39CF6E52 */ +}; +#ifdef __STDC__ +static const double qs2[6] = { +#else +static double qs2[6] = { +#endif + 2.95333629060523854548e+01, /* 0x403D888A, 0x78AE64FF */ + 2.52981549982190529136e+02, /* 0x406F9F68, 0xDB821CBA */ + 7.57502834868645436472e+02, /* 0x4087AC05, 0xCE49A0F7 */ + 7.39393205320467245656e+02, /* 0x40871B25, 0x48D4C029 */ + 1.55949003336666123687e+02, /* 0x40637E5E, 0x3C3ED8D4 */ + -4.95949898822628210127e+00, /* 0xC013D686, 0xE71BE86B */ +}; + +#ifdef __STDC__ + static double qone(double x) +#else + static double qone(x) + double x; +#endif +{ +#ifdef __STDC__ + const double *p,*q; +#else + double *p,*q; +#endif + fd_twoints un; + double s,r,z; + int ix; + un.d = x; + ix = 0x7fffffff&__HI(un); + if(ix>=0x40200000) {p = qr8; q= qs8;} + else if(ix>=0x40122E8B){p = qr5; q= qs5;} + else if(ix>=0x4006DB6D){p = qr3; q= qs3;} + else if(ix>=0x40000000){p = qr2; q= qs2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (.375 + r/s)/x; +} diff --git a/src/extension/script/js/fdlibm/e_jn.c b/src/extension/script/js/fdlibm/e_jn.c new file mode 100644 index 000000000..2b61b4439 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_jn.c @@ -0,0 +1,315 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_jn.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __ieee754_jn(n, x), __ieee754_yn(n, x) + * floating point Bessel's function of the 1st and 2nd kind + * of order n + * + * Special cases: + * y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; + * y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. + * Note 2. About jn(n,x), yn(n,x) + * For n=0, j0(x) is called, + * for n=1, j1(x) is called, + * for nx, a continued fraction approximation to + * j(n,x)/j(n-1,x) is evaluated and then backward + * recursion is used starting from a supposed value + * for j(n,x). The resulting value of j(0,x) is + * compared with the actual value to correct the + * supposed value of j(n,x). + * + * yn(n,x) is similar in all respects, except + * that forward recursion is used for all + * values of n>1. + * + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ +one = 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ + +static double zero = 0.00000000000000000000e+00; + +#ifdef __STDC__ + double __ieee754_jn(int n, double x) +#else + double __ieee754_jn(n,x) + int n; double x; +#endif +{ + fd_twoints u; + int i,hx,ix,lx, sgn; + double a, b, temp, di; + double z, w; + + /* J(-n,x) = (-1)^n * J(n, x), J(n, -x) = (-1)^n * J(n, x) + * Thus, J(-n,x) = J(n,-x) + */ + u.d = x; + hx = __HI(u); + ix = 0x7fffffff&hx; + lx = __LO(u); + /* if J(n,NaN) is NaN */ + if((ix|((unsigned)(lx|-lx))>>31)>0x7ff00000) return x+x; + if(n<0){ + n = -n; + x = -x; + hx ^= 0x80000000; + } + if(n==0) return(__ieee754_j0(x)); + if(n==1) return(__ieee754_j1(x)); + sgn = (n&1)&(hx>>31); /* even n -- 0, odd n -- sign(x) */ + x = fd_fabs(x); + if((ix|lx)==0||ix>=0x7ff00000) /* if x is 0 or inf */ + b = zero; + else if((double)n<=x) { + /* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */ + if(ix>=0x52D00000) { /* x > 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch(n&3) { + case 0: temp = fd_cos(x)+fd_sin(x); break; + case 1: temp = -fd_cos(x)+fd_sin(x); break; + case 2: temp = -fd_cos(x)-fd_sin(x); break; + case 3: temp = fd_cos(x)-fd_sin(x); break; + } + b = invsqrtpi*temp/fd_sqrt(x); + } else { + a = __ieee754_j0(x); + b = __ieee754_j1(x); + for(i=1;i33) /* underflow */ + b = zero; + else { + temp = x*0.5; b = temp; + for (a=one,i=2;i<=n;i++) { + a *= (double)i; /* a = n! */ + b *= temp; /* b = (x/2)^n */ + } + b = b/a; + } + } else { + /* use backward recurrence */ + /* x x^2 x^2 + * J(n,x)/J(n-1,x) = ---- ------ ------ ..... + * 2n - 2(n+1) - 2(n+2) + * + * 1 1 1 + * (for large x) = ---- ------ ------ ..... + * 2n 2(n+1) 2(n+2) + * -- - ------ - ------ - + * x x x + * + * Let w = 2n/x and h=2/x, then the above quotient + * is equal to the continued fraction: + * 1 + * = ----------------------- + * 1 + * w - ----------------- + * 1 + * w+h - --------- + * w+2h - ... + * + * To determine how many terms needed, let + * Q(0) = w, Q(1) = w(w+h) - 1, + * Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + * When Q(k) > 1e4 good for single + * When Q(k) > 1e9 good for double + * When Q(k) > 1e17 good for quadruple + */ + /* determine k */ + double t,v; + double q0,q1,h,tmp; int k,m; + w = (n+n)/(double)x; h = 2.0/(double)x; + q0 = w; z = w+h; q1 = w*z - 1.0; k=1; + while(q1<1.0e9) { + k += 1; z += h; + tmp = z*q1 - q0; + q0 = q1; + q1 = tmp; + } + m = n+n; + for(t=zero, i = 2*(n+k); i>=m; i -= 2) t = one/(i/x-t); + a = t; + b = one; + /* estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n) + * Hence, if n*(log(2n/x)) > ... + * single 8.8722839355e+01 + * double 7.09782712893383973096e+02 + * long double 1.1356523406294143949491931077970765006170e+04 + * then recurrent value may overflow and the result is + * likely underflow to zero + */ + tmp = n; + v = two/x; + tmp = tmp*__ieee754_log(fd_fabs(v*tmp)); + if(tmp<7.09782712893383973096e+02) { + for(i=n-1,di=(double)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + } + } else { + for(i=n-1,di=(double)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + /* scale b to avoid spurious overflow */ + if(b>1e100) { + a /= b; + t /= b; + b = one; + } + } + } + b = (t*__ieee754_j0(x)/b); + } + } + if(sgn==1) return -b; else return b; +} + +#ifdef __STDC__ + double __ieee754_yn(int n, double x) +#else + double __ieee754_yn(n,x) + int n; double x; +#endif +{ + fd_twoints u; + int i,hx,ix,lx; + int sign; + double a, b, temp; + + u.d = x; + hx = __HI(u); + ix = 0x7fffffff&hx; + lx = __LO(u); + /* if Y(n,NaN) is NaN */ + if((ix|((unsigned)(lx|-lx))>>31)>0x7ff00000) return x+x; + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + sign = 1; + if(n<0){ + n = -n; + sign = 1 - ((n&1)<<1); + } + if(n==0) return(__ieee754_y0(x)); + if(n==1) return(sign*__ieee754_y1(x)); + if(ix==0x7ff00000) return zero; + if(ix>=0x52D00000) { /* x > 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch(n&3) { + case 0: temp = fd_sin(x)-fd_cos(x); break; + case 1: temp = -fd_sin(x)-fd_cos(x); break; + case 2: temp = -fd_sin(x)+fd_cos(x); break; + case 3: temp = fd_sin(x)+fd_cos(x); break; + } + b = invsqrtpi*temp/fd_sqrt(x); + } else { + a = __ieee754_y0(x); + b = __ieee754_y1(x); + /* quit if b is -inf */ + u.d = b; + for(i=1;i0) return b; else return -b; +} diff --git a/src/extension/script/js/fdlibm/e_lgamma.c b/src/extension/script/js/fdlibm/e_lgamma.c new file mode 100644 index 000000000..beb3bd932 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_lgamma.c @@ -0,0 +1,71 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_lgamma.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_lgamma(x) + * Return the logarithm of the Gamma function of x. + * + * Method: call __ieee754_lgamma_r + */ + +#include "fdlibm.h" + +extern int signgam; + +#ifdef __STDC__ + double __ieee754_lgamma(double x) +#else + double __ieee754_lgamma(x) + double x; +#endif +{ + return __ieee754_lgamma_r(x,&signgam); +} diff --git a/src/extension/script/js/fdlibm/e_lgamma_r.c b/src/extension/script/js/fdlibm/e_lgamma_r.c new file mode 100644 index 000000000..df92e7a26 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_lgamma_r.c @@ -0,0 +1,347 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_lgamma_r.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_lgamma_r(x, signgamp) + * Reentrant version of the logarithm of the Gamma function + * with user provide pointer for the sign of Gamma(x). + * + * Method: + * 1. Argument Reduction for 0 < x <= 8 + * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may + * reduce x to a number in [1.5,2.5] by + * lgamma(1+s) = log(s) + lgamma(s) + * for example, + * lgamma(7.3) = log(6.3) + lgamma(6.3) + * = log(6.3*5.3) + lgamma(5.3) + * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) + * 2. Polynomial approximation of lgamma around its + * minimun ymin=1.461632144968362245 to maintain monotonicity. + * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use + * Let z = x-ymin; + * lgamma(x) = -1.214862905358496078218 + z^2*poly(z) + * where + * poly(z) is a 14 degree polynomial. + * 2. Rational approximation in the primary interval [2,3] + * We use the following approximation: + * s = x-2.0; + * lgamma(x) = 0.5*s + s*P(s)/Q(s) + * with accuracy + * |P/Q - (lgamma(x)-0.5s)| < 2**-61.71 + * Our algorithms are based on the following observation + * + * zeta(2)-1 2 zeta(3)-1 3 + * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... + * 2 3 + * + * where Euler = 0.5771... is the Euler constant, which is very + * close to 0.5. + * + * 3. For x>=8, we have + * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... + * (better formula: + * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) + * Let z = 1/x, then we approximation + * f(z) = lgamma(x) - (x-0.5)(log(x)-1) + * by + * 3 5 11 + * w = w0 + w1*z + w2*z + w3*z + ... + w6*z + * where + * |w - f(z)| < 2**-58.74 + * + * 4. For negative x, since (G is gamma function) + * -x*G(-x)*G(x) = pi/sin(pi*x), + * we have + * G(x) = pi/(sin(pi*x)*(-x)*G(-x)) + * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 + * Hence, for x<0, signgam = sign(sin(pi*x)) and + * lgamma(x) = log(|Gamma(x)|) + * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); + * Note: one should avoid compute pi*(-x) directly in the + * computation of sin(pi*(-x)). + * + * 5. Special Cases + * lgamma(2+s) ~ s*(1-Euler) for tiny s + * lgamma(1)=lgamma(2)=0 + * lgamma(x) ~ -log(x) for tiny x + * lgamma(0) = lgamma(inf) = inf + * lgamma(-integer) = +-inf + * + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +two52= 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ +half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ +a0 = 7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */ +a1 = 3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */ +a2 = 6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */ +a3 = 2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */ +a4 = 7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */ +a5 = 2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */ +a6 = 1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */ +a7 = 5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */ +a8 = 2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */ +a9 = 1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */ +a10 = 2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */ +a11 = 4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */ +tc = 1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */ +tf = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */ +/* tt = -(tail of tf) */ +tt = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */ +t0 = 4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */ +t1 = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */ +t2 = 6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */ +t3 = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */ +t4 = 1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */ +t5 = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */ +t6 = 6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */ +t7 = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */ +t8 = 2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */ +t9 = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */ +t10 = 8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */ +t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */ +t12 = 3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */ +t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */ +t14 = 3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */ +u0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ +u1 = 6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */ +u2 = 1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */ +u3 = 9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */ +u4 = 2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */ +u5 = 1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */ +v1 = 2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */ +v2 = 2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */ +v3 = 7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */ +v4 = 1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */ +v5 = 3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */ +s0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ +s1 = 2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */ +s2 = 3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */ +s3 = 1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */ +s4 = 2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */ +s5 = 1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */ +s6 = 3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */ +r1 = 1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */ +r2 = 7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */ +r3 = 1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */ +r4 = 1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */ +r5 = 7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */ +r6 = 7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */ +w0 = 4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */ +w1 = 8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */ +w2 = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */ +w3 = 7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */ +w4 = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */ +w5 = 8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */ +w6 = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */ + +static double zero= 0.00000000000000000000e+00; + +#ifdef __STDC__ + static double sin_pi(double x) +#else + static double sin_pi(x) + double x; +#endif +{ + fd_twoints u; + double y,z; + int n,ix; + + u.d = x; + ix = 0x7fffffff&__HI(u); + + if(ix<0x3fd00000) return __kernel_sin(pi*x,zero,0); + y = -x; /* x is assume negative */ + + /* + * argument reduction, make sure inexact flag not raised if input + * is an integer + */ + z = fd_floor(y); + if(z!=y) { /* inexact anyway */ + y *= 0.5; + y = 2.0*(y - fd_floor(y)); /* y = |x| mod 2.0 */ + n = (int) (y*4.0); + } else { + if(ix>=0x43400000) { + y = zero; n = 0; /* y must be even */ + } else { + if(ix<0x43300000) z = y+two52; /* exact */ + u.d = z; + n = __LO(u)&1; /* lower word of z */ + y = n; + n<<= 2; + } + } + switch (n) { + case 0: y = __kernel_sin(pi*y,zero,0); break; + case 1: + case 2: y = __kernel_cos(pi*(0.5-y),zero); break; + case 3: + case 4: y = __kernel_sin(pi*(one-y),zero,0); break; + case 5: + case 6: y = -__kernel_cos(pi*(y-1.5),zero); break; + default: y = __kernel_sin(pi*(y-2.0),zero,0); break; + } + return -y; +} + + +#ifdef __STDC__ + double __ieee754_lgamma_r(double x, int *signgamp) +#else + double __ieee754_lgamma_r(x,signgamp) + double x; int *signgamp; +#endif +{ + fd_twoints u; + double t,y,z,nadj,p,p1,p2,p3,q,r,w; + int i,hx,lx,ix; + + u.d = x; + hx = __HI(u); + lx = __LO(u); + + /* purge off +-inf, NaN, +-0, and negative arguments */ + *signgamp = 1; + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x*x; + if((ix|lx)==0) return one/zero; + if(ix<0x3b900000) { /* |x|<2**-70, return -log(|x|) */ + if(hx<0) { + *signgamp = -1; + return -__ieee754_log(-x); + } else return -__ieee754_log(x); + } + if(hx<0) { + if(ix>=0x43300000) /* |x|>=2**52, must be -integer */ + return one/zero; + t = sin_pi(x); + if(t==zero) return one/zero; /* -integer */ + nadj = __ieee754_log(pi/fd_fabs(t*x)); + if(t=0x3FE76944) {y = one-x; i= 0;} + else if(ix>=0x3FCDA661) {y= x-(tc-one); i=1;} + else {y = x; i=2;} + } else { + r = zero; + if(ix>=0x3FFBB4C3) {y=2.0-x;i=0;} /* [1.7316,2] */ + else if(ix>=0x3FF3B4C4) {y=x-tc;i=1;} /* [1.23,1.73] */ + else {y=x-one;i=2;} + } + switch(i) { + case 0: + z = y*y; + p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10)))); + p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11))))); + p = y*p1+p2; + r += (p-0.5*y); break; + case 1: + z = y*y; + w = z*y; + p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */ + p2 = t1+w*(t4+w*(t7+w*(t10+w*t13))); + p3 = t2+w*(t5+w*(t8+w*(t11+w*t14))); + p = z*p1-(tt-w*(p2+y*p3)); + r += (tf + p); break; + case 2: + p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5))))); + p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5)))); + r += (-0.5*y + p1/p2); + } + } + else if(ix<0x40200000) { /* x < 8.0 */ + i = (int)x; + t = zero; + y = x-(double)i; + p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))); + q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6))))); + r = half*y+p/q; + z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ + switch(i) { + case 7: z *= (y+6.0); /* FALLTHRU */ + case 6: z *= (y+5.0); /* FALLTHRU */ + case 5: z *= (y+4.0); /* FALLTHRU */ + case 4: z *= (y+3.0); /* FALLTHRU */ + case 3: z *= (y+2.0); /* FALLTHRU */ + r += __ieee754_log(z); break; + } + /* 8.0 <= x < 2**58 */ + } else if (ix < 0x43900000) { + t = __ieee754_log(x); + z = one/x; + y = z*z; + w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6))))); + r = (x-half)*(t-one)+w; + } else + /* 2**58 <= x <= inf */ + r = x*(__ieee754_log(x)-one); + if(hx<0) r = nadj - r; + return r; +} diff --git a/src/extension/script/js/fdlibm/e_log.c b/src/extension/script/js/fdlibm/e_log.c new file mode 100644 index 000000000..8645d6efd --- /dev/null +++ b/src/extension/script/js/fdlibm/e_log.c @@ -0,0 +1,184 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static double zero = 0.0; + +#ifdef __STDC__ + double __ieee754_log(double x) +#else + double __ieee754_log(x) + double x; +#endif +{ + fd_twoints u; + double hfsq,f,s,z,R,w,t1,t2,dk; + int k,hx,i,j; + unsigned lx; + + u.d = x; + hx = __HI(u); /* high word of x */ + lx = __LO(u); /* low word of x */ + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + u.d = x; + hx = __HI(u); /* high word of x */ + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + u.d = x; + __HI(u) = hx|(i^0x3ff00000); /* normalize x or x/2 */ + x = u.d; + k += (i>>20); + f = x-1.0; + if((0x000fffff&(2+hx))<3) { /* |f| < 2**-20 */ + if(f==zero) { + if(k==0) return zero; else {dk=(double)k; + return dk*ln2_hi+dk*ln2_lo;} + } + R = f*f*(0.5-0.33333333333333333*f); + if(k==0) return f-R; else {dk=(double)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/(2.0+f); + dk = (double)k; + z = s*s; + i = hx-0x6147a; + w = z*z; + j = 0x6b851-hx; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} diff --git a/src/extension/script/js/fdlibm/e_log10.c b/src/extension/script/js/fdlibm/e_log10.c new file mode 100644 index 000000000..5f88f4b4c --- /dev/null +++ b/src/extension/script/js/fdlibm/e_log10.c @@ -0,0 +1,134 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_log10.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_log10(x) + * Return the base 10 logarithm of x + * + * Method : + * Let log10_2hi = leading 40 bits of log10(2) and + * log10_2lo = log10(2) - log10_2hi, + * ivln10 = 1/log(10) rounded. + * Then + * n = ilogb(x), + * if(n<0) n = n+1; + * x = scalbn(x,-n); + * log10(x) := n*log10_2hi + (n*log10_2lo + ivln10*log(x)) + * + * Note 1: + * To guarantee log10(10**n)=n, where 10**n is normal, the rounding + * mode must set to Round-to-Nearest. + * Note 2: + * [1/log(10)] rounded to 53 bits has error .198 ulps; + * log10 is monotonic at all binary break points. + * + * Special cases: + * log10(x) is NaN with signal if x < 0; + * log10(+INF) is +INF with no signal; log10(0) is -INF with signal; + * log10(NaN) is that NaN with no signal; + * log10(10**N) = N for N=0,1,...,22. + * + * Constants: + * The hexadecimal values are the intended ones for the following constants. + * The decimal values may be used, provided that the compiler will convert + * from decimal to binary accurately enough to produce the hexadecimal values + * shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +ivln10 = 4.34294481903251816668e-01, /* 0x3FDBCB7B, 0x1526E50E */ +log10_2hi = 3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */ +log10_2lo = 3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */ + +static double zero = 0.0; + +#ifdef __STDC__ + double __ieee754_log10(double x) +#else + double __ieee754_log10(x) + double x; +#endif +{ + fd_twoints u; + double y,z; + int i,k,hx; + unsigned lx; + + u.d = x; + hx = __HI(u); /* high word of x */ + lx = __LO(u); /* low word of x */ + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + u.d = x; + hx = __HI(u); /* high word of x */ + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + i = ((unsigned)k&0x80000000)>>31; + hx = (hx&0x000fffff)|((0x3ff-i)<<20); + y = (double)(k+i); + u.d = x; + __HI(u) = hx; + x = u.d; + z = y*log10_2lo + ivln10*__ieee754_log(x); + return z+y*log10_2hi; +} diff --git a/src/extension/script/js/fdlibm/e_pow.c b/src/extension/script/js/fdlibm/e_pow.c new file mode 100644 index 000000000..18c8d0695 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_pow.c @@ -0,0 +1,386 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_pow.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#if defined(_MSC_VER) +/* Microsoft Compiler */ +#pragma warning( disable : 4723 ) /* disables potential divide by 0 warning */ +#endif + +#ifdef __STDC__ +static const double +#else +static double +#endif +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +zero = 0.0, +one = 1.0, +two = 2.0, +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ +really_big = 1.0e300, +tiny = 1.0e-300, + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ +L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ +L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ +L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ +L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ +L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +#ifdef __STDC__ + double __ieee754_pow(double x, double y) +#else + double __ieee754_pow(x,y) + double x, y; +#endif +{ + fd_twoints ux, uy, uz; + double y1,t1,p_h,t,z,ax; + double z_h,z_l,p_l; + double t2,r,s,u,v,w; + int i,j,k,yisint,n; + int hx,hy,ix,iy; + unsigned lx,ly; + + ux.d = x; uy.d = y; + hx = __HI(ux); lx = __LO(ux); + hy = __HI(uy); ly = __LO(uy); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if((j<<(52-k))==(int)ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) +#ifdef _WIN32 +/* VC++ optimizer reduces y - y to 0 */ + return y / y; +#else + return y - y; /* inf**+-1 is NaN */ +#endif + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return fd_sqrt(x); + } + } + + ax = fd_fabs(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) { +#ifdef HPUX + uz.d = z; + __HI(uz) ^= 1<<31; /* some HPUXes cannot negate 0.. */ + z = uz.d; +#else + z = -z; /* (x<0)**odd = -(|x|**odd) */ +#endif + } + } + return z; + } + } + + /* (x<0)**(non-int) is NaN */ + if((((hx>>31)+1)|yisint)==0) return (x-x)/(x-x); + + /* |y| is really_big */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? really_big*really_big:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? really_big*really_big:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? really_big*really_big:tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? really_big*really_big:tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = x-1; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + uz.d = t1; + __LO(uz) = 0; + t1 = uz.d; + t2 = v-(t1-u); + } else { + double s_h,t_h; + double s2,s_l,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; uz.d = ax; ix = __HI(uz); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|>1)|0x20000000)+0x00080000+(k<<18); + t_h = uz.d; + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = s*s; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+s); + s2 = s_h*s_h; + t_h = 3.0+s2+r; + uz.d = t_h; + __LO(uz) = 0; + t_h = uz.d; + t_l = r-((t_h-3.0)-s2); + /* u+v = s*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*s; + /* 2/(3log2)*(s+...) */ + p_h = u+v; + uz.d = p_h; + __LO(uz) = 0; + p_h = uz.d; + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + uz.d = t1; + __LO(uz) = 0; + t1 = uz.d; + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((((hx>>31)+1)|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + uy.d = y1; + __LO(uy) = 0; + y1 = uy.d; + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + uz.d = z; + j = __HI(uz); + i = __LO(uz); + + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*really_big*really_big; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*really_big*really_big; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zero; + uz.d = t; + __HI(uz) = (n&~(0x000fffff>>k)); + t = uz.d; + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + uz.d = t; + __LO(uz) = 0; + t = uz.d; + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + uz.d = z; + j = __HI(uz); + j += (n<<20); + if((j>>20)<=0) z = fd_scalbn(z,n); /* subnormal output */ + else { uz.d = z; __HI(uz) += (n<<20); z = uz.d; } + return s*z; +} diff --git a/src/extension/script/js/fdlibm/e_rem_pio2.c b/src/extension/script/js/fdlibm/e_rem_pio2.c new file mode 100644 index 000000000..6f9423551 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_rem_pio2.c @@ -0,0 +1,221 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_rem_pio2.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_rem_pio2(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +#include "fdlibm.h" + +/* + * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi + */ +#ifdef __STDC__ +static const int two_over_pi[] = { +#else +static int two_over_pi[] = { +#endif +0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, +0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, +0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, +0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, +0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, +0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, +0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, +0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, +0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, +0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, +0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, +}; + +#ifdef __STDC__ +static const int npio2_hw[] = { +#else +static int npio2_hw[] = { +#endif +0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, +0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, +0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A, +0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C, +0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB, +0x404858EB, 0x404921FB, +}; + +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 33 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 33 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +#ifdef __STDC__ +static const double +#else +static double +#endif +zero = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ +pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ +pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ +pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ +pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ +pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ +pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ + +#ifdef __STDC__ + int __ieee754_rem_pio2(double x, double *y) +#else + int __ieee754_rem_pio2(x,y) + double x,y[]; +#endif +{ + fd_twoints u, ux, uz; + double z,w,t,r,fn; + double tx[3]; + int e0,i,j,nx,n,ix,hx; + + u.d = x; + hx = __HI(u); /* high word of x */ + ix = hx&0x7fffffff; + if(ix<=0x3fe921fb) /* |x| ~<= pi/4 , no need for reduction */ + {y[0] = x; y[1] = 0; return 0;} + if(ix<0x4002d97c) { /* |x| < 3pi/4, special case with n=+-1 */ + if(hx>0) { + z = x - pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z - pio2_1t; + y[1] = (z-y[0])-pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z -= pio2_2; + y[0] = z - pio2_2t; + y[1] = (z-y[0])-pio2_2t; + } + return 1; + } else { /* negative x */ + z = x + pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z + pio2_1t; + y[1] = (z-y[0])+pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z += pio2_2; + y[0] = z + pio2_2t; + y[1] = (z-y[0])+pio2_2t; + } + return -1; + } + } + if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */ + t = fd_fabs(x); + n = (int) (t*invpio2+half); + fn = (double)n; + r = t-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 85 bit */ + if(n<32&&ix!=npio2_hw[n-1]) { + y[0] = r-w; /* quick check no cancellation */ + } else { + j = ix>>20; + y[0] = r-w; + u.d = y[0]; + i = j-(((__HI(u))>>20)&0x7ff); + if(i>16) { /* 2nd iteration needed, good to 118 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + u.d = y[0]; + i = j-(((__HI(u))>>20)&0x7ff); + if(i>49) { /* 3rd iteration need, 151 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + else return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + ux.d = x; uz.d = z; + __LO(uz) = __LO(ux); + z = uz.d; + e0 = (ix>>20)-1046; /* e0 = ilogb(z)-23; */ + uz.d = z; + __HI(uz) = ix - (e0<<20); + z = uz.d; + for(i=0;i<2;i++) { + tx[i] = (double)((int)(z)); + z = (z-tx[i])*two24; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,y,e0,nx,2,two_over_pi); + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + return n; +} diff --git a/src/extension/script/js/fdlibm/e_remainder.c b/src/extension/script/js/fdlibm/e_remainder.c new file mode 100644 index 000000000..de40f0c2a --- /dev/null +++ b/src/extension/script/js/fdlibm/e_remainder.c @@ -0,0 +1,120 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_remainder.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_remainder(x,p) + * Return : + * returns x REM p = x - [x/p]*p as if in infinite + * precise arithmetic, where [x/p] is the (infinite bit) + * integer nearest x/p (in half way case choose the even one). + * Method : + * Based on fmod() return x-[x/p]chopped*p exactlp. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double zero = 0.0; +#else +static double zero = 0.0; +#endif + + +#ifdef __STDC__ + double __ieee754_remainder(double x, double p) +#else + double __ieee754_remainder(x,p) + double x,p; +#endif +{ + fd_twoints u; + int hx,hp; + unsigned sx,lx,lp; + double p_half; + + u.d = x; + hx = __HI(u); /* high word of x */ + lx = __LO(u); /* low word of x */ + u.d = p; + hp = __HI(u); /* high word of p */ + lp = __LO(u); /* low word of p */ + sx = hx&0x80000000; + hp &= 0x7fffffff; + hx &= 0x7fffffff; + + /* purge off exception values */ + if((hp|lp)==0) return (x*p)/(x*p); /* p = 0 */ + if((hx>=0x7ff00000)|| /* x not finite */ + ((hp>=0x7ff00000)&& /* p is NaN */ + (((hp-0x7ff00000)|lp)!=0))) + return (x*p)/(x*p); + + + if (hp<=0x7fdfffff) x = __ieee754_fmod(x,p+p); /* now x < 2p */ + if (((hx-hp)|(lx-lp))==0) return zero*x; + x = fd_fabs(x); + p = fd_fabs(p); + if (hp<0x00200000) { + if(x+x>p) { + x-=p; + if(x+x>=p) x -= p; + } + } else { + p_half = 0.5*p; + if(x>p_half) { + x-=p; + if(x>=p_half) x -= p; + } + } + u.d = x; + __HI(u) ^= sx; + x = u.d; + return x; +} diff --git a/src/extension/script/js/fdlibm/e_scalb.c b/src/extension/script/js/fdlibm/e_scalb.c new file mode 100644 index 000000000..621704ea0 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_scalb.c @@ -0,0 +1,89 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_scalb.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __ieee754_scalb(x, fn) is provide for + * passing various standard test suite. One + * should use scalbn() instead. + */ + +#include "fdlibm.h" + +#ifdef _SCALB_INT +#ifdef __STDC__ + double __ieee754_scalb(double x, int fn) +#else + double __ieee754_scalb(x,fn) + double x; int fn; +#endif +#else +#ifdef __STDC__ + double __ieee754_scalb(double x, double fn) +#else + double __ieee754_scalb(x,fn) + double x, fn; +#endif +#endif +{ +#ifdef _SCALB_INT + return fd_scalbn(x,fn); +#else + if (fd_isnan(x)||fd_isnan(fn)) return x*fn; + if (!fd_finite(fn)) { + if(fn>0.0) return x*fn; + else return x/(-fn); + } + if (fd_rint(fn)!=fn) return (fn-fn)/(fn-fn); + if ( fn > 65000.0) return fd_scalbn(x, 65000); + if (-fn > 65000.0) return fd_scalbn(x,-65000); + return fd_scalbn(x,(int)fn); +#endif +} diff --git a/src/extension/script/js/fdlibm/e_sinh.c b/src/extension/script/js/fdlibm/e_sinh.c new file mode 100644 index 000000000..98ab9b5a3 --- /dev/null +++ b/src/extension/script/js/fdlibm/e_sinh.c @@ -0,0 +1,122 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)e_sinh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_sinh(x) + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinh(-x) = -sinh(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + * 2 + * + * 22 <= x <= lnovft : sinh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : sinh(x) := x*shuge (overflow) + * + * Special cases: + * sinh(x) is |x| if x is +INF, -INF, or NaN. + * only sinh(0)=0 is exact for finite x. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double one = 1.0, shuge = 1.0e307; +#else +static double one = 1.0, shuge = 1.0e307; +#endif + +#ifdef __STDC__ + double __ieee754_sinh(double x) +#else + double __ieee754_sinh(x) + double x; +#endif +{ + fd_twoints u; + double t,w,h; + int ix,jx; + unsigned lx; + + /* High word of |x|. */ + u.d = x; + jx = __HI(u); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3e300000) /* |x|<2**-28 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = fd_expm1(fd_fabs(x)); + if(ix<0x3ff00000) return h*(2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x40862E42) return h*__ieee754_exp(fd_fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x); + if (ix<0x408633CE || (ix==0x408633ce)&&(lx<=(unsigned)0x8fb9f87d)) { + w = __ieee754_exp(0.5*fd_fabs(x)); + t = h*w; + return t*w; + } + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; +} diff --git a/src/extension/script/js/fdlibm/e_sqrt.c b/src/extension/script/js/fdlibm/e_sqrt.c new file mode 100644 index 000000000..91802839b --- /dev/null +++ b/src/extension/script/js/fdlibm/e_sqrt.c @@ -0,0 +1,497 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ +/* @(#)e_sqrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_sqrt(x) + * Return correctly rounded sqrt. + * ------------------------------------------ + * | Use the hardware sqrt if you have one | + * ------------------------------------------ + * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization + * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then + * sqrt(y) = 2^k * sqrt(x) + * 2. Bit by bit computation + * Let q = sqrt(y) truncated to i bit after binary point (q = 1), + * i 0 + * i+1 2 + * s = 2*q , and y = 2 * ( y - q ). (1) + * i i i i + * + * To compute q from q , one checks whether + * i+1 i + * + * -(i+1) 2 + * (q + 2 ) <= y. (2) + * i + * -(i+1) + * If (2) is false, then q = q ; otherwise q = q + 2 . + * i+1 i i+1 i + * + * With some algebric manipulation, it is not difficult to see + * that (2) is equivalent to + * -(i+1) + * s + 2 <= y (3) + * i i + * + * The advantage of (3) is that s and y can be computed by + * i i + * the following recurrence formula: + * if (3) is false + * + * s = s , y = y ; (4) + * i+1 i i+1 i + * + * otherwise, + * -i -(i+1) + * s = s + 2 , y = y - s - 2 (5) + * i+1 i i+1 i i + * + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits, + * it does not necessary to do a full (53-bit) comparison + * in (3). + * 3. Final rounding + * After generating the 53 bits result, we compute one more bit. + * Together with the remainder, we can decide whether the + * result is exact, bigger than 1/2ulp, or less than 1/2ulp + * (it will never equal to 1/2ulp). + * The rounding mode can be detected by checking whether + * huge + tiny is equal to huge, and whether huge - tiny is + * equal to huge for some floating point number "huge" and "tiny". + * + * Special cases: + * sqrt(+-0) = +-0 ... exact + * sqrt(inf) = inf + * sqrt(-ve) = NaN ... with invalid signal + * sqrt(NaN) = NaN ... with invalid signal for signaling NaN + * + * Other methods : see the appended file at the end of the program below. + *--------------- + */ + +#include "fdlibm.h" + +#if defined(_MSC_VER) +/* Microsoft Compiler */ +#pragma warning( disable : 4723 ) /* disables potential divide by 0 warning */ +#endif + +#ifdef __STDC__ +static const double one = 1.0, tiny=1.0e-300; +#else +static double one = 1.0, tiny=1.0e-300; +#endif + +#ifdef __STDC__ + double __ieee754_sqrt(double x) +#else + double __ieee754_sqrt(x) + double x; +#endif +{ + fd_twoints u; + double z; + int sign = (int)0x80000000; + unsigned r,t1,s1,ix1,q1; + int ix0,s0,q,m,t,i; + + u.d = x; + ix0 = __HI(u); /* high word of x */ + ix1 = __LO(u); /* low word of x */ + + /* take care of Inf and NaN */ + if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix0<=0) { + if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix0<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0>>20); + if(m==0) { /* subnormal x */ + while(ix0==0) { + m -= 21; + ix0 |= (ix1>>11); ix1 <<= 21; + } + for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; + m -= i-1; + ix0 |= (ix1>>(32-i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0&0x000fffff)|0x00100000; + if(m&1){ /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + r = sign; + while(r!=0) { + t1 = s1+r; + t = s0; + if((t>31); + ix1 += ix1; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if((ix0|ix1)!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (q1==(unsigned)0xffffffff) { q1=0; q += 1;} + else if (z>one) { + if (q1==(unsigned)0xfffffffe) q+=1; + q1+=2; + } else + q1 += (q1&1); + } + } + ix0 = (q>>1)+0x3fe00000; + ix1 = q1>>1; + if ((q&1)==1) ix1 |= sign; + ix0 += (m <<20); + u.d = z; + __HI(u) = ix0; + __LO(u) = ix1; + z = u.d; + return z; +} + +/* +Other methods (use floating-point arithmetic) +------------- +(This is a copy of a drafted paper by Prof W. Kahan +and K.C. Ng, written in May, 1986) + + Two algorithms are given here to implement sqrt(x) + (IEEE double precision arithmetic) in software. + Both supply sqrt(x) correctly rounded. The first algorithm (in + Section A) uses newton iterations and involves four divisions. + The second one uses reciproot iterations to avoid division, but + requires more multiplications. Both algorithms need the ability + to chop results of arithmetic operations instead of round them, + and the INEXACT flag to indicate when an arithmetic operation + is executed exactly with no roundoff error, all part of the + standard (IEEE 754-1985). The ability to perform shift, add, + subtract and logical AND operations upon 32-bit words is needed + too, though not part of the standard. + +A. sqrt(x) by Newton Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + + 1 11 52 ...widths + ------------------------------------------------------ + x: |s| e | f | + ------------------------------------------------------ + msb lsb msb lsb ...order + + + ------------------------ ------------------------ + x0: |s| e | f1 | x1: | f2 | + ------------------------ ------------------------ + + By performing shifts and subtracts on x0 and x1 (both regarded + as integers), we obtain an 8-bit approximation of sqrt(x) as + follows. + + k := (x0>>1) + 0x1ff80000; + y0 := k - T1[31&(k>>15)]. ... y ~ sqrt(x) to 8 bits + Here k is a 32-bit integer and T1[] is an integer array containing + correction terms. Now magically the floating value of y (y's + leading 32-bit word is y0, the value of its trailing word is 0) + approximates sqrt(x) to almost 8-bit. + + Value of T1: + static int T1[32]= { + 0, 1024, 3062, 5746, 9193, 13348, 18162, 23592, + 29598, 36145, 43202, 50740, 58733, 67158, 75992, 85215, + 83599, 71378, 60428, 50647, 41945, 34246, 27478, 21581, + 16499, 12183, 8588, 5674, 3403, 1742, 661, 130,}; + + (2) Iterative refinement + + Apply Heron's rule three times to y, we have y approximates + sqrt(x) to within 1 ulp (Unit in the Last Place): + + y := (y+x/y)/2 ... almost 17 sig. bits + y := (y+x/y)/2 ... almost 35 sig. bits + y := y-(y-x/y)/2 ... within 1 ulp + + + Remark 1. + Another way to improve y to within 1 ulp is: + + y := (y+x/y) ... almost 17 sig. bits to 2*sqrt(x) + y := y - 0x00100006 ... almost 18 sig. bits to sqrt(x) + + 2 + (x-y )*y + y := y + 2* ---------- ...within 1 ulp + 2 + 3y + x + + + This formula has one division fewer than the one above; however, + it requires more multiplications and additions. Also x must be + scaled in advance to avoid spurious overflow in evaluating the + expression 3y*y+x. Hence it is not recommended uless division + is slow. If division is very slow, then one should use the + reciproot algorithm given in section B. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + I := FALSE; ... reset INEXACT flag I + R := RZ; ... set rounding mode to round-toward-zero + z := x/y; ... chopped quotient, possibly inexact + If(not I) then { ... if the quotient is exact + if(z=y) { + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + } else { + z := z - ulp; ... special rounding + } + } + i := TRUE; ... sqrt(x) is inexact + If (r=RN) then z=z+ulp ... rounded-to-nearest + If (r=RP) then { ... round-toward-+inf + y = y+ulp; z=z+ulp; + } + y := y+z; ... chopped sum + y0:=y0-0x00100000; ... y := y/2 is correctly rounded. + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + + (4) Special cases + + Square root of +inf, +-0, or NaN is itself; + Square root of a negative number is NaN with invalid signal. + + +B. sqrt(x) by Reciproot Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + (see section A). By performing shifs and subtracts on x0 and y0, + we obtain a 7.8-bit approximation of 1/sqrt(x) as follows. + + k := 0x5fe80000 - (x0>>1); + y0:= k - T2[63&(k>>14)]. ... y ~ 1/sqrt(x) to 7.8 bits + + Here k is a 32-bit integer and T2[] is an integer array + containing correction terms. Now magically the floating + value of y (y's leading 32-bit word is y0, the value of + its trailing word y1 is set to zero) approximates 1/sqrt(x) + to almost 7.8-bit. + + Value of T2: + static int T2[64]= { + 0x1500, 0x2ef8, 0x4d67, 0x6b02, 0x87be, 0xa395, 0xbe7a, 0xd866, + 0xf14a, 0x1091b,0x11fcd,0x13552,0x14999,0x15c98,0x16e34,0x17e5f, + 0x18d03,0x19a01,0x1a545,0x1ae8a,0x1b5c4,0x1bb01,0x1bfde,0x1c28d, + 0x1c2de,0x1c0db,0x1ba73,0x1b11c,0x1a4b5,0x1953d,0x18266,0x16be0, + 0x1683e,0x179d8,0x18a4d,0x19992,0x1a789,0x1b445,0x1bf61,0x1c989, + 0x1d16d,0x1d77b,0x1dddf,0x1e2ad,0x1e5bf,0x1e6e8,0x1e654,0x1e3cd, + 0x1df2a,0x1d635,0x1cb16,0x1be2c,0x1ae4e,0x19bde,0x1868e,0x16e2e, + 0x1527f,0x1334a,0x11051,0xe951, 0xbe01, 0x8e0d, 0x5924, 0x1edd,}; + + (2) Iterative refinement + + Apply Reciproot iteration three times to y and multiply the + result by x to get an approximation z that matches sqrt(x) + to about 1 ulp. To be exact, we will have + -1ulp < sqrt(x)-z<1.0625ulp. + + ... set rounding mode to Round-to-nearest + y := y*(1.5-0.5*x*y*y) ... almost 15 sig. bits to 1/sqrt(x) + y := y*((1.5-2^-30)+0.5*x*y*y)... about 29 sig. bits to 1/sqrt(x) + ... special arrangement for better accuracy + z := x*y ... 29 bits to sqrt(x), with z*y<1 + z := z + 0.5*z*(1-z*y) ... about 1 ulp to sqrt(x) + + Remark 2. The constant 1.5-2^-30 is chosen to bias the error so that + (a) the term z*y in the final iteration is always less than 1; + (b) the error in the final result is biased upward so that + -1 ulp < sqrt(x) - z < 1.0625 ulp + instead of |sqrt(x)-z|<1.03125ulp. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + R := RZ; ... set rounding mode to round-toward-zero + switch(r) { + case RN: ... round-to-nearest + if(x<= z*(z-ulp)...chopped) z = z - ulp; else + if(x<= z*(z+ulp)...chopped) z = z; else z = z+ulp; + break; + case RZ:case RM: ... round-to-zero or round-to--inf + R:=RP; ... reset rounding mod to round-to-+inf + if(x=(z+ulp)*(z+ulp) ...rounded up) z = z+ulp; + break; + case RP: ... round-to-+inf + if(x>(z+ulp)*(z+ulp)...chopped) z = z+2*ulp; else + if(x>z*z ...chopped) z = z+ulp; + break; + } + + Remark 3. The above comparisons can be done in fixed point. For + example, to compare x and w=z*z chopped, it suffices to compare + x1 and w1 (the trailing parts of x and w), regarding them as + two's complement integers. + + ...Is z an exact square root? + To determine whether z is an exact square root of x, let z1 be the + trailing part of z, and also let x0 and x1 be the leading and + trailing parts of x. + + If ((z1&0x03ffffff)!=0) ... not exact if trailing 26 bits of z!=0 + I := 1; ... Raise Inexact flag: z is not exact + else { + j := 1 - [(x0>>20)&1] ... j = logb(x) mod 2 + k := z1 >> 26; ... get z's 25-th and 26-th + fraction bits + I := i or (k&j) or ((k&(j+j+1))!=(x1&3)); + } + R:= r ... restore rounded mode + return sqrt(x):=z. + + If multiplication is cheaper then the foregoing red tape, the + Inexact flag can be evaluated by + + I := i; + I := (z*z!=x) or I. + + Note that z*z can overwrite I; this value must be sensed if it is + True. + + Remark 4. If z*z = x exactly, then bit 25 to bit 0 of z1 must be + zero. + + -------------------- + z1: | f2 | + -------------------- + bit 31 bit 0 + + Further more, bit 27 and 26 of z1, bit 0 and 1 of x1, and the odd + or even of logb(x) have the following relations: + + ------------------------------------------------- + bit 27,26 of z1 bit 1,0 of x1 logb(x) + ------------------------------------------------- + 00 00 odd and even + 01 01 even + 10 10 odd + 10 00 even + 11 01 even + ------------------------------------------------- + + (4) Special cases (see (4) of Section A). + + */ + diff --git a/src/extension/script/js/fdlibm/fdlibm.h b/src/extension/script/js/fdlibm/fdlibm.h new file mode 100644 index 000000000..b4063f82f --- /dev/null +++ b/src/extension/script/js/fdlibm/fdlibm.h @@ -0,0 +1,273 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)fdlibm.h 1.5 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* Modified defines start here.. */ +#undef __LITTLE_ENDIAN + +#ifdef _WIN32 +#define huge myhuge +#define __LITTLE_ENDIAN +#endif + +#ifdef XP_OS2 +#define __LITTLE_ENDIAN +#endif + +#if defined(linux) && defined(__i386__) +#define __LITTLE_ENDIAN +#endif + +/* End here. The rest is the standard file. */ + +#ifdef __NEWVALID /* special setup for Sun test regime */ +#if defined(i386) || defined(i486) || \ + defined(intel) || defined(x86) || defined(i86pc) +#define __LITTLE_ENDIAN +#endif +#endif + +typedef union { +#ifdef __LITTLE_ENDIAN + struct { int lo, hi; } ints; +#else + struct { int hi, lo; } ints; +#endif + double d; +} fd_twoints; + +#define __HI(x) x.ints.hi +#define __LO(x) x.ints.lo + +#undef __P +#ifdef __STDC__ +#define __P(p) p +#else +#define __P(p) () +#endif + +/* + * ANSI/POSIX + */ + +extern int signgam; + +#define MAXFLOAT ((float)3.40282346638528860e+38) + +enum fdversion {fdlibm_ieee = -1, fdlibm_svid, fdlibm_xopen, fdlibm_posix}; + +#define _LIB_VERSION_TYPE enum fdversion +#define _LIB_VERSION _fdlib_version + +/* if global variable _LIB_VERSION is not desirable, one may + * change the following to be a constant by: + * #define _LIB_VERSION_TYPE const enum version + * In that case, after one initializes the value _LIB_VERSION (see + * s_lib_version.c) during compile time, it cannot be modified + * in the middle of a program + */ +extern _LIB_VERSION_TYPE _LIB_VERSION; + +#define _IEEE_ fdlibm_ieee +#define _SVID_ fdlibm_svid +#define _XOPEN_ fdlibm_xopen +#define _POSIX_ fdlibm_posix + +struct exception { + int type; + char *name; + double arg1; + double arg2; + double retval; +}; + +#define HUGE MAXFLOAT + +/* + * set X_TLOSS = pi*2**52, which is possibly defined in + * (one may replace the following line by "#include ") + */ + +#define X_TLOSS 1.41484755040568800000e+16 + +#define DOMAIN 1 +#define SING 2 +#define OVERFLOW 3 +#define UNDERFLOW 4 +#define TLOSS 5 +#define PLOSS 6 + +/* + * ANSI/POSIX + */ + +extern double fd_acos __P((double)); +extern double fd_asin __P((double)); +extern double fd_atan __P((double)); +extern double fd_atan2 __P((double, double)); +extern double fd_cos __P((double)); +extern double fd_sin __P((double)); +extern double fd_tan __P((double)); + +extern double fd_cosh __P((double)); +extern double fd_sinh __P((double)); +extern double fd_tanh __P((double)); + +extern double fd_exp __P((double)); +extern double fd_frexp __P((double, int *)); +extern double fd_ldexp __P((double, int)); +extern double fd_log __P((double)); +extern double fd_log10 __P((double)); +extern double fd_modf __P((double, double *)); + +extern double fd_pow __P((double, double)); +extern double fd_sqrt __P((double)); + +extern double fd_ceil __P((double)); +extern double fd_fabs __P((double)); +extern double fd_floor __P((double)); +extern double fd_fmod __P((double, double)); + +extern double fd_erf __P((double)); +extern double fd_erfc __P((double)); +extern double fd_gamma __P((double)); +extern double fd_hypot __P((double, double)); +extern int fd_isnan __P((double)); +extern int fd_finite __P((double)); +extern double fd_j0 __P((double)); +extern double fd_j1 __P((double)); +extern double fd_jn __P((int, double)); +extern double fd_lgamma __P((double)); +extern double fd_y0 __P((double)); +extern double fd_y1 __P((double)); +extern double fd_yn __P((int, double)); + +extern double fd_acosh __P((double)); +extern double fd_asinh __P((double)); +extern double fd_atanh __P((double)); +extern double fd_cbrt __P((double)); +extern double fd_logb __P((double)); +extern double fd_nextafter __P((double, double)); +extern double fd_remainder __P((double, double)); +#ifdef _SCALB_INT +extern double fd_scalb __P((double, int)); +#else +extern double fd_scalb __P((double, double)); +#endif + +extern int fd_matherr __P((struct exception *)); + +/* + * IEEE Test Vector + */ +extern double significand __P((double)); + +/* + * Functions callable from C, intended to support IEEE arithmetic. + */ +extern double fd_copysign __P((double, double)); +extern int fd_ilogb __P((double)); +extern double fd_rint __P((double)); +extern double fd_scalbn __P((double, int)); + +/* + * BSD math library entry points + */ +extern double fd_expm1 __P((double)); +extern double fd_log1p __P((double)); + +/* + * Reentrant version of gamma & lgamma; passes signgam back by reference + * as the second argument; user must allocate space for signgam. + */ +#ifdef _REENTRANT +extern double gamma_r __P((double, int *)); +extern double lgamma_r __P((double, int *)); +#endif /* _REENTRANT */ + +/* ieee style elementary functions */ +extern double __ieee754_sqrt __P((double)); +extern double __ieee754_acos __P((double)); +extern double __ieee754_acosh __P((double)); +extern double __ieee754_log __P((double)); +extern double __ieee754_atanh __P((double)); +extern double __ieee754_asin __P((double)); +extern double __ieee754_atan2 __P((double,double)); +extern double __ieee754_exp __P((double)); +extern double __ieee754_cosh __P((double)); +extern double __ieee754_fmod __P((double,double)); +extern double __ieee754_pow __P((double,double)); +extern double __ieee754_lgamma_r __P((double,int *)); +extern double __ieee754_gamma_r __P((double,int *)); +extern double __ieee754_lgamma __P((double)); +extern double __ieee754_gamma __P((double)); +extern double __ieee754_log10 __P((double)); +extern double __ieee754_sinh __P((double)); +extern double __ieee754_hypot __P((double,double)); +extern double __ieee754_j0 __P((double)); +extern double __ieee754_j1 __P((double)); +extern double __ieee754_y0 __P((double)); +extern double __ieee754_y1 __P((double)); +extern double __ieee754_jn __P((int,double)); +extern double __ieee754_yn __P((int,double)); +extern double __ieee754_remainder __P((double,double)); +extern int __ieee754_rem_pio2 __P((double,double*)); +#ifdef _SCALB_INT +extern double __ieee754_scalb __P((double,int)); +#else +extern double __ieee754_scalb __P((double,double)); +#endif + +/* fdlibm kernel function */ +extern double __kernel_standard __P((double,double,int,int*)); +extern double __kernel_sin __P((double,double,int)); +extern double __kernel_cos __P((double,double)); +extern double __kernel_tan __P((double,double,int)); +extern int __kernel_rem_pio2 __P((double*,double*,int,int,int,const int*)); diff --git a/src/extension/script/js/fdlibm/k_cos.c b/src/extension/script/js/fdlibm/k_cos.c new file mode 100644 index 000000000..17b505d58 --- /dev/null +++ b/src/extension/script/js/fdlibm/k_cos.c @@ -0,0 +1,134 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __kernel_cos( x, y ) + * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * + * Algorithm + * 1. Since cos(-x) = cos(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. + * 3. cos(x) is approximated by a polynomial of degree 14 on + * [0,pi/4] + * 4 14 + * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x + * where the remez error is + * + * | 2 4 6 8 10 12 14 | -58 + * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 + * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then + * cos(x) = 1 - x*x/2 + r + * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y, + * a correction term is necessary in cos(x) and hence + * cos(x+y) = 1 - (x*x/2 - (r - x*y)) + * For better accuracy when x > 0.3, let qx = |x|/4 with + * the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. + * Then + * cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)). + * Note that 1-qx and (x*x/2-qx) is EXACT here, and the + * magnitude of the latter is at least a quarter of x*x/2, + * thus, reducing the rounding error in the subtraction. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ +C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ +C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ +C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ +C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ +C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ + +#ifdef __STDC__ + double __kernel_cos(double x, double y) +#else + double __kernel_cos(x, y) + double x,y; +#endif +{ + fd_twoints u; + double a,hz,z,r,qx; + int ix; + u.d = x; + ix = __HI(u)&0x7fffffff; /* ix = |x|'s high word*/ + if(ix<0x3e400000) { /* if x < 2**27 */ + if(((int)x)==0) return one; /* generate inexact */ + } + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); + if(ix < 0x3FD33333) /* if |x| < 0.3 */ + return one - (0.5*z - (z*r - x*y)); + else { + if(ix > 0x3fe90000) { /* x > 0.78125 */ + qx = 0.28125; + } else { + u.d = qx; + __HI(u) = ix-0x00200000; /* x/4 */ + __LO(u) = 0; + qx = u.d; + } + hz = 0.5*z-qx; + a = one-qx; + return a - (hz - (z*r-x*y)); + } +} diff --git a/src/extension/script/js/fdlibm/k_rem_pio2.c b/src/extension/script/js/fdlibm/k_rem_pio2.c new file mode 100644 index 000000000..d261e190a --- /dev/null +++ b/src/extension/script/js/fdlibm/k_rem_pio2.c @@ -0,0 +1,354 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)k_rem_pio2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __kernel_rem_pio2(x,y,e0,nx,prec,ipio2) + * double x[],y[]; int e0,nx,prec; int ipio2[]; + * + * __kernel_rem_pio2 return the last three digits of N with + * y = x - N*pi/2 + * so that |y| < pi/2. + * + * The method is to compute the integer (mod 8) and fraction parts of + * (2/pi)*x without doing the full multiplication. In general we + * skip the part of the product that are known to be a huge integer ( + * more accurately, = 0 mod 8 ). Thus the number of operations are + * independent of the exponent of the input. + * + * (2/pi) is represented by an array of 24-bit integers in ipio2[]. + * + * Input parameters: + * x[] The input value (must be positive) is broken into nx + * pieces of 24-bit integers in double precision format. + * x[i] will be the i-th 24 bit of x. The scaled exponent + * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 + * match x's up to 24 bits. + * + * Example of breaking a double positive z into x[0]+x[1]+x[2]: + * e0 = ilogb(z)-23 + * z = scalbn(z,-e0) + * for i = 0,1,2 + * x[i] = floor(z) + * z = (z-x[i])*2**24 + * + * + * y[] ouput result in an array of double precision numbers. + * The dimension of y[] is: + * 24-bit precision 1 + * 53-bit precision 2 + * 64-bit precision 2 + * 113-bit precision 3 + * The actual value is the sum of them. Thus for 113-bit + * precison, one may have to do something like: + * + * long double t,w,r_head, r_tail; + * t = (long double)y[2] + (long double)y[1]; + * w = (long double)y[0]; + * r_head = t+w; + * r_tail = w - (r_head - t); + * + * e0 The exponent of x[0] + * + * nx dimension of x[] + * + * prec an integer indicating the precision: + * 0 24 bits (single) + * 1 53 bits (double) + * 2 64 bits (extended) + * 3 113 bits (quad) + * + * ipio2[] + * integer array, contains the (24*i)-th to (24*i+23)-th + * bit of 2/pi after binary point. The corresponding + * floating value is + * + * ipio2[i] * 2^(-24(i+1)). + * + * External function: + * double scalbn(), floor(); + * + * + * Here is the description of some local variables: + * + * jk jk+1 is the initial number of terms of ipio2[] needed + * in the computation. The recommended value is 2,3,4, + * 6 for single, double, extended,and quad. + * + * jz local integer variable indicating the number of + * terms of ipio2[] used. + * + * jx nx - 1 + * + * jv index for pointing to the suitable ipio2[] for the + * computation. In general, we want + * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 + * is an integer. Thus + * e0-3-24*jv >= 0 or (e0-3)/24 >= jv + * Hence jv = max(0,(e0-3)/24). + * + * jp jp+1 is the number of terms in PIo2[] needed, jp = jk. + * + * q[] double array with integral value, representing the + * 24-bits chunk of the product of x and 2/pi. + * + * q0 the corresponding exponent of q[0]. Note that the + * exponent for q[i] would be q0-24*i. + * + * PIo2[] double precision array, obtained by cutting pi/2 + * into 24 bits chunks. + * + * f[] ipio2[] in floating point + * + * iq[] integer array by breaking up q[] in 24-bits chunk. + * + * fq[] final product of x*(2/pi) in fq[0],..,fq[jk] + * + * ih integer. If >0 it indicates q[] is >= 0.5, hence + * it also indicates the *sign* of the result. + * + */ + + +/* + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const int init_jk[] = {2,3,4,6}; /* initial value for jk */ +#else +static int init_jk[] = {2,3,4,6}; +#endif + +#ifdef __STDC__ +static const double PIo2[] = { +#else +static double PIo2[] = { +#endif + 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ + 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ + 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ + 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ + 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ + 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ + 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ + 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ +}; + +#ifdef __STDC__ +static const double +#else +static double +#endif +zero = 0.0, +one = 1.0, +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +twon24 = 5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */ + +#ifdef __STDC__ + int __kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec, const int *ipio2) +#else + int __kernel_rem_pio2(x,y,e0,nx,prec,ipio2) + double x[], y[]; int e0,nx,prec; int ipio2[]; +#endif +{ + int jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; + double z,fw,f[20],fq[20],q[20]; + + /* initialize jk*/ + jk = init_jk[prec]; + jp = jk; + + /* determine jx,jv,q0, note that 3>q0 */ + jx = nx-1; + jv = (e0-3)/24; if(jv<0) jv=0; + q0 = e0-24*(jv+1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + j = jv-jx; m = jx+jk; + for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (double) ipio2[j]; + + /* compute q[0],q[1],...q[jk] */ + for (i=0;i<=jk;i++) { + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw; + } + + jz = jk; +recompute: + /* distill q[] into iq[] reversingly */ + for(i=0,j=jz,z=q[jz];j>0;i++,j--) { + fw = (double)((int)(twon24* z)); + iq[i] = (int)(z-two24*fw); + z = q[j-1]+fw; + } + + /* compute n */ + z = fd_scalbn(z,q0); /* actual value of z */ + z -= 8.0*fd_floor(z*0.125); /* trim off integer >= 8 */ + n = (int) z; + z -= (double)n; + ih = 0; + if(q0>0) { /* need iq[jz-1] to determine n */ + i = (iq[jz-1]>>(24-q0)); n += i; + iq[jz-1] -= i<<(24-q0); + ih = iq[jz-1]>>(23-q0); + } + else if(q0==0) ih = iq[jz-1]>>23; + else if(z>=0.5) ih=2; + + if(ih>0) { /* q > 0.5 */ + n += 1; carry = 0; + for(i=0;i0) { /* rare case: chance is 1 in 12 */ + switch(q0) { + case 1: + iq[jz-1] &= 0x7fffff; break; + case 2: + iq[jz-1] &= 0x3fffff; break; + } + } + if(ih==2) { + z = one - z; + if(carry!=0) z -= fd_scalbn(one,q0); + } + } + + /* check if recomputation is needed */ + if(z==zero) { + j = 0; + for (i=jz-1;i>=jk;i--) j |= iq[i]; + if(j==0) { /* need recomputation */ + for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ + + for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ + f[jx+i] = (double) ipio2[jv+i]; + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + /* chop off zero terms */ + if(z==0.0) { + jz -= 1; q0 -= 24; + while(iq[jz]==0) { jz--; q0-=24;} + } else { /* break z into 24-bit if necessary */ + z = fd_scalbn(z,-q0); + if(z>=two24) { + fw = (double)((int)(twon24*z)); + iq[jz] = (int)(z-two24*fw); + jz += 1; q0 += 24; + iq[jz] = (int) fw; + } else iq[jz] = (int) z ; + } + + /* convert integer "bit" chunk to floating-point value */ + fw = fd_scalbn(one,q0); + for(i=jz;i>=0;i--) { + q[i] = fw*(double)iq[i]; fw*=twon24; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + for(i=jz;i>=0;i--) { + for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; + fq[jz-i] = fw; + } + + /* compress fq[] into y[] */ + switch(prec) { + case 0: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + break; + case 1: + case 2: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + fw = fq[0]-fw; + for (i=1;i<=jz;i++) fw += fq[i]; + y[1] = (ih==0)? fw: -fw; + break; + case 3: /* painful */ + for (i=jz;i>0;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (i=jz;i>1;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; + if(ih==0) { + y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; + } else { + y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; + } + } + return n&7; +} diff --git a/src/extension/script/js/fdlibm/k_sin.c b/src/extension/script/js/fdlibm/k_sin.c new file mode 100644 index 000000000..d2bdabd6d --- /dev/null +++ b/src/extension/script/js/fdlibm/k_sin.c @@ -0,0 +1,114 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __kernel_sin( x, y, iy) + * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + * + * Algorithm + * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0. + * 3. sin(x) is approximated by a polynomial of degree 13 on + * [0,pi/4] + * 3 13 + * sin(x) ~ x + S1*x + ... + S6*x + * where + * + * |sin(x) 2 4 6 8 10 12 | -58 + * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 + * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y + * ~ sin(x) + (1-x*x/2)*y + * For better accuracy, let + * 3 2 2 2 2 + * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ +S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ +S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ +S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ +S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ + +#ifdef __STDC__ + double __kernel_sin(double x, double y, int iy) +#else + double __kernel_sin(x, y, iy) + double x,y; int iy; /* iy=0 if y is zero */ +#endif +{ + fd_twoints u; + double z,r,v; + int ix; + u.d = x; + ix = __HI(u)&0x7fffffff; /* high word of x */ + if(ix<0x3e400000) /* |x| < 2**-27 */ + {if((int)x==0) return x;} /* generate inexact */ + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*S6))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/src/extension/script/js/fdlibm/k_standard.c b/src/extension/script/js/fdlibm/k_standard.c new file mode 100644 index 000000000..720109c9d --- /dev/null +++ b/src/extension/script/js/fdlibm/k_standard.c @@ -0,0 +1,785 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)k_standard.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "fdlibm.h" + +/* XXX ugly hack to get msvc to link without error. */ +#if _LIB_VERSION == _IEEE_ && !(defined(DARWIN) || defined(XP_MACOSX)) + int errno; +# define EDOM 0 +# define ERANGE 0 +#else +# include +#endif + + +#ifndef _USE_WRITE +#include /* fputs(), stderr */ +#define WRITE2(u,v) fputs(u, stderr) +#else /* !defined(_USE_WRITE) */ +#include /* write */ +#define WRITE2(u,v) write(2, u, v) +#undef fflush +#endif /* !defined(_USE_WRITE) */ + +static double zero = 0.0; /* used as const */ + +/* + * Standard conformance (non-IEEE) on exception cases. + * Mapping: + * 1 -- acos(|x|>1) + * 2 -- asin(|x|>1) + * 3 -- atan2(+-0,+-0) + * 4 -- hypot overflow + * 5 -- cosh overflow + * 6 -- exp overflow + * 7 -- exp underflow + * 8 -- y0(0) + * 9 -- y0(-ve) + * 10-- y1(0) + * 11-- y1(-ve) + * 12-- yn(0) + * 13-- yn(-ve) + * 14-- lgamma(finite) overflow + * 15-- lgamma(-integer) + * 16-- log(0) + * 17-- log(x<0) + * 18-- log10(0) + * 19-- log10(x<0) + * 20-- pow(0.0,0.0) + * 21-- pow(x,y) overflow + * 22-- pow(x,y) underflow + * 23-- pow(0,negative) + * 24-- pow(neg,non-integral) + * 25-- sinh(finite) overflow + * 26-- sqrt(negative) + * 27-- fmod(x,0) + * 28-- remainder(x,0) + * 29-- acosh(x<1) + * 30-- atanh(|x|>1) + * 31-- atanh(|x|=1) + * 32-- scalb overflow + * 33-- scalb underflow + * 34-- j0(|x|>X_TLOSS) + * 35-- y0(x>X_TLOSS) + * 36-- j1(|x|>X_TLOSS) + * 37-- y1(x>X_TLOSS) + * 38-- jn(|x|>X_TLOSS, n) + * 39-- yn(x>X_TLOSS, n) + * 40-- gamma(finite) overflow + * 41-- gamma(-integer) + * 42-- pow(NaN,0.0) + */ + + +#ifdef __STDC__ + double __kernel_standard(double x, double y, int type, int *err) +#else + double __kernel_standard(x,y,type, err) + double x,y; int type;int *err; +#endif +{ + struct exception exc; +#ifndef HUGE_VAL /* this is the only routine that uses HUGE_VAL */ +#define HUGE_VAL inf + double inf = 0.0; + fd_twoints u; + + u.d = inf; + __HI(u) = 0x7ff00000; /* set inf to infinite */ + inf = u.d; +#endif + + *err = 0; + +#ifdef _USE_WRITE + (void) fflush(stdout); +#endif + exc.arg1 = x; + exc.arg2 = y; + switch(type) { + case 1: + /* acos(|x|>1) */ + exc.type = DOMAIN; + exc.name = "acos"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if(_LIB_VERSION == _SVID_) { + (void) WRITE2("acos: DOMAIN error\n", 19); + } + *err = EDOM; + } + break; + case 2: + /* asin(|x|>1) */ + exc.type = DOMAIN; + exc.name = "asin"; + exc.retval = zero; + if(_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if(_LIB_VERSION == _SVID_) { + (void) WRITE2("asin: DOMAIN error\n", 19); + } + *err = EDOM; + } + break; + case 3: + /* atan2(+-0,+-0) */ + exc.arg1 = y; + exc.arg2 = x; + exc.type = DOMAIN; + exc.name = "atan2"; + exc.retval = zero; + if(_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if(_LIB_VERSION == _SVID_) { + (void) WRITE2("atan2: DOMAIN error\n", 20); + } + *err = EDOM; + } + break; + case 4: + /* hypot(finite,finite) overflow */ + exc.type = OVERFLOW; + exc.name = "hypot"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 5: + /* cosh(finite) overflow */ + exc.type = OVERFLOW; + exc.name = "cosh"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 6: + /* exp(finite) overflow */ + exc.type = OVERFLOW; + exc.name = "exp"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 7: + /* exp(finite) underflow */ + exc.type = UNDERFLOW; + exc.name = "exp"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 8: + /* y0(0) = -inf */ + exc.type = DOMAIN; /* should be SING for IEEE */ + exc.name = "y0"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("y0: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 9: + /* y0(x<0) = NaN */ + exc.type = DOMAIN; + exc.name = "y0"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("y0: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 10: + /* y1(0) = -inf */ + exc.type = DOMAIN; /* should be SING for IEEE */ + exc.name = "y1"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("y1: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 11: + /* y1(x<0) = NaN */ + exc.type = DOMAIN; + exc.name = "y1"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("y1: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 12: + /* yn(n,0) = -inf */ + exc.type = DOMAIN; /* should be SING for IEEE */ + exc.name = "yn"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("yn: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 13: + /* yn(x<0) = NaN */ + exc.type = DOMAIN; + exc.name = "yn"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("yn: DOMAIN error\n", 17); + } + *err = EDOM; + } + break; + case 14: + /* lgamma(finite) overflow */ + exc.type = OVERFLOW; + exc.name = "lgamma"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 15: + /* lgamma(-integer) or lgamma(0) */ + exc.type = SING; + exc.name = "lgamma"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("lgamma: SING error\n", 19); + } + *err = EDOM; + } + break; + case 16: + /* log(0) */ + exc.type = SING; + exc.name = "log"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("log: SING error\n", 16); + } + *err = EDOM; + } + break; + case 17: + /* log(x<0) */ + exc.type = DOMAIN; + exc.name = "log"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("log: DOMAIN error\n", 18); + } + *err = EDOM; + } + break; + case 18: + /* log10(0) */ + exc.type = SING; + exc.name = "log10"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("log10: SING error\n", 18); + } + *err = EDOM; + } + break; + case 19: + /* log10(x<0) */ + exc.type = DOMAIN; + exc.name = "log10"; + if (_LIB_VERSION == _SVID_) + exc.retval = -HUGE; + else + exc.retval = -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("log10: DOMAIN error\n", 20); + } + *err = EDOM; + } + break; + case 20: + /* pow(0.0,0.0) */ + /* error only if _LIB_VERSION == _SVID_ */ + exc.type = DOMAIN; + exc.name = "pow"; + exc.retval = zero; + if (_LIB_VERSION != _SVID_) exc.retval = 1.0; + else if (!fd_matherr(&exc)) { + (void) WRITE2("pow(0,0): DOMAIN error\n", 23); + *err = EDOM; + } + break; + case 21: + /* pow(x,y) overflow */ + exc.type = OVERFLOW; + exc.name = "pow"; + if (_LIB_VERSION == _SVID_) { + exc.retval = HUGE; + y *= 0.5; + if(xzero) ? HUGE : -HUGE); + else + exc.retval = ( (x>zero) ? HUGE_VAL : -HUGE_VAL); + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 26: + /* sqrt(x<0) */ + exc.type = DOMAIN; + exc.name = "sqrt"; + if (_LIB_VERSION == _SVID_) + exc.retval = zero; + else + exc.retval = zero/zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("sqrt: DOMAIN error\n", 19); + } + *err = EDOM; + } + break; + case 27: + /* fmod(x,0) */ + exc.type = DOMAIN; + exc.name = "fmod"; + if (_LIB_VERSION == _SVID_) + exc.retval = x; + else + exc.retval = zero/zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("fmod: DOMAIN error\n", 20); + } + *err = EDOM; + } + break; + case 28: + /* remainder(x,0) */ + exc.type = DOMAIN; + exc.name = "remainder"; + exc.retval = zero/zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("remainder: DOMAIN error\n", 24); + } + *err = EDOM; + } + break; + case 29: + /* acosh(x<1) */ + exc.type = DOMAIN; + exc.name = "acosh"; + exc.retval = zero/zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("acosh: DOMAIN error\n", 20); + } + *err = EDOM; + } + break; + case 30: + /* atanh(|x|>1) */ + exc.type = DOMAIN; + exc.name = "atanh"; + exc.retval = zero/zero; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("atanh: DOMAIN error\n", 20); + } + *err = EDOM; + } + break; + case 31: + /* atanh(|x|=1) */ + exc.type = SING; + exc.name = "atanh"; + exc.retval = x/zero; /* sign(x)*inf */ + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("atanh: SING error\n", 18); + } + *err = EDOM; + } + break; + case 32: + /* scalb overflow; SVID also returns +-HUGE_VAL */ + exc.type = OVERFLOW; + exc.name = "scalb"; + exc.retval = x > zero ? HUGE_VAL : -HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 33: + /* scalb underflow */ + exc.type = UNDERFLOW; + exc.name = "scalb"; + exc.retval = fd_copysign(zero,x); + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 34: + /* j0(|x|>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "j0"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 35: + /* y0(x>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "y0"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 36: + /* j1(|x|>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "j1"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 37: + /* y1(x>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "y1"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 38: + /* jn(|x|>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "jn"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 39: + /* yn(x>X_TLOSS) */ + exc.type = TLOSS; + exc.name = "yn"; + exc.retval = zero; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2(exc.name, 2); + (void) WRITE2(": TLOSS error\n", 14); + } + *err = ERANGE; + } + break; + case 40: + /* gamma(finite) overflow */ + exc.type = OVERFLOW; + exc.name = "gamma"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = ERANGE; + else if (!fd_matherr(&exc)) { + *err = ERANGE; + } + break; + case 41: + /* gamma(-integer) or gamma(0) */ + exc.type = SING; + exc.name = "gamma"; + if (_LIB_VERSION == _SVID_) + exc.retval = HUGE; + else + exc.retval = HUGE_VAL; + if (_LIB_VERSION == _POSIX_) + *err = EDOM; + else if (!fd_matherr(&exc)) { + if (_LIB_VERSION == _SVID_) { + (void) WRITE2("gamma: SING error\n", 18); + } + *err = EDOM; + } + break; + case 42: + /* pow(NaN,0.0) */ + /* error only if _LIB_VERSION == _SVID_ & _XOPEN_ */ + exc.type = DOMAIN; + exc.name = "pow"; + exc.retval = x; + if (_LIB_VERSION == _IEEE_ || + _LIB_VERSION == _POSIX_) exc.retval = 1.0; + else if (!fd_matherr(&exc)) { + *err = EDOM; + } + break; + } + return exc.retval; +} diff --git a/src/extension/script/js/fdlibm/k_tan.c b/src/extension/script/js/fdlibm/k_tan.c new file mode 100644 index 000000000..1e7681b88 --- /dev/null +++ b/src/extension/script/js/fdlibm/k_tan.c @@ -0,0 +1,170 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)k_tan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __kernel_tan( x, y, k ) + * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k=1) or + * -1/tan (if k= -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. + * 3. tan(x) is approximated by a odd polynomial of degree 27 on + * [0,0.67434] + * 3 27 + * tan(x) ~ x + T1*x + ... + T13*x + * where + * + * |tan(x) 2 4 26 | -59.2 + * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 + * | x | + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * 3 2 2 2 2 + * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) + * then + * 3 2 + * tan(x+y) = x + (T1*x + (x *(r+y)+y)) + * + * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +#include "fdlibm.h" +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pio4 = 7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */ +pio4lo= 3.06161699786838301793e-17, /* 0x3C81A626, 0x33145C07 */ +T[] = { + 3.33333333333334091986e-01, /* 0x3FD55555, 0x55555563 */ + 1.33333333333201242699e-01, /* 0x3FC11111, 0x1110FE7A */ + 5.39682539762260521377e-02, /* 0x3FABA1BA, 0x1BB341FE */ + 2.18694882948595424599e-02, /* 0x3F9664F4, 0x8406D637 */ + 8.86323982359930005737e-03, /* 0x3F8226E3, 0xE96E8493 */ + 3.59207910759131235356e-03, /* 0x3F6D6D22, 0xC9560328 */ + 1.45620945432529025516e-03, /* 0x3F57DBC8, 0xFEE08315 */ + 5.88041240820264096874e-04, /* 0x3F4344D8, 0xF2F26501 */ + 2.46463134818469906812e-04, /* 0x3F3026F7, 0x1A8D1068 */ + 7.81794442939557092300e-05, /* 0x3F147E88, 0xA03792A6 */ + 7.14072491382608190305e-05, /* 0x3F12B80F, 0x32F0A7E9 */ + -1.85586374855275456654e-05, /* 0xBEF375CB, 0xDB605373 */ + 2.59073051863633712884e-05, /* 0x3EFB2A70, 0x74BF7AD4 */ +}; + +#ifdef __STDC__ + double __kernel_tan(double x, double y, int iy) +#else + double __kernel_tan(x, y, iy) + double x,y; int iy; +#endif +{ + fd_twoints u; + double z,r,v,w,s; + int ix,hx; + u.d = x; + hx = __HI(u); /* high word of x */ + ix = hx&0x7fffffff; /* high word of |x| */ + if(ix<0x3e300000) /* x < 2**-28 */ + {if((int)x==0) { /* generate inexact */ + u.d =x; + if(((ix|__LO(u))|(iy+1))==0) return one/fd_fabs(x); + else return (iy==1)? x: -one/x; + } + } + if(ix>=0x3FE59428) { /* |x|>=0.6744 */ + if(hx<0) {x = -x; y = -y;} + z = pio4-x; + w = pio4lo-y; + x = z+w; y = 0.0; + } + z = x*x; + w = z*z; + /* Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11])))); + v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12]))))); + s = z*x; + r = y + z*(s*(r+v)+y); + r += T[0]*s; + w = x+r; + if(ix>=0x3FE59428) { + v = (double)iy; + return (double)(1-((hx>>30)&2))*(v-2.0*(x-(w*w/(w+v)-r))); + } + if(iy==1) return w; + else { /* if allow error up to 2 ulp, + simply return -1.0/(x+r) here */ + /* compute -1.0/(x+r) accurately */ + double a,t; + z = w; + u.d = z; + __LO(u) = 0; + z = u.d; + v = r-(z - x); /* z+v = r+x */ + t = a = -1.0/w; /* a = -1.0/w */ + u.d = t; + __LO(u) = 0; + t = u.d; + s = 1.0+t*z; + return t+a*(s+t*v); + } +} diff --git a/src/extension/script/js/fdlibm/s_asinh.c b/src/extension/script/js/fdlibm/s_asinh.c new file mode 100644 index 000000000..fdf70a91c --- /dev/null +++ b/src/extension/script/js/fdlibm/s_asinh.c @@ -0,0 +1,101 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_asinh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* asinh(x) + * Method : + * Based on + * asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] + * we have + * asinh(x) := x if 1+x*x=1, + * := sign(x)*(log(x)+ln2)) for large |x|, else + * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else + * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +ln2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +really_big= 1.00000000000000000000e+300; + +#ifdef __STDC__ + double fd_asinh(double x) +#else + double fd_asinh(x) + double x; +#endif +{ + fd_twoints u; + double t,w; + int hx,ix; + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x+x; /* x is inf or NaN */ + if(ix< 0x3e300000) { /* |x|<2**-28 */ + if(really_big+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x41b00000) { /* |x| > 2**28 */ + w = __ieee754_log(fd_fabs(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = fd_fabs(x); + w = __ieee754_log(2.0*t+one/(fd_sqrt(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =fd_log1p(fd_fabs(x)+t/(one+fd_sqrt(one+t))); + } + if(hx>0) return w; else return -w; +} diff --git a/src/extension/script/js/fdlibm/s_atan.c b/src/extension/script/js/fdlibm/s_atan.c new file mode 100644 index 000000000..99a00c686 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_atan.c @@ -0,0 +1,175 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_atan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* atan(x) + * Method + * 1. Reduce x to positive by atan(x) = -atan(-x). + * 2. According to the integer k=4t+0.25 chopped, t=x, the argument + * is further reduced to one of the following intervals and the + * arctangent of t is evaluated by the corresponding formula: + * + * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) + * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) + * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) + * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) + * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double atanhi[] = { +#else +static double atanhi[] = { +#endif + 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ + 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ + 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ + 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ +}; + +#ifdef __STDC__ +static const double atanlo[] = { +#else +static double atanlo[] = { +#endif + 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ + 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ + 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ + 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ +}; + +#ifdef __STDC__ +static const double aT[] = { +#else +static double aT[] = { +#endif + 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ + -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ + 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ + -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ + 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ + -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ + 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ + -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ + 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ + -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ + 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ +}; + +#ifdef __STDC__ + static const double +#else + static double +#endif +one = 1.0, +really_big = 1.0e300; + +#ifdef __STDC__ + double fd_atan(double x) +#else + double fd_atan(x) + double x; +#endif +{ + fd_twoints u; + double w,s1,s2,z; + int ix,hx,id; + + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + if(ix>=0x44100000) { /* if |x| >= 2^66 */ + u.d = x; + if(ix>0x7ff00000|| + (ix==0x7ff00000&&(__LO(u)!=0))) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+atanlo[3]; + else return -atanhi[3]-atanlo[3]; + } if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ + if (ix < 0x3e200000) { /* |x| < 2^-29 */ + if(really_big+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fd_fabs(x); + if (ix < 0x3ff30000) { /* |x| < 1.1875 */ + if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x40038000) { /* |x| < 2.4375 */ + id = 2; x = (x-1.5)/(one+1.5*x); + } else { /* 2.4375 <= |x| < 2^66 */ + id = 3; x = -1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); + s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} diff --git a/src/extension/script/js/fdlibm/s_cbrt.c b/src/extension/script/js/fdlibm/s_cbrt.c new file mode 100644 index 000000000..4aed19b1b --- /dev/null +++ b/src/extension/script/js/fdlibm/s_cbrt.c @@ -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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_cbrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "fdlibm.h" + +/* cbrt(x) + * Return cube root of x + */ +#ifdef __STDC__ +static const unsigned +#else +static unsigned +#endif + B1 = 715094163, /* B1 = (682-0.03306235651)*2**20 */ + B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ + +#ifdef __STDC__ +static const double +#else +static double +#endif +C = 5.42857142857142815906e-01, /* 19/35 = 0x3FE15F15, 0xF15F15F1 */ +D = -7.05306122448979611050e-01, /* -864/1225 = 0xBFE691DE, 0x2532C834 */ +E = 1.41428571428571436819e+00, /* 99/70 = 0x3FF6A0EA, 0x0EA0EA0F */ +F = 1.60714285714285720630e+00, /* 45/28 = 0x3FF9B6DB, 0x6DB6DB6E */ +G = 3.57142857142857150787e-01; /* 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 */ + +#ifdef __STDC__ + double fd_cbrt(double x) +#else + double fd_cbrt(x) + double x; +#endif +{ + fd_twoints u; + int hx; + double r,s,t=0.0,w; + unsigned sign; + + u.d = x; + hx = __HI(u); /* high word of x */ + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */ + if((hx|__LO(u))==0) { + x = u.d; + return(x); /* cbrt(0) is itself */ + } + u.d = x; + __HI(u) = hx; /* x <- |x| */ + x = u.d; + /* rough cbrt to 5 bits */ + if(hx<0x00100000) /* subnormal number */ + {u.d = t; __HI(u)=0x43500000; t=u.d; /* set t= 2**54 */ + t*=x; __HI(u)=__HI(u)/3+B2; + } + else { + u.d = t; __HI(u)=hx/3+B1; t = u.d; + } + + + /* new cbrt to 23 bits, may be implemented in single precision */ + r=t*t/x; + s=C+r*t; + t*=G+F/(s+E+D/s); + + /* chopped to 20 bits and make it larger than cbrt(x) */ + u.d = t; + __LO(u)=0; __HI(u)+=0x00000001; + t = u.d; + + /* one step newton iteration to 53 bits with error less than 0.667 ulps */ + s=t*t; /* t*t is exact */ + r=x/s; + w=t+t; + r=(r-t)/(w+r); /* r-s is exact */ + t=t+t*r; + + /* retore the sign bit */ + u.d = t; + __HI(u) |= sign; + t = u.d; + return(t); +} diff --git a/src/extension/script/js/fdlibm/s_ceil.c b/src/extension/script/js/fdlibm/s_ceil.c new file mode 100644 index 000000000..826bcac6c --- /dev/null +++ b/src/extension/script/js/fdlibm/s_ceil.c @@ -0,0 +1,120 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_ceil.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * ceil(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceil(x). + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double really_big = 1.0e300; +#else +static double really_big = 1.0e300; +#endif + +#ifdef __STDC__ + double fd_ceil(double x) +#else + double fd_ceil(x) + double x; +#endif +{ + fd_twoints u; + int i0,i1,j0; + unsigned i,j; + u.d = x; + i0 = __HI(u); + i1 = __LO(u); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(really_big+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;i1=0;} + else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(really_big+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((unsigned)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(really_big+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(j0==20) i0+=1; + else { + j = i1 + (1<<(52-j0)); + if((int)j=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_cos(y[0],y[1]); + case 1: return -__kernel_sin(y[0],y[1],1); + case 2: return -__kernel_cos(y[0],y[1]); + default: + return __kernel_sin(y[0],y[1],1); + } + } +} diff --git a/src/extension/script/js/fdlibm/s_erf.c b/src/extension/script/js/fdlibm/s_erf.c new file mode 100644 index 000000000..6eae8de3b --- /dev/null +++ b/src/extension/script/js/fdlibm/s_erf.c @@ -0,0 +1,356 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_erf.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * Note that + * erf(-x) = -erf(x) + * erfc(-x) = 2 - erfc(x) + * + * Method: + * 1. For |x| in [0, 0.84375] + * erf(x) = x + x*R(x^2) + * erfc(x) = 1 - erf(x) if x in [-.84375,0.25] + * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] + * where R = P/Q where P is an odd poly of degree 8 and + * Q is an odd poly of degree 10. + * -57.90 + * | R - (erf(x)-x)/x | <= 2 + * + * + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. The interval is chosen because the fix + * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is + * near 0.6174), and by some experiment, 0.84375 is chosen to + * guarantee the error is less than one ulp for erf. + * + * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(x) = sign(x) * (c + P1(s)/Q1(s)) + * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 + * 1+(c+P1(s)/Q1(s)) if x < 0 + * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * That is, we use rational approximation to approximate + * erf(1+s) - (c = (single)0.84506291151) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * where + * P1(s) = degree 6 poly in s + * Q1(s) = degree 6 poly in s + * + * 3. For x in [1.25,1/0.35(~2.857143)], + * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) + * erf(x) = 1 - erfc(x) + * where + * R1(z) = degree 7 poly in z, (z=1/x^2) + * S1(z) = degree 8 poly in z + * + * 4. For x in [1/0.35,28] + * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 + * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28 + * erf(x) = sign(x) *(1 - tiny) (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) if x > 0 + * = 2 - tiny if x<0 + * + * 7. Special case: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +tiny = 1e-300, +half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ + /* c = (float)0.84506291151 */ +erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */ +/* + * Coefficients for approximation to erf on [0,0.84375] + */ +efx = 1.28379167095512586316e-01, /* 0x3FC06EBA, 0x8214DB69 */ +efx8= 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */ +pp0 = 1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */ +pp1 = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */ +pp2 = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */ +pp3 = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */ +pp4 = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */ +qq1 = 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */ +qq2 = 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */ +qq3 = 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */ +qq4 = 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */ +qq5 = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */ +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +pa0 = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */ +pa1 = 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */ +pa2 = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */ +pa3 = 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */ +pa4 = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */ +pa5 = 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */ +pa6 = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */ +qa1 = 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */ +qa2 = 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */ +qa3 = 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */ +qa4 = 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */ +qa5 = 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */ +qa6 = 1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */ +/* + * Coefficients for approximation to erfc in [1.25,1/0.35] + */ +ra0 = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */ +ra1 = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */ +ra2 = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */ +ra3 = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */ +ra4 = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */ +ra5 = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */ +ra6 = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */ +ra7 = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */ +sa1 = 1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */ +sa2 = 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */ +sa3 = 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */ +sa4 = 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */ +sa5 = 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */ +sa6 = 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */ +sa7 = 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */ +sa8 = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */ +/* + * Coefficients for approximation to erfc in [1/.35,28] + */ +rb0 = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */ +rb1 = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */ +rb2 = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */ +rb3 = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */ +rb4 = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */ +rb5 = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */ +rb6 = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */ +sb1 = 3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */ +sb2 = 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */ +sb3 = 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */ +sb4 = 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */ +sb5 = 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */ +sb6 = 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */ +sb7 = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */ + +#ifdef __STDC__ + double fd_erf(double x) +#else + double fd_erf(x) + double x; +#endif +{ + fd_twoints u; + int hx,ix,i; + double R,S,P,Q,s,y,z,r; + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erf(nan)=nan */ + i = ((unsigned)hx>>31)<<1; + return (double)(1-i)+one/x; /* erf(+-inf)=+-1 */ + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3e300000) { /* |x|<2**-28 */ + if (ix < 0x00800000) + return 0.125*(8.0*x+efx8*x); /*avoid underflow */ + return x + efx*x; + } + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + return x + x*y; + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fd_fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) return erx + P/Q; else return -erx - P/Q; + } + if (ix >= 0x40180000) { /* inf>|x|>=6 */ + if(hx>=0) return one-tiny; else return tiny-one; + } + x = fd_fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6E) { /* |x| < 1/0.35 */ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/0.35 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + u.d = z; + __LO(u) = 0; + z = u.d; + r = __ieee754_exp(-z*z-0.5625)*__ieee754_exp((z-x)*(z+x)+R/S); + if(hx>=0) return one-r/x; else return r/x-one; +} + +#ifdef __STDC__ + double erfc(double x) +#else + double erfc(x) + double x; +#endif +{ + fd_twoints u; + int hx,ix; + double R,S,P,Q,s,y,z,r; + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (double)(((unsigned)hx>>31)<<1)+one/x; + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3c700000) /* |x|<2**-56 */ + return one-x; + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + if(hx < 0x3fd00000) { /* x<1/4 */ + return one-(x+x*y); + } else { + r = x*y; + r += (x-half); + return half - r ; + } + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fd_fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) { + z = one-erx; return z - P/Q; + } else { + z = erx+P/Q; return one+z; + } + } + if (ix < 0x403c0000) { /* |x|<28 */ + x = fd_fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6D) { /* |x| < 1/.35 ~ 2.857143*/ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/.35 ~ 2.857143 */ + if(hx<0&&ix>=0x40180000) return two-tiny;/* x < -6 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + u.d = z; + __LO(u) = 0; + z = u.d; + r = __ieee754_exp(-z*z-0.5625)* + __ieee754_exp((z-x)*(z+x)+R/S); + if(hx>0) return r/x; else return two-r/x; + } else { + if(hx>0) return tiny*tiny; else return two-tiny; + } +} diff --git a/src/extension/script/js/fdlibm/s_expm1.c b/src/extension/script/js/fdlibm/s_expm1.c new file mode 100644 index 000000000..578d2e144 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_expm1.c @@ -0,0 +1,267 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_expm1.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* expm1(x) + * Returns exp(x)-1, the exponential of x minus 1. + * + * Method + * 1. Argument reduction: + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 + * + * Here a correction term c will be computed to compensate + * the error in r when rounded to a floating-point number. + * + * 2. Approximating expm1(r) by a special rational function on + * the interval [0,0.34658]: + * Since + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ... + * we define R1(r*r) by + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r) + * That is, + * R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) + * = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) + * = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... + * We use a special Reme algorithm on [0,0.347] to generate + * a polynomial of degree 5 in r*r to approximate R1. The + * maximum error of this polynomial approximation is bounded + * by 2**-61. In other words, + * R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 + * where Q1 = -1.6666666666666567384E-2, + * Q2 = 3.9682539681370365873E-4, + * Q3 = -9.9206344733435987357E-6, + * Q4 = 2.5051361420808517002E-7, + * Q5 = -6.2843505682382617102E-9; + * (where z=r*r, and the values of Q1 to Q5 are listed below) + * with error bounded by + * | 5 | -61 + * | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 + * | | + * + * expm1(r) = exp(r)-1 is then computed by the following + * specific way which minimize the accumulation rounding error: + * 2 3 + * r r [ 3 - (R1 + R1*r/2) ] + * expm1(r) = r + --- + --- * [--------------------] + * 2 2 [ 6 - r*(3 - R1*r/2) ] + * + * To compensate the error in the argument reduction, we use + * expm1(r+c) = expm1(r) + c + expm1(r)*c + * ~ expm1(r) + c + r*c + * Thus c+r*c will be added in as the correction terms for + * expm1(r+c). Now rearrange the term to avoid optimization + * screw up: + * ( 2 2 ) + * ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) + * expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) + * ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) + * ( ) + * + * = r - E + * 3. Scale back to obtain expm1(x): + * From step 1, we have + * expm1(x) = either 2^k*[expm1(r)+1] - 1 + * = or 2^k*[expm1(r) + (1-2^-k)] + * 4. Implementation notes: + * (A). To save one multiplication, we scale the coefficient Qi + * to Qi*2^i, and replace z by (x^2)/2. + * (B). To achieve maximum accuracy, we compute expm1(x) by + * (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) + * (ii) if k=0, return r-E + * (iii) if k=-1, return 0.5*(r-E)-0.5 + * (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) + * else return 1.0+2.0*(r-E); + * (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) + * (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else + * (vii) return 2^k(1-((E+2^-k)-r)) + * + * Special cases: + * expm1(INF) is INF, expm1(NaN) is NaN; + * expm1(-INF) is -1, and + * for finite argument, only expm1(0)=0 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then expm1(x) overflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +one = 1.0, +really_big = 1.0e+300, +tiny = 1.0e-300, +o_threshold = 7.09782712893383973096e+02,/* 0x40862E42, 0xFEFA39EF */ +ln2_hi = 6.93147180369123816490e-01,/* 0x3fe62e42, 0xfee00000 */ +ln2_lo = 1.90821492927058770002e-10,/* 0x3dea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00,/* 0x3ff71547, 0x652b82fe */ + /* scaled coefficients related to expm1 */ +Q1 = -3.33333333333331316428e-02, /* BFA11111 111110F4 */ +Q2 = 1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */ +Q3 = -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */ +Q4 = 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */ +Q5 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */ + +#ifdef __STDC__ + double fd_expm1(double x) +#else + double fd_expm1(x) + double x; +#endif +{ + fd_twoints u; + double y,hi,lo,c,t,e,hxs,hfx,r1; + int k,xsb; + unsigned hx; + + u.d = x; + hx = __HI(u); /* high word of x */ + xsb = hx&0x80000000; /* sign bit of x */ + if(xsb==0) y=x; else y= -x; /* y = |x| */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out huge and non-finite argument */ + if(hx >= 0x4043687A) { /* if |x|>=56*ln2 */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + u.d = x; + if(((hx&0xfffff)|__LO(u))!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:-1.0;/* exp(+-inf)={inf,-1} */ + } + if(x > o_threshold) return really_big*really_big; /* overflow */ + } + if(xsb!=0) { /* x < -56*ln2, return -1.0 with inexact */ + if(x+tiny<0.0) /* raise inexact */ + return tiny-one; /* return -1 */ + } + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + if(xsb==0) + {hi = x - ln2_hi; lo = ln2_lo; k = 1;} + else + {hi = x + ln2_hi; lo = -ln2_lo; k = -1;} + } else { + k = (int)(invln2*x+((xsb==0)?0.5:-0.5)); + t = k; + hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ + lo = t*ln2_lo; + } + x = hi - lo; + c = (hi-x)-lo; + } + else if(hx < 0x3c900000) { /* when |x|<2**-54, return x */ + t = really_big+x; /* return x with inexact flags when x!=0 */ + return x - (t-(really_big+x)); + } + else k = 0; + + /* x is now in primary range */ + hfx = 0.5*x; + hxs = x*hfx; + r1 = one+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))); + t = 3.0-r1*hfx; + e = hxs*((r1-t)/(6.0 - x*t)); + if(k==0) return x - (x*e-hxs); /* c is 0 */ + else { + e = (x*(e-c)-c); + e -= hxs; + if(k== -1) return 0.5*(x-e)-0.5; + if(k==1) + if(x < -0.25) return -2.0*(e-(x+0.5)); + else return one+2.0*(x-e); + if (k <= -2 || k>56) { /* suffice to return exp(x)-1 */ + y = one-(e-x); + u.d = y; + __HI(u) += (k<<20); /* add k to y's exponent */ + y = u.d; + return y-one; + } + t = one; + if(k<20) { + u.d = t; + __HI(u) = 0x3ff00000 - (0x200000>>k); /* t=1-2^-k */ + t = u.d; + y = t-(e-x); + u.d = y; + __HI(u) += (k<<20); /* add k to y's exponent */ + y = u.d; + } else { + u.d = t; + __HI(u) = ((0x3ff-k)<<20); /* 2^-k */ + t = u.d; + y = x-(e+t); + y += one; + u.d = y; + __HI(u) += (k<<20); /* add k to y's exponent */ + y = u.d; + } + } + return y; +} diff --git a/src/extension/script/js/fdlibm/s_fabs.c b/src/extension/script/js/fdlibm/s_fabs.c new file mode 100644 index 000000000..6b029da10 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_fabs.c @@ -0,0 +1,70 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_fabs.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fabs(x) returns the absolute value of x. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_fabs(double x) +#else + double fd_fabs(x) + double x; +#endif +{ + fd_twoints u; + u.d = x; + __HI(u) &= 0x7fffffff; + x = u.d; + return x; +} diff --git a/src/extension/script/js/fdlibm/s_finite.c b/src/extension/script/js/fdlibm/s_finite.c new file mode 100644 index 000000000..4a0a4d3c6 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_finite.c @@ -0,0 +1,71 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_finite.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * finite(x) returns 1 is x is finite, else 0; + * no branching! + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + int fd_finite(double x) +#else + int fd_finite(x) + double x; +#endif +{ + fd_twoints u; + int hx; + u.d = x; + hx = __HI(u); + return (unsigned)((hx&0x7fffffff)-0x7ff00000)>>31; +} diff --git a/src/extension/script/js/fdlibm/s_floor.c b/src/extension/script/js/fdlibm/s_floor.c new file mode 100644 index 000000000..6c23495f0 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_floor.c @@ -0,0 +1,121 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_floor.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * floor(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floor(x). + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double really_big = 1.0e300; +#else +static double really_big = 1.0e300; +#endif + +#ifdef __STDC__ + double fd_floor(double x) +#else + double fd_floor(x) + double x; +#endif +{ + fd_twoints u; + int i0,i1,j0; + unsigned i,j; + u.d = x; + i0 = __HI(u); + i1 = __LO(u); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(really_big+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=i1=0;} + else if(((i0&0x7fffffff)|i1)!=0) + { i0=0xbff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(really_big+x>0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((unsigned)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(really_big+x>0.0) { /* raise inexact flag */ + if(i0<0) { + if(j0==20) i0+=1; + else { + j = i1+(1<<(52-j0)); + if((int)j=0x7ff00000||((ix|lx)==0)) return x; /* 0,inf,nan */ + if (ix<0x00100000) { /* subnormal */ + x *= two54; + u.d = x; + hx = __HI(u); + ix = hx&0x7fffffff; + *eptr = -54; + } + *eptr += (ix>>20)-1022; + hx = (hx&0x800fffff)|0x3fe00000; + u.d = x; + __HI(u) = hx; + x = u.d; + return x; +} diff --git a/src/extension/script/js/fdlibm/s_ilogb.c b/src/extension/script/js/fdlibm/s_ilogb.c new file mode 100644 index 000000000..f769781a6 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_ilogb.c @@ -0,0 +1,85 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_ilogb.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* ilogb(double x) + * return the binary exponent of non-zero x + * ilogb(0) = 0x80000001 + * ilogb(inf/NaN) = 0x7fffffff (no signal is raised) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + int fd_ilogb(double x) +#else + int fd_ilogb(x) + double x; +#endif +{ + int hx,lx,ix; + fd_twoints u; + u.d = x; + hx = (__HI(u))&0x7fffffff; /* high word of x */ + if(hx<0x00100000) { + lx = __LO(u); + if((hx|lx)==0) + return 0x80000001; /* ilogb(0) = 0x80000001 */ + else /* subnormal x */ + if(hx==0) { + for (ix = -1043; lx>0; lx<<=1) ix -=1; + } else { + for (ix = -1022,hx<<=11; hx>0; hx<<=1) ix -=1; + } + return ix; + } + else if (hx<0x7ff00000) return (hx>>20)-1023; + else return 0x7fffffff; +} diff --git a/src/extension/script/js/fdlibm/s_isnan.c b/src/extension/script/js/fdlibm/s_isnan.c new file mode 100644 index 000000000..52f8759c7 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_isnan.c @@ -0,0 +1,74 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_isnan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * isnan(x) returns 1 is x is nan, else 0; + * no branching! + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + int fd_isnan(double x) +#else + int fd_isnan(x) + double x; +#endif +{ + fd_twoints u; + int hx,lx; + u.d = x; + hx = (__HI(u)&0x7fffffff); + lx = __LO(u); + hx |= (unsigned)(lx|(-lx))>>31; + hx = 0x7ff00000 - hx; + return ((unsigned)(hx))>>31; +} diff --git a/src/extension/script/js/fdlibm/s_ldexp.c b/src/extension/script/js/fdlibm/s_ldexp.c new file mode 100644 index 000000000..9475520d4 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_ldexp.c @@ -0,0 +1,66 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_ldexp.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "fdlibm.h" +#include + +#ifdef __STDC__ + double fd_ldexp(double value, int exp) +#else + double fd_ldexp(value, exp) + double value; int exp; +#endif +{ + if(!fd_finite(value)||value==0.0) return value; + value = fd_scalbn(value,exp); + if(!fd_finite(value)||value==0.0) errno = ERANGE; + return value; +} diff --git a/src/extension/script/js/fdlibm/s_lib_version.c b/src/extension/script/js/fdlibm/s_lib_version.c new file mode 100644 index 000000000..2ccf67d51 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_lib_version.c @@ -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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_lib_version.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * MACRO for standards + */ + +#include "fdlibm.h" + +/* + * define and initialize _LIB_VERSION + */ +#ifdef _POSIX_MODE +_LIB_VERSION_TYPE _LIB_VERSION = _POSIX_; +#else +#ifdef _XOPEN_MODE +_LIB_VERSION_TYPE _LIB_VERSION = _XOPEN_; +#else +#ifdef _SVID3_MODE +_LIB_VERSION_TYPE _LIB_VERSION = _SVID_; +#else /* default _IEEE_MODE */ +_LIB_VERSION_TYPE _LIB_VERSION = _IEEE_; +#endif +#endif +#endif diff --git a/src/extension/script/js/fdlibm/s_log1p.c b/src/extension/script/js/fdlibm/s_log1p.c new file mode 100644 index 000000000..1840156b1 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_log1p.c @@ -0,0 +1,211 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_log1p.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* double log1p(double x) + * + * Method : + * 1. Argument Reduction: find k and f such that + * 1+x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * Note. If k=0, then f=x is exact. However, if k!=0, then f + * may not be representable exactly. In that case, a correction + * term is need. Let u=1+x rounded. Let c = (1+x)-u, then + * log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), + * and add back the correction term c/u. + * (Note: when x > 2**53, one can simply return log(x)) + * + * 2. Approximation of log1p(f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s + * (the values of Lp1 to Lp7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lp1*s +...+Lp7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log1p(f) = f - (hfsq - s*(hfsq+R)). + * + * 3. Finally, log1p(x) = k*ln2 + log1p(f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log1p(x) is NaN with signal if x < -1 (including -INF) ; + * log1p(+INF) is +INF; log1p(-1) is -INF with signal; + * log1p(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + * + * Note: Assuming log() return accurate answer, the following + * algorithm can be used to compute log1p(x) to within a few ULP: + * + * u = 1+x; + * if(u==1.0) return x ; else + * return log(u)*(x/(u-1.0)); + * + * See HP-15C Advanced Functions Handbook, p.193. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lp1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lp2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lp3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lp4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lp5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lp6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lp7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static double zero = 0.0; + +#ifdef __STDC__ + double fd_log1p(double x) +#else + double fd_log1p(x) + double x; +#endif +{ + double hfsq,f,c,s,z,R,u; + int k,hx,hu,ax; + fd_twoints un; + + un.d = x; + hx = __HI(un); /* high word of x */ + ax = hx&0x7fffffff; + + k = 1; + if (hx < 0x3FDA827A) { /* x < 0.41422 */ + if(ax>=0x3ff00000) { /* x <= -1.0 */ + if(x==-1.0) return -two54/zero; /* log1p(-1)=+inf */ + else return (x-x)/(x-x); /* log1p(x<-1)=NaN */ + } + if(ax<0x3e200000) { /* |x| < 2**-29 */ + if(two54+x>zero /* raise inexact */ + &&ax<0x3c900000) /* |x| < 2**-54 */ + return x; + else + return x - x*x*0.5; + } + if(hx>0||hx<=((int)0xbfd2bec3)) { + k=0;f=x;hu=1;} /* -0.2929= 0x7ff00000) return x+x; + if(k!=0) { + if(hx<0x43400000) { + u = 1.0+x; + un.d = u; + hu = __HI(un); /* high word of u */ + k = (hu>>20)-1023; + c = (k>0)? 1.0-(u-x):x-(u-1.0);/* correction term */ + c /= u; + } else { + u = x; + un.d = u; + hu = __HI(un); /* high word of u */ + k = (hu>>20)-1023; + c = 0; + } + hu &= 0x000fffff; + if(hu<0x6a09e) { + un.d = u; + __HI(un) = hu|0x3ff00000; /* normalize u */ + u = un.d; + } else { + k += 1; + un.d = u; + __HI(un) = hu|0x3fe00000; /* normalize u/2 */ + u = un.d; + hu = (0x00100000-hu)>>2; + } + f = u-1.0; + } + hfsq=0.5*f*f; + if(hu==0) { /* |f| < 2**-20 */ + if(f==zero) if(k==0) return zero; + else {c += k*ln2_lo; return k*ln2_hi+c;} + R = hfsq*(1.0-0.66666666666666666*f); + if(k==0) return f-R; else + return k*ln2_hi-((R-(k*ln2_lo+c))-f); + } + s = f/(2.0+f); + z = s*s; + R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))); + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f); +} diff --git a/src/extension/script/js/fdlibm/s_logb.c b/src/extension/script/js/fdlibm/s_logb.c new file mode 100644 index 000000000..f885c4dc3 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_logb.c @@ -0,0 +1,79 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_logb.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * double logb(x) + * IEEE 754 logb. Included to pass IEEE test suite. Not recommend. + * Use ilogb instead. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_logb(double x) +#else + double fd_logb(x) + double x; +#endif +{ + int lx,ix; + fd_twoints u; + + u.d = x; + ix = (__HI(u))&0x7fffffff; /* high |x| */ + lx = __LO(u); /* low x */ + if((ix|lx)==0) return -1.0/fd_fabs(x); + if(ix>=0x7ff00000) return x*x; + if((ix>>=20)==0) /* IEEE 754 logb */ + return -1022.0; + else + return (double) (ix-1023); +} diff --git a/src/extension/script/js/fdlibm/s_matherr.c b/src/extension/script/js/fdlibm/s_matherr.c new file mode 100644 index 000000000..cd99ca88f --- /dev/null +++ b/src/extension/script/js/fdlibm/s_matherr.c @@ -0,0 +1,64 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_matherr.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + int fd_matherr(struct exception *x) +#else + int fd_matherr(x) + struct exception *x; +#endif +{ + int n=0; + if(x->arg1!=x->arg1) return 0; + return n; +} diff --git a/src/extension/script/js/fdlibm/s_modf.c b/src/extension/script/js/fdlibm/s_modf.c new file mode 100644 index 000000000..3b182bd3b --- /dev/null +++ b/src/extension/script/js/fdlibm/s_modf.c @@ -0,0 +1,132 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_modf.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * modf(double x, double *iptr) + * return fraction part of x, and return x's integral part in *iptr. + * Method: + * Bit twiddling. + * + * Exception: + * No exception. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double one = 1.0; +#else +static double one = 1.0; +#endif + +#ifdef __STDC__ + double fd_modf(double x, double *iptr) +#else + double fd_modf(x, iptr) + double x,*iptr; +#endif +{ + int i0,i1,j0; + unsigned i; + fd_twoints u; + u.d = x; + i0 = __HI(u); /* high x */ + i1 = __LO(u); /* low x */ + j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */ + if(j0<20) { /* integer part in high x */ + if(j0<0) { /* |x|<1 */ + u.d = *iptr; + __HI(u) = i0&0x80000000; + __LO(u) = 0; /* *iptr = +-0 */ + *iptr = u.d; + return x; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) { /* x is integral */ + *iptr = x; + u.d = x; + __HI(u) &= 0x80000000; + __LO(u) = 0; /* return +-0 */ + x = u.d; + return x; + } else { + u.d = *iptr; + __HI(u) = i0&(~i); + __LO(u) = 0; + *iptr = u.d; + return x - *iptr; + } + } + } else if (j0>51) { /* no fraction part */ + *iptr = x*one; + u.d = x; + __HI(u) &= 0x80000000; + __LO(u) = 0; /* return +-0 */ + x = u.d; + return x; + } else { /* fraction part in low x */ + i = ((unsigned)(0xffffffff))>>(j0-20); + if((i1&i)==0) { /* x is integral */ + *iptr = x; + u.d = x; + __HI(u) &= 0x80000000; + __LO(u) = 0; /* return +-0 */ + x = u.d; + return x; + } else { + u.d = *iptr; + __HI(u) = i0; + __LO(u) = i1&(~i); + *iptr = u.d; + return x - *iptr; + } + } +} diff --git a/src/extension/script/js/fdlibm/s_nextafter.c b/src/extension/script/js/fdlibm/s_nextafter.c new file mode 100644 index 000000000..f71c5c835 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_nextafter.c @@ -0,0 +1,124 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_nextafter.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nextafter(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_nextafter(double x, double y) +#else + double fd_nextafter(x,y) + double x,y; +#endif +{ + int hx,hy,ix,iy; + unsigned lx,ly; + fd_twoints ux, uy; + + ux.d = x; uy.d = y; + hx = __HI(ux); /* high word of x */ + lx = __LO(ux); /* low word of x */ + hy = __HI(uy); /* high word of y */ + ly = __LO(uy); /* low word of y */ + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffff; /* |y| */ + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || /* x is nan */ + ((iy>=0x7ff00000)&&((iy-0x7ff00000)|ly)!=0)) /* y is nan */ + return x+y; + if(x==y) return x; /* x=y, return x */ + if((ix|lx)==0) { /* x == 0 */ + ux.d = x; + __HI(ux) = hy&0x80000000; /* return +-minsubnormal */ + __LO(ux) = 1; + x = ux.d; + y = x*x; + if(y==x) return y; else return x; /* raise underflow flag */ + } + if(hx>=0) { /* x > 0 */ + if(hx>hy||((hx==hy)&&(lx>ly))) { /* x > y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy||((hx==hy)&&(lx>ly))){/* x < y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } + hy = hx&0x7ff00000; + if(hy>=0x7ff00000) return x+x; /* overflow */ + if(hy<0x00100000) { /* underflow */ + y = x*x; + if(y!=x) { /* raise underflow flag */ + uy.d = y; + __HI(uy) = hx; __LO(uy) = lx; + y = uy.d; + return y; + } + } + ux.d = x; + __HI(ux) = hx; __LO(ux) = lx; + x = ux.d; + return x; +} diff --git a/src/extension/script/js/fdlibm/s_rint.c b/src/extension/script/js/fdlibm/s_rint.c new file mode 100644 index 000000000..3c4fab6d9 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_rint.c @@ -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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_rint.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * rint(x) + * Return x rounded to integral value according to the prevailing + * rounding mode. + * Method: + * Using floating addition. + * Exception: + * Inexact flag raised if x not equal to rint(x). + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +TWO52[2]={ + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ +}; + +#ifdef __STDC__ + double fd_rint(double x) +#else + double fd_rint(x) + double x; +#endif +{ + int i0,j0,sx; + unsigned i,i1; + double w,t; + fd_twoints u; + + u.d = x; + i0 = __HI(u); + sx = (i0>>31)&1; + i1 = __LO(u); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { + if(((i0&0x7fffffff)|i1)==0) return x; + i1 |= (i0&0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1|-(int)i1)>>12)&0x80000; + u.d = x; + __HI(u)=i0; + x = u.d; + w = TWO52[sx]+x; + t = w-TWO52[sx]; + u.d = t; + i0 = __HI(u); + __HI(u) = (i0&0x7fffffff)|(sx<<31); + t = u.d; + return t; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + i>>=1; + if(((i0&i)|i1)!=0) { + if(j0==19) i1 = 0x40000000; else + i0 = (i0&(~i))|((0x20000)>>j0); + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((unsigned)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + i>>=1; + if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); + } + u.d = x; + __HI(u) = i0; + __LO(u) = i1; + x = u.d; + w = TWO52[sx]+x; + return w-TWO52[sx]; +} diff --git a/src/extension/script/js/fdlibm/s_scalbn.c b/src/extension/script/js/fdlibm/s_scalbn.c new file mode 100644 index 000000000..3deeaa305 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_scalbn.c @@ -0,0 +1,107 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_scalbn.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ +really_big = 1.0e+300, +tiny = 1.0e-300; + +#ifdef __STDC__ + double fd_scalbn (double x, int n) +#else + double fd_scalbn (x,n) + double x; int n; +#endif +{ + fd_twoints u; + int k,hx,lx; + u.d = x; + hx = __HI(u); + lx = __LO(u); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + u.d = x; + hx = __HI(u); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return really_big*fd_copysign(really_big,x); /* overflow */ + if (k > 0) /* normal result */ + {u.d = x; __HI(u) = (hx&0x800fffff)|(k<<20); x = u.d; return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return really_big*fd_copysign(really_big,x); /*overflow*/ + else return tiny*fd_copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + u.d = x; + __HI(u) = (hx&0x800fffff)|(k<<20); + x = u.d; + return x*twom54; +} diff --git a/src/extension/script/js/fdlibm/s_signgam.c b/src/extension/script/js/fdlibm/s_signgam.c new file mode 100644 index 000000000..4eb8ce72f --- /dev/null +++ b/src/extension/script/js/fdlibm/s_signgam.c @@ -0,0 +1,40 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 "fdlibm.h" +int signgam = 0; diff --git a/src/extension/script/js/fdlibm/s_significand.c b/src/extension/script/js/fdlibm/s_significand.c new file mode 100644 index 000000000..2e1c0b28f --- /dev/null +++ b/src/extension/script/js/fdlibm/s_significand.c @@ -0,0 +1,68 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_significand.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * significand(x) computes just + * scalb(x, (double) -ilogb(x)), + * for exercising the fraction-part(F) IEEE 754-1985 test vector. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_significand(double x) +#else + double fd_significand(x) + double x; +#endif +{ + return __ieee754_scalb(x,(double) -fd_ilogb(x)); +} diff --git a/src/extension/script/js/fdlibm/s_sin.c b/src/extension/script/js/fdlibm/s_sin.c new file mode 100644 index 000000000..8bbc5c62d --- /dev/null +++ b/src/extension/script/js/fdlibm/s_sin.c @@ -0,0 +1,118 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sin(x) + * Return sine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_sin(double x) +#else + double fd_sin(x) + double x; +#endif +{ + fd_twoints u; + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + u.d = x; + ix = __HI(u); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_sin(x,z,0); + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_sin(y[0],y[1],1); + case 1: return __kernel_cos(y[0],y[1]); + case 2: return -__kernel_sin(y[0],y[1],1); + default: + return -__kernel_cos(y[0],y[1]); + } + } +} diff --git a/src/extension/script/js/fdlibm/s_tan.c b/src/extension/script/js/fdlibm/s_tan.c new file mode 100644 index 000000000..ded36c1d7 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_tan.c @@ -0,0 +1,112 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_tan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* tan(x) + * Return tangent function of x. + * + * kernel function: + * __kernel_tan ... tangent function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_tan(double x) +#else + double fd_tan(x) + double x; +#endif +{ + fd_twoints u; + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + u.d = x; + ix = __HI(u); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1); + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +} diff --git a/src/extension/script/js/fdlibm/s_tanh.c b/src/extension/script/js/fdlibm/s_tanh.c new file mode 100644 index 000000000..aa6809f85 --- /dev/null +++ b/src/extension/script/js/fdlibm/s_tanh.c @@ -0,0 +1,122 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)s_tanh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* Tanh(x) + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanh(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanh(-x) = -tanh(x). + * 2. 0 <= x <= 2**-55 : tanh(x) := x*(one+x) + * -t + * 2**-55 < x <= 1 : tanh(x) := -----; t = expm1(-2x) + * t + 2 + * 2 + * 1 <= x <= 22.0 : tanh(x) := 1- ----- ; t=expm1(2x) + * t + 2 + * 22.0 < x <= INF : tanh(x) := 1. + * + * Special cases: + * tanh(NaN) is NaN; + * only tanh(0)=0 is exact for finite argument. + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double one=1.0, two=2.0, tiny = 1.0e-300; +#else +static double one=1.0, two=2.0, tiny = 1.0e-300; +#endif + +#ifdef __STDC__ + double fd_tanh(double x) +#else + double fd_tanh(x) + double x; +#endif +{ + double t,z; + int jx,ix; + fd_twoints u; + + /* High word of |x|. */ + u.d = x; + jx = __HI(u); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 22 */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3c800000) /* |x|<2**-55 */ + return x*(one+x); /* tanh(small) = small */ + if (ix>=0x3ff00000) { /* |x|>=1 */ + t = fd_expm1(two*fd_fabs(x)); + z = one - two/(t+two); + } else { + t = fd_expm1(-two*fd_fabs(x)); + z= -t/(t+two); + } + /* |x| > 22, return +-1 */ + } else { + z = one - tiny; /* raised inexact flag */ + } + return (jx>=0)? z: -z; +} diff --git a/src/extension/script/js/fdlibm/w_acos.c b/src/extension/script/js/fdlibm/w_acos.c new file mode 100644 index 000000000..872c81d20 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_acos.c @@ -0,0 +1,78 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_acos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrap_acos(x) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_acos(double x) /* wrapper acos */ +#else + double fd_acos(x) /* wrapper acos */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_acos(x); +#else + double z; + z = __ieee754_acos(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(fd_fabs(x)>1.0) { + int err; + return __kernel_standard(x,x,1,&err); /* acos(|x|>1) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_acosh.c b/src/extension/script/js/fdlibm/w_acosh.c new file mode 100644 index 000000000..745d402ea --- /dev/null +++ b/src/extension/script/js/fdlibm/w_acosh.c @@ -0,0 +1,78 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_acosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* + * wrapper acosh(x) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_acosh(double x) /* wrapper acosh */ +#else + double fd_acosh(x) /* wrapper acosh */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_acosh(x); +#else + double z; + z = __ieee754_acosh(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(x<1.0) { + int err; + return __kernel_standard(x,x,29,&err); /* acosh(x<1) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_asin.c b/src/extension/script/js/fdlibm/w_asin.c new file mode 100644 index 000000000..18aaefde9 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_asin.c @@ -0,0 +1,80 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_asin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* + * wrapper asin(x) + */ + + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_asin(double x) /* wrapper asin */ +#else + double fd_asin(x) /* wrapper asin */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_asin(x); +#else + double z; + z = __ieee754_asin(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(fd_fabs(x)>1.0) { + int err; + return __kernel_standard(x,x,2,&err); /* asin(|x|>1) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_atan2.c b/src/extension/script/js/fdlibm/w_atan2.c new file mode 100644 index 000000000..8cfa4bbbd --- /dev/null +++ b/src/extension/script/js/fdlibm/w_atan2.c @@ -0,0 +1,79 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_atan2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* + * wrapper atan2(y,x) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_atan2(double y, double x) /* wrapper atan2 */ +#else + double fd_atan2(y,x) /* wrapper atan2 */ + double y,x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_atan2(y,x); +#else + double z; + z = __ieee754_atan2(y,x); + if(_LIB_VERSION == _IEEE_||fd_isnan(x)||fd_isnan(y)) return z; + if(x==0.0&&y==0.0) { + int err; + return __kernel_standard(y,x,3,&err); /* atan2(+-0,+-0) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_atanh.c b/src/extension/script/js/fdlibm/w_atanh.c new file mode 100644 index 000000000..6ba52d1e2 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_atanh.c @@ -0,0 +1,81 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_atanh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ +/* + * wrapper atanh(x) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_atanh(double x) /* wrapper atanh */ +#else + double fd_atanh(x) /* wrapper atanh */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_atanh(x); +#else + double z,y; + z = __ieee754_atanh(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + y = fd_fabs(x); + if(y>=1.0) { + int err; + if(y>1.0) + return __kernel_standard(x,x,30,&err); /* atanh(|x|>1) */ + else + return __kernel_standard(x,x,31,&err); /* atanh(|x|==1) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_cosh.c b/src/extension/script/js/fdlibm/w_cosh.c new file mode 100644 index 000000000..146449e02 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_cosh.c @@ -0,0 +1,77 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_cosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper cosh(x) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_cosh(double x) /* wrapper cosh */ +#else + double fd_cosh(x) /* wrapper cosh */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_cosh(x); +#else + double z; + z = __ieee754_cosh(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(fd_fabs(x)>7.10475860073943863426e+02) { + int err; + return __kernel_standard(x,x,5,&err); /* cosh overflow */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_exp.c b/src/extension/script/js/fdlibm/w_exp.c new file mode 100644 index 000000000..f5dea0b01 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_exp.c @@ -0,0 +1,88 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_exp.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper exp(x) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ +static const double +#else +static double +#endif +o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ +u_threshold= -7.45133219101941108420e+02; /* 0xc0874910, 0xD52D3051 */ + +#ifdef __STDC__ + double fd_exp(double x) /* wrapper exp */ +#else + double fd_exp(x) /* wrapper exp */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_exp(x); +#else + double z; + z = __ieee754_exp(x); + if(_LIB_VERSION == _IEEE_) return z; + if(fd_finite(x)) { + int err; + if(x>o_threshold) + return __kernel_standard(x,x,6,&err); /* exp overflow */ + else if(xX_TLOSS) { + int err; + return __kernel_standard(x,x,34,&err); /* j0(|x|>X_TLOSS) */ + } else + return z; +#endif +} + +#ifdef __STDC__ + double y0(double x) /* wrapper y0 */ +#else + double y0(x) /* wrapper y0 */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_y0(x); +#else + double z; + int err; + z = __ieee754_y0(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z; + if(x <= 0.0){ + if(x==0.0) + /* d= -one/(x-x); */ + return __kernel_standard(x,x,8,&err); + else + /* d = zero/(x-x); */ + return __kernel_standard(x,x,9,&err); + } + if(x>X_TLOSS) { + return __kernel_standard(x,x,35,&err); /* y0(x>X_TLOSS) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_j1.c b/src/extension/script/js/fdlibm/w_j1.c new file mode 100644 index 000000000..86a506bc2 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_j1.c @@ -0,0 +1,106 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_j1.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper of j1,y1 + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_j1(double x) /* wrapper j1 */ +#else + double fd_j1(x) /* wrapper j1 */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_j1(x); +#else + double z; + z = __ieee754_j1(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z; + if(fd_fabs(x)>X_TLOSS) { + int err; + return __kernel_standard(x,x,36,&err); /* j1(|x|>X_TLOSS) */ + } else + return z; +#endif +} + +#ifdef __STDC__ + double y1(double x) /* wrapper y1 */ +#else + double y1(x) /* wrapper y1 */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_y1(x); +#else + double z; + int err; + z = __ieee754_y1(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z; + if(x <= 0.0){ + if(x==0.0) + /* d= -one/(x-x); */ + return __kernel_standard(x,x,10,&err); + else + /* d = zero/(x-x); */ + return __kernel_standard(x,x,11,&err); + } + if(x>X_TLOSS) { + return __kernel_standard(x,x,37,&err); /* y1(x>X_TLOSS) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_jn.c b/src/extension/script/js/fdlibm/w_jn.c new file mode 100644 index 000000000..6926b0da8 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_jn.c @@ -0,0 +1,128 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_jn.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper jn(int n, double x), yn(int n, double x) + * floating point Bessel's function of the 1st and 2nd kind + * of order n + * + * Special cases: + * y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; + * y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. + * Note 2. About jn(n,x), yn(n,x) + * For n=0, j0(x) is called, + * for n=1, j1(x) is called, + * for nx, a continued fraction approximation to + * j(n,x)/j(n-1,x) is evaluated and then backward + * recursion is used starting from a supposed value + * for j(n,x). The resulting value of j(0,x) is + * compared with the actual value to correct the + * supposed value of j(n,x). + * + * yn(n,x) is similar in all respects, except + * that forward recursion is used for all + * values of n>1. + * + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_jn(int n, double x) /* wrapper jn */ +#else + double fd_jn(n,x) /* wrapper jn */ + double x; int n; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_jn(n,x); +#else + double z; + z = __ieee754_jn(n,x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z; + if(fd_fabs(x)>X_TLOSS) { + int err; + return __kernel_standard((double)n,x,38,&err); /* jn(|x|>X_TLOSS,n) */ + } else + return z; +#endif +} + +#ifdef __STDC__ + double yn(int n, double x) /* wrapper yn */ +#else + double yn(n,x) /* wrapper yn */ + double x; int n; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_yn(n,x); +#else + double z; + int err; + z = __ieee754_yn(n,x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z; + if(x <= 0.0){ + if(x==0.0) + /* d= -one/(x-x); */ + return __kernel_standard((double)n,x,12,&err); + else + /* d = zero/(x-x); */ + return __kernel_standard((double)n,x,13,&err); + } + if(x>X_TLOSS) { + return __kernel_standard((double)n,x,39,&err); /* yn(x>X_TLOSS,n) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_lgamma.c b/src/extension/script/js/fdlibm/w_lgamma.c new file mode 100644 index 000000000..f7576e892 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_lgamma.c @@ -0,0 +1,85 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_lgamma.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* double lgamma(double x) + * Return the logarithm of the Gamma function of x. + * + * Method: call __ieee754_lgamma_r + */ + +#include "fdlibm.h" + +extern int signgam; + +#ifdef __STDC__ + double fd_lgamma(double x) +#else + double fd_lgamma(x) + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_lgamma_r(x,&signgam); +#else + double y; + y = __ieee754_lgamma_r(x,&signgam); + if(_LIB_VERSION == _IEEE_) return y; + if(!fd_finite(y)&&fd_finite(x)) { + int err; + if(fd_floor(x)==x&&x<=0.0) + return __kernel_standard(x,x,15,&err); /* lgamma pole */ + else + return __kernel_standard(x,x,14,&err); /* lgamma overflow */ + } else + return y; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_lgamma_r.c b/src/extension/script/js/fdlibm/w_lgamma_r.c new file mode 100644 index 000000000..ba2ad5933 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_lgamma_r.c @@ -0,0 +1,81 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_lgamma_r.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper double lgamma_r(double x, int *signgamp) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_lgamma_r(double x, int *signgamp) /* wrapper lgamma_r */ +#else + double fd_lgamma_r(x,signgamp) /* wrapper lgamma_r */ + double x; int *signgamp; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_lgamma_r(x,signgamp); +#else + double y; + y = __ieee754_lgamma_r(x,signgamp); + if(_LIB_VERSION == _IEEE_) return y; + if(!fd_finite(y)&&fd_finite(x)) { + int err; + if(fd_floor(x)==x&&x<=0.0) + return __kernel_standard(x,x,15,&err); /* lgamma pole */ + else + return __kernel_standard(x,x,14,&err); /* lgamma overflow */ + } else + return y; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_log.c b/src/extension/script/js/fdlibm/w_log.c new file mode 100644 index 000000000..7e358fcf1 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_log.c @@ -0,0 +1,78 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper log(x) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_log(double x) /* wrapper log */ +#else + double fd_log(x) /* wrapper log */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_log(x); +#else + double z; + int err; + z = __ieee754_log(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x) || x > 0.0) return z; + if(x==0.0) + return __kernel_standard(x,x,16,&err); /* log(0) */ + else + return __kernel_standard(x,x,17,&err); /* log(x<0) */ +#endif +} diff --git a/src/extension/script/js/fdlibm/w_log10.c b/src/extension/script/js/fdlibm/w_log10.c new file mode 100644 index 000000000..6b298b236 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_log10.c @@ -0,0 +1,81 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_log10.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper log10(X) + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_log10(double x) /* wrapper log10 */ +#else + double fd_log10(x) /* wrapper log10 */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_log10(x); +#else + double z; + z = __ieee754_log10(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(x<=0.0) { + int err; + if(x==0.0) + return __kernel_standard(x,x,18,&err); /* log10(0) */ + else + return __kernel_standard(x,x,19,&err); /* log10(x<0) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_pow.c b/src/extension/script/js/fdlibm/w_pow.c new file mode 100644 index 000000000..3d2c15ad3 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_pow.c @@ -0,0 +1,99 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + + +/* @(#)w_pow.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper pow(x,y) return x**y + */ + +#include "fdlibm.h" + + +#ifdef __STDC__ + double fd_pow(double x, double y) /* wrapper pow */ +#else + double fd_pow(x,y) /* wrapper pow */ + double x,y; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_pow(x,y); +#else + double z; + int err; + z=__ieee754_pow(x,y); + if(_LIB_VERSION == _IEEE_|| fd_isnan(y)) return z; + if(fd_isnan(x)) { + if(y==0.0) + return __kernel_standard(x,y,42,&err); /* pow(NaN,0.0) */ + else + return z; + } + if(x==0.0){ + if(y==0.0) + return __kernel_standard(x,y,20,&err); /* pow(0.0,0.0) */ + if(fd_finite(y)&&y<0.0) + return __kernel_standard(x,y,23,&err); /* pow(0.0,negative) */ + return z; + } + if(!fd_finite(z)) { + if(fd_finite(x)&&fd_finite(y)) { + if(fd_isnan(z)) + return __kernel_standard(x,y,24,&err); /* pow neg**non-int */ + else + return __kernel_standard(x,y,21,&err); /* pow overflow */ + } + } + if(z==0.0&&fd_finite(x)&&fd_finite(y)) + return __kernel_standard(x,y,22,&err); /* pow underflow */ + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_remainder.c b/src/extension/script/js/fdlibm/w_remainder.c new file mode 100644 index 000000000..25d1ba171 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_remainder.c @@ -0,0 +1,77 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_remainder.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper remainder(x,p) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_remainder(double x, double y) /* wrapper remainder */ +#else + double fd_remainder(x,y) /* wrapper remainder */ + double x,y; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_remainder(x,y); +#else + double z; + z = __ieee754_remainder(x,y); + if(_LIB_VERSION == _IEEE_ || fd_isnan(y)) return z; + if(y==0.0) { + int err; + return __kernel_standard(x,y,28,&err); /* remainder(x,0) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_scalb.c b/src/extension/script/js/fdlibm/w_scalb.c new file mode 100644 index 000000000..35c16a500 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_scalb.c @@ -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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_scalb.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper scalb(double x, double fn) is provide for + * passing various standard test suite. One + * should use scalbn() instead. + */ + +#include "fdlibm.h" + +#include + +#ifdef __STDC__ +#ifdef _SCALB_INT + double fd_scalb(double x, int fn) /* wrapper scalb */ +#else + double fd_scalb(double x, double fn) /* wrapper scalb */ +#endif +#else + double fd_scalb(x,fn) /* wrapper scalb */ +#ifdef _SCALB_INT + double x; int fn; +#else + double x,fn; +#endif +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_scalb(x,fn); +#else + double z; + int err; + z = __ieee754_scalb(x,fn); + if(_LIB_VERSION == _IEEE_) return z; + if(!(fd_finite(z)||fd_isnan(z))&&fd_finite(x)) { + return __kernel_standard(x,(double)fn,32,&err); /* scalb overflow */ + } + if(z==0.0&&z!=x) { + return __kernel_standard(x,(double)fn,33,&err); /* scalb underflow */ + } +#ifndef _SCALB_INT + if(!fd_finite(fn)) errno = ERANGE; +#endif + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_sinh.c b/src/extension/script/js/fdlibm/w_sinh.c new file mode 100644 index 000000000..8b04ecb7f --- /dev/null +++ b/src/extension/script/js/fdlibm/w_sinh.c @@ -0,0 +1,77 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_sinh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper sinh(x) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_sinh(double x) /* wrapper sinh */ +#else + double fd_sinh(x) /* wrapper sinh */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_sinh(x); +#else + double z; + z = __ieee754_sinh(x); + if(_LIB_VERSION == _IEEE_) return z; + if(!fd_finite(z)&&fd_finite(x)) { + int err; + return __kernel_standard(x,x,25,&err); /* sinh overflow */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/fdlibm/w_sqrt.c b/src/extension/script/js/fdlibm/w_sqrt.c new file mode 100644 index 000000000..462d776f8 --- /dev/null +++ b/src/extension/script/js/fdlibm/w_sqrt.c @@ -0,0 +1,77 @@ +/* -*- 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 + * Sun Microsystems, Inc. + * 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 ***** */ + +/* @(#)w_sqrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * wrapper sqrt(x) + */ + +#include "fdlibm.h" + +#ifdef __STDC__ + double fd_sqrt(double x) /* wrapper sqrt */ +#else + double fd_sqrt(x) /* wrapper sqrt */ + double x; +#endif +{ +#ifdef _IEEE_LIBM + return __ieee754_sqrt(x); +#else + double z; + z = __ieee754_sqrt(x); + if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z; + if(x<0.0) { + int err; + return __kernel_standard(x,x,26,&err); /* sqrt(negative) */ + } else + return z; +#endif +} diff --git a/src/extension/script/js/js.c b/src/extension/script/js/js.c new file mode 100644 index 000000000..5bd296223 --- /dev/null +++ b/src/extension/script/js/js.c @@ -0,0 +1,2333 @@ +/* -*- 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 shell. + */ +#include "jsstddef.h" +#include +#include +#include +#include +#include "jstypes.h" +#include "jsarena.h" +#include "jsutil.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" + +#ifdef PERLCONNECT +#include "perlconnect/jsperl.h" +#endif + +#ifdef LIVECONNECT +#include "jsjava.h" +#endif + +#ifdef JSDEBUGGER +#include "jsdebug.h" +#ifdef JSDEBUGGER_JAVA_UI +#include "jsdjava.h" +#endif /* JSDEBUGGER_JAVA_UI */ +#ifdef JSDEBUGGER_C_UI +#include "jsdb.h" +#endif /* JSDEBUGGER_C_UI */ +#endif /* JSDEBUGGER */ + +#ifdef XP_UNIX +#include +#include +#include +#include +#endif + +#if defined(XP_WIN) || defined(XP_OS2) +#include /* for isatty() */ +#endif + +#define EXITCODE_RUNTIME_ERROR 3 +#define EXITCODE_FILE_NOT_FOUND 4 + +size_t gStackChunkSize = 8192; +static size_t gMaxStackSize = 0; +static jsuword gStackBase; +int gExitCode = 0; +JSBool gQuitting = JS_FALSE; +FILE *gErrFile = NULL; +FILE *gOutFile = NULL; + +#ifdef XP_MAC +#if defined(MAC_TEST_HACK) || defined(XP_MAC_MPW) +/* this is the data file that all Print strings will be echoed into */ +FILE *gTestResultFile = NULL; +#define isatty(f) 0 +#else +#define isatty(f) 1 +#endif + +char *strdup(const char *str) +{ + char *copy = (char *) malloc(strlen(str)+1); + if (copy) + strcpy(copy, str); + return copy; +} + +#ifdef XP_MAC_MPW +/* Macintosh MPW replacements for the ANSI routines. These translate LF's to CR's because + the MPW libraries supplied by Metrowerks don't do that for some reason. */ +static void translateLFtoCR(char *str, int length) +{ + char *limit = str + length; + while (str != limit) { + if (*str == '\n') + *str = '\r'; + str++; + } +} + +int fputc(int c, FILE *file) +{ + char buffer = c; + if (buffer == '\n') + buffer = '\r'; + return fwrite(&buffer, 1, 1, file); +} + +int fputs(const char *s, FILE *file) +{ + char buffer[4096]; + int n = strlen(s); + int extra = 0; + + while (n > sizeof buffer) { + memcpy(buffer, s, sizeof buffer); + translateLFtoCR(buffer, sizeof buffer); + extra += fwrite(buffer, 1, sizeof buffer, file); + n -= sizeof buffer; + s += sizeof buffer; + } + memcpy(buffer, s, n); + translateLFtoCR(buffer, n); + return extra + fwrite(buffer, 1, n, file); +} + +int fprintf(FILE* file, const char *format, ...) +{ + va_list args; + char smallBuffer[4096]; + int n; + int bufferSize = sizeof smallBuffer; + char *buffer = smallBuffer; + int result; + + va_start(args, format); + n = vsnprintf(buffer, bufferSize, format, args); + va_end(args); + while (n < 0) { + if (buffer != smallBuffer) + free(buffer); + bufferSize <<= 1; + buffer = malloc(bufferSize); + if (!buffer) { + JS_ASSERT(JS_FALSE); + return 0; + } + va_start(args, format); + n = vsnprintf(buffer, bufferSize, format, args); + va_end(args); + } + translateLFtoCR(buffer, n); + result = fwrite(buffer, 1, n, file); + if (buffer != smallBuffer) + free(buffer); + return result; +} + + +#else +#include +#include + +static char* mac_argv[] = { "js", NULL }; + +static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv) +{ + SIOUXSettings.autocloseonquit = true; + SIOUXSettings.asktosaveonclose = false; + /* SIOUXSettings.initializeTB = false; + SIOUXSettings.showstatusline = true;*/ + puts(startupMessage); + SIOUXSetTitle(consoleName); + + /* set up a buffer for stderr (otherwise it's a pig). */ + setvbuf(stderr, (char *) malloc(BUFSIZ), _IOLBF, BUFSIZ); + + *argc = 1; + *argv = mac_argv; +} + +#ifdef LIVECONNECT +/* Little hack to provide a default CLASSPATH on the Mac. */ +#define getenv(var) mac_getenv(var) +static char* mac_getenv(const char* var) +{ + if (strcmp(var, "CLASSPATH") == 0) { + static char class_path[] = "liveconnect.jar"; + return class_path; + } + return NULL; +} +#endif /* LIVECONNECT */ + +#endif +#endif + +#ifdef JSDEBUGGER +static JSDContext *_jsdc; +#ifdef JSDEBUGGER_JAVA_UI +static JSDJContext *_jsdjc; +#endif /* JSDEBUGGER_JAVA_UI */ +#endif /* JSDEBUGGER */ + +static JSBool reportWarnings = JS_TRUE; + +typedef enum JSShellErrNum { +#define MSG_DEF(name, number, count, exception, format) \ + name = number, +#include "jsshell.msg" +#undef MSG_DEF + JSShellErr_Limit +#undef MSGDEF +} JSShellErrNum; + +static const JSErrorFormatString * +my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); + +#ifdef EDITLINE +extern char *readline(const char *prompt); +extern void add_history(char *line); +#endif + +static JSBool +GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { +#ifdef EDITLINE + /* + * Use readline only if file is stdin, because there's no way to specify + * another handle. Are other filehandles interactive? + */ + if (file == stdin) { + char *linep = readline(prompt); + if (!linep) + return JS_FALSE; + if (linep[0] != '\0') + add_history(linep); + strcpy(bufp, linep); + JS_free(cx, linep); + bufp += strlen(bufp); + *bufp++ = '\n'; + *bufp = '\0'; + } else +#endif + { + char line[256]; + fprintf(gOutFile, prompt); + fflush(gOutFile); +#ifdef XP_MAC_MPW + /* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */ + fputc('\n', gOutFile); +#endif + if (!fgets(line, sizeof line, file)) + return JS_FALSE; + strcpy(bufp, line); + } + return JS_TRUE; +} + +static void +Process(JSContext *cx, JSObject *obj, char *filename) +{ + JSBool ok, hitEOF; + JSScript *script; + jsval result; + JSString *str; + char buffer[4096]; + char *bufp; + int lineno; + int startline; + FILE *file; + jsuword stackLimit; + + if (!filename || strcmp(filename, "-") == 0) { + file = stdin; + } else { + file = fopen(filename, "r"); + if (!file) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_CANT_OPEN, filename, strerror(errno)); + gExitCode = EXITCODE_FILE_NOT_FOUND; + return; + } + } + + if (gMaxStackSize == 0) { + /* + * Disable checking for stack overflow if limit is zero. + */ + stackLimit = 0; + } else { +#if JS_STACK_GROWTH_DIRECTION > 0 + stackLimit = gStackBase + gMaxStackSize; +#else + stackLimit = gStackBase - gMaxStackSize; +#endif + } + JS_SetThreadStackLimit(cx, stackLimit); + + if (!isatty(fileno(file))) { + /* + * It's not interactive - just execute it. + * + * Support the UNIX #! shell hack; gobble the first line if it starts + * with '#'. TODO - this isn't quite compatible with sharp variables, + * as a legal js program (using sharp variables) might start with '#'. + * But that would require multi-character lookahead. + */ + int ch = fgetc(file); + if (ch == '#') { + while((ch = fgetc(file)) != EOF) { + if (ch == '\n' || ch == '\r') + break; + } + } + ungetc(ch, file); + script = JS_CompileFileHandle(cx, obj, filename, file); + if (script) { + (void)JS_ExecuteScript(cx, obj, script, &result); + JS_DestroyScript(cx, script); + } + return; + } + + /* It's an interactive filehandle; drop into read-eval-print loop. */ + lineno = 1; + hitEOF = JS_FALSE; + do { + bufp = buffer; + *bufp = '\0'; + + /* + * Accumulate lines until we get a 'compilable unit' - one that either + * generates an error (before running out of source) or that compiles + * cleanly. This should be whenever we get a complete statement that + * coincides with the end of a line. + */ + startline = lineno; + do { + if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { + hitEOF = JS_TRUE; + break; + } + bufp += strlen(bufp); + lineno++; + } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); + + /* Clear any pending exception from previous failed compiles. */ + JS_ClearPendingException(cx); + script = JS_CompileScript(cx, obj, buffer, strlen(buffer), +#ifdef JSDEBUGGER + "typein", +#else + NULL, +#endif + startline); + if (script) { + ok = JS_ExecuteScript(cx, obj, script, &result); + if (ok && result != JSVAL_VOID) { + str = JS_ValueToString(cx, result); + if (str) + fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); + else + ok = JS_FALSE; + } + JS_DestroyScript(cx, script); + } + } while (!hitEOF && !gQuitting); + fprintf(gOutFile, "\n"); + return; +} + +static int +usage(void) +{ + fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); + fprintf(gErrFile, "usage: js [-PswW] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); + return 2; +} + +static uint32 gBranchCount; +static uint32 gBranchLimit; + +static JSBool +my_BranchCallback(JSContext *cx, JSScript *script) +{ + if (++gBranchCount == gBranchLimit) { + if (script->filename) + fprintf(gErrFile, "%s:", script->filename); + fprintf(gErrFile, "%u: script branches too much (%u callbacks)\n", + script->lineno, gBranchLimit); + gBranchCount = 0; + return JS_FALSE; + } + return JS_TRUE; +} + +extern JSClass global_class; + +static int +ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) +{ + int i, j, length; + JSObject *argsObj; + char *filename = NULL; + JSBool isInteractive = JS_TRUE; + + /* + * Scan past all optional arguments so we can create the arguments object + * before processing any -f options, which must interleave properly with + * -v and -w options. This requires two passes, and without getopt, we'll + * have to keep the option logic here and in the second for loop in sync. + */ + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-' || argv[i][1] == '\0') { + ++i; + break; + } + switch (argv[i][1]) { + case 'b': + case 'c': + case 'f': + case 'v': + case 'S': + ++i; + break; + } + } + + /* + * Create arguments early and define it to root it, so it's safe from any + * GC calls nested below, and so it is available to -f arguments. + */ + argsObj = JS_NewArrayObject(cx, 0, NULL); + if (!argsObj) + return 1; + if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), + NULL, NULL, 0)) { + return 1; + } + + length = argc - i; + for (j = 0; j < length; j++) { + JSString *str = JS_NewStringCopyZ(cx, argv[i++]); + if (!str) + return 1; + if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), + NULL, NULL, JSPROP_ENUMERATE)) { + return 1; + } + } + + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-' || argv[i][1] == '\0') { + filename = argv[i++]; + isInteractive = JS_FALSE; + break; + } + + switch (argv[i][1]) { + case 'v': + if (++i == argc) { + return usage(); + } + JS_SetVersion(cx, (JSVersion) atoi(argv[i])); + break; + + case 'w': + reportWarnings = JS_TRUE; + break; + + case 'W': + reportWarnings = JS_FALSE; + break; + + case 's': + JS_ToggleOptions(cx, JSOPTION_STRICT); + break; + + case 'P': + if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { + JSObject *gobj; + + if (!JS_SealObject(cx, obj, JS_TRUE)) + return JS_FALSE; + gobj = JS_NewObject(cx, &global_class, NULL, NULL); + if (!gobj) + return JS_FALSE; + if (!JS_SetPrototype(cx, gobj, obj)) + return JS_FALSE; + JS_SetParent(cx, gobj, NULL); + JS_SetGlobalObject(cx, gobj); + obj = gobj; + } + break; + + case 'b': + gBranchLimit = atoi(argv[++i]); + JS_SetBranchCallback(cx, my_BranchCallback); + break; + + case 'c': + /* set stack chunk size */ + gStackChunkSize = atoi(argv[++i]); + break; + + case 'f': + if (++i == argc) { + return usage(); + } + Process(cx, obj, argv[i]); + /* + * XXX: js -f foo.js should interpret foo.js and then + * drop into interactive mode, but that breaks test + * harness. Just execute foo.js for now. + */ + isInteractive = JS_FALSE; + break; + + case 'S': + if (++i == argc) { + return usage(); + } + /* Set maximum stack size. */ + gMaxStackSize = atoi(argv[i]); + break; + + default: + return usage(); + } + } + + if (filename || isInteractive) + Process(cx, obj, filename); + return gExitCode; +} + + +static JSBool +Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc > 0 && JSVAL_IS_INT(argv[0])) + *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0]))); + else + *rval = INT_TO_JSVAL(JS_GetVersion(cx)); + return JS_TRUE; +} + +static struct { + const char *name; + uint32 flag; +} js_options[] = { + {"strict", JSOPTION_STRICT}, + {"werror", JSOPTION_WERROR}, + {0, 0} +}; + +static JSBool +Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uint32 optset, flag; + uintN i, j, found; + JSString *str; + const char *opt; + char *names; + + optset = 0; + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + opt = JS_GetStringBytes(str); + for (j = 0; js_options[j].name; j++) { + if (strcmp(js_options[j].name, opt) == 0) { + optset |= js_options[j].flag; + break; + } + } + } + optset = JS_ToggleOptions(cx, optset); + + names = NULL; + found = 0; + while (optset != 0) { + flag = optset; + optset &= optset - 1; + flag &= ~optset; + for (j = 0; js_options[j].name; j++) { + if (js_options[j].flag == flag) { + names = JS_sprintf_append(names, "%s%s", + names ? "," : "", js_options[j].name); + found++; + break; + } + } + } + if (!found) + names = strdup(""); + if (!names) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + str = JS_NewString(cx, names, strlen(names)); + if (!str) { + free(names); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static void +my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); + +static void +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); + +static JSBool +Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString *str; + const char *filename; + JSScript *script; + JSBool ok; + jsval result; + JSErrorReporter older; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str); + filename = JS_GetStringBytes(str); + errno = 0; + older = JS_SetErrorReporter(cx, my_LoadErrorReporter); + script = JS_CompileFile(cx, obj, filename); + if (!script) + ok = JS_FALSE; + else { + ok = JS_ExecuteScript(cx, obj, script, &result); + JS_DestroyScript(cx, script); + } + JS_SetErrorReporter(cx, older); + if (!ok) + return JS_FALSE; + } + + return JS_TRUE; +} + +static JSBool +Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i, n; + JSString *str; + + for (i = n = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str)); + } + n++; + if (n) + fputc('\n', gOutFile); + return JS_TRUE; +} + +static JSBool +Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +static JSBool +Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#ifdef LIVECONNECT + JSJ_SimpleShutdown(); +#endif + + JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); + + gQuitting = JS_TRUE; + return JS_FALSE; +} + +#ifdef GC_MARK_DEBUG +extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; +#endif + +static JSBool +GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSRuntime *rt; + uint32 preBytes; + + rt = cx->runtime; + preBytes = rt->gcBytes; +#ifdef GC_MARK_DEBUG + if (argc && JSVAL_IS_STRING(argv[0])) { + char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); + FILE *file = fopen(name, "w"); + if (!file) { + fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno)); + return JS_FALSE; + } + js_DumpGCHeap = file; + } else { + js_DumpGCHeap = stdout; + } +#endif + JS_GC(cx); +#ifdef GC_MARK_DEBUG + if (js_DumpGCHeap != stdout) + fclose(js_DumpGCHeap); + js_DumpGCHeap = NULL; +#endif + fprintf(gOutFile, "before %lu, after %lu, break %08lx\n", + (unsigned long)preBytes, (unsigned long)rt->gcBytes, +#ifdef XP_UNIX + (unsigned long)sbrk(0) +#else + 0 +#endif + ); +#ifdef JS_GCMETER + js_DumpGCStats(rt, stdout); +#endif + return JS_TRUE; +} + +static JSScript * +ValueToScript(JSContext *cx, jsval v) +{ + JSScript *script; + JSFunction *fun; + + if (JSVAL_IS_OBJECT(v) && + JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) { + script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + } else { + fun = JS_ValueToFunction(cx, v); + if (!fun) + return NULL; + script = fun->script; + } + return script; +} + +static JSBool +GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, + int32 *ip) +{ + uintN intarg; + JSScript *script; + + *scriptp = cx->fp->down->script; + *ip = 0; + if (argc != 0) { + intarg = 0; + if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) { + script = ValueToScript(cx, argv[0]); + if (!script) + return JS_FALSE; + *scriptp = script; + intarg++; + } + if (argc > intarg) { + if (!JS_ValueToInt32(cx, argv[intarg], ip)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSTrapStatus +TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, + void *closure) +{ + JSString *str; + JSStackFrame *caller; + + str = (JSString *) closure; + caller = JS_GetScriptedCaller(cx, NULL); + if (!JS_EvaluateScript(cx, caller->scopeChain, + JS_GetStringBytes(str), JS_GetStringLength(str), + caller->script->filename, caller->script->lineno, + rval)) { + return JSTRAP_ERROR; + } + if (*rval != JSVAL_VOID) + return JSTRAP_RETURN; + return JSTRAP_CONTINUE; +} + +static JSBool +Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + JSScript *script; + int32 i; + + if (argc == 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE); + return JS_FALSE; + } + argc--; + str = JS_ValueToString(cx, argv[argc]); + if (!str) + return JS_FALSE; + argv[argc] = STRING_TO_JSVAL(str); + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + return JS_SetTrap(cx, script, script->code + i, TrapHandler, str); +} + +static JSBool +Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + JS_ClearTrap(cx, script, script->code + i, NULL, NULL); + return JS_TRUE; +} + +static JSBool +LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + uintN lineno; + jsbytecode *pc; + + if (argc == 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE); + return JS_FALSE; + } + script = cx->fp->down->script; + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + lineno = (i == 0) ? script->lineno : (uintN)i; + pc = JS_LineNumberToPC(cx, script, lineno); + if (!pc) + return JS_FALSE; + *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode)); + return JS_TRUE; +} + +static JSBool +PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + int32 i; + uintN lineno; + + if (!GetTrapArgs(cx, argc, argv, &script, &i)) + return JS_FALSE; + lineno = JS_PCToLineNumber(cx, script, script->code + i); + if (!lineno) + return JS_FALSE; + *rval = INT_TO_JSVAL(lineno); + return JS_TRUE; +} + +#ifdef DEBUG + +static void +SrcNotes(JSContext *cx, JSScript *script) +{ + uintN offset, delta, caseOff; + jssrcnote *notes, *sn; + JSSrcNoteType type; + jsatomid atomIndex; + JSAtom *atom; + + fprintf(gOutFile, "\nSource notes:\n"); + offset = 0; + notes = SCRIPT_NOTES(script); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + delta = SN_DELTA(sn); + offset += delta; + fprintf(gOutFile, "%3u: %5u [%4u] %-8s", + PTRDIFF(sn, notes, jssrcnote), offset, delta, + js_SrcNoteSpec[SN_TYPE(sn)].name); + type = (JSSrcNoteType) SN_TYPE(sn); + switch (type) { + case SRC_SETLINE: + fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); + break; + case SRC_FOR: + fprintf(gOutFile, " cond %u update %u tail %u", + (uintN) js_GetSrcNoteOffset(sn, 0), + (uintN) js_GetSrcNoteOffset(sn, 1), + (uintN) js_GetSrcNoteOffset(sn, 2)); + break; + case SRC_COND: + case SRC_IF_ELSE: + case SRC_WHILE: + case SRC_PCBASE: + case SRC_PCDELTA: + fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); + break; + case SRC_LABEL: + case SRC_LABELBRACE: + case SRC_BREAK2LABEL: + case SRC_CONT2LABEL: + case SRC_FUNCDEF: { + const char *bytes; + JSFunction *fun; + JSString *str; + + atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + if (type != SRC_FUNCDEF) { + bytes = js_AtomToPrintableString(cx, atom); + } else { + fun = (JSFunction *) + JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); + str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); + bytes = str ? JS_GetStringBytes(str) : "N/A"; + } + fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes); + break; + } + case SRC_SWITCH: + fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0)); + caseOff = (uintN) js_GetSrcNoteOffset(sn, 1); + if (caseOff) + fprintf(gOutFile, " first case offset %u", caseOff); + break; + case SRC_CATCH: + delta = (uintN) js_GetSrcNoteOffset(sn, 0); + if (delta) + fprintf(gOutFile, " guard size %u", delta); + break; + default:; + } + fputc('\n', gOutFile); + } +} + +static JSBool +Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSScript *script; + + for (i = 0; i < argc; i++) { + script = ValueToScript(cx, argv[i]); + if (!script) + continue; + + SrcNotes(cx, script); + } + return JS_TRUE; +} + +static JSBool +TryNotes(JSContext *cx, JSScript *script) +{ + JSTryNote *tn = script->trynotes; + + if (!tn) + return JS_TRUE; + fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n"); + while (tn->start && tn->catchStart) { + fprintf(gOutFile, " %d\t%d\t%d\n", + tn->start, tn->start + tn->length, tn->catchStart); + tn++; + } + return JS_TRUE; +} + +static JSBool +Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool lines; + uintN i; + JSScript *script; + + if (argc > 0 && + JSVAL_IS_STRING(argv[0]) && + !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) { + lines = JS_TRUE; + argv++, argc--; + } else { + lines = JS_FALSE; + } + for (i = 0; i < argc; i++) { + script = ValueToScript(cx, argv[i]); + if (!script) + continue; + + if (JSVAL_IS_FUNCTION(cx, argv[i])) { + JSFunction *fun = JS_ValueToFunction(cx, argv[i]); + if (fun && (fun->flags & JSFUN_FLAGS_MASK)) { + uint8 flags = fun->flags; + fputs("flags:", stdout); + +#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout); + + SHOW_FLAG(LAMBDA); + SHOW_FLAG(SETTER); + SHOW_FLAG(GETTER); + SHOW_FLAG(BOUND_METHOD); + SHOW_FLAG(HEAVYWEIGHT); + +#undef SHOW_FLAG + putchar('\n'); + } + } + + js_Disassemble(cx, script, lines, stdout); + SrcNotes(cx, script); + TryNotes(cx, script); + } + return JS_TRUE; +} + +static JSBool +DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ +#define LINE_BUF_LEN 512 + uintN i, len, line1, line2, bupline; + JSScript *script; + FILE *file; + char linebuf[LINE_BUF_LEN]; + jsbytecode *pc, *end; + static char sep[] = ";-------------------------"; + + for (i = 0; i < argc; i++) { + script = ValueToScript(cx, argv[i]); + if (!script) + continue; + + if (!script || !script->filename) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_FILE_SCRIPTS_ONLY); + return JS_FALSE; + } + + file = fopen(script->filename, "r"); + if (!file) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_CANT_OPEN, + script->filename, strerror(errno)); + return JS_FALSE; + } + + pc = script->code; + end = pc + script->length; + + /* burn the leading lines */ + line2 = JS_PCToLineNumber(cx, script, pc); + for (line1 = 0; line1 < line2 - 1; line1++) + fgets(linebuf, LINE_BUF_LEN, file); + + bupline = 0; + while (pc < end) { + line2 = JS_PCToLineNumber(cx, script, pc); + + if (line2 < line1) { + if (bupline != line2) { + bupline = line2; + fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2); + } + } else { + if (bupline && line1 == line2) + fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2); + bupline = 0; + while (line1 < line2) { + if (!fgets(linebuf, LINE_BUF_LEN, file)) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_UNEXPECTED_EOF, + script->filename); + goto bail; + } + line1++; + fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf); + } + } + + len = js_Disassemble1(cx, script, pc, + PTRDIFF(pc, script->code, jsbytecode), + JS_TRUE, stdout); + if (!len) + return JS_FALSE; + pc += len; + } + + bail: + fclose(file); + } + return JS_TRUE; +#undef LINE_BUF_LEN +} + +static JSBool +Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool bval; + JSString *str; + + if (argc == 0) { + *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0); + return JS_TRUE; + } + + switch (JS_TypeOfValue(cx, argv[0])) { + case JSTYPE_NUMBER: + bval = JSVAL_IS_INT(argv[0]) + ? JSVAL_TO_INT(argv[0]) + : (jsint) *JSVAL_TO_DOUBLE(argv[0]); + break; + case JSTYPE_BOOLEAN: + bval = JSVAL_TO_BOOLEAN(argv[0]); + break; + default: + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + fprintf(gErrFile, "tracing: illegal argument %s\n", + JS_GetStringBytes(str)); + return JS_TRUE; + } + cx->tracefp = bval ? stderr : NULL; + return JS_TRUE; +} + +typedef struct DumpAtomArgs { + JSContext *cx; + FILE *fp; +} DumpAtomArgs; + +static int +DumpAtom(JSHashEntry *he, int i, void *arg) +{ + DumpAtomArgs *args = (DumpAtomArgs *)arg; + FILE *fp = args->fp; + JSAtom *atom = (JSAtom *)he; + + fprintf(fp, "%3d %08x %5lu ", + i, (uintN)he->keyHash, (unsigned long)atom->number); + if (ATOM_IS_STRING(atom)) + fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom)); + else if (ATOM_IS_INT(atom)) + fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom)); + else + fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom)); + return HT_ENUMERATE_NEXT; +} + +static void +DumpScope(JSContext *cx, JSObject *obj, FILE *fp) +{ + uintN i; + JSScope *scope; + JSScopeProperty *sprop; + + i = 0; + scope = OBJ_SCOPE(obj); + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) + continue; + fprintf(fp, "%3u %p", i, sprop); + if (JSVAL_IS_INT(sprop->id)) { + fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); + } else { + fprintf(fp, " \"%s\"", + js_AtomToPrintableString(cx, (JSAtom *)sprop->id)); + } + +#define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) + DUMP_ATTR(ENUMERATE); + DUMP_ATTR(READONLY); + DUMP_ATTR(PERMANENT); + DUMP_ATTR(EXPORTED); + DUMP_ATTR(GETTER); + DUMP_ATTR(SETTER); +#undef DUMP_ATTR + + fprintf(fp, " slot %lu flags %x shortid %d\n", + sprop->slot, sprop->flags, sprop->shortid); + } +} + +static JSBool +DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i; + JSString *str; + const char *bytes; + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + jsval value; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + bytes = JS_GetStringBytes(str); + if (strcmp(bytes, "arena") == 0) { +#ifdef JS_ARENAMETER + JS_DumpArenaStats(stdout); +#endif + } else if (strcmp(bytes, "atom") == 0) { + DumpAtomArgs args; + + fprintf(gOutFile, "\natom table contents:\n"); + args.cx = cx; + args.fp = stdout; + JS_HashTableEnumerateEntries(cx->runtime->atomState.table, + DumpAtom, + &args); +#ifdef HASHMETER + JS_HashTableDumpMeter(cx->runtime->atomState.table, + DumpAtom, + stdout); +#endif + } else if (strcmp(bytes, "global") == 0) { + DumpScope(cx, cx->globalObject, stdout); + } else { + atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); + if (!atom) + return JS_FALSE; + if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value)) + return JS_FALSE; + } + if (!prop || !JSVAL_IS_OBJECT(value)) { + fprintf(gErrFile, "js: invalid stats argument %s\n", + bytes); + continue; + } + obj = JSVAL_TO_OBJECT(value); + if (obj) + DumpScope(cx, obj, stdout); + } + } + return JS_TRUE; +} + +#endif /* DEBUG */ + +#ifdef TEST_EXPORT +static JSBool +DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSAtom *atom; + JSObject *obj2; + JSProperty *prop; + JSBool ok; + uintN attrs; + + if (argc != 2) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE); + return JS_FALSE; + } + if (!JS_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(obj); + atom = js_ValueToStringAtom(cx, argv[1]); + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} +#endif + +#ifdef TEST_CVTARGS +#include + +static const char * +EscapeWideString(jschar *w) +{ + static char enuf[80]; + static char hex[] = "0123456789abcdef"; + jschar u; + unsigned char b, c; + int i, j; + + if (!w) + return ""; + for (i = j = 0; i < sizeof enuf - 1; i++, j++) { + u = w[j]; + if (u == 0) + break; + b = (unsigned char)(u >> 8); + c = (unsigned char)(u); + if (b) { + if (i >= sizeof enuf - 6) + break; + enuf[i++] = '\\'; + enuf[i++] = 'u'; + enuf[i++] = hex[b >> 4]; + enuf[i++] = hex[b & 15]; + enuf[i++] = hex[c >> 4]; + enuf[i] = hex[c & 15]; + } else if (!isprint(c)) { + if (i >= sizeof enuf - 4) + break; + enuf[i++] = '\\'; + enuf[i++] = 'x'; + enuf[i++] = hex[c >> 4]; + enuf[i] = hex[c & 15]; + } else { + enuf[i] = (char)c; + } + } + enuf[i] = 0; + return enuf; +} + +#include + +static JSBool +ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, + va_list *app) +{ + jsval *vp; + va_list ap; + jsdouble re, im; + + printf("entering ZZ_formatter"); + vp = *vpp; + ap = *app; + if (fromJS) { + if (!JS_ValueToNumber(cx, vp[0], &re)) + return JS_FALSE; + if (!JS_ValueToNumber(cx, vp[1], &im)) + return JS_FALSE; + *va_arg(ap, jsdouble *) = re; + *va_arg(ap, jsdouble *) = im; + } else { + re = va_arg(ap, jsdouble); + im = va_arg(ap, jsdouble); + if (!JS_NewNumberValue(cx, re, &vp[0])) + return JS_FALSE; + if (!JS_NewNumberValue(cx, im, &vp[1])) + return JS_FALSE; + } + *vpp = vp + 2; + *app = ap; + printf("leaving ZZ_formatter"); + return JS_TRUE; +} + +static JSBool +ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool b = JS_FALSE; + jschar c = 0; + int32 i = 0, j = 0; + uint32 u = 0; + jsdouble d = 0, I = 0, re = 0, im = 0; + char *s = NULL; + JSString *str = NULL; + jschar *w = NULL; + JSObject *obj2 = NULL; + JSFunction *fun = NULL; + jsval v = JSVAL_VOID; + JSBool ok; + + if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter)) + return JS_FALSE;; + ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*", + &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2, + &fun, &v, &re, &im); + JS_RemoveArgumentFormatter(cx, "ZZ"); + if (!ok) + return JS_FALSE; + fprintf(gOutFile, + "b %u, c %x (%c), i %ld, u %lu, j %ld\n", + b, c, (char)c, i, u, j); + fprintf(gOutFile, + "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n" + "v %s, re %g, im %g\n", + d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w), + JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))), + fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "", + JS_GetStringBytes(JS_ValueToString(cx, v)), re, im); + return JS_TRUE; +} +#endif + +static JSBool +BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__); + return JS_TRUE; +} + +static JSBool +Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + JS_ClearScope(cx, obj); + return JS_TRUE; +} + +static JSBool +Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + if (!JS_InternUCStringN(cx, JS_GetStringChars(str), + JS_GetStringLength(str))) { + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFunction *fun; + JSObject *funobj, *parent, *clone; + + fun = JS_ValueToFunction(cx, argv[0]); + if (!fun) + return JS_FALSE; + funobj = JS_GetFunctionObject(fun); + if (argc > 1) { + if (!JS_ValueToObject(cx, argv[1], &parent)) + return JS_FALSE; + } else { + parent = JS_GetParent(cx, funobj); + } + clone = JS_CloneFunctionObject(cx, funobj, parent); + if (!clone) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(clone); + return JS_TRUE; +} + +static JSBool +Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *target; + JSBool deep = JS_FALSE; + + if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) + return JS_FALSE; + if (!target) + return JS_TRUE; + return JS_SealObject(cx, target, deep); +} + +static JSFunctionSpec shell_functions[] = { + {"version", Version, 0}, + {"options", Options, 0}, + {"load", Load, 1}, + {"print", Print, 0}, + {"help", Help, 0}, + {"quit", Quit, 0}, + {"gc", GC, 0}, + {"trap", Trap, 3}, + {"untrap", Untrap, 2}, + {"line2pc", LineToPC, 0}, + {"pc2line", PCToLine, 0}, +#ifdef DEBUG + {"dis", Disassemble, 1}, + {"dissrc", DisassWithSrc, 1}, + {"notes", Notes, 1}, + {"tracing", Tracing, 0}, + {"stats", DumpStats, 1}, +#endif +#ifdef TEST_EXPORT + {"xport", DoExport, 2}, +#endif +#ifdef TEST_CVTARGS + {"cvtargs", ConvertArgs, 0, 0, 12}, +#endif + {"build", BuildDate, 0}, + {"clear", Clear, 0}, + {"intern", Intern, 1}, + {"clone", Clone, 1}, + {"seal", Seal, 1, 0, 1}, + {0} +}; + +/* NOTE: These must be kept in sync with the above. */ + +static char *shell_help_messages[] = { + "version([number]) Get or set JavaScript version number", + "options([option ...]) Get or toggle JavaScript options", + "load(['foo.js' ...]) Load files named by string arguments", + "print([exp ...]) Evaluate and print expressions", + "help([name ...]) Display usage and help messages", + "quit() Quit the shell", + "gc() Run the garbage collector", + "trap([fun, [pc,]] exp) Trap bytecode execution", + "untrap(fun[, pc]) Remove a trap", + "line2pc([fun,] line) Map line number to PC", + "pc2line(fun[, pc]) Map PC to line number", +#ifdef DEBUG + "dis([fun]) Disassemble functions into bytecodes", + "dissrc([fun]) Disassemble functions with source lines", + "notes([fun]) Show source notes for functions", + "tracing([toggle]) Turn tracing on or off", + "stats([string ...]) Dump 'arena', 'atom', 'global' stats", +#endif +#ifdef TEST_EXPORT + "xport(obj, id) Export identified property from object", +#endif +#ifdef TEST_CVTARGS + "cvtargs(b, c, ...) Test JS_ConvertArguments", +#endif + "build() Show build date and time", + "clear([obj]) Clear properties of object", + "intern(str) Internalize str in the atom table", + "clone(fun[, scope]) Clone function object", + "seal(obj[, deep]) Seal object, or object graph if deep", + 0 +}; + +static void +ShowHelpHeader(void) +{ + fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description"); + fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "==========="); +} + +static void +ShowHelpForCommand(uintN n) +{ + fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]); +} + +static JSBool +Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + uintN i, j; + int did_header, did_something; + JSType type; + JSFunction *fun; + JSString *str; + const char *bytes; + + fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); + if (argc == 0) { + ShowHelpHeader(); + for (i = 0; shell_functions[i].name; i++) + ShowHelpForCommand(i); + } else { + did_header = 0; + for (i = 0; i < argc; i++) { + did_something = 0; + type = JS_TypeOfValue(cx, argv[i]); + if (type == JSTYPE_FUNCTION) { + fun = JS_ValueToFunction(cx, argv[i]); + str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + } else if (type == JSTYPE_STRING) { + str = JSVAL_TO_STRING(argv[i]); + } else { + str = NULL; + } + if (str) { + bytes = JS_GetStringBytes(str); + for (j = 0; shell_functions[j].name; j++) { + if (!strcmp(bytes, shell_functions[j].name)) { + if (!did_header) { + did_header = 1; + ShowHelpHeader(); + } + did_something = 1; + ShowHelpForCommand(j); + break; + } + } + } + if (!did_something) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + fprintf(gErrFile, "Sorry, no help for %s\n", + JS_GetStringBytes(str)); + } + } + } + return JS_TRUE; +} + +/* + * Define a JS object called "it". Give it class operations that printf why + * they're being called for tutorial purposes. + */ +enum its_tinyid { + ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY +}; + +static JSPropertySpec its_props[] = { + {"color", ITS_COLOR, JSPROP_ENUMERATE}, + {"height", ITS_HEIGHT, JSPROP_ENUMERATE}, + {"width", ITS_WIDTH, JSPROP_ENUMERATE}, + {"funny", ITS_FUNNY, JSPROP_ENUMERATE}, + {"array", ITS_ARRAY, JSPROP_ENUMERATE}, + {"rdonly", ITS_RDONLY, JSPROP_READONLY}, + {0} +}; + +static JSBool +its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + if (argc != 0) + JS_SetCallReturnValue2(cx, argv[0]); + return JS_TRUE; +} + +static JSFunctionSpec its_methods[] = { + {"item", its_item, 0}, + {0} +}; + +#ifdef JSD_LOWLEVEL_SOURCE +/* + * This facilitates sending source to JSD (the debugger system) in the shell + * where the source is loaded using the JSFILE hack in jsscan. The function + * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook. + * A more normal embedding (e.g. mozilla) loads source itself and can send + * source directly to JSD without using this hook scheme. + */ +static void +SendSourceToJSDebugger(const char *filename, uintN lineno, + jschar *str, size_t length, + void **listenerTSData, JSDContext* jsdc) +{ + JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData; + + if (!jsdsrc) { + if (!filename) + filename = "typein"; + if (1 == lineno) { + jsdsrc = JSD_NewSourceText(jsdc, filename); + } else { + jsdsrc = JSD_FindSourceForURL(jsdc, filename); + if (jsdsrc && JSD_SOURCE_PARTIAL != + JSD_GetSourceStatus(jsdc, jsdsrc)) { + jsdsrc = NULL; + } + } + } + if (jsdsrc) { + jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length, + JSD_SOURCE_PARTIAL); + } + *listenerTSData = jsdsrc; +} +#endif /* JSD_LOWLEVEL_SOURCE */ + +static JSBool its_noisy; /* whether to be noisy when finalizing it */ + +static JSBool +its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + fprintf(gOutFile, "adding its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + fprintf(gOutFile, " initial value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + fprintf(gOutFile, "deleting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + fprintf(gOutFile, " current value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + fprintf(gOutFile, "getting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + fprintf(gOutFile, " current value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + return JS_TRUE; +} + +static JSBool +its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + if (its_noisy) { + fprintf(gOutFile, "setting its property %s,", + JS_GetStringBytes(JS_ValueToString(cx, id))); + fprintf(gOutFile, " new value %s\n", + JS_GetStringBytes(JS_ValueToString(cx, *vp))); + } + if (JSVAL_IS_STRING(id) && + !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) { + return JS_ValueToBoolean(cx, *vp, &its_noisy); + } + return JS_TRUE; +} + +static JSBool +its_enumerate(JSContext *cx, JSObject *obj) +{ + if (its_noisy) + fprintf(gOutFile, "enumerate its properties\n"); + return JS_TRUE; +} + +static JSBool +its_resolve(JSContext *cx, JSObject *obj, jsval id) +{ + if (its_noisy) { + fprintf(gOutFile, "resolving its property %s\n", + JS_GetStringBytes(JS_ValueToString(cx, id))); + } + return JS_TRUE; +} + +static JSBool +its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + if (its_noisy) + fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type)); + return JS_TRUE; +} + +static void +its_finalize(JSContext *cx, JSObject *obj) +{ + if (its_noisy) + fprintf(gOutFile, "finalizing it\n"); +} + +static JSClass its_class = { + "It", 0, + its_addProperty, its_delProperty, its_getProperty, its_setProperty, + its_enumerate, its_resolve, its_convert, its_finalize +}; + +JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = { +#if JS_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 "jsshell.msg" +#undef MSG_DEF +}; + +static const JSErrorFormatString * +my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) +{ + if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit)) + return &jsShell_ErrorFormatString[errorNumber]; + return NULL; +} + +static void +my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + if (!report) { + fprintf(gErrFile, "%s\n", message); + return; + } + + /* Ignore any exceptions */ + if (JSREPORT_IS_EXCEPTION(report->flags)) + return; + + /* Otherwise, fall back to the ordinary error reporter. */ + my_ErrorReporter(cx, message, report); +} + +static void +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + int i, j, k, n; + char *prefix, *tmp; + const char *ctmp; + + if (!report) { + fprintf(gErrFile, "%s\n", message); + return; + } + + /* Conditionally ignore reported warnings. */ + if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) + return; + + prefix = NULL; + if (report->filename) + prefix = JS_smprintf("%s:", report->filename); + if (report->lineno) { + tmp = prefix; + prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno); + JS_free(cx, tmp); + } + if (JSREPORT_IS_WARNING(report->flags)) { + tmp = prefix; + prefix = JS_smprintf("%s%swarning: ", + tmp ? tmp : "", + JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); + JS_free(cx, tmp); + } + + /* embedded newlines -- argh! */ + while ((ctmp = strchr(message, '\n')) != 0) { + ctmp++; + if (prefix) + fputs(prefix, gErrFile); + fwrite(message, 1, ctmp - message, gErrFile); + message = ctmp; + } + + /* If there were no filename or lineno, the prefix might be empty */ + if (prefix) + fputs(prefix, gErrFile); + fputs(message, gErrFile); + + if (!report->linebuf) { + fputc('\n', gErrFile); + goto out; + } + + /* report->linebuf usually ends with a newline. */ + n = strlen(report->linebuf); + fprintf(gErrFile, ":\n%s%s%s%s", + prefix, + report->linebuf, + (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", + prefix); + n = PTRDIFF(report->tokenptr, report->linebuf, char); + for (i = j = 0; i < n; i++) { + if (report->linebuf[i] == '\t') { + for (k = (j + 8) & ~7; j < k; j++) { + fputc('.', gErrFile); + } + continue; + } + fputc('.', gErrFile); + j++; + } + fputs("^\n", gErrFile); + out: + if (!JSREPORT_IS_WARNING(report->flags)) + gExitCode = EXITCODE_RUNTIME_ERROR; + JS_free(cx, prefix); +} + +#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) +static JSBool +Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFunction *fun; + const char *name, **nargv; + uintN i, nargc; + JSString *str; + pid_t pid; + int status; + + fun = JS_ValueToFunction(cx, argv[-2]); + if (!fun) + return JS_FALSE; + if (!fun->atom) + return JS_TRUE; + name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom)); + nargc = 1 + argc; + nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *)); + if (!nargv) + return JS_FALSE; + nargv[0] = name; + for (i = 1; i < nargc; i++) { + str = JS_ValueToString(cx, argv[i-1]); + if (!str) { + JS_free(cx, nargv); + return JS_FALSE; + } + nargv[i] = JS_GetStringBytes(str); + } + nargv[nargc] = 0; + pid = fork(); + switch (pid) { + case -1: + perror("js"); + break; + case 0: + (void) execvp(name, (char **)nargv); + perror("js"); + exit(127); + default: + while (waitpid(pid, &status, 0) < 0 && errno == EINTR) + continue; + break; + } + JS_free(cx, nargv); + return JS_TRUE; +} +#endif + +#define LAZY_STANDARD_CLASSES + +static JSBool +global_enumerate(JSContext *cx, JSObject *obj) +{ +#ifdef LAZY_STANDARD_CLASSES + return JS_EnumerateStandardClasses(cx, obj); +#else + return JS_TRUE; +#endif +} + +static JSBool +global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ +#ifdef LAZY_STANDARD_CLASSES + if ((flags & JSRESOLVE_ASSIGNING) == 0) { + JSBool resolved; + + if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) + return JS_FALSE; + if (resolved) { + *objp = obj; + return JS_TRUE; + } + } +#endif + +#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) + if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) { + /* + * Do this expensive hack only for unoptimized Unix builds, which are + * not used for benchmarking. + */ + char *path, *comp, *full; + const char *name; + JSBool ok, found; + JSFunction *fun; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + path = getenv("PATH"); + if (!path) + return JS_TRUE; + path = JS_strdup(cx, path); + if (!path) + return JS_FALSE; + name = JS_GetStringBytes(JSVAL_TO_STRING(id)); + ok = JS_TRUE; + for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) { + if (*comp != '\0') { + full = JS_smprintf("%s/%s", comp, name); + if (!full) { + JS_ReportOutOfMemory(cx); + ok = JS_FALSE; + break; + } + } else { + full = (char *)name; + } + found = (access(full, X_OK) == 0); + if (*comp != '\0') + free(full); + if (found) { + fun = JS_DefineFunction(cx, obj, name, Exec, 0, + JSPROP_ENUMERATE); + ok = (fun != NULL); + if (ok) + *objp = obj; + break; + } + } + JS_free(cx, path); + return ok; + } +#else + return JS_TRUE; +#endif +} + +JSClass global_class = { + "global", JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + global_enumerate, (JSResolveOp) global_resolve, + JS_ConvertStub, JS_FinalizeStub +}; + +static JSBool +env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ +/* XXX porting may be easy, but these don't seem to supply setenv by default */ +#if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS + JSString *idstr, *valstr; + const char *name, *value; + int rv; + + idstr = JS_ValueToString(cx, id); + valstr = JS_ValueToString(cx, *vp); + if (!idstr || !valstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = JS_GetStringBytes(valstr); +#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX + { + char *waste = JS_smprintf("%s=%s", name, value); + if (!waste) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + rv = putenv(waste); +#ifdef XP_WIN + /* + * HPUX9 at least still has the bad old non-copying putenv. + * + * Per mail from , OSF1 also has a putenv + * that will crash if you pass it an auto char array (so it must place + * its argument directly in the char *environ[] array). + */ + free(waste); +#endif + } +#else + rv = setenv(name, value, 1); +#endif + if (rv < 0) { + JS_ReportError(cx, "can't set envariable %s to %s", name, value); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(valstr); +#endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ + return JS_TRUE; +} + +static JSBool +env_enumerate(JSContext *cx, JSObject *obj) +{ + static JSBool reflected; + char **evp, *name, *value; + JSString *valstr; + JSBool ok; + + if (reflected) + return JS_TRUE; + + for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { + value = strchr(name, '='); + if (!value) + continue; + *value++ = '\0'; + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) { + ok = JS_FALSE; + } else { + ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE); + } + value[-1] = '='; + if (!ok) + return JS_FALSE; + } + + reflected = JS_TRUE; + return JS_TRUE; +} + +static JSBool +env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSString *idstr, *valstr; + const char *name, *value; + + if (flags & JSRESOLVE_ASSIGNING) + return JS_TRUE; + + idstr = JS_ValueToString(cx, id); + if (!idstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = getenv(name); + if (value) { + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) + return JS_FALSE; + if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +static JSClass env_class = { + "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, env_setProperty, + env_enumerate, (JSResolveOp) env_resolve, + JS_ConvertStub, JS_FinalizeStub +}; + +int +main(int argc, char **argv, char **envp) +{ + int stackDummy; + JSVersion version; + JSRuntime *rt; + JSContext *cx; + JSObject *glob, *it, *envobj; + int result; +#ifdef LIVECONNECT + JavaVM *java_vm = NULL; +#endif +#ifdef JSDEBUGGER_JAVA_UI + JNIEnv *java_env; +#endif + + gStackBase = (jsuword)&stackDummy; + +#ifdef XP_OS2 + /* these streams are normally line buffered on OS/2 and need a \n, * + * so we need to unbuffer then to get a reasonable prompt */ + setbuf(stdout,0); + setbuf(stderr,0); +#endif + + gErrFile = stderr; + gOutFile = stdout; + +#ifdef XP_MAC +#ifndef XP_MAC_MPW + initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv); +#endif +#endif + +#ifdef MAC_TEST_HACK +/* + Open a file "testArgs.txt" and read each line into argc/argv. + Re-direct all output to "results.txt" +*/ + { + char argText[256]; + FILE *f = fopen("testargs.txt", "r"); + if (f) { + int maxArgs = 32; /* arbitrary max !!! */ + int argText_strlen; + argc = 1; + argv = malloc(sizeof(char *) * maxArgs); + argv[0] = NULL; + while (fgets(argText, 255, f)) { + /* argText includes '\n' */ + argText_strlen = strlen(argText); + argv[argc] = malloc(argText_strlen); + strncpy(argv[argc], argText, argText_strlen - 1); + argv[argc][argText_strlen - 1] = '\0'; + argc++; + if (argc >= maxArgs) + break; + } + fclose(f); + } + gTestResultFile = fopen("results.txt", "w"); + } + + gErrFile = gTestResultFile; + gOutFile = gTestResultFile; +#endif + + version = JSVERSION_DEFAULT; + + argc--; + argv++; + + rt = JS_NewRuntime(8L * 1024L * 1024L); + if (!rt) + return 1; + + cx = JS_NewContext(rt, gStackChunkSize); + if (!cx) + return 1; + JS_SetErrorReporter(cx, my_ErrorReporter); + + glob = JS_NewObject(cx, &global_class, NULL, NULL); + if (!glob) + return 1; +#ifdef LAZY_STANDARD_CLASSES + JS_SetGlobalObject(cx, glob); +#else + if (!JS_InitStandardClasses(cx, glob)) + return 1; +#endif + if (!JS_DefineFunctions(cx, glob, shell_functions)) + return 1; + + /* Set version only after there is a global object. */ + if (version != JSVERSION_DEFAULT) + JS_SetVersion(cx, version); + + it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); + if (!it) + return 1; + if (!JS_DefineProperties(cx, it, its_props)) + return 1; + if (!JS_DefineFunctions(cx, it, its_methods)) + return 1; + +#ifdef PERLCONNECT + if (!JS_InitPerlClass(cx, glob)) + return 1; +#endif + +#ifdef JSDEBUGGER + /* + * XXX A command line option to enable debugging (or not) would be good + */ + _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL); + if (!_jsdc) + return 1; + JSD_JSContextInUse(_jsdc, cx); +#ifdef JSD_LOWLEVEL_SOURCE + JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc); +#endif /* JSD_LOWLEVEL_SOURCE */ +#ifdef JSDEBUGGER_JAVA_UI + _jsdjc = JSDJ_CreateContext(); + if (! _jsdjc) + return 1; + JSDJ_SetJSDContext(_jsdjc, _jsdc); + java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc); +#ifdef LIVECONNECT + if (java_env) + (*java_env)->GetJavaVM(java_env, &java_vm); +#endif + /* + * XXX This would be the place to wait for the debugger to start. + * Waiting would be nice in general, but especially when a js file + * is passed on the cmd line. + */ +#endif /* JSDEBUGGER_JAVA_UI */ +#ifdef JSDEBUGGER_C_UI + JSDB_InitDebugger(rt, _jsdc, 0); +#endif /* JSDEBUGGER_C_UI */ +#endif /* JSDEBUGGER */ + +#ifdef LIVECONNECT + if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH"))) + return 1; +#endif + + envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); + if (!envobj || !JS_SetPrivate(cx, envobj, envp)) + return 1; + + result = ProcessArgs(cx, glob, argv, argc); + +#ifdef JSDEBUGGER + if (_jsdc) + JSD_DebuggerOff(_jsdc); +#endif /* JSDEBUGGER */ + +#ifdef MAC_TEST_HACK + fclose(gTestResultFile); +#endif + + JS_DestroyContext(cx); + JS_DestroyRuntime(rt); + JS_ShutDown(); + return result; +} diff --git a/src/extension/script/js/js.mak b/src/extension/script/js/js.mak new file mode 100644 index 000000000..0f8b8f53c --- /dev/null +++ b/src/extension/script/js/js.mak @@ -0,0 +1,4025 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +!IF "$(CFG)" == "" +CFG=jsshell - Win32 Debug +!MESSAGE No configuration specified. Defaulting to jsshell - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "js - Win32 Release" && "$(CFG)" != "js - Win32 Debug" &&\ + "$(CFG)" != "jsshell - Win32 Release" && "$(CFG)" != "jsshell - Win32 Debug" &&\ + "$(CFG)" != "fdlibm - Win32 Release" && "$(CFG)" != "fdlibm - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "js.mak" CFG="jsshell - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "js - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "js - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "jsshell - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "jsshell - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "fdlibm - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "fdlibm - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "jsshell - Win32 Debug" + +!IF "$(CFG)" == "js - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "js___Wi1" +# PROP BASE Intermediate_Dir "js___Wi1" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "fdlibm - Win32 Release" "$(OUTDIR)\js32.dll" + +CLEAN : + -@erase "$(INTDIR)\jsapi.obj" + -@erase "$(INTDIR)\jsarena.obj" + -@erase "$(INTDIR)\jsarray.obj" + -@erase "$(INTDIR)\jsatom.obj" + -@erase "$(INTDIR)\jsbool.obj" + -@erase "$(INTDIR)\jscntxt.obj" + -@erase "$(INTDIR)\jsdate.obj" + -@erase "$(INTDIR)\jsdbgapi.obj" + -@erase "$(INTDIR)\jsdhash.obj" + -@erase "$(INTDIR)\jsdtoa.obj" + -@erase "$(INTDIR)\jsemit.obj" + -@erase "$(INTDIR)\jsexn.obj" + -@erase "$(INTDIR)\jsfun.obj" + -@erase "$(INTDIR)\jsgc.obj" + -@erase "$(INTDIR)\jshash.obj" + -@erase "$(INTDIR)\jsinterp.obj" + -@erase "$(INTDIR)\jslock.obj" + -@erase "$(INTDIR)\jslog2.obj" + -@erase "$(INTDIR)\jslong.obj" + -@erase "$(INTDIR)\jsmath.obj" + -@erase "$(INTDIR)\jsnum.obj" + -@erase "$(INTDIR)\jsobj.obj" + -@erase "$(INTDIR)\jsopcode.obj" + -@erase "$(INTDIR)\jsparse.obj" + -@erase "$(INTDIR)\jsprf.obj" + -@erase "$(INTDIR)\jsregexp.obj" + -@erase "$(INTDIR)\jsscan.obj" + -@erase "$(INTDIR)\jsscope.obj" + -@erase "$(INTDIR)\jsscript.obj" + -@erase "$(INTDIR)\jsstr.obj" + -@erase "$(INTDIR)\jsutil.obj" + -@erase "$(INTDIR)\jsxdrapi.obj" + -@erase "$(INTDIR)\prmjtime.obj" + -@erase "$(OUTDIR)\js32.dll" + -@erase "$(OUTDIR)\js32.exp" + -@erase "$(OUTDIR)\js32.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D\ + "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /Fp"$(INTDIR)/js.pch" /YX\ + /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/js.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/js32.dll" +# SUBTRACT LINK32 /nodefaultlib +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/js32.pdb" /machine:I386 /out:"$(OUTDIR)/js32.dll"\ + /implib:"$(OUTDIR)/js32.lib" /opt:ref /opt:noicf +LINK32_OBJS= \ + "$(INTDIR)\jsapi.obj" \ + "$(INTDIR)\jsarena.obj" \ + "$(INTDIR)\jsarray.obj" \ + "$(INTDIR)\jsatom.obj" \ + "$(INTDIR)\jsbool.obj" \ + "$(INTDIR)\jscntxt.obj" \ + "$(INTDIR)\jsdate.obj" \ + "$(INTDIR)\jsdbgapi.obj" \ + "$(INTDIR)\jsdhash.obj" \ + "$(INTDIR)\jsdtoa.obj" \ + "$(INTDIR)\jsemit.obj" \ + "$(INTDIR)\jsexn.obj" \ + "$(INTDIR)\jsfun.obj" \ + "$(INTDIR)\jsgc.obj" \ + "$(INTDIR)\jshash.obj" \ + "$(INTDIR)\jsinterp.obj" \ + "$(INTDIR)\jslock.obj" \ + "$(INTDIR)\jslog2.obj" \ + "$(INTDIR)\jslong.obj" \ + "$(INTDIR)\jsmath.obj" \ + "$(INTDIR)\jsnum.obj" \ + "$(INTDIR)\jsobj.obj" \ + "$(INTDIR)\jsopcode.obj" \ + "$(INTDIR)\jsparse.obj" \ + "$(INTDIR)\jsprf.obj" \ + "$(INTDIR)\jsregexp.obj" \ + "$(INTDIR)\jsscan.obj" \ + "$(INTDIR)\jsscope.obj" \ + "$(INTDIR)\jsscript.obj" \ + "$(INTDIR)\jsstr.obj" \ + "$(INTDIR)\jsutil.obj" \ + "$(INTDIR)\jsxdrapi.obj" \ + "$(INTDIR)\prmjtime.obj" \ + "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)\js32.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "js___Wi2" +# PROP BASE Intermediate_Dir "js___Wi2" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "fdlibm - Win32 Debug" "$(OUTDIR)\js32.dll" + +CLEAN : + -@erase "$(INTDIR)\jsapi.obj" + -@erase "$(INTDIR)\jsarena.obj" + -@erase "$(INTDIR)\jsarray.obj" + -@erase "$(INTDIR)\jsatom.obj" + -@erase "$(INTDIR)\jsbool.obj" + -@erase "$(INTDIR)\jscntxt.obj" + -@erase "$(INTDIR)\jsdate.obj" + -@erase "$(INTDIR)\jsdbgapi.obj" + -@erase "$(INTDIR)\jsdhash.obj" + -@erase "$(INTDIR)\jsdtoa.obj" + -@erase "$(INTDIR)\jsemit.obj" + -@erase "$(INTDIR)\jsexn.obj" + -@erase "$(INTDIR)\jsfun.obj" + -@erase "$(INTDIR)\jsgc.obj" + -@erase "$(INTDIR)\jshash.obj" + -@erase "$(INTDIR)\jsinterp.obj" + -@erase "$(INTDIR)\jslock.obj" + -@erase "$(INTDIR)\jslog2.obj" + -@erase "$(INTDIR)\jslong.obj" + -@erase "$(INTDIR)\jsmath.obj" + -@erase "$(INTDIR)\jsnum.obj" + -@erase "$(INTDIR)\jsobj.obj" + -@erase "$(INTDIR)\jsopcode.obj" + -@erase "$(INTDIR)\jsparse.obj" + -@erase "$(INTDIR)\jsprf.obj" + -@erase "$(INTDIR)\jsregexp.obj" + -@erase "$(INTDIR)\jsscan.obj" + -@erase "$(INTDIR)\jsscope.obj" + -@erase "$(INTDIR)\jsscript.obj" + -@erase "$(INTDIR)\jsstr.obj" + -@erase "$(INTDIR)\jsutil.obj" + -@erase "$(INTDIR)\jsxdrapi.obj" + -@erase "$(INTDIR)\prmjtime.obj" + -@erase "$(INTDIR)\vc40.idb" + -@erase "$(INTDIR)\vc40.pdb" + -@erase "$(OUTDIR)\js32.dll" + -@erase "$(OUTDIR)\js32.exp" + -@erase "$(OUTDIR)\js32.ilk" + -@erase "$(OUTDIR)\js32.lib" + -@erase "$(OUTDIR)\js32.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "DEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "DEBUG" /D _X86_=1 /D "_WINDOWS"\ + /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /Fp"$(INTDIR)/js.pch" /YX\ + /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/js.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Debug/js32.dll" +# SUBTRACT LINK32 /nodefaultlib +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:yes\ + /pdb:"$(OUTDIR)/js32.pdb" /debug /machine:I386 /out:"$(OUTDIR)/js32.dll"\ + /implib:"$(OUTDIR)/js32.lib" +LINK32_OBJS= \ + "$(INTDIR)\jsapi.obj" \ + "$(INTDIR)\jsarena.obj" \ + "$(INTDIR)\jsarray.obj" \ + "$(INTDIR)\jsatom.obj" \ + "$(INTDIR)\jsbool.obj" \ + "$(INTDIR)\jscntxt.obj" \ + "$(INTDIR)\jsdate.obj" \ + "$(INTDIR)\jsdbgapi.obj" \ + "$(INTDIR)\jsdhash.obj" \ + "$(INTDIR)\jsdtoa.obj" \ + "$(INTDIR)\jsemit.obj" \ + "$(INTDIR)\jsexn.obj" \ + "$(INTDIR)\jsfun.obj" \ + "$(INTDIR)\jsgc.obj" \ + "$(INTDIR)\jshash.obj" \ + "$(INTDIR)\jsinterp.obj" \ + "$(INTDIR)\jslock.obj" \ + "$(INTDIR)\jslog2.obj" \ + "$(INTDIR)\jslong.obj" \ + "$(INTDIR)\jsmath.obj" \ + "$(INTDIR)\jsnum.obj" \ + "$(INTDIR)\jsobj.obj" \ + "$(INTDIR)\jsopcode.obj" \ + "$(INTDIR)\jsparse.obj" \ + "$(INTDIR)\jsprf.obj" \ + "$(INTDIR)\jsregexp.obj" \ + "$(INTDIR)\jsscan.obj" \ + "$(INTDIR)\jsscope.obj" \ + "$(INTDIR)\jsscript.obj" \ + "$(INTDIR)\jsstr.obj" \ + "$(INTDIR)\jsutil.obj" \ + "$(INTDIR)\jsxdrapi.obj" \ + "$(INTDIR)\prmjtime.obj" \ + "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)\js32.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "jsshell - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "jsshell\Release" +# PROP BASE Intermediate_Dir "jsshell\Release" +# PROP BASE Target_Dir "jsshell" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "jsshell" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "js - Win32 Release" "$(OUTDIR)\jsshell.exe" + +CLEAN : + -@erase "$(INTDIR)\js.obj" + -@erase "$(OUTDIR)\jsshell.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "XP_WIN" /D "JSFILE" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D\ + "XP_WIN" /D "JSFILE" /Fp"$(INTDIR)/jsshell.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/jsshell.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/jsshell.pdb" /machine:I386 /out:"$(OUTDIR)/jsshell.exe" +LINK32_OBJS= \ + "$(INTDIR)\js.obj" \ + "$(OUTDIR)\js32.lib" + +"$(OUTDIR)\jsshell.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "jsshell - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "jsshell\jsshell_" +# PROP BASE Intermediate_Dir "jsshell\jsshell_" +# PROP BASE Target_Dir "jsshell" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "jsshell" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "js - Win32 Debug" "$(OUTDIR)\jsshell.exe" + +CLEAN : + -@erase "$(INTDIR)\js.obj" + -@erase "$(INTDIR)\vc40.idb" + -@erase "$(INTDIR)\vc40.pdb" + -@erase "$(OUTDIR)\jsshell.exe" + -@erase "$(OUTDIR)\jsshell.ilk" + -@erase "$(OUTDIR)\jsshell.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_CONSOLE" /D "_DEBUG" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "DEBUG" /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_CONSOLE" /D "_DEBUG" /D "WIN32"\ + /D "XP_WIN" /D "JSFILE" /D "DEBUG" /Fp"$(INTDIR)/jsshell.pch" /YX\ + /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/jsshell.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:console /incremental:yes\ + /pdb:"$(OUTDIR)/jsshell.pdb" /debug /machine:I386 /out:"$(OUTDIR)/jsshell.exe" +LINK32_OBJS= \ + "$(INTDIR)\js.obj" \ + "$(OUTDIR)\js32.lib" + +"$(OUTDIR)\jsshell.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "fdlibm\Release" +# PROP BASE Intermediate_Dir "fdlibm\Release" +# PROP BASE Target_Dir "fdlibm" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "fdlibm" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(OUTDIR)\fdlibm.lib" + +CLEAN : + -@erase "$(INTDIR)\e_atan2.obj" + -@erase "$(INTDIR)\e_pow.obj" + -@erase "$(INTDIR)\e_sqrt.obj" + -@erase "$(INTDIR)\k_standard.obj" + -@erase "$(INTDIR)\s_atan.obj" + -@erase "$(INTDIR)\s_copysign.obj" + -@erase "$(INTDIR)\s_fabs.obj" + -@erase "$(INTDIR)\s_finite.obj" + -@erase "$(INTDIR)\s_isnan.obj" + -@erase "$(INTDIR)\s_matherr.obj" + -@erase "$(INTDIR)\s_rint.obj" + -@erase "$(INTDIR)\s_scalbn.obj" + -@erase "$(INTDIR)\w_atan2.obj" + -@erase "$(INTDIR)\w_pow.obj" + -@erase "$(INTDIR)\w_sqrt.obj" + -@erase "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D "_IEEE_LIBM" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D\ + "_IEEE_LIBM" /D "XP_WIN" /I .\ /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/fdlibm.bsc" +BSC32_SBRS= \ + +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +LIB32_FLAGS=/nologo /out:"$(OUTDIR)/fdlibm.lib" +LIB32_OBJS= \ + "$(INTDIR)\e_atan2.obj" \ + "$(INTDIR)\e_pow.obj" \ + "$(INTDIR)\e_sqrt.obj" \ + "$(INTDIR)\k_standard.obj" \ + "$(INTDIR)\s_atan.obj" \ + "$(INTDIR)\s_copysign.obj" \ + "$(INTDIR)\s_fabs.obj" \ + "$(INTDIR)\s_finite.obj" \ + "$(INTDIR)\s_isnan.obj" \ + "$(INTDIR)\s_matherr.obj" \ + "$(INTDIR)\s_rint.obj" \ + "$(INTDIR)\s_scalbn.obj" \ + "$(INTDIR)\w_atan2.obj" \ + "$(INTDIR)\w_pow.obj" \ + "$(INTDIR)\w_sqrt.obj" + +"$(OUTDIR)\fdlibm.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "fdlibm\Debug" +# PROP BASE Intermediate_Dir "fdlibm\Debug" +# PROP BASE Target_Dir "fdlibm" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "fdlibm" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "$(OUTDIR)\fdlibm.lib" + +CLEAN : + -@erase "$(INTDIR)\e_atan2.obj" + -@erase "$(INTDIR)\e_pow.obj" + -@erase "$(INTDIR)\e_sqrt.obj" + -@erase "$(INTDIR)\k_standard.obj" + -@erase "$(INTDIR)\s_atan.obj" + -@erase "$(INTDIR)\s_copysign.obj" + -@erase "$(INTDIR)\s_fabs.obj" + -@erase "$(INTDIR)\s_finite.obj" + -@erase "$(INTDIR)\s_isnan.obj" + -@erase "$(INTDIR)\s_matherr.obj" + -@erase "$(INTDIR)\s_rint.obj" + -@erase "$(INTDIR)\s_scalbn.obj" + -@erase "$(INTDIR)\w_atan2.obj" + -@erase "$(INTDIR)\w_pow.obj" + -@erase "$(INTDIR)\w_sqrt.obj" + -@erase "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /GX /Z7 /Od /D "_DEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D "_IEEE_LIBM" /YX /c +CPP_PROJ=/nologo /MDd /W3 /GX /Z7 /Od /D "_DEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D\ + "_IEEE_LIBM" /D "XP_WIN" -I .\ /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/fdlibm.bsc" +BSC32_SBRS= \ + +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +LIB32_FLAGS=/nologo /out:"$(OUTDIR)/fdlibm.lib" +LIB32_OBJS= \ + "$(INTDIR)\e_atan2.obj" \ + "$(INTDIR)\e_pow.obj" \ + "$(INTDIR)\e_sqrt.obj" \ + "$(INTDIR)\k_standard.obj" \ + "$(INTDIR)\s_atan.obj" \ + "$(INTDIR)\s_copysign.obj" \ + "$(INTDIR)\s_fabs.obj" \ + "$(INTDIR)\s_finite.obj" \ + "$(INTDIR)\s_isnan.obj" \ + "$(INTDIR)\s_matherr.obj" \ + "$(INTDIR)\s_rint.obj" \ + "$(INTDIR)\s_scalbn.obj" \ + "$(INTDIR)\w_atan2.obj" \ + "$(INTDIR)\w_pow.obj" \ + "$(INTDIR)\w_sqrt.obj" + +"$(OUTDIR)\fdlibm.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +################################################################################ +# Begin Target + +# Name "js - Win32 Release" +# Name "js - Win32 Debug" + +!IF "$(CFG)" == "js - Win32 Release" + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\jsapi.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSAPI=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdate.h"\ + ".\jsemit.h"\ + ".\jsexn.h"\ + ".\jsfile.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsmath.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSAPI=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsapi.obj" : $(SOURCE) $(DEP_CPP_JSAPI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSAPI=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdate.h"\ + ".\jsemit.h"\ + ".\jsexn.h"\ + ".\jsfile.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsmath.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSAPI=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsapi.obj" : $(SOURCE) $(DEP_CPP_JSAPI) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsarena.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSARE=\ + ".\jsarena.h"\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsstddef.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSARE=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsarena.obj" : $(SOURCE) $(DEP_CPP_JSARE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSARE=\ + ".\jsarena.h"\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsstddef.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSARE=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsarena.obj" : $(SOURCE) $(DEP_CPP_JSARE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsarray.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSARR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSARR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsarray.obj" : $(SOURCE) $(DEP_CPP_JSARR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSARR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSARR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsarray.obj" : $(SOURCE) $(DEP_CPP_JSARR) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsatom.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSATO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSATO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsatom.obj" : $(SOURCE) $(DEP_CPP_JSATO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSATO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSATO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsatom.obj" : $(SOURCE) $(DEP_CPP_JSATO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsbool.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSBOO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSBOO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsbool.obj" : $(SOURCE) $(DEP_CPP_JSBOO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSBOO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSBOO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsbool.obj" : $(SOURCE) $(DEP_CPP_JSBOO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jscntxt.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSCNT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsexn.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSCNT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jscntxt.obj" : $(SOURCE) $(DEP_CPP_JSCNT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSCNT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsexn.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSCNT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jscntxt.obj" : $(SOURCE) $(DEP_CPP_JSCNT) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsdate.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSDAT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdate.h"\ + ".\jsdtoa.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDAT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdate.obj" : $(SOURCE) $(DEP_CPP_JSDAT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSDAT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdate.h"\ + ".\jsdtoa.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDAT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdate.obj" : $(SOURCE) $(DEP_CPP_JSDAT) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsdbgapi.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSDBG=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDBG=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdbgapi.obj" : $(SOURCE) $(DEP_CPP_JSDBG) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSDBG=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDBG=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdbgapi.obj" : $(SOURCE) $(DEP_CPP_JSDBG) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsdhash.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSDHA=\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jsdhash.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDHA=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsdhash.obj" : $(SOURCE) $(DEP_CPP_JSDHA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSDHA=\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jsdhash.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDHA=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsdhash.obj" : $(SOURCE) $(DEP_CPP_JSDHA) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsdtoa.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSDTO=\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsstddef.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDTO=\ + ".\jsautocfg.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdtoa.obj" : $(SOURCE) $(DEP_CPP_JSDTO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSDTO=\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsstddef.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSDTO=\ + ".\jsautocfg.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsdtoa.obj" : $(SOURCE) $(DEP_CPP_JSDTO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsemit.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSEMI=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSEMI=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsemit.obj" : $(SOURCE) $(DEP_CPP_JSEMI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSEMI=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSEMI=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsemit.obj" : $(SOURCE) $(DEP_CPP_JSEMI) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsexn.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSEXN=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsexn.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSEXN=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsexn.obj" : $(SOURCE) $(DEP_CPP_JSEXN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSEXN=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsexn.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSEXN=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsexn.obj" : $(SOURCE) $(DEP_CPP_JSEXN) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsfun.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSFUN=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSFUN=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsfun.obj" : $(SOURCE) $(DEP_CPP_JSFUN) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSFUN=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSFUN=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsfun.obj" : $(SOURCE) $(DEP_CPP_JSFUN) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsgc.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSGC_=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSGC_=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsgc.obj" : $(SOURCE) $(DEP_CPP_JSGC_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSGC_=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSGC_=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsgc.obj" : $(SOURCE) $(DEP_CPP_JSGC_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jshash.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSHAS=\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jshash.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSHAS=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jshash.obj" : $(SOURCE) $(DEP_CPP_JSHAS) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSHAS=\ + ".\jsbit.h"\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jshash.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSHAS=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jshash.obj" : $(SOURCE) $(DEP_CPP_JSHAS) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsinterp.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSINT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSINT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsinterp.obj" : $(SOURCE) $(DEP_CPP_JSINT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSINT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSINT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsinterp.obj" : $(SOURCE) $(DEP_CPP_JSINT) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jslock.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSLOC=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLOC=\ + ".\jsautocfg.h"\ + ".\pratom.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + ".\prthread.h"\ + + +"$(INTDIR)\jslock.obj" : $(SOURCE) $(DEP_CPP_JSLOC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSLOC=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLOC=\ + ".\jsautocfg.h"\ + ".\pratom.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + ".\prthread.h"\ + + +"$(INTDIR)\jslock.obj" : $(SOURCE) $(DEP_CPP_JSLOC) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jslog2.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSLOG=\ + ".\jsbit.h"\ + ".\jscpucfg.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLOG=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jslog2.obj" : $(SOURCE) $(DEP_CPP_JSLOG) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSLOG=\ + ".\jsbit.h"\ + ".\jscpucfg.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLOG=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jslog2.obj" : $(SOURCE) $(DEP_CPP_JSLOG) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jslong.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSLON=\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLON=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jslong.obj" : $(SOURCE) $(DEP_CPP_JSLON) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSLON=\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jstypes.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSLON=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jslong.obj" : $(SOURCE) $(DEP_CPP_JSLON) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsmath.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSMAT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslibmath.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsmath.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSMAT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsmath.obj" : $(SOURCE) $(DEP_CPP_JSMAT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSMAT=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslibmath.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsmath.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSMAT=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsmath.obj" : $(SOURCE) $(DEP_CPP_JSMAT) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsnum.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSNUM=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSNUM=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsnum.obj" : $(SOURCE) $(DEP_CPP_JSNUM) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSNUM=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSNUM=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsnum.obj" : $(SOURCE) $(DEP_CPP_JSNUM) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsobj.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSOBJ=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSOBJ=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsobj.obj" : $(SOURCE) $(DEP_CPP_JSOBJ) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSOBJ=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSOBJ=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsobj.obj" : $(SOURCE) $(DEP_CPP_JSOBJ) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsopcode.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSOPC=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsdtoa.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSOPC=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsopcode.obj" : $(SOURCE) $(DEP_CPP_JSOPC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSOPC=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsdtoa.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSOPC=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsopcode.obj" : $(SOURCE) $(DEP_CPP_JSOPC) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsparse.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSPAR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSPAR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsparse.obj" : $(SOURCE) $(DEP_CPP_JSPAR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSPAR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSPAR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsparse.obj" : $(SOURCE) $(DEP_CPP_JSPAR) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsprf.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSPRF=\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSPRF=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsprf.obj" : $(SOURCE) $(DEP_CPP_JSPRF) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSPRF=\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSPRF=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsprf.obj" : $(SOURCE) $(DEP_CPP_JSPRF) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsregexp.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSREG=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSREG=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsregexp.obj" : $(SOURCE) $(DEP_CPP_JSREG) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSREG=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSREG=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsregexp.obj" : $(SOURCE) $(DEP_CPP_JSREG) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsscan.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSSCA=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jsexn.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCA=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSSCA=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdtoa.h"\ + ".\jsexn.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCA=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsscope.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSSCO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscope.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSSCO=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCO=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscope.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsscript.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSSCR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscript.obj" : $(SOURCE) $(DEP_CPP_JSSCR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSSCR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSCR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsscript.obj" : $(SOURCE) $(DEP_CPP_JSSCR) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsstr.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSSTR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSTR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsstr.obj" : $(SOURCE) $(DEP_CPP_JSSTR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSSTR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsarray.h"\ + ".\jsatom.h"\ + ".\jsbool.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsnum.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSSTR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsstr.obj" : $(SOURCE) $(DEP_CPP_JSSTR) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsutil.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSUTI=\ + ".\jscpucfg.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSUTI=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsutil.obj" : $(SOURCE) $(DEP_CPP_JSUTI) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSUTI=\ + ".\jscpucfg.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSUTI=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\jsutil.obj" : $(SOURCE) $(DEP_CPP_JSUTI) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\jsxdrapi.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_JSXDR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSXDR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsxdrapi.obj" : $(SOURCE) $(DEP_CPP_JSXDR) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_JSXDR=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscope.h"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + ".\jsxdrapi.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JSXDR=\ + ".\jsautocfg.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\jsxdrapi.obj" : $(SOURCE) $(DEP_CPP_JSXDR) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\prmjtime.c + +!IF "$(CFG)" == "js - Win32 Release" + +DEP_CPP_PRMJT=\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\TIMEB.H"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_PRMJT=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\prmjtime.obj" : $(SOURCE) $(DEP_CPP_PRMJT) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "js - Win32 Debug" + +DEP_CPP_PRMJT=\ + ".\jscompat.h"\ + ".\jscpucfg.h"\ + ".\jslong.h"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsprf.h"\ + ".\jstypes.h"\ + ".\prmjtime.h"\ + {$(INCLUDE)}"\sys\TIMEB.H"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_PRMJT=\ + ".\jsautocfg.h"\ + + +"$(INTDIR)\prmjtime.obj" : $(SOURCE) $(DEP_CPP_PRMJT) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "fdlibm" + +!IF "$(CFG)" == "js - Win32 Debug" + +"fdlibm - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" + +!ELSEIF "$(CFG)" == "js - Win32 Release" + +"fdlibm - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" + +!ENDIF + +# End Project Dependency +# End Target +################################################################################ +# Begin Target + +# Name "jsshell - Win32 Release" +# Name "jsshell - Win32 Debug" + +!IF "$(CFG)" == "jsshell - Win32 Release" + +!ELSEIF "$(CFG)" == "jsshell - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\js.c +DEP_CPP_JS_C42=\ + ".\js.msg"\ + ".\jsapi.h"\ + ".\jsarena.h"\ + ".\jsatom.h"\ + ".\jsclist.h"\ + ".\jscntxt.h"\ + ".\jscompat.h"\ + ".\jsconfig.h"\ + ".\jscpucfg.h"\ + ".\jsdbgapi.h"\ + ".\jsemit.h"\ + ".\jsfun.h"\ + ".\jsgc.h"\ + ".\jshash.h"\ + ".\jsinterp.h"\ + ".\jslock.h"\ + ".\jslong.h"\ + ".\jsobj.h"\ + ".\jsopcode.h"\ + ".\jsopcode.tbl"\ + ".\jsosdep.h"\ + ".\jsotypes.h"\ + ".\jsparse.h"\ + ".\jsprf.h"\ + ".\jsprvtd.h"\ + ".\jspubtd.h"\ + ".\jsregexp.h"\ + ".\jsscan.h"\ + ".\jsscope.h"\ + ".\jsscript.h"\ + ".\jsshell.msg"\ + ".\jsstddef.h"\ + ".\jsstr.h"\ + ".\jstypes.h"\ + ".\jsutil.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_JS_C42=\ + ".\jsautocfg.h"\ + ".\jsdb.h"\ + ".\jsdebug.h"\ + ".\jsdjava.h"\ + ".\jsjava.h"\ + ".\jsperl.h"\ + ".\prcvar.h"\ + ".\prlock.h"\ + + +"$(INTDIR)\js.obj" : $(SOURCE) $(DEP_CPP_JS_C42) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "js" + +!IF "$(CFG)" == "jsshell - Win32 Release" + +"js - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" + +!ELSEIF "$(CFG)" == "jsshell - Win32 Debug" + +"js - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" + +!ENDIF + +# End Project Dependency +# End Target +################################################################################ +# Begin Target + +# Name "fdlibm - Win32 Release" +# Name "fdlibm - Win32 Debug" + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\w_atan2.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_W_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_atan2.obj" : $(SOURCE) $(DEP_CPP_W_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_W_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_atan2.obj" : $(SOURCE) $(DEP_CPP_W_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_copysign.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_COP=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_copysign.obj" : $(SOURCE) $(DEP_CPP_S_COP) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_COP=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_copysign.obj" : $(SOURCE) $(DEP_CPP_S_COP) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\w_pow.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_W_POW=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_pow.obj" : $(SOURCE) $(DEP_CPP_W_POW) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_W_POW=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_pow.obj" : $(SOURCE) $(DEP_CPP_W_POW) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\e_pow.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_E_POW=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_pow.obj" : $(SOURCE) $(DEP_CPP_E_POW) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_E_POW=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_pow.obj" : $(SOURCE) $(DEP_CPP_E_POW) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\k_standard.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_K_STA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\k_standard.obj" : $(SOURCE) $(DEP_CPP_K_STA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_K_STA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\k_standard.obj" : $(SOURCE) $(DEP_CPP_K_STA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\e_atan2.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_E_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_atan2.obj" : $(SOURCE) $(DEP_CPP_E_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_E_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_atan2.obj" : $(SOURCE) $(DEP_CPP_E_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_isnan.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_ISN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_isnan.obj" : $(SOURCE) $(DEP_CPP_S_ISN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_ISN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_isnan.obj" : $(SOURCE) $(DEP_CPP_S_ISN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_fabs.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_FAB=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_fabs.obj" : $(SOURCE) $(DEP_CPP_S_FAB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_FAB=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_fabs.obj" : $(SOURCE) $(DEP_CPP_S_FAB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\w_sqrt.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_W_SQR=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_sqrt.obj" : $(SOURCE) $(DEP_CPP_W_SQR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_W_SQR=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\w_sqrt.obj" : $(SOURCE) $(DEP_CPP_W_SQR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_scalbn.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_SCA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_scalbn.obj" : $(SOURCE) $(DEP_CPP_S_SCA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_SCA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_scalbn.obj" : $(SOURCE) $(DEP_CPP_S_SCA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\e_sqrt.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_E_SQR=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_sqrt.obj" : $(SOURCE) $(DEP_CPP_E_SQR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_E_SQR=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\e_sqrt.obj" : $(SOURCE) $(DEP_CPP_E_SQR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_rint.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_RIN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_rint.obj" : $(SOURCE) $(DEP_CPP_S_RIN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_RIN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_rint.obj" : $(SOURCE) $(DEP_CPP_S_RIN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_atan.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_atan.obj" : $(SOURCE) $(DEP_CPP_S_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_ATA=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_atan.obj" : $(SOURCE) $(DEP_CPP_S_ATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_finite.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_FIN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_finite.obj" : $(SOURCE) $(DEP_CPP_S_FIN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_FIN=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_finite.obj" : $(SOURCE) $(DEP_CPP_S_FIN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm\s_matherr.c + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +DEP_CPP_S_MAT=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_matherr.obj" : $(SOURCE) $(DEP_CPP_S_MAT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +DEP_CPP_S_MAT=\ + ".\fdlibm\fdlibm.h"\ + + +"$(INTDIR)\s_matherr.obj" : $(SOURCE) $(DEP_CPP_S_MAT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/src/extension/script/js/js.msg b/src/extension/script/js/js.msg new file mode 100644 index 000000000..dbc571fdf --- /dev/null +++ b/src/extension/script/js/js.msg @@ -0,0 +1,251 @@ +/* -*- 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_NO_REG_EXPS, 2, 1, JSEXN_INTERNALERR, "sorry, regular expression are not supported") +MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_NONE, "{0} requires more than {1} argument{2}") +MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_NONE, "invalid format character {0}") +MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_NONE, "unknown type {0}") +MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_NONE, "can't lock memory") +MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_NONE, "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_NONE, "{0} has no constructor") +MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_NONE, "can't alias {0} to {1} in class {2}") +MSG_DEF(JSMSG_UNUSED11, 11, 0, JSEXN_NONE, "") +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_NONE, "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_UNUSED18, 18, 0, JSEXN_NONE, "") +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_SAME_FORMAL, 21, 1, JSEXN_NONE, "duplicate formal argument {0}") +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_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}") +MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_NONE, "{0} is not exported") +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_NONE, "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_NAN, 34, 1, JSEXN_ERR, "{0} is not a number") +MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_NONE, "can't convert {0} to an integer") +MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") +MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent") +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_NONE, "can't find class id {0}") +MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_NONE, "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_ZERO_QUANTIFIER, 50, 1, JSEXN_SYNTAXERR, "zero quantifier {0}") +MSG_DEF(JSMSG_UNTERM_QUANTIFIER, 51, 1, JSEXN_SYNTAXERR, "unterminated quantifier {0}") +MSG_DEF(JSMSG_EMPTY_BEFORE_STAR, 52, 0, JSEXN_SYNTAXERR, "regular expression before * could be empty") +MSG_DEF(JSMSG_EMPTY_BEFORE_PLUS, 53, 0, JSEXN_SYNTAXERR, "regular expression before + could be empty") +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_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") +MSG_DEF(JSMSG_NO_INPUT, 59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}") +MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_NONE, "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_NONE, "unexpected end of data") +MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_NONE, "illegal seek beyond start") +MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_NONE, "illegal seek beyond end") +MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_NONE, "illegal end-based seek") +MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_NONE, "unknown seek whence: {0}") +MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_NONE, "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_NO_IMPORT_NAME, 76, 0, JSEXN_SYNTAXERR, "missing name in import statement") +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_NO_EXPORT_NAME, 79, 0, JSEXN_SYNTAXERR, "missing name in export statement") +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_TYPEERR, "duplicate formal argument {0}") +MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_NONE, "test for equality (==) mistyped as assignment (=)?{0}") +MSG_DEF(JSMSG_BAD_IMPORT, 113, 0, JSEXN_SYNTAXERR, "invalid import expression") +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, "invalid break") +MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue") +MSG_DEF(JSMSG_BAD_RETURN, 124, 0, JSEXN_SYNTAXERR, "invalid return") +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} hides 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_REGEXP_FLAG, 142, 0, JSEXN_SYNTAXERR, "invalid flag after regular expression") +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_NONE, "{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_NONE, "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_NONE, "can't describe non-native properties of class {0}") +MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 0, JSEXN_TYPEERR, "second argument to Function.prototype.apply 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_TYPEERR, "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_TRAILING_COMMA, 161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers") +MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_TYPEERR, "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_CANT_UNSEAL_OBJECT, 169, 1, JSEXN_ERR, "can't unseal {0} objects") diff --git a/src/extension/script/js/jsapi.c b/src/extension/script/js/jsapi.c new file mode 100644 index 000000000..faf02d983 --- /dev/null +++ b/src/extension/script/js/jsapi.c @@ -0,0 +1,4187 @@ +/* -*- 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 ***** */ + +/* + * JavaScript API. + */ +#include "jsstddef.h" +#include +#include +#include +#include +#include "jstypes.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 "jscntxt.h" +#include "jsconfig.h" +#include "jsdate.h" +#include "jsdtoa.h" +#include "jsemit.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.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 "prmjtime.h" + +#if JS_HAS_FILE_OBJECT +#include "jsfile.h" +#endif + +#ifdef HAVE_VA_LIST_AS_ARRAY +#define JS_ADDRESSOF_VA_LIST(ap) (ap) +#else +#define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) +#endif + +#if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE) +#define CHECK_REQUEST(cx) JS_ASSERT(cx->requestDepth) +#else +#define CHECK_REQUEST(cx) ((void)0) +#endif + +JS_PUBLIC_API(int64) +JS_Now() +{ + return PRMJ_Now(); +} + +JS_PUBLIC_API(jsval) +JS_GetNaNValue(JSContext *cx) +{ + return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); +} + +JS_PUBLIC_API(jsval) +JS_GetNegativeInfinityValue(JSContext *cx) +{ + return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); +} + +JS_PUBLIC_API(jsval) +JS_GetPositiveInfinityValue(JSContext *cx) +{ + return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); +} + +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': + if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) + return JS_FALSE; + 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') + *va_arg(ap, char **) = JS_GetStringBytes(str); + else if (c == 'W') + *va_arg(ap, jschar **) = JS_GetStringChars(str); + 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': + fun = js_ValueToFunction(cx, sp, 0); + if (!fun) + return JS_FALSE; + *sp = OBJECT_TO_JSVAL(fun->object); + *va_arg(ap, JSFunction **) = fun; + 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++; + } + 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': + 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) : 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_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 *) JS_malloc(cx, 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; + JS_free(cx, map); + return; + } + mpp = &map->next; + } +} + +JS_PUBLIC_API(JSBool) +JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) +{ + JSBool ok, b; + JSObject *obj; + JSFunction *fun; + 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: + fun = js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); + ok = (fun != NULL); + if (ok) + *vp = OBJECT_TO_JSVAL(fun->object); + 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_NewDouble(cx, d); + ok = (dp != NULL); + if (ok) + *vp = DOUBLE_TO_JSVAL(dp); + } + break; + case JSTYPE_BOOLEAN: + ok = js_ValueToBoolean(cx, v, &b); + if (ok) + *vp = BOOLEAN_TO_JSVAL(b); + break; + 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(JSBool) +JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) +{ + CHECK_REQUEST(cx); + return js_ValueToNumber(cx, v, dp); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAInt32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAUint32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToInt32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToUint16(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) +{ + CHECK_REQUEST(cx); + return js_ValueToBoolean(cx, v, bp); +} + +JS_PUBLIC_API(JSType) +JS_TypeOfValue(JSContext *cx, jsval v) +{ + JSType type; + JSObject *obj; + JSObjectOps *ops; + JSClass *clasp; + + CHECK_REQUEST(cx); + if (JSVAL_IS_OBJECT(v)) { + /* XXX JSVAL_IS_OBJECT(v) is true for null too! Can we change ECMA? */ + obj = JSVAL_TO_OBJECT(v); + if (obj && + (ops = obj->map->ops, + ops == &js_ObjectOps + ? (clasp = OBJ_GET_CLASS(cx, obj), + clasp->call || clasp == &js_FunctionClass) + : ops->call != 0)) { + type = JSTYPE_FUNCTION; + } else { + type = JSTYPE_OBJECT; + } + } 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(JSRuntime *) +JS_NewRuntime(uint32 maxbytes) +{ + JSRuntime *rt; + +#ifdef DEBUG + JS_BEGIN_MACRO + /* + * 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 compiletime 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 + JS_END_MACRO; +#endif /* DEBUG */ + + if (!js_InitScriptGlobals()) + return NULL; + if (!js_InitStringGlobals()) + return NULL; + rt = (JSRuntime *) malloc(sizeof(JSRuntime)); + if (!rt) + return NULL; + + /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ + memset(rt, 0, sizeof(JSRuntime)); + JS_INIT_CLIST(&rt->contextList); + JS_INIT_CLIST(&rt->trapList); + JS_INIT_CLIST(&rt->watchPointList); + + if (!js_InitGC(rt, maxbytes)) + goto bad; +#ifdef JS_THREADSAFE + rt->gcLock = JS_NEW_LOCK(); + if (!rt->gcLock) + goto bad; + rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->gcDone) + goto bad; + rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->requestDone) + goto bad; + js_SetupLocks(8, 16); /* this is asymmetric with JS_ShutDown. */ + rt->rtLock = JS_NEW_LOCK(); + if (!rt->rtLock) + goto bad; + rt->stateChange = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->stateChange) + goto bad; + rt->setSlotLock = JS_NEW_LOCK(); + if (!rt->setSlotLock) + goto bad; + rt->setSlotDone = JS_NEW_CONDVAR(rt->setSlotLock); + if (!rt->setSlotDone) + goto bad; + rt->scopeSharingDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->scopeSharingDone) + goto bad; + rt->scopeSharingTodo = NO_SCOPE_SHARING_TODO; +#endif + rt->propertyCache.empty = JS_TRUE; + if (!js_InitPropertyTree(rt)) + goto bad; + return rt; + +bad: + JS_DestroyRuntime(rt); + return NULL; +} + +JS_PUBLIC_API(void) +JS_DestroyRuntime(JSRuntime *rt) +{ +#ifdef DEBUG + /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ + if (!JS_CLIST_IS_EMPTY(&rt->contextList)) { + JSContext *cx, *iter = NULL; + uintN cxcount = 0; + while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) + cxcount++; + fprintf(stderr, +"JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n", + cxcount); + } +#endif + + js_FinishAtomState(&rt->atomState); + js_FinishGC(rt); +#ifdef JS_THREADSAFE + if (rt->gcLock) + JS_DESTROY_LOCK(rt->gcLock); + if (rt->gcDone) + JS_DESTROY_CONDVAR(rt->gcDone); + if (rt->requestDone) + JS_DESTROY_CONDVAR(rt->requestDone); + if (rt->rtLock) + JS_DESTROY_LOCK(rt->rtLock); + if (rt->stateChange) + JS_DESTROY_CONDVAR(rt->stateChange); + if (rt->setSlotLock) + JS_DESTROY_LOCK(rt->setSlotLock); + if (rt->setSlotDone) + JS_DESTROY_CONDVAR(rt->setSlotDone); + if (rt->scopeSharingDone) + JS_DESTROY_CONDVAR(rt->scopeSharingDone); +#endif + js_FinishPropertyTree(rt); + free(rt); +} + +JS_PUBLIC_API(void) +JS_ShutDown(void) +{ + JS_ArenaShutDown(); + js_FinishDtoa(); + js_FreeScriptGlobals(); + js_FreeStringGlobals(); +#ifdef JS_THREADSAFE + js_CleanupLocks(); +#endif +} + +JS_PUBLIC_API(void *) +JS_GetRuntimePrivate(JSRuntime *rt) +{ + return rt->data; +} + +JS_PUBLIC_API(void) +JS_SetRuntimePrivate(JSRuntime *rt, void *data) +{ + rt->data = data; +} + +#ifdef JS_THREADSAFE + +JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx) +{ + JSRuntime *rt; + + JS_ASSERT(cx->thread); + if (!cx->requestDepth) { + /* Wait until the GC is finished. */ + rt = cx->runtime; + JS_LOCK_GC(rt); + + /* NB: we use cx->thread here, not js_CurrentThreadId(). */ + 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; + JS_UNLOCK_GC(rt); + return; + } + cx->requestDepth++; +} + +JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx) +{ + JSRuntime *rt; + JSScope *scope, **todop; + uintN nshares; + + CHECK_REQUEST(cx); + JS_ASSERT(cx->requestDepth > 0); + if (cx->requestDepth == 1) { + /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ + rt = cx->runtime; + JS_LOCK_GC(rt); + cx->requestDepth = 0; + + /* See whether cx has any single-threaded scopes to start sharing. */ + todop = &rt->scopeSharingTodo; + nshares = 0; + while ((scope = *todop) != NO_SCOPE_SHARING_TODO) { + if (scope->ownercx != cx) { + todop = &scope->u.link; + continue; + } + *todop = scope->u.link; + scope->u.link = NULL; /* null u.link for sanity ASAP */ + + /* + * If js_DropObjectMap returns null, 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 a branch callback) during + * this request, but possible. + */ + if (js_DropObjectMap(cx, &scope->map, NULL)) { + js_InitLock(&scope->lock); + scope->u.count = 0; /* NULL may not pun as 0 */ + js_FinishSharingScope(rt, scope); /* set ownercx = NULL */ + nshares++; + } + } + if (nshares) + JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); + + /* 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--; +} + +/* Yield to pending GC operations, regardless of request depth */ +JS_PUBLIC_API(void) +JS_YieldRequest(JSContext *cx) +{ + JSRuntime *rt; + + JS_ASSERT(cx->thread); + CHECK_REQUEST(cx); + + rt = cx->runtime; + JS_LOCK_GC(rt); + JS_ASSERT(rt->requestCount > 0); + rt->requestCount--; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + JS_UNLOCK_GC(rt); + /* XXXbe give the GC or another request calling it a chance to run here? + Assumes FIFO scheduling */ + JS_LOCK_GC(rt); + rt->requestCount++; + JS_UNLOCK_GC(rt); +} + +JS_PUBLIC_API(jsrefcount) +JS_SuspendRequest(JSContext *cx) +{ + jsrefcount saveDepth = cx->requestDepth; + + while (cx->requestDepth) + JS_EndRequest(cx); + return saveDepth; +} + +JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) +{ + JS_ASSERT(!cx->requestDepth); + while (--saveDepth >= 0) + JS_BeginRequest(cx); +} + +#endif /* JS_THREADSAFE */ + +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(JSContext *) +JS_NewContext(JSRuntime *rt, size_t stackChunkSize) +{ + return js_NewContext(rt, stackChunkSize); +} + +JS_PUBLIC_API(void) +JS_DestroyContext(JSContext *cx) +{ + js_DestroyContext(cx, JS_FORCE_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextNoGC(JSContext *cx) +{ + js_DestroyContext(cx, JS_NO_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextMaybeGC(JSContext *cx) +{ + js_DestroyContext(cx, JS_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 cx->version; +} + +JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version) +{ + JSVersion oldVersion; + + oldVersion = cx->version; + if (version == oldVersion) + return oldVersion; + + cx->version = version; + +#if !JS_BUG_FALLIBLE_EQOPS + if (cx->version == JSVERSION_1_2) { + cx->jsop_eq = JSOP_NEW_EQ; + cx->jsop_ne = JSOP_NEW_NE; + } else { + cx->jsop_eq = JSOP_EQ; + cx->jsop_ne = JSOP_NE; + } +#endif /* !JS_BUG_FALLIBLE_EQOPS */ + + 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_DEFAULT, "default"}, + {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) +{ + uint32 oldopts = cx->options; + cx->options = options; + return oldopts; +} + +JS_PUBLIC_API(uint32) +JS_ToggleOptions(JSContext *cx, uint32 options) +{ + uint32 oldopts = cx->options; + cx->options ^= options; + return oldopts; +} + +JS_PUBLIC_API(const char *) +JS_GetImplementationVersion(void) +{ + return "JavaScript-C 1.5 pre-release 6 2004-01-27"; +} + + +JS_PUBLIC_API(JSObject *) +JS_GetGlobalObject(JSContext *cx) +{ + return cx->globalObject; +} + +JS_PUBLIC_API(void) +JS_SetGlobalObject(JSContext *cx, JSObject *obj) +{ + cx->globalObject = obj; +} + +static JSObject * +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) + cx->globalObject = obj; + + /* Record Function and Object in cx->resolvingTable, if we are resolving. */ + table = cx->resolvingTable; + resolving = (table && table->entryCount); + if (resolving) { + rt = cx->runtime; + key.obj = obj; + key.id = (jsid) rt->atomState.FunctionAtom; + 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 = (jsid) rt->atomState.ObjectAtom; + 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; + } + + /* Initialize the function class first so constructors can be made. */ + fun_proto = js_InitFunctionClass(cx, obj); + if (!fun_proto) + goto out; + + /* Initialize the object class next so Object.prototype works. */ + 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. */ + if (resolving) + JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); + return fun_proto; +} + +JS_PUBLIC_API(JSBool) +JS_InitStandardClasses(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + +#if JS_HAS_UNDEFINED +{ + /* Define a top-level property 'undefined' with the undefined value. */ + JSAtom *atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; + if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } +} +#endif + + /* Function and Object require cooperative bootstrapping magic. */ + if (!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_InitMathClass(cx, obj) && + js_InitNumberClass(cx, obj) && + js_InitStringClass(cx, obj) && +#if JS_HAS_CALL_OBJECT + js_InitCallClass(cx, obj) && +#endif +#if JS_HAS_REGEXPS + js_InitRegExpClass(cx, obj) && +#endif +#if JS_HAS_SCRIPT_OBJECT + js_InitScriptClass(cx, obj) && +#endif +#if JS_HAS_ERROR_EXCEPTIONS + js_InitExceptionClasses(cx, obj) && +#endif +#if JS_HAS_FILE_OBJECT + js_InitFileClass(cx, obj, JS_TRUE) && +#endif + js_InitDateClass(cx, obj); +} + +#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) +#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) + +/* + * Table of class initializers and their atom offsets in rt->atomState. + * If you add a "standard" class, remember to update this table. + */ +static struct { + JSObjectOp init; + size_t atomOffset; +} standard_class_atoms[] = { + {InitFunctionAndObjectClasses, ATOM_OFFSET(Function)}, + {InitFunctionAndObjectClasses, ATOM_OFFSET(Object)}, + {js_InitArrayClass, ATOM_OFFSET(Array)}, + {js_InitBooleanClass, ATOM_OFFSET(Boolean)}, + {js_InitDateClass, ATOM_OFFSET(Date)}, + {js_InitMathClass, ATOM_OFFSET(Math)}, + {js_InitNumberClass, ATOM_OFFSET(Number)}, + {js_InitStringClass, ATOM_OFFSET(String)}, +#if JS_HAS_CALL_OBJECT + {js_InitCallClass, ATOM_OFFSET(Call)}, +#endif +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, ATOM_OFFSET(Error)}, +#endif +#if JS_HAS_REGEXPS + {js_InitRegExpClass, ATOM_OFFSET(RegExp)}, +#endif +#if JS_HAS_SCRIPT_OBJECT + {js_InitScriptClass, ATOM_OFFSET(Script)}, +#endif + {NULL, 0} +}; + +/* + * 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. + */ +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 */ +} 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; +} + +#define EAGERLY_PINNED_ATOM(name) ATOM_OFFSET(name), NULL +#define LAZILY_PINNED_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str + +static JSStdName standard_class_names[] = { + /* ECMA requires that eval be a direct property of the global object. */ + {js_InitObjectClass, EAGERLY_PINNED_ATOM(eval)}, + + /* Global properties and functions defined by the Number class. */ + {js_InitNumberClass, LAZILY_PINNED_ATOM(NaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(Infinity)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isNaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isFinite)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseFloat)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseInt)}, + + /* String global functions. */ + {js_InitStringClass, LAZILY_PINNED_ATOM(escape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(unescape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURIComponent)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURIComponent)}, +#if JS_HAS_UNEVAL + {js_InitStringClass, LAZILY_PINNED_ATOM(uneval)}, +#endif + + /* Exception constructors. */ +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, EAGERLY_PINNED_ATOM(Error)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(InternalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(EvalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(RangeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(ReferenceError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(SyntaxError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(TypeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(URIError)}, +#endif + + {NULL, 0, NULL} +}; + +static JSStdName object_prototype_names[] = { + /* Object.prototype properties (global delegates to Object.prototype). */ + {js_InitObjectClass, EAGERLY_PINNED_ATOM(proto)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(parent)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(count)}, +#if JS_HAS_TOSOURCE + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toSource)}, +#endif + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toLocaleString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(valueOf)}, +#if JS_HAS_OBJ_WATCHPOINT + {js_InitObjectClass, LAZILY_PINNED_ATOM(watch)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(unwatch)}, +#endif +#if JS_HAS_NEW_OBJ_METHODS + {js_InitObjectClass, LAZILY_PINNED_ATOM(hasOwnProperty)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(isPrototypeOf)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(propertyIsEnumerable)}, +#endif +#if JS_HAS_GETTER_SETTER + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineSetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupSetter)}, +#endif + + {NULL, 0, NULL} +}; + +#undef EAGERLY_PINNED_ATOM +#undef LAZILY_PINNED_ATOM + +JS_PUBLIC_API(JSBool) +JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, + JSBool *resolved) +{ + JSString *idstr; + JSRuntime *rt; + JSAtom *atom; + JSObjectOp init; + uintN i; + + CHECK_REQUEST(cx); + *resolved = JS_FALSE; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + idstr = JSVAL_TO_STRING(id); + rt = cx->runtime; + +#if JS_HAS_UNDEFINED + /* See if 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_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, + JSPROP_PERMANENT, NULL); + } +#endif + + /* Try for class constructors/prototypes named by well-known atoms. */ + init = 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)) { + init = standard_class_atoms[i].init; + break; + } + } + + if (!init) { + /* 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)) { + init = standard_class_names[i].init; + break; + } + } + + if (!init && !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)) { + init = standard_class_names[i].init; + break; + } + } + } + } + + if (init) { + if (!init(cx, obj)) + return JS_FALSE; + *resolved = JS_TRUE; + } + return JS_TRUE; +} + +static JSBool +HasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom, JSBool *ownp) +{ + JSObject *pobj; + JSProperty *prop; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + return JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + *ownp = (pobj == obj && prop); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) +{ + JSRuntime *rt; + JSAtom *atom; + JSBool found; + uintN i; + + CHECK_REQUEST(cx); + rt = cx->runtime; + +#if JS_HAS_UNDEFINED + /* See if we need to bind 'undefined' and define it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + if (!HasOwnProperty(cx, obj, atom, &found)) + return JS_FALSE; + if (!found && + !OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } +#endif + + /* 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 (!HasOwnProperty(cx, obj, atom, &found)) + return JS_FALSE; + if (!found && !standard_class_atoms[i].init(cx, obj)) + return JS_FALSE; + } + + return JS_TRUE; +} + +#undef ATOM_OFFSET +#undef OFFSET_TO_ATOM + +JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx) +{ + return cx->fp ? cx->fp->scopeChain : NULL; +} + +JS_PUBLIC_API(void *) +JS_malloc(JSContext *cx, size_t nbytes) +{ + void *p; + + JS_ASSERT(nbytes != 0); + if (nbytes == 0) + nbytes = 1; + cx->runtime->gcMallocBytes += nbytes; + p = malloc(nbytes); + if (!p) + JS_ReportOutOfMemory(cx); + return p; +} + +JS_PUBLIC_API(void *) +JS_realloc(JSContext *cx, void *p, size_t nbytes) +{ + p = realloc(p, nbytes); + if (!p) + JS_ReportOutOfMemory(cx); + return p; +} + +JS_PUBLIC_API(void) +JS_free(JSContext *cx, void *p) +{ + if (p) + free(p); +} + +JS_PUBLIC_API(char *) +JS_strdup(JSContext *cx, const char *s) +{ + size_t n; + void *p; + + n = strlen(s) + 1; + p = JS_malloc(cx, 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_NewDouble(cx, d); +} + +JS_PUBLIC_API(JSBool) +JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) +{ + CHECK_REQUEST(cx); + return js_NewDoubleValue(cx, d, rval); +} + +JS_PUBLIC_API(JSBool) +JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) +{ + CHECK_REQUEST(cx); + return js_NewNumberValue(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) +{ + uintN i; + + for (i = 0; i < GCX_NTYPES; i++) + cx->newborn[i] = NULL; + cx->lastAtom = NULL; +} + +#include "jshash.h" /* Added by JSIFY */ + +#ifdef DEBUG + +typedef struct NamedRootDumpArgs { + void (*dump)(const char *name, void *rp, void *data); + void *data; +} NamedRootDumpArgs; + +JS_STATIC_DLL_CALLBACK(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_PUBLIC_API(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); +} + +#endif /* DEBUG */ + +typedef struct GCRootMapArgs { + JSGCRootMapFun map; + void *data; +} GCRootMapArgs; + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + GCRootMapArgs *args = (GCRootMapArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + intN mapflags; + JSDHashOperator 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 op; +} + +JS_PUBLIC_API(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; +} + +JS_PUBLIC_API(JSBool) +JS_LockGCThing(JSContext *cx, void *thing) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_LockGCThing(cx, thing); + if (!ok) + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK); + 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_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) +{ + JS_ASSERT(cx->runtime->gcLevel > 0); +#ifdef JS_THREADSAFE + JS_ASSERT(cx->runtime->gcThread == js_CurrentThreadId()); +#endif + + GC_MARK(cx, thing, name, arg); +} + +JS_PUBLIC_API(void) +JS_GC(JSContext *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_ForceGC(cx, 0); +} + +JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx) +{ + JSRuntime *rt; + uint32 bytes, lastBytes; + + rt = cx->runtime; + bytes = rt->gcBytes; + lastBytes = rt->gcLastBytes; + if ((bytes > 8192 && bytes > lastBytes + lastBytes / 2) || + rt->gcMallocBytes > rt->gcMaxBytes) { + /* + * Run the GC if we have half again as many bytes of GC-things as + * the last time we GC'd, or if we have malloc'd more bytes through + * JS_malloc than we were told to allocate by JS_NewRuntime. + */ + JS_GC(cx); + } +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb) +{ + 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(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) +{ + JSString *str; + + CHECK_REQUEST(cx); + JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); + + str = (JSString *) js_AllocGCThing(cx, (uintN) type); + if (!str) + return NULL; + str->length = length; + str->chars = chars; + return str; +} + +JS_PUBLIC_API(intN) +JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) +{ + uint8 type = (uint8) (*js_GetGCThingFlags(str) & GCF_TYPEMASK); + + if (type >= GCX_EXTERNAL_STRING) + return (intN)type; + JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING); + return -1; +} + +#ifdef DEBUG +static void +CheckStackGrowthDirection(int *dummy1addr, jsuword limitAddr) +{ + int dummy2; + +#if JS_STACK_GROWTH_DIRECTION > 0 + JS_ASSERT(dummy1addr < &dummy2); + JS_ASSERT((jsuword)&dummy2 < limitAddr); +#else + /* Stack grows downward, the common case on modern architectures. */ + JS_ASSERT(&dummy2 < dummy1addr); + JS_ASSERT(limitAddr < (jsuword)&dummy2); +#endif +} +#endif + +JS_PUBLIC_API(void) +JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) +{ +#ifdef DEBUG + int dummy1; + + CheckStackGrowthDirection(&dummy1, limitAddr); +#endif + +#if JS_STACK_GROWTH_DIRECTION > 0 + if (limitAddr == 0) + limitAddr = (jsuword)-1; +#endif + cx->stackLimit = limitAddr; +} + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) +{ + JS_free(cx, ida); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + if (JSVAL_IS_INT(v)) { + *idp = v; + } else { + atom = js_ValueToStringAtom(cx, v); + if (!atom) + return JS_FALSE; + *idp = (jsid)atom; + } + return JS_TRUE; +} + +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) +{ +#if JS_BUG_EAGER_TOSTRING + if (type == JSTYPE_STRING) + return JS_TRUE; +#endif + 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) +{ + JSAtom *atom; + JSObject *proto, *ctor; + JSBool named; + JSFunction *fun; + jsval junk; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + if (!atom) + return NULL; + + /* Create a prototype object for this class. */ + proto = js_NewObject(cx, clasp, parent_proto, obj); + if (!proto) + return NULL; + + if (!constructor) { + /* Lacking a constructor, name the prototype (e.g., Math). */ + named = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(proto), + NULL, NULL, 0, NULL); + if (!named) + goto bad; + ctor = proto; + } else { + /* Define the constructor function in obj's scope. */ + fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); + 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 = clasp; + + /* Connect constructor and prototype by named properties. */ + ctor = fun->object; + 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) { + /* XXXMLM - this fails in framesets that are writing over + * themselves! + * JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); + */ + 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; + } + return proto; + +bad: + if (named) + (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk); + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj) +{ + return (JSClass *) + JSVAL_TO_PRIVATE(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_CLASS)); +} +#else +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSObject *obj) +{ + return LOCKED_OBJ_GET_CLASS(obj); +} +#endif + +JS_PUBLIC_API(JSBool) +JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) +{ + JSFunction *fun; + + CHECK_REQUEST(cx); + if (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_GET_CLASS(cx, obj)->name); + } + } + return JS_FALSE; +} + +JS_PUBLIC_API(void *) +JS_GetPrivate(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_INT(v)) + return NULL; + return JSVAL_TO_PRIVATE(v); +} + +JS_PUBLIC_API(JSBool) +JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) +{ + JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); + return JS_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 JS_GetPrivate(cx, obj); +} + +JS_PUBLIC_API(JSObject *) +JS_GetPrototype(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + CHECK_REQUEST(cx); + proto = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PROTO)); + + /* 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); + if (obj->map->ops->setProto) + return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); + OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_GetParent(JSContext *cx, JSObject *obj) +{ + JSObject *parent; + + parent = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PARENT)); + + /* 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); + if (obj->map->ops->setParent) + return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); + OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_GetConstructor(JSContext *cx, JSObject *proto) +{ + jsval cval; + + CHECK_REQUEST(cx); + if (!OBJ_GET_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, + &cval)) { + return NULL; + } + if (!JSVAL_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) +{ + *idp = (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(JSBool) +JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) +{ + JSScope *scope; + JSIdArray *ida; + uint32 nslots; + jsval v, *vp, *end; + + 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->ownercx != cx) { + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_SCOPE(obj) == scope); + JS_ASSERT(scope->ownercx == cx); + JS_UNLOCK_SCOPE(cx, scope); + } +#endif + + /* Nothing to do if obj's scope is already sealed. */ + if (SCOPE_IS_SEALED(scope)) + 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_SET_SEALED(scope); + JS_UNLOCK_SCOPE(cx, scope); + if (!scope) + return JS_FALSE; + + /* If we are not sealing an entire object graph, we're done. */ + if (!deep) + return JS_TRUE; + + /* Walk obj->slots and if any value is a non-null object, seal it. */ + nslots = JS_MIN(scope->map.freeslot, scope->map.nslots); + for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { + v = *vp; + 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 +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_JSVAL((jsint)name); + atom = NULL; + attrs &= ~JSPROP_INDEX; + } else { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + id = (jsid)atom; + } + if (flags != 0 && OBJ_IS_NATIVE(obj)) { + return js_DefineNativeProperty(cx, obj, id, value, getter, setter, + attrs, flags, tinyid, NULL); + } + return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, + NULL); +} + +#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)) { + return js_DefineNativeProperty(cx, obj, (jsid)atom, value, + getter, setter, attrs, flags, tinyid, + NULL); + } + return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter, + attrs, NULL); +} + +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)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + return nobj; +} + +JS_PUBLIC_API(JSBool) +JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) +{ + JSBool ok; + jsval value; + uintN flags; + + CHECK_REQUEST(cx); + for (ok = JS_TRUE; cds->name; cds++) { + ok = js_NewNumberValue(cx, cds->dval, &value); + if (!ok) + break; + flags = cds->flags; + if (!flags) + flags = JSPROP_READONLY | JSPROP_PERMANENT; + ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, 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_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 +LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + JSProperty **propp) +{ + JSAtom *atom; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); +} + +static JSBool +LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSObject **objp, JSProperty **propp) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, 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, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + OBJ_DROP_PROPERTY(cx, obj2, 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, (jsid)atom, + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + } + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +static jsval +LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) +{ + JSScopeProperty *sprop; + jsval rval; + + if (!prop) { + /* XXX bad API: no way to tell "not defined" from "void value" */ + return JSVAL_VOID; + } + if (OBJ_IS_NATIVE(obj2)) { + /* Peek at the native property's slot value, without doing a Get. */ + sprop = (JSScopeProperty *)prop; + rval = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) + ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) + : JSVAL_TRUE; + } else { + /* XXX bad API: no way to return "defined but value unknown" */ + rval = JSVAL_TRUE; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + return rval; +} + +static JSBool +GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN *attrsp, JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +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 (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + return JS_FALSE; + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj, 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); +} + +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); +} + +JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +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; + return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, 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; + return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) +{ + jsval junk; + + CHECK_REQUEST(cx); + 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; + return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, 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); +} + +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_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +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; + return OBJ_GET_PROPERTY(cx, obj, (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; + return OBJ_SET_PROPERTY(cx, obj, (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; + return OBJ_DELETE_PROPERTY(cx, obj, (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_GET_CLASS(cx, obj) == &js_ArrayClass; +} + +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) +{ + CHECK_REQUEST(cx); + return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(index), value, + getter, setter, attrs, NULL); +} + +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, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + char numBuf[12]; + OBJ_DROP_PROPERTY(cx, obj2, 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_JSVAL(alias), + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + CHECK_REQUEST(cx); + return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + CHECK_REQUEST(cx); + return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) +{ + jsval junk; + + CHECK_REQUEST(cx); + return JS_DeleteElement2(cx, obj, index, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) +{ + CHECK_REQUEST(cx); + return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSVAL(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); +} + +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; + + /* Get the number of properties to enumerate. */ + if (!OBJ_ENUMERATE(cx, obj, 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 = js_NewIdArray(cx, n); + if (!ida) + goto error; + + i = 0; + vector = &ida->vector[0]; + for (;;) { + if (i == ida->length) { + /* Grow length by factor of 1.5 instead of doubling. */ + jsint newlen = ida->length + (((jsuint)ida->length + 1) >> 1); + ida = js_GrowIdArray(cx, ida, newlen); + if (!ida) + goto error; + vector = &ida->vector[0]; + } + + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) + goto error; + + /* No more jsid's to enumerate ? */ + if (iter_state == JSVAL_NULL) + break; + vector[i++] = id; + } + ida->length = i; + return ida; + +error: + if (iter_state != JSVAL_NULL) + OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); + if (ida) + JS_DestroyIdArray(cx, ida); + return NULL; +} + +JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + CHECK_REQUEST(cx); + return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); +} + +JS_PUBLIC_API(JSCheckAccessOp) +JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) +{ + JSCheckAccessOp oldacb; + + oldacb = rt->checkObjectAccess; + rt->checkObjectAccess = acb; + return oldacb; +} + +JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +{ + JSClass *clasp; + uint32 slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + if (index >= JSCLASS_RESERVED_SLOTS(clasp)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_RESERVED_SLOT_RANGE); + return JS_FALSE; + } + slot = JSSLOT_START(clasp) + index; + *vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) +{ + JSClass *clasp; + uint32 slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + if (index >= JSCLASS_RESERVED_SLOTS(clasp)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_RESERVED_SLOT_RANGE); + return JS_FALSE; + } + slot = JSSLOT_START(clasp) + index; + OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); + return JS_TRUE; +} + +#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(JSPrincipalsTranscoder) +JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) +{ + JSPrincipalsTranscoder oldpx; + + oldpx = rt->principalsTranscoder; + rt->principalsTranscoder = px; + return oldpx; +} + +JS_PUBLIC_API(JSObjectPrincipalsFinder) +JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop) +{ + JSObjectPrincipalsFinder oldfop; + + oldfop = cx->findObjectPrincipals; + cx->findObjectPrincipals = fop; + return oldfop; +} + +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) { + /* Indicate we cannot clone this object. */ + return funobj; + } + return js_CloneFunctionObject(cx, funobj, parent); +} + +JS_PUBLIC_API(JSObject *) +JS_GetFunctionObject(JSFunction *fun) +{ + return fun->object; +} + +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(JSBool) +JS_ObjectIsFunction(JSContext *cx, JSObject *obj) +{ + return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; +} + +JS_PUBLIC_API(JSBool) +JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) +{ + JSFunction *fun; + + CHECK_REQUEST(cx); + for (; fs->name; fs++) { + fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, + fs->flags); + if (!fun) + return JS_FALSE; + fun->extra = 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); +} + +static JSScript * +CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, + void *tempMark, JSBool *eofp) +{ + JSBool eof; + JSArenaPool codePool, notePool; + JSCodeGenerator cg; + JSScript *script; + + CHECK_REQUEST(cx); + eof = JS_FALSE; + JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); + JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); + if (!js_InitCodeGenerator(cx, &cg, &codePool, ¬ePool, + ts->filename, ts->lineno, + ts->principals)) { + script = NULL; + } else if (!js_CompileTokenStream(cx, obj, ts, &cg)) { + script = NULL; + eof = (ts->flags & TSF_EOF) != 0; + } else { + script = js_NewScriptFromCG(cx, &cg, NULL); + } + if (eofp) + *eofp = eof; + if (!js_CloseTokenStream(cx, ts)) { + if (script) + js_DestroyScript(cx, script); + script = NULL; + } + cg.tempMark = tempMark; + js_FinishCodeGenerator(cx, &cg); + JS_FinishArenaPool(&codePool); + JS_FinishArenaPool(¬ePool); + return script; +} + +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); + JS_free(cx, 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); + JS_free(cx, 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); +} + +JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); + if (!ts) + return NULL; + script = CompileTokenStream(cx, obj, ts, mark, NULL); +#if JS_HAS_EXCEPTIONS + if (!script && !cx->fp) + js_ReportUncaughtException(cx); +#endif + return script; +} + +JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length) +{ + jschar *chars; + JSBool result; + JSExceptionState *exnState; + void *tempMark; + JSTokenStream *ts; + 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); + tempMark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); + if (ts) { + older = JS_SetErrorReporter(cx, NULL); + if (!js_ParseTokenStream(cx, obj, ts)) { + /* + * We ran into an error. If it was because we ran out of source, + * we return false, so our caller will know to try to collect more + * buffered source. + */ + result = (ts->flags & TSF_EOF) == 0; + } + + JS_SetErrorReporter(cx, older); + js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, tempMark); + } + + JS_free(cx, chars); + JS_RestoreExceptionState(cx, exnState); + return result; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewFileTokenStream(cx, filename, stdin); + if (!ts) + return NULL; + script = CompileTokenStream(cx, obj, ts, mark, NULL); +#if JS_HAS_EXCEPTIONS + if (!script && !cx->fp) + js_ReportUncaughtException(cx); +#endif + 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) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewFileTokenStream(cx, NULL, file); + if (!ts) + return NULL; + ts->filename = filename; + /* XXXshaver js_NewFileTokenStream should do this, because it drops */ + if (principals) { + ts->principals = principals; + JSPRINCIPALS_HOLD(cx, ts->principals); + } + script = CompileTokenStream(cx, obj, ts, mark, NULL); +#if JS_HAS_EXCEPTIONS + if (!script && !cx->fp) + js_ReportUncaughtException(cx); +#endif + return script; +} + +JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script) +{ + JSObject *obj; + + /* + * We use a dummy stack frame to protect the script from a GC caused + * by debugger-hook execution. + * + * XXX We really need a way to manage local roots and such more + * XXX automatically, at which point we can remove this one-off hack + * XXX and others within the engine. See bug 40757 for discussion. + */ + JSStackFrame dummy; + + CHECK_REQUEST(cx); + + memset(&dummy, 0, sizeof dummy); + dummy.down = cx->fp; + dummy.script = script; + cx->fp = &dummy; + + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + + cx->fp = dummy.down; + if (!obj) + return NULL; + + if (script) { + if (!JS_SetPrivate(cx, obj, script)) + return NULL; + script->object = obj; + } + return obj; +} + +JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script) +{ + return script->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); + JS_free(cx, 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); + JS_free(cx, 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) +{ + void *mark; + JSTokenStream *ts; + JSFunction *fun; + JSAtom *funAtom, *argAtom; + uintN i; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); + if (!ts) { + fun = NULL; + goto out; + } + if (!name) { + funAtom = NULL; + } else { + funAtom = js_Atomize(cx, name, strlen(name), 0); + if (!funAtom) { + fun = NULL; + goto out; + } + } + fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); + if (!fun) + goto out; + if (nargs) { + for (i = 0; i < nargs; i++) { + argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); + if (!argAtom) + break; + if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_SHARED, + SPROP_HAS_SHORTID, i)) { + break; + } + } + if (i < nargs) { + fun = NULL; + goto out; + } + } + if (!js_CompileFunctionBody(cx, ts, fun)) { + fun = NULL; + goto out; + } + if (obj && funAtom) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)funAtom, + OBJECT_TO_JSVAL(fun->object), + NULL, NULL, 0, NULL)) { + return NULL; + } + } +out: + if (ts) + js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, mark); +#if JS_HAS_EXCEPTIONS + if (!fun && !cx->fp) + js_ReportUncaughtException(cx); +#endif + 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, + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + 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) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, JS_GetFunctionName(fun), + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + if (!jp) + return NULL; + if (js_DecompileFunction(jp, fun)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, JS_GetFunctionName(fun), + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + if (!jp) + return NULL; + if (js_DecompileFunctionBody(jp, fun)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) +{ + CHECK_REQUEST(cx); + if (!js_Execute(cx, obj, script, NULL, 0, rval)) { +#if JS_HAS_EXCEPTIONS + if (!cx->fp) + js_ReportUncaughtException(cx); +#endif + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval) +{ + JSScript tmp; + JSRuntime *rt; + JSBool ok; + + /* Make a temporary copy of the JSScript structure and farble it a bit. */ + tmp = *script; + if (part == JSEXEC_PROLOG) { + tmp.length = PTRDIFF(tmp.main, tmp.code, jsbytecode); + } else { + tmp.length -= PTRDIFF(tmp.main, tmp.code, jsbytecode); + tmp.code = tmp.main; + } + + /* Tell the debugger about our temporary copy of the script structure. */ + rt = cx->runtime; + if (rt->newScriptHook) { + rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, + rt->newScriptHookData); + } + + /* Execute the farbled struct and tell the debugger to forget about it. */ + ok = JS_ExecuteScript(cx, obj, &tmp, rval); + if (rt->destroyScriptHook) + rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + 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); + JS_free(cx, chars); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + 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); + JS_free(cx, 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 = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, + filename, lineno); + if (!script) + return JS_FALSE; + ok = js_Execute(cx, obj, script, NULL, 0, rval); +#if JS_HAS_EXCEPTIONS + if (!ok && !cx->fp) + js_ReportUncaughtException(cx); +#endif + JS_DestroyScript(cx, script); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval) +{ + CHECK_REQUEST(cx); + if (!js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, + rval)) { +#if JS_HAS_EXCEPTIONS + if (!cx->fp) + js_ReportUncaughtException(cx); +#endif + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval) +{ + jsval fval; + + CHECK_REQUEST(cx); + if (!JS_GetProperty(cx, obj, name, &fval)) + return JS_FALSE; + if (!js_InternalCall(cx, obj, fval, argc, argv, rval)) { +#if JS_HAS_EXCEPTIONS + if (!cx->fp) + js_ReportUncaughtException(cx); +#endif + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval) +{ + CHECK_REQUEST(cx); + if (!js_InternalCall(cx, obj, fval, argc, argv, rval)) { +#if JS_HAS_EXCEPTIONS + if (!cx->fp) + js_ReportUncaughtException(cx); +#endif + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBranchCallback) +JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) +{ + JSBranchCallback oldcb; + + oldcb = cx->branchCallback; + cx->branchCallback = cb; + return oldcb; +} + +JS_PUBLIC_API(JSBool) +JS_IsRunning(JSContext *cx) +{ + return cx->fp != NULL; +} + +JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx) +{ + return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING); +} + +JS_FRIEND_API(JSBool) +JS_IsAssigning(JSContext *cx) +{ + JSStackFrame *fp; + jsbytecode *pc; + + for (fp = cx->fp; fp && !fp->script; fp = fp->down) + continue; + if (!fp || !(pc = fp->pc)) + return JS_FALSE; + return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0; +} + +JS_PUBLIC_API(void) +JS_SetCallReturnValue2(JSContext *cx, jsval v) +{ +#if JS_HAS_LVALUE_RETURN + cx->rval2 = v; + cx->rval2set = JS_TRUE; +#endif +} + +/************************************************************************/ + +JS_PUBLIC_API(JSString *) +JS_NewString(JSContext *cx, char *bytes, size_t length) +{ + jschar *chars; + JSString *str; + + CHECK_REQUEST(cx); + /* Make a Unicode 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, 0); + if (!str) { + JS_free(cx, chars); + return NULL; + } + + /* Hand off bytes to the deflated string cache, if possible. */ + if (!js_SetStringBytes(str, bytes, length)) + JS_free(cx, 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, 0); + if (!str) + JS_free(cx, 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, 0); + if (!str) + JS_free(cx, 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, 0); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) +{ + CHECK_REQUEST(cx); + return js_NewStringCopyN(cx, s, n, 0); +} + +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, 0); +} + +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) +{ + char *bytes; + + bytes = js_GetStringBytes(str); + return bytes ? bytes : ""; +} + +JS_PUBLIC_API(jschar *) +JS_GetStringChars(JSString *str) +{ + /* + * API botch (again, shades of JS_GetStringBytes): we have no cx to pass + * to js_UndependString (called by js_GetStringChars) for out-of-memory + * error reports, so js_UndependString passes NULL and suppresses errors. + * If it fails 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. + */ + jschar *chars; + + chars = js_GetStringChars(str); + return chars ? chars : JSSTRING_CHARS(str); +} + +JS_PUBLIC_API(size_t) +JS_GetStringLength(JSString *str) +{ + return JSSTRING_LENGTH(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) +{ + CHECK_REQUEST(cx); + return js_NewString(cx, chars, length, GCF_MUTABLE); +} + +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, 0); +} + +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); + if (!js_UndependString(cx, str)) + return JS_FALSE; + + *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; + return JS_TRUE; +} + +/************************************************************************/ + +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_GetErrorMessage); +} + +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) +{ +#if JS_HAS_REGEXPS + 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); + JS_free(cx, chars); + return obj; +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif +} + +JS_PUBLIC_API(JSObject *) +JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) +{ + CHECK_REQUEST(cx); +#if JS_HAS_REGEXPS + return js_NewRegExpObject(cx, NULL, chars, length, flags); +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif +} + +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; + 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) +{ +#if JS_HAS_EXCEPTIONS + return (JSBool) cx->throwing; +#else + return JS_FALSE; +#endif +} + +JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (!cx->throwing) + return JS_FALSE; + *vp = cx->exception; + return JS_TRUE; +#else + return JS_FALSE; +#endif +} + +JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_TRUE; + cx->exception = v; +#endif +} + +JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; +#endif +} + +#if JS_HAS_EXCEPTIONS +struct JSExceptionState { + JSBool throwing; + jsval exception; +}; +#endif + +JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + JSExceptionState *state; + + CHECK_REQUEST(cx); + state = (JSExceptionState *) JS_malloc(cx, 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; +#else + return NULL; +#endif +} + +JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (state) { + if (state->throwing) + JS_SetPendingException(cx, state->exception); + else + JS_ClearPendingException(cx); + JS_DropExceptionState(cx, state); + } +#endif +} + +JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (state) { + if (state->throwing && JSVAL_IS_GCTHING(state->exception)) + JS_RemoveRoot(cx, &state->exception); + JS_free(cx, state); + } +#endif +} + +JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + return js_ErrorFromException(cx, v); +#else + return NULL; +#endif +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(jsword) +JS_GetContextThread(JSContext *cx) +{ + return cx->thread; +} + +JS_PUBLIC_API(jsword) +JS_SetContextThread(JSContext *cx) +{ + intN old = cx->thread; + cx->thread = js_CurrentThreadId(); + return old; +} + +JS_PUBLIC_API(intN) +JS_ClearContextThread(JSContext *cx) +{ + intN old = cx->thread; + cx->thread = 0; + return old; +} +#endif + +/************************************************************************/ + +#if defined(XP_WIN) +#include +/* + * Initialization routine for the JS DLL... + */ + +/* + * Global Instance handle... + * In Win32 this is the module handle of the DLL. + * + * In Win16 this is the instance handle of the application + * which loaded the DLL. + */ + +#ifdef _WIN32 +BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + return TRUE; +} + +#else /* !_WIN32 */ + +int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, + WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + return TRUE; +} + +BOOL CALLBACK __loadds WEP(BOOL fSystemExit) +{ + return TRUE; +} + +#endif /* !_WIN32 */ +#endif /* XP_WIN */ diff --git a/src/extension/script/js/jsapi.h b/src/extension/script/js/jsapi.h new file mode 100644 index 000000000..92b4e1d59 --- /dev/null +++ b/src/extension/script/js/jsapi.h @@ -0,0 +1,1752 @@ +/* -*- 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 jsapi_h___ +#define jsapi_h___ +/* + * JavaScript API. + */ +#include +#include +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Type tags stored in the low bits of a jsval. + */ +#define JSVAL_OBJECT 0x0 /* untagged reference to object */ +#define JSVAL_INT 0x1 /* tagged 31-bit integer value */ +#define JSVAL_DOUBLE 0x2 /* tagged reference to double */ +#define JSVAL_STRING 0x4 /* tagged reference to string */ +#define JSVAL_BOOLEAN 0x6 /* tagged boolean value */ + +/* Type tag bitfield length and derived macros. */ +#define JSVAL_TAGBITS 3 +#define JSVAL_TAGMASK JS_BITMASK(JSVAL_TAGBITS) +#define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK) +#define JSVAL_SETTAG(v,t) ((v) | (t)) +#define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK) +#define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) + +/* Predicates for type testing. */ +#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) +#define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)) +#define JSVAL_IS_INT(v) (((v) & JSVAL_INT) && (v) != JSVAL_VOID) +#define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) +#define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) +#define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) +#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) +#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) +#define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) + +/* Objects, strings, and doubles are GC'ed. */ +#define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) +#define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v)) +#define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v)) +#define JSVAL_TO_DOUBLE(v) ((jsdouble *)JSVAL_TO_GCTHING(v)) +#define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v)) +#define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) +#define DOUBLE_TO_JSVAL(dp) JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE) +#define STRING_TO_JSVAL(str) 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)1 - JSVAL_INT_POW2(30)) +#define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) +#define INT_FITS_IN_JSVAL(i) ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX) +#define JSVAL_TO_INT(v) ((jsint)(v) >> 1) +#define INT_TO_JSVAL(i) (((jsval)(i) << 1) | JSVAL_INT) + +/* Convert between boolean and jsval. */ +#define JSVAL_TO_BOOLEAN(v) ((JSBool)((v) >> JSVAL_TAGBITS)) +#define BOOLEAN_TO_JSVAL(b) JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS, \ + JSVAL_BOOLEAN) + +/* 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_EXPORTED 0x08 /* property is exported from object */ +#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_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ + +/* + * Well-known JS values. The extern'd variables are initialized when the + * first JSContext is created by JS_NewContext (see below). + */ +#define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30)) +#define JSVAL_NULL OBJECT_TO_JSVAL(0) +#define JSVAL_ZERO INT_TO_JSVAL(0) +#define JSVAL_ONE INT_TO_JSVAL(1) +#define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) +#define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) + +/* + * 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(); + +/* 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(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); + +/************************************************************************/ + +/* + * Initialization, locking, contexts, and memory allocation. + */ +#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_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); + +#ifdef JS_THREADSAFE + +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); + +#endif /* JS_THREADSAFE */ + +extern JS_PUBLIC_API(void) +JS_Lock(JSRuntime *rt); + +extern JS_PUBLIC_API(void) +JS_Unlock(JSRuntime *rt); + +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 */ + +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); + +extern JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx); + +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(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. + * + * XXXbe See bug 40757 (http://bugzilla.mozilla.org/show_bug.cgi?id=40757), + * which proposes switching (with an #ifdef, alas, if we want to maintain API + * compatibility) to a JNI-like extensible local root frame stack model. + */ +extern JS_PUBLIC_API(void) +JS_ClearNewbornRoots(JSContext *cx); + +#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 +(* JS_DLL_CALLBACK 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); + +/* + * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a + * property or other strong ref identified for debugging purposes by name. + * The name argument's storage needs to live only as long as the call to + * this routine. + * + * The final arg is used by GC_MARK_DEBUG code to build a ref path through + * the GC's live thing graph. Implementors of JSObjectOps.mark should pass + * its final arg through to this function when marking all GC-things that are + * directly reachable from the object being marked. + * + * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below. + */ +extern JS_PUBLIC_API(void) +JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); + +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_IsAboutToBeFinalized(JSContext *cx, void *thing); + +/* + * Add an external string finalizer, one 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); + +/************************************************************************/ + +/* + * 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; + jsword spare; +}; + +#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 */ + +/* + * 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) + +/* Initializer for unused members of statically initialized JSClass structs. */ +#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 + +/* For detailed comments on these function pointer types, see jspubtd.h. */ +struct JSObjectOps { + /* Mandatory non-null function pointer members. */ + JSNewObjectMapOp newObjectMap; + JSObjectMapOp destroyObjectMap; + 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; + JSXDRObjectOp xdrObject; + JSHasInstanceOp hasInstance; + JSSetObjectSlotOp setProto; + JSSetObjectSlotOp setParent; + JSMarkOp mark; + JSFinalizeOp clear; + JSGetRequiredSlotOp getRequiredSlot; + JSSetRequiredSlotOp setRequiredSlot; +}; + +/* + * Classes that expose JSObjectOps via a non-null getObjectOps class hook may + * derive a property structure from this struct, return a pointer to it from + * lookupProperty and defineProperty, and use the pointer to avoid rehashing + * in getAttributes and setAttributes. + * + * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an + * internal pointer that is opaque to users of this API, but which users may + * convert from and to a jsval using JS_ValueToId and JS_IdToValue. + */ +struct JSProperty { + jsid id; +}; + +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); + +#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ +#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ + +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; + uint8 nargs; + uint8 flags; + uint16 extra; /* number of arg slots for local GC roots */ +}; + +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(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); + +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); + +/* + * 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); + +/* + * 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_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, 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_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); + +/* + * 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_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_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); + +extern JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp); + +extern JS_PUBLIC_API(JSCheckAccessOp) +JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); + +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; + void * (* JS_DLL_CALLBACK getPrincipalArray)(JSContext *cx, JSPrincipals *); + JSBool (* JS_DLL_CALLBACK globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); + + /* Don't call "destroy"; use reference counting macros below. */ + jsrefcount refcount; + void (* JS_DLL_CALLBACK destroy)(JSContext *cx, struct 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 + +extern JS_PUBLIC_API(JSPrincipalsTranscoder) +JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); + +extern JS_PUBLIC_API(JSObjectPrincipalsFinder) +JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop); + +/************************************************************************/ + +/* + * 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); + +/* + * 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(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); + +extern JS_PUBLIC_API(JSBranchCallback) +JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); + +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); + +/* + * Set the second return value, which should be a string or int jsval that + * identifies a property in the returned object, to form an ECMA reference + * type value (obj, id). Only native methods can return reference types, + * and if the returned value is used on the left-hand side of an assignment + * op, the identified property will be set. If the return value is in an + * r-value, the interpreter just gets obj[id]'s value. + */ +extern JS_PUBLIC_API(void) +JS_SetCallReturnValue2(JSContext *cx, jsval v); + +/************************************************************************/ + +/* + * 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(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); + +/************************************************************************/ + +/* + * Locale specific string conversion callback. + */ +struct JSLocaleCallbacks { + JSLocaleToUpperCase localeToUpperCase; + JSLocaleToLowerCase localeToLowerCase; + JSLocaleCompare localeCompare; +}; + +/* + * 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); + +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 */ + +/* + * 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) + +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 */ + +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); + +/* + * 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); + +#ifdef JS_THREADSAFE + +/* + * 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(intN) +JS_ClearContextThread(JSContext *cx); + +#endif /* JS_THREADSAFE */ + +/************************************************************************/ + +JS_END_EXTERN_C + +#endif /* jsapi_h___ */ diff --git a/src/extension/script/js/jsarena.c b/src/extension/script/js/jsarena.c new file mode 100644 index 000000000..4cc2e7c53 --- /dev/null +++ b/src/extension/script/js/jsarena.c @@ -0,0 +1,565 @@ +/* -*- 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 "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jslock.h" + +static JSArena *arena_freelist; + +#ifdef JS_THREADSAFE +static JSLock *arena_freelist_lock; +#endif + +#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) +{ +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!arena_freelist_lock) { + arena_freelist_lock = JS_NEW_LOCK(); + JS_ASSERT(arena_freelist_lock); + } +#endif + 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; +#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 greatest distance between a + * pointer aligned on an M boundary and one aligned on a P boundary is M-P. + * If M and P are powers of two, then M-P = (pool->mask - POINTER_MASK). + * + * How much extra padding might spill over unused into the remainder of the + * allocation, in the worst case (where M > P)? + * + * If we add M-P to the nominal back-pointer address and then round down to + * align on a P boundary, we will use at most M-P bytes of padding, and at + * least P (M > P => M >= 2P; M == 2P gives the least padding, P). So if we + * use P bytes of padding, then we will overallocate a by P+M-1 bytes, as we + * also add M-1 to the estimated size in case malloc returns an odd pointer. + * a->limit must include this overestimation to satisfy a->avail in [a->base, + * a->limit]. + * + * Similarly, if pool->mask is less than POINTER_MASK, 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 P > M 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 \ + : (pool)->mask - POINTER_MASK)) +#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, **bp, *a, *b; + jsuword extra, hdrsz, gross, sz; + void *p; + + /* Search pool from current forward till we find or make enough space. */ + JS_ASSERT((nb & pool->mask) == 0); + for (a = pool->current; a->avail + nb > a->limit; pool->current = a) { + ap = &a->next; + if (!*ap) { + /* Not enough space in pool -- try to reclaim a free arena. */ + extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; + hdrsz = sizeof *a + extra + pool->mask; + gross = hdrsz + JS_MAX(nb, pool->arenasize); + bp = &arena_freelist; + JS_ACQUIRE_LOCK(arena_freelist_lock); + while ((b = *bp) != NULL) { + /* + * Insist on exact arenasize match if nb is not greater than + * arenasize. Otherwise take any arena big enough, but not by + * more than gross + arenasize. + */ + sz = JS_UPTRDIFF(b->limit, b); + if (extra + ? sz >= gross && sz <= gross + pool->arenasize + : sz == gross) { + *bp = b->next; + JS_RELEASE_LOCK(arena_freelist_lock); + b->next = NULL; + COUNT(pool, nreclaims); + goto claim; + } + bp = &b->next; + } + + /* Nothing big enough on the freelist, so we must malloc. */ + JS_RELEASE_LOCK(arena_freelist_lock); + b = (JSArena *) malloc(gross); + if (!b) + return 0; + b->next = NULL; + b->limit = (jsuword)b + gross; + JS_COUNT_ARENA(pool,++); + COUNT(pool, nmallocs); + + claim: + /* 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; + + /* + * 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 = 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; + a = (JSArena *) 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 = JS_ARENA_ALIGN(pool, 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, JSBool reallyFree) +{ + 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 + + if (reallyFree) { + do { + *ap = a->next; + JS_CLEAR_ARENA(a); + JS_COUNT_ARENA(pool,--); + free(a); + } while ((a = *ap) != NULL); + } else { + /* Insert the whole arena chain at the front of the freelist. */ + do { + ap = &(*ap)->next; + } while (*ap); + JS_ACQUIRE_LOCK(arena_freelist_lock); + *ap = arena_freelist; + arena_freelist = a; + JS_RELEASE_LOCK(arena_freelist_lock); + head->next = 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_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) { + a->avail = JS_ARENA_ALIGN(pool, mark); + JS_ASSERT(a->avail <= a->limit); + FreeArenaList(pool, a, JS_TRUE); + return; + } + } +} + +JS_PUBLIC_API(void) +JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size) +{ + JSArena **ap, *a, *b; + jsuword q; + + /* + * If the allocation is oversized, it consumes an entire arena, and it has + * a header just before the allocation pointing back to its predecessor's + * next member. Otherwise, we have to search pool for a. + */ + if (size > pool->arenasize) { + ap = *PTR_TO_HEADER(pool, p); + a = *ap; + } else { + q = (jsuword)p + size; + q = JS_ARENA_ALIGN(pool, q); + ap = &pool->first.next; + while ((a = *ap) != NULL) { + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + + if (a->avail == q) { + /* + * If a is consumed by the allocation at p, we can free it to + * the malloc heap. + */ + if (a->base == (jsuword)p) + break; + + /* + * We can't free a, but we can "retract" its avail cursor -- + * whether there are others after it in pool. + */ + a->avail = (jsuword)p; + return; + } + ap = &a->next; + } + } + + /* + * At this point, a is doomed, so ensure that pool->current doesn't point + * at it. What's more, force future allocations to scavenge all arenas on + * pool, in case some have free space. + */ + if (pool->current == a) + pool->current = &pool->first; + + /* + * This is a non-LIFO deallocation, so take care to fix up a->next's back + * pointer in its header, if a->next is oversized. + */ + *ap = b = a->next; + if (b && b->avail - b->base > pool->arenasize) { + JS_ASSERT(GET_HEADER(pool, b) == &a->next); + SET_HEADER(pool, b, ap); + } + JS_CLEAR_ARENA(a); + JS_COUNT_ARENA(pool,--); + free(a); +} + +JS_PUBLIC_API(void) +JS_FreeArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first, JS_FALSE); + COUNT(pool, ndeallocs); +} + +JS_PUBLIC_API(void) +JS_FinishArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first, JS_TRUE); +#ifdef JS_ARENAMETER + { + JSArenaStats *stats, **statsp; + + if (pool->stats.name) + free(pool->stats.name); + 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() +{ + JSArena *a, *next; + + JS_ACQUIRE_LOCK(arena_freelist_lock); + a = arena_freelist; + arena_freelist = NULL; + JS_RELEASE_LOCK(arena_freelist_lock); + for (; a; a = next) { + next = a->next; + free(a); + } +} + +JS_PUBLIC_API(void) +JS_ArenaShutDown(void) +{ +#ifdef JS_THREADSAFE + /* Must come through here once in the process's last thread! */ + if (arena_freelist_lock) { + JS_DESTROY_LOCK(arena_freelist_lock); + arena_freelist_lock = NULL; + } +#endif +} + +#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 +#include + +JS_PUBLIC_API(void) +JS_DumpArenaStats(FILE *fp) +{ + JSArenaStats *stats; + uint32 nallocs, nbytes; + double mean, variance, sigma; + + for (stats = arena_stats_list; stats; stats = stats->next) { + nallocs = stats->nallocs; + if (nallocs != 0) { + nbytes = stats->nbytes; + mean = (double)nbytes / nallocs; + variance = stats->variance * nallocs - nbytes * nbytes; + if (variance < 0 || nallocs == 1) + variance = 0; + else + variance /= nallocs * (nallocs - 1); + sigma = sqrt(variance); + } else { + mean = variance = sigma = 0; + } + + 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 free arena reclaims: %u\n", stats->nreclaims); + 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/src/extension/script/js/jsarena.h b/src/extension/script/js/jsarena.h new file mode 100644 index 000000000..e52398a1a --- /dev/null +++ b/src/extension/script/js/jsarena.h @@ -0,0 +1,302 @@ +/* -*- 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 nreclaims; /* number of reclaims from freeArenas */ + 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) */ +#ifdef JS_ARENAMETER + JSArenaStats stats; +#endif +}; + +/* + * If the including .c file uses only one power-of-2 alignment, it may define + * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions + * per ALLOCATE and GROW. + */ +#ifdef JS_ARENA_CONST_ALIGN_MASK +#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \ + & ~(jsuword)JS_ARENA_CONST_ALIGN_MASK) + +#define JS_INIT_ARENA_POOL(pool, name, size) \ + JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1) +#else +#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) +#endif + +#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_CAST(p, type *, pool, sizeof(type)) + +#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ + JS_BEGIN_MACRO \ + JSArena *_a = (pool)->current; \ + size_t _nb = JS_ARENA_ALIGN(pool, nb); \ + jsuword _p = _a->avail; \ + jsuword _q = _p + _nb; \ + JS_ASSERT(_q >= _p); \ + if (_q > _a->limit) \ + _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ + else \ + _a->avail = _q; \ + 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); \ + jsuword _q = (jsuword)(p) + JS_ARENA_ALIGN(pool, _nb); \ + if (_q <= _a->limit) { \ + _a->avail = _q; \ + 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)) + +#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_UPTRDIFF(_m, _a->base) <= JS_UPTRDIFF(_a->avail, _a->base)) { \ + _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 the given name for debugging and metering, + * 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); + +/* + * 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); + +/* + * Finish using arenas, freeing all memory associated with them except for + * any locks needed for thread safety. + */ +extern JS_PUBLIC_API(void) +JS_ArenaFinish(void); + +/* + * Free any locks or other memory needed for thread safety, just before + * shutting down. At that point, we must be called by a single thread. + * + * After shutting down, the next thread to call JS_InitArenaPool must not + * race with any other thread. Once a pool has been initialized, threads + * may safely call jsarena.c functions on thread-local pools. The upshot + * is that pools are per-thread, but the underlying global freelist is + * thread-safe, provided that both the first pool initialization and the + * shut-down call are single-threaded. + */ +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); + +/* + * Function to be used directly when an allocation has likely grown to consume + * an entire JSArena, in which case the arena is returned to the malloc heap. + */ +extern JS_PUBLIC_API(void) +JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size); + +#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/src/extension/script/js/jsarray.c b/src/extension/script/js/jsarray.c new file mode 100644 index 000000000..250c67557 --- /dev/null +++ b/src/extension/script/js/jsarray.c @@ -0,0 +1,1429 @@ +/* -*- 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 array class. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsstr.h" + +/* 2^32 - 1 as a number and a string */ +#define MAXINDEX 4294967295u +#define MAXSTR "4294967295" + +/* + * Determine if the id represents an array 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. + */ +static JSBool +IdIsIndex(jsid 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; + } + + /* It must be a string. */ + str = JSVAL_TO_STRING(id); + cp = JSSTRING_CHARS(str); + if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < 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++; + } + } + /* Make sure all characters were consumed and that it couldn't + * have overflowed. + */ + if (*cp == 0 && + (oldIndex < (MAXINDEX / 10) || + (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) + { + *indexp = index; + return JS_TRUE; + } + } + return JS_FALSE; +} + +static JSBool +ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) +{ + jsint i; + jsdouble d; + + if (JSVAL_IS_INT(v)) { + i = JSVAL_TO_INT(v); + if (i < 0) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ARRAY_LENGTH); + return JS_FALSE; + } + *lengthp = (jsuint) i; + return JS_TRUE; + } + + if (!js_ValueToNumber(cx, v, &d)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ARRAY_LENGTH); + return JS_FALSE; + } + if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ARRAY_LENGTH); + return JS_FALSE; + } + if (JSDOUBLE_IS_NaN(d) || d != *lengthp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ARRAY_LENGTH); + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + jsid id; + jsint i; + jsval v; + + id = (jsid) cx->runtime->atomState.lengthAtom; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + + /* Short-circuit, because js_ValueToECMAUint32 fails when + * called during init time. + */ + if (JSVAL_IS_INT(v)) { + i = JSVAL_TO_INT(v); + /* jsuint cast does ToUint32. */ + *lengthp = (jsuint)i; + return JS_TRUE; + } + return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); +} + +static JSBool +IndexToValue(JSContext *cx, jsuint length, jsval *vp) +{ + if (length <= JSVAL_INT_MAX) { + *vp = INT_TO_JSVAL(length); + return JS_TRUE; + } + return js_NewDoubleValue(cx, (jsdouble)length, vp); +} + +static JSBool +IndexToId(JSContext *cx, jsuint length, jsid *idp) +{ + JSString *str; + JSAtom *atom; + + if (length <= JSVAL_INT_MAX) { + *idp = (jsid) INT_TO_JSVAL(length); + } else { + str = js_NumberToString(cx, (jsdouble)length); + if (!str) + return JS_FALSE; + atom = js_AtomizeString(cx, str, 0); + if (!atom) + return JS_FALSE; + *idp = (jsid)atom; + + } + return JS_TRUE; +} + +JSBool +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) +{ + jsval v; + jsid id; + + if (!IndexToValue(cx, length, &v)) + return JS_FALSE; + id = (jsid) cx->runtime->atomState.lengthAtom; + return OBJ_SET_PROPERTY(cx, obj, id, &v); +} + +JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + JSErrorReporter older; + jsid id; + JSBool ok; + jsval v; + + older = JS_SetErrorReporter(cx, NULL); + id = (jsid) cx->runtime->atomState.lengthAtom; + ok = OBJ_GET_PROPERTY(cx, obj, id, &v); + JS_SetErrorReporter(cx, older); + if (!ok) + return JS_FALSE; + return ValueIsLength(cx, v, lengthp); +} + +/* + * This get function is specific to Array.prototype.length and other array + * instance length properties. It calls back through the class get function + * in case some magic happens there (see call_getProperty in jsfun.c). + */ +static JSBool +array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); +} + +static JSBool +array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsuint newlen, oldlen, slot; + jsid id2; + jsval junk; + + if (!ValueIsLength(cx, *vp, &newlen)) + return JS_FALSE; + if (!js_GetLengthProperty(cx, obj, &oldlen)) + return JS_FALSE; + slot = oldlen; + while (slot > newlen) { + --slot; + if (!IndexToId(cx, slot, &id2)) + return JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; + } + return IndexToValue(cx, newlen, vp); +} + +static JSBool +array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsuint index, length; + + if (!(IdIsIndex(id, &index))) + return JS_TRUE; + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (index >= length) { + length = index + 1; + return js_SetLengthProperty(cx, obj, length); + } + return JS_TRUE; +} + +static JSBool +array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + jsuint length; + + if (cx->version == JSVERSION_1_2) { + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + switch (type) { + case JSTYPE_NUMBER: + return IndexToValue(cx, length, vp); + case JSTYPE_BOOLEAN: + *vp = BOOLEAN_TO_JSVAL(length > 0); + return JS_TRUE; + default: + return JS_TRUE; + } + } + return js_TryValueOf(cx, obj, type, vp); +} + +JSClass js_ArrayClass = { + "Array", + 0, + array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, + jsval *rval, JSBool localeString) +{ + JSBool ok; + jsval v; + jsuint length, index; + jschar *chars, *ochars; + size_t nchars, growth, seplen, tmplen; + const jschar *sepstr; + JSString *str; + JSHashEntry *he; + JSObject *obj2; + + ok = js_GetLengthProperty(cx, obj, &length); + if (!ok) + return JS_FALSE; + ok = JS_TRUE; + + he = js_EnterSharpObject(cx, obj, NULL, &chars); + if (!he) + return JS_FALSE; + if (literalize) { + if (IS_SHARP(he)) { +#if JS_HAS_SHARP_VARS + nchars = js_strlen(chars); +#else + chars[0] = '['; + chars[1] = ']'; + chars[2] = 0; + nchars = 2; +#endif + goto make_string; + } + + /* + * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the + * terminating 0. + */ + growth = (1 + 3 + 1) * sizeof(jschar); + if (!chars) { + nchars = 0; + chars = (jschar *) malloc(growth); + if (!chars) + goto done; + } else { + MAKE_SHARP(he); + nchars = js_strlen(chars); + chars = (jschar *) + realloc((ochars = chars), nchars * sizeof(jschar) + growth); + if (!chars) { + free(ochars); + goto done; + } + } + chars[nchars++] = '['; + } else { + /* + * Free any sharp variable definition in chars. Normally, we would + * MAKE_SHARP(he) so that only the first sharp variable annotation is + * a definition, and all the rest are references, but in the current + * case of (!literalize), we don't need chars at all. + */ + if (chars) + JS_free(cx, chars); + chars = NULL; + nchars = 0; + + /* Return the empty string on a cycle as well as on empty join. */ + if (IS_BUSY(he) || length == 0) { + js_LeaveSharpObject(cx, NULL); + *rval = JS_GetEmptyStringValue(cx); + return ok; + } + + /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ + MAKE_BUSY(he); + } + sepstr = NULL; + seplen = JSSTRING_LENGTH(sep); + + v = JSVAL_NULL; + for (index = 0; index < length; index++) { + ok = JS_GetElement(cx, obj, index, &v); + if (!ok) + goto done; + + if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) { + str = cx->runtime->emptyString; + } else { + if (localeString) { + if (!js_ValueToObject(cx, v, &obj2) || + !js_TryMethod(cx, obj2, + cx->runtime->atomState.toLocaleStringAtom, + 0, NULL, &v)) { + str = NULL; + } else { + str = js_ValueToString(cx, v); + } + } else { + str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); + } + if (!str) { + ok = JS_FALSE; + goto done; + } + } + + /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ + growth = (nchars + (sepstr ? seplen : 0) + + JSSTRING_LENGTH(str) + + 3 + 1) * sizeof(jschar); + if (!chars) { + chars = (jschar *) malloc(growth); + if (!chars) + goto done; + } else { + chars = (jschar *) realloc((ochars = chars), growth); + if (!chars) { + free(ochars); + goto done; + } + } + + if (sepstr) { + js_strncpy(&chars[nchars], sepstr, seplen); + nchars += seplen; + } + sepstr = JSSTRING_CHARS(sep); + + tmplen = JSSTRING_LENGTH(str); + js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); + nchars += tmplen; + } + + done: + if (literalize) { + if (chars) { + if (JSVAL_IS_VOID(v)) { + chars[nchars++] = ','; + chars[nchars++] = ' '; + } + chars[nchars++] = ']'; + } + } else { + CLEAR_BUSY(he); + } + js_LeaveSharpObject(cx, NULL); + if (!ok) { + if (chars) + free(chars); + return ok; + } + + make_string: + if (!chars) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + chars[nchars] = 0; + str = js_NewString(cx, chars, nchars, 0); + if (!str) { + free(chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static jschar comma_space_ucstr[] = {',', ' ', 0}; +static jschar comma_ucstr[] = {',', 0}; +static JSString comma_space = {2, comma_space_ucstr}; +static JSString comma = {1, comma_ucstr}; + +#if JS_HAS_TOSOURCE +static JSBool +array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval, JS_FALSE); +} +#endif + +static JSBool +array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSBool literalize; + + /* + * JS1.2 arrays convert to array literals, with a comma followed by a space + * between each element. + */ + literalize = (cx->version == JSVERSION_1_2); + return array_join_sub(cx, obj, literalize ? &comma_space : &comma, + literalize, rval, JS_FALSE); +} + +static JSBool +array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + /* + * Passing comma here as the separator. Need a way to get a + * locale-specific version. + */ + return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_TRUE); +} + +static JSBool +InitArrayElements(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) +{ + jsuint index; + jsid id; + + for (index = 0; index < length; index++) { + if (!IndexToId(cx, index, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &vector[index])) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) +{ + jsval v; + jsid id; + + if (!IndexToValue(cx, length, &v)) + return JS_FALSE; + id = (jsid) cx->runtime->atomState.lengthAtom; + if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, + array_length_getter, array_length_setter, + JSPROP_PERMANENT, + NULL)) { + return JS_FALSE; + } + if (!vector) + return JS_TRUE; + return InitArrayElements(cx, obj, length, vector); +} + +#if JS_HAS_SOME_PERL_FUN +/* + * Perl-inspired join, reverse, and sort. + */ +static JSBool +array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + if (JSVAL_IS_VOID(argv[0])) + return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE); + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE); +} + +static JSBool +array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsuint len, half, i; + jsid id, id2; + jsval v, v2; + + if (!js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + + half = len / 2; + for (i = 0; i < half; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!IndexToId(cx, len - i - 1, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2)) + return JS_FALSE; + +#if JS_HAS_SPARSE_ARRAYS + /* This part isn't done yet. */ + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */ + continue; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); +#endif + + if (!OBJ_SET_PROPERTY(cx, obj, id, &v2)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +typedef struct HSortArgs { + void *vec; + size_t elsize; + void *pivot; + JSComparator cmp; + void *arg; + JSBool fastcopy; +} HSortArgs; + +static int +sort_compare(const void *a, const void *b, void *arg); + +static int +sort_compare_strings(const void *a, const void *b, void *arg); + +static void +HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) +{ + void *pivot, *vec, *vec2, *arg, *a, *b; + size_t elsize; + JSComparator cmp; + JSBool fastcopy; + size_t j, hiDiv2; + + pivot = hsa->pivot; + vec = hsa->vec; + elsize = hsa->elsize; + vec2 = (char *)vec - 2 * elsize; + cmp = hsa->cmp; + arg = hsa->arg; + + fastcopy = hsa->fastcopy; +#define MEMCPY(p,q,n) \ + (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) + + if (lo == 1) { + j = 2; + b = (char *)vec + elsize; + if (j < hi && cmp(vec, b, arg) < 0) + j++; + a = (char *)vec + (hi - 1) * elsize; + b = (char *)vec2 + j * elsize; + + /* + * During sorting phase b points to a member of heap that cannot be + * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 + * always holds. + */ + if ((building || hi == 2) && cmp(a, b, arg) >= 0) + return; + + MEMCPY(pivot, a, elsize); + MEMCPY(a, b, elsize); + lo = j; + } else { + a = (char *)vec2 + lo * elsize; + MEMCPY(pivot, a, elsize); + } + + hiDiv2 = hi/2; + while (lo <= hiDiv2) { + j = lo + lo; + a = (char *)vec2 + j * elsize; + b = (char *)vec + (j - 1) * elsize; + if (j < hi && cmp(a, b, arg) < 0) + j++; + b = (char *)vec2 + j * elsize; + if (cmp(pivot, b, arg) >= 0) + break; + + a = (char *)vec2 + lo * elsize; + MEMCPY(a, b, elsize); + lo = j; + } + + a = (char *)vec2 + lo * elsize; + MEMCPY(a, pivot, elsize); +#undef MEMCPY +} + +JSBool +js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg) +{ + void *pivot; + HSortArgs hsa; + size_t i; + + pivot = malloc(elsize); + if (!pivot) + return JS_FALSE; + hsa.vec = vec; + hsa.elsize = elsize; + hsa.pivot = pivot; + hsa.cmp = cmp; + hsa.arg = arg; + hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); + + for (i = nel/2; i != 0; i--) + HeapSortHelper(JS_TRUE, &hsa, i, nel); + while (nel > 2) + HeapSortHelper(JS_FALSE, &hsa, 1, --nel); + + free(pivot); + return JS_TRUE; +} + +typedef struct CompareArgs { + JSContext *context; + jsval fval; + JSBool status; +} CompareArgs; + +static int +sort_compare(const void *a, const void *b, void *arg) +{ + jsval av = *(const jsval *)a, bv = *(const jsval *)b; + CompareArgs *ca = (CompareArgs *) arg; + JSContext *cx = ca->context; + jsdouble cmp = -1; + jsval fval, argv[2], rval; + JSBool ok; + + fval = ca->fval; + if (fval == JSVAL_NULL) { + JSString *astr, *bstr; + + if (av == bv) { + cmp = 0; + } else if (av == JSVAL_VOID || bv == JSVAL_VOID) { + /* Put undefined properties at the end. */ + cmp = (av == JSVAL_VOID) ? 1 : -1; + } else if ((astr = js_ValueToString(cx, av)) != NULL && + (bstr = js_ValueToString(cx, bv)) != NULL) { + cmp = js_CompareStrings(astr, bstr); + } else { + ca->status = JS_FALSE; + } + } else { + argv[0] = av; + argv[1] = bv; + ok = js_InternalCall(cx, + OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), + fval, 2, argv, &rval); + if (ok) { + ok = js_ValueToNumber(cx, rval, &cmp); + /* Clamp cmp to -1, 0, 1. */ + if (JSDOUBLE_IS_NaN(cmp)) { + /* XXX 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. + */ + cmp = 0; + } else if (cmp != 0) { + cmp = cmp > 0 ? 1 : -1; + } + } else { + ca->status = ok; + } + } + return (int)cmp; +} + +static int +sort_compare_strings(const void *a, const void *b, void *arg) +{ + jsval av = *(const jsval *)a, bv = *(const jsval *)b; + + return (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); +} + +/* XXXmccabe do the sort helper functions need to take int? (Or can we claim + * that 2^32 * 32 is too large to worry about?) Something dumps when I change + * to unsigned int; is qsort using -1 as a fencepost? + */ +static JSBool +array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval fval; + CompareArgs ca; + jsuint len, newlen, i; + jsval *vec; + jsid id; + size_t nbytes; + + /* + * Optimize the default compare function case if all of obj's elements + * have values of type string. + */ + JSBool all_strings; + + 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]; + all_strings = JS_FALSE; /* non-default compare function */ + } else { + fval = JSVAL_NULL; + all_strings = JS_TRUE; /* check for all string values */ + } + + if (!js_GetLengthProperty(cx, obj, &len)) + return JS_FALSE; + if (len == 0) { + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + /* + * Test for size_t overflow, which could lead to indexing beyond the end + * of the malloc'd vector. + */ + nbytes = len * sizeof(jsval); + if (nbytes != (double) len * sizeof(jsval)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + vec = (jsval *) JS_malloc(cx, nbytes); + if (!vec) + return JS_FALSE; + +#if JS_HAS_SPARSE_ARRAYS + newlen = 0; +#else + newlen = len; +#endif + + for (i = 0; i < len; i++) { + ca.status = IndexToId(cx, i, &id); + if (!ca.status) + goto out; +#if JS_HAS_SPARSE_ARRAYS + { + JSObject *obj2; + JSProperty *prop; + ca.status = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ca.status) + goto out; + if (!prop) { + vec[i] = JSVAL_VOID; + continue; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + newlen++; + } +#endif + ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]); + if (!ca.status) + goto out; + + /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ + all_strings &= JSVAL_IS_STRING(vec[i]); + } + + ca.context = cx; + ca.fval = fval; + ca.status = JS_TRUE; + if (!js_HeapSort(vec, (size_t) len, sizeof(jsval), + all_strings ? sort_compare_strings : sort_compare, + &ca)) { + JS_ReportOutOfMemory(cx); + ca.status = JS_FALSE; + } + + if (ca.status) { + ca.status = InitArrayElements(cx, obj, newlen, vec); + if (ca.status) + *rval = OBJECT_TO_JSVAL(obj); +#if JS_HAS_SPARSE_ARRAYS + /* set length of newly-created array object to old length. */ + if (ca.status && newlen < len) { + ca.status = js_SetLengthProperty(cx, obj, len); + + /* Delete any leftover properties greater than newlen. */ + while (ca.status && newlen < len) { + jsval junk; + + ca.status = !IndexToId(cx, newlen, &id) || + !OBJ_DELETE_PROPERTY(cx, obj, id, &junk); + newlen++; + } + } +#endif + } + +out: + if (vec) + JS_free(cx, vec); + return ca.status; +} +#endif /* JS_HAS_SOME_PERL_FUN */ + +#if JS_HAS_MORE_PERL_FUN +/* + * Perl-inspired push, pop, shift, unshift, and splice methods. + */ +static JSBool +array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint length; + uintN i; + jsid id; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, length + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } + + /* + * If JS1.2, follow Perl4 by returning the last thing pushed. Otherwise, + * return the new array length. + */ + length += argc; + if (cx->version == JSVERSION_1_2) { + *rval = argc ? argv[argc-1] : JSVAL_VOID; + } else { + if (!IndexToValue(cx, length, rval)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, length); +} + +static JSBool +array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint index; + jsid id; + jsval junk; + + if (!js_GetLengthProperty(cx, obj, &index)) + return JS_FALSE; + if (index > 0) { + index--; + if (!IndexToId(cx, index, &id)) + return JS_FALSE; + + /* Get the to-be-deleted property's value into rval. */ + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; + + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, index); +} + +static JSBool +array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint length, i; + jsid id, id2; + jsval v, junk; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (length > 0) { + length--; + id = JSVAL_ZERO; + + /* Get the to-be-deleted property's value into rval ASAP. */ + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; + + /* + * Slide down the array above the first element. + */ + if (length > 0) { + for (i = 1; i <= length; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!IndexToId(cx, i - 1, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } + } + + /* Delete the only or last element. */ + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, length); +} + +static JSBool +array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsuint length, last; + uintN i; + jsid id, id2; + jsval v; +#if JS_HAS_SPARSE_ARRAYS + JSObject *obj2; + JSProperty *prop; +#endif + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (argc > 0) { + /* Slide up the array to make room for argc at the bottom. */ + if (length > 0) { + last = length; + while (last--) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last + argc, &id2)) + return JS_FALSE; +#if JS_HAS_SPARSE_ARRAYS + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */ + continue; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); +#endif + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } + } + + /* Copy from argv to the bottom of the array. */ + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } + + /* Follow Perl by returning the new array length. */ + length += argc; + if (!js_SetLengthProperty(cx, obj, length)) + return JS_FALSE; + } + return IndexToValue(cx, length, rval); +} + +static JSBool +array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsuint length, begin, end, count, delta, last; + uintN i; + jsdouble d; + jsid id, id2; + jsval v; + JSObject *obj2; + + /* Nothing to do if no args. Otherwise lock and load length. */ + if (argc == 0) + return JS_TRUE; + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + /* Convert the first argument into a starting index. */ + if (!js_ValueToNumber(cx, *argv, &d)) + 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 { + if (!js_ValueToNumber(cx, *argv, &d)) + 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++; + } + + if (count == 1 && cx->version == JSVERSION_1_2) { + /* + * JS lacks "list context", whereby in Perl one turns the single + * scalar that's spliced out into an array just by assigning it to + * @single instead of $single, or by using it as Perl push's first + * argument, for instance. + * + * JS1.2 emulated Perl too closely and returned a non-Array for + * the single-splice-out case, requiring callers to test and wrap + * in [] if necessary. So JS1.3, default, and other versions all + * return an array of length 1 for uniformity. + */ + if (!IndexToId(cx, begin, &id)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; + } else { + if (cx->version != JSVERSION_1_2 || count > 0) { + /* + * 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; + *rval = OBJECT_TO_JSVAL(obj2); + + /* If there are elements to remove, put them into the return value. */ + if (count > 0) { + for (last = begin; last < end; last++) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last - begin, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v)) + 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; + /* (uint) end could be 0, so can't use vanilla >= test */ + while (last-- > end) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last + delta, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } + length += delta; + } else if (argc < count) { + delta = count - (jsuint)argc; + for (last = end; last < length; last++) { + if (!IndexToId(cx, last, &id)) + return JS_FALSE; + if (!IndexToId(cx, last - delta, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + return JS_FALSE; + } + length -= delta; + } + + /* Copy from argv into the hole to complete the splice. */ + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, begin + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } + + /* Update length in case we deleted elements from the end. */ + return js_SetLengthProperty(cx, obj, length); +} +#endif /* JS_HAS_MORE_PERL_FUN */ + +#if JS_HAS_SEQUENCE_OPS +/* + * Python-esque sequence operations. + */ +static JSBool +array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *nobj, *aobj; + jsuint length, alength, slot; + uintN i; + jsval v; + jsid id, id2; + + /* Treat obj as the first argument; see ECMA 15.4.4.4. */ + --argv; + JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); + + /* Create a new Array object and store it in the rval local root. */ + nobj = js_NewArrayObject(cx, 0, NULL); + if (!nobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(nobj); + + /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ + length = 0; + for (i = 0; i <= argc; i++) { + v = argv[i]; + if (JSVAL_IS_OBJECT(v)) { + aobj = JSVAL_TO_OBJECT(v); + if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { + if (!OBJ_GET_PROPERTY(cx, aobj, + (jsid)cx->runtime->atomState.lengthAtom, + &v)) { + return JS_FALSE; + } + if (!ValueIsLength(cx, v, &alength)) + return JS_FALSE; + for (slot = 0; slot < alength; slot++) { + if (!IndexToId(cx, slot, &id)) + return JS_FALSE; + if (!IndexToId(cx, length + slot, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, aobj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + return JS_FALSE; + } + length += alength; + continue; + } + } + + if (!IndexToId(cx, length, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id, &v)) + return JS_FALSE; + length++; + } + + return JS_TRUE; +} + +static JSBool +array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *nobj; + jsuint length, begin, end, slot; + jsdouble d; + jsid id, id2; + jsval v; + + nobj = js_NewArrayObject(cx, 0, NULL); + if (!nobj) + return JS_FALSE; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + begin = 0; + end = length; + + if (argc > 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + 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) { + if (!js_ValueToNumber(cx, argv[1], &d)) + 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; + } + } + + for (slot = begin; slot < end; slot++) { + if (!IndexToId(cx, slot, &id)) + return JS_FALSE; + if (!IndexToId(cx, slot - begin, &id2)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + return JS_FALSE; + } + *rval = OBJECT_TO_JSVAL(nobj); + return JS_TRUE; +} +#endif /* JS_HAS_SEQUENCE_OPS */ + +static JSFunctionSpec array_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, array_toSource, 0,0,0}, +#endif + {js_toString_str, array_toString, 0,0,0}, + {js_toLocaleString_str, array_toLocaleString, 0,0,0}, + + /* Perl-ish methods. */ +#if JS_HAS_SOME_PERL_FUN + {"join", array_join, 1,0,0}, + {"reverse", array_reverse, 0,0,0}, + {"sort", array_sort, 1,0,0}, +#endif +#if JS_HAS_MORE_PERL_FUN + {"push", array_push, 1,0,0}, + {"pop", array_pop, 0,0,0}, + {"shift", array_shift, 0,0,0}, + {"unshift", array_unshift, 1,0,0}, + {"splice", array_splice, 1,0,0}, +#endif + + /* Python-esque sequence methods. */ +#if JS_HAS_SEQUENCE_OPS + {"concat", array_concat, 0,0,0}, + {"slice", array_slice, 0,0,0}, +#endif + + {0,0,0,0,0} +}; + +static JSBool +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 (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + 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 (cx->version == JSVERSION_1_2) { + length = (jsuint) argc; + vector = argv; + } else if (argc > 1) { + length = (jsuint) argc; + vector = argv; + } else if (!JSVAL_IS_NUMBER(argv[0])) { + length = 1; + vector = argv; + } else { + if (!ValueIsLength(cx, argv[0], &length)) + return JS_FALSE; + vector = NULL; + } + return InitArrayObject(cx, obj, length, vector); +} + +JSObject * +js_InitArrayClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, + NULL, array_methods, NULL, NULL); + + /* 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) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + if (!obj) + return NULL; + if (!InitArrayObject(cx, obj, length, vector)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + return obj; +} diff --git a/src/extension/script/js/jsarray.h b/src/extension/script/js/jsarray.h new file mode 100644 index 000000000..cbb2aedf1 --- /dev/null +++ b/src/extension/script/js/jsarray.h @@ -0,0 +1,77 @@ +/* -*- 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" + +JS_BEGIN_EXTERN_C + +extern JSClass js_ArrayClass; + +extern JSObject * +js_InitArrayClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector); + +extern JSBool +js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JSBool +js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); + +extern JSBool +js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); + +/* + * JS-specific heap sort function. + */ +typedef int (*JSComparator)(const void *a, const void *b, void *arg); + +extern JSBool +js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg); + +JS_END_EXTERN_C + +#endif /* jsarray_h___ */ diff --git a/src/extension/script/js/jsatom.c b/src/extension/script/js/jsatom.c new file mode 100644 index 000000000..59cb4b801 --- /dev/null +++ b/src/extension/script/js/jsatom.c @@ -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 ***** */ + +/* + * JS atom table. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsstr.h" + +JS_FRIEND_API(const char *) +js_AtomToPrintableString(JSContext *cx, JSAtom *atom) +{ + JSString *str; + const char *bytes; + + str = js_QuoteString(cx, ATOM_TO_STRING(atom), 0); + if (!str) + return NULL; + bytes = js_GetStringBytes(str); + if (!bytes) + JS_ReportOutOfMemory(cx); + return bytes; +} + +extern const char js_Error_str[]; /* trivial, from jsexn.h */ + +/* + * Keep this in sync with jspubtd.h -- an assertion below will insist that + * its length match the JSType enum's JSTYPE_LIMIT limit value. + */ +const char *js_type_str[] = { + "undefined", + "object", + "function", + "string", + "number", + "boolean", +}; + +const char *js_boolean_str[] = { + js_false_str, + js_true_str +}; + +const char js_Arguments_str[] = "Arguments"; +const char js_Array_str[] = "Array"; +const char js_Boolean_str[] = "Boolean"; +const char js_Call_str[] = "Call"; +const char js_Date_str[] = "Date"; +const char js_Function_str[] = "Function"; +const char js_Math_str[] = "Math"; +const char js_Number_str[] = "Number"; +const char js_Object_str[] = "Object"; +const char js_RegExp_str[] = "RegExp"; +const char js_Script_str[] = "Script"; +const char js_String_str[] = "String"; +const char js_anonymous_str[] = "anonymous"; +const char js_arguments_str[] = "arguments"; +const char js_arity_str[] = "arity"; +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_eval_str[] = "eval"; +const char js_getter_str[] = "getter"; +const char js_get_str[] = "get"; +const char js_index_str[] = "index"; +const char js_input_str[] = "input"; +const char js_length_str[] = "length"; +const char js_name_str[] = "name"; +const char js_noSuchMethod_str[] = "__noSuchMethod__"; +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_toSource_str[] = "toSource"; +const char js_toString_str[] = "toString"; +const char js_toLocaleString_str[] = "toLocaleString"; +const char js_valueOf_str[] = "valueOf"; + +#define HASH_OBJECT(o) ((JSHashNumber)(o) >> JSVAL_TAGBITS) +#define HASH_INT(i) ((JSHashNumber)(i)) +#define HASH_DOUBLE(dp) ((JSHashNumber)(((uint32*)(dp))[0] ^ ((uint32*)(dp))[1])) +#define HASH_BOOLEAN(b) ((JSHashNumber)(b)) + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_atom_key(const void *key) +{ + jsval v; + jsdouble *dp; + + /* Order JSVAL_IS_* tests by likelihood of success. */ + v = (jsval)key; + if (JSVAL_IS_STRING(v)) + return js_HashString(JSVAL_TO_STRING(v)); + if (JSVAL_IS_INT(v)) + return HASH_INT(JSVAL_TO_INT(v)); + if (JSVAL_IS_DOUBLE(v)) { + dp = JSVAL_TO_DOUBLE(v); + return HASH_DOUBLE(dp); + } + if (JSVAL_IS_OBJECT(v)) + return HASH_OBJECT(JSVAL_TO_OBJECT(v)); + if (JSVAL_IS_BOOLEAN(v)) + return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v)); + return (JSHashNumber)v; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_compare_atom_keys(const void *k1, const void *k2) +{ + jsval v1, v2; + + v1 = (jsval)k1, v2 = (jsval)k2; + if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) + return !js_CompareStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); + if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { + double d1 = *JSVAL_TO_DOUBLE(v1); + double d2 = *JSVAL_TO_DOUBLE(v2); + 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; + } + return v1 == v2; +} + +JS_STATIC_DLL_CALLBACK(int) +js_compare_stub(const void *v1, const void *v2) +{ + return 1; +} + +/* These next two are exported to jsscript.c and used similarly there. */ +void * JS_DLL_CALLBACK +js_alloc_table_space(void *priv, size_t size) +{ + return malloc(size); +} + +void JS_DLL_CALLBACK +js_free_table_space(void *priv, void *item) +{ + free(item); +} + +JS_STATIC_DLL_CALLBACK(JSHashEntry *) +js_alloc_atom(void *priv, const void *key) +{ + JSAtomState *state = (JSAtomState *) priv; + JSAtom *atom; + + atom = (JSAtom *) malloc(sizeof(JSAtom)); + if (!atom) + return NULL; +#ifdef JS_THREADSAFE + state->tablegen++; +#endif + atom->entry.key = key; + atom->entry.value = NULL; + atom->flags = 0; + atom->number = state->number++; + return &atom->entry; +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_atom(void *priv, JSHashEntry *he, uintN flag) +{ + if (flag != HT_FREE_ENTRY) + return; +#ifdef JS_THREADSAFE + ((JSAtomState *)priv)->tablegen++; +#endif + free(he); +} + +static JSHashAllocOps atom_alloc_ops = { + js_alloc_table_space, js_free_table_space, + js_alloc_atom, js_free_atom +}; + +#define JS_ATOM_HASH_SIZE 1024 + +JSBool +js_InitAtomState(JSContext *cx, JSAtomState *state) +{ + state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, + js_compare_atom_keys, js_compare_stub, + &atom_alloc_ops, state); + if (!state->table) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + state->runtime = cx->runtime; +#ifdef JS_THREADSAFE + js_InitLock(&state->lock); + state->tablegen = 0; +#endif + + if (!js_InitPinnedAtoms(cx, state)) { + js_FreeAtomState(cx, state); + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) +{ + uintN i; + +#define FROB(lval,str) \ + JS_BEGIN_MACRO \ + if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) \ + return JS_FALSE; \ + JS_END_MACRO + + JS_ASSERT(sizeof js_type_str / sizeof js_type_str[0] == JSTYPE_LIMIT); + for (i = 0; i < JSTYPE_LIMIT; i++) + FROB(typeAtoms[i], js_type_str[i]); + + FROB(booleanAtoms[0], js_false_str); + FROB(booleanAtoms[1], js_true_str); + FROB(nullAtom, js_null_str); + + FROB(ArgumentsAtom, js_Arguments_str); + FROB(ArrayAtom, js_Array_str); + FROB(BooleanAtom, js_Boolean_str); + FROB(CallAtom, js_Call_str); + FROB(DateAtom, js_Date_str); + FROB(ErrorAtom, js_Error_str); + FROB(FunctionAtom, js_Function_str); + FROB(MathAtom, js_Math_str); + FROB(NumberAtom, js_Number_str); + FROB(ObjectAtom, js_Object_str); + FROB(RegExpAtom, js_RegExp_str); + FROB(ScriptAtom, js_Script_str); + FROB(StringAtom, js_String_str); + FROB(anonymousAtom, js_anonymous_str); + FROB(argumentsAtom, js_arguments_str); + FROB(arityAtom, js_arity_str); + FROB(calleeAtom, js_callee_str); + FROB(callerAtom, js_caller_str); + FROB(classPrototypeAtom, js_class_prototype_str); + FROB(constructorAtom, js_constructor_str); + FROB(countAtom, js_count_str); + FROB(evalAtom, js_eval_str); + FROB(getAtom, js_get_str); + FROB(getterAtom, js_getter_str); + FROB(indexAtom, js_index_str); + FROB(inputAtom, js_input_str); + FROB(lengthAtom, js_length_str); + FROB(nameAtom, js_name_str); + FROB(noSuchMethodAtom, js_noSuchMethod_str); + FROB(parentAtom, js_parent_str); + FROB(protoAtom, js_proto_str); + FROB(setAtom, js_set_str); + FROB(setterAtom, js_setter_str); + FROB(toSourceAtom, js_toSource_str); + FROB(toStringAtom, js_toString_str); + FROB(toLocaleStringAtom, js_toLocaleString_str); + FROB(valueOfAtom, js_valueOf_str); + +#undef FROB + + memset(&state->lazy, 0, sizeof state->lazy); + return JS_TRUE; +} + +/* NB: cx unused; js_FinishAtomState calls us with null cx. */ +void +js_FreeAtomState(JSContext *cx, JSAtomState *state) +{ + if (state->table) + JS_HashTableDestroy(state->table); +#ifdef JS_THREADSAFE + js_FinishLock(&state->lock); +#endif + memset(state, 0, sizeof *state); +} + +typedef struct UninternArgs { + JSRuntime *rt; + jsatomid leaks; +} UninternArgs; + +JS_STATIC_DLL_CALLBACK(intN) +js_atom_uninterner(JSHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + UninternArgs *args; + + atom = (JSAtom *)he; + args = (UninternArgs *)arg; + if (ATOM_IS_STRING(atom)) + js_FinalizeStringRT(args->rt, ATOM_TO_STRING(atom)); + else if (ATOM_IS_OBJECT(atom)) + args->leaks++; + return HT_ENUMERATE_NEXT; +} + +void +js_FinishAtomState(JSAtomState *state) +{ + UninternArgs args; + + if (!state->table) + return; + args.rt = state->runtime; + args.leaks = 0; + JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args); +#ifdef DEBUG + if (args.leaks != 0) { + fprintf(stderr, +"JS engine warning: %lu atoms remain after destroying the JSRuntime.\n" +" These atoms may point to freed memory. Things reachable\n" +" through them have not been finalized.\n", + (unsigned long) args.leaks); + } +#endif + js_FreeAtomState(NULL, state); +} + +typedef struct MarkArgs { + uintN gcflags; + JSGCThingMarker mark; + void *data; +} MarkArgs; + +JS_STATIC_DLL_CALLBACK(intN) +js_atom_marker(JSHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + MarkArgs *args; + jsval key; + + atom = (JSAtom *)he; + args = (MarkArgs *)arg; + if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || + (args->gcflags & GC_KEEP_ATOMS)) { + atom->flags |= ATOM_MARK; + key = ATOM_KEY(atom); + if (JSVAL_IS_GCTHING(key)) + args->mark(JSVAL_TO_GCTHING(key), args->data); + } + return HT_ENUMERATE_NEXT; +} + +void +js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark, + void *data) +{ + MarkArgs args; + + if (!state->table) + return; + args.gcflags = gcflags; + args.mark = mark; + args.data = data; + JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args); +} + +JS_STATIC_DLL_CALLBACK(intN) +js_atom_sweeper(JSHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + JSAtomState *state; + + atom = (JSAtom *)he; + if (atom->flags & ATOM_MARK) { + atom->flags &= ~ATOM_MARK; + state = (JSAtomState *)arg; + state->liveAtoms++; + return HT_ENUMERATE_NEXT; + } + JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); + atom->entry.key = NULL; + atom->flags = 0; + return HT_ENUMERATE_REMOVE; +} + +void +js_SweepAtomState(JSAtomState *state) +{ + state->liveAtoms = 0; + if (state->table) + JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state); +} + +JS_STATIC_DLL_CALLBACK(intN) +js_atom_unpinner(JSHashEntry *he, intN i, void *arg) +{ + JSAtom *atom; + + atom = (JSAtom *)he; + atom->flags &= ~ATOM_PINNED; + return HT_ENUMERATE_NEXT; +} + +void +js_UnpinPinnedAtoms(JSAtomState *state) +{ + if (state->table) + JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); +} + +static JSAtom * +js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) +{ + JSAtomState *state; + JSHashTable *table; + JSHashEntry *he, **hep; + JSAtom *atom; + + state = &cx->runtime->atomState; + JS_LOCK(&state->lock, cx); + table = state->table; + hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) == NULL) { + he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + atom = NULL; + goto out; + } + } + + atom = (JSAtom *)he; + atom->flags |= flags; + cx->lastAtom = atom; +out: + JS_UNLOCK(&state->lock,cx); + return atom; +} + +JSAtom * +js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags) +{ + jsval key; + JSHashNumber keyHash; + + /* XXX must be set in the following order or MSVC1.52 will crash */ + keyHash = HASH_OBJECT(obj); + key = OBJECT_TO_JSVAL(obj); + return js_AtomizeHashedKey(cx, key, keyHash, flags); +} + +JSAtom * +js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags) +{ + jsval key; + JSHashNumber keyHash; + + key = BOOLEAN_TO_JSVAL(b); + keyHash = HASH_BOOLEAN(b); + return js_AtomizeHashedKey(cx, key, keyHash, flags); +} + +JSAtom * +js_AtomizeInt(JSContext *cx, jsint i, uintN flags) +{ + jsval key; + JSHashNumber keyHash; + + key = INT_TO_JSVAL(i); + keyHash = HASH_INT(i); + return js_AtomizeHashedKey(cx, key, keyHash, flags); +} + +/* Worst-case alignment grain and aligning macro for 2x-sized buffer. */ +#define ALIGNMENT(t) JS_MAX(JSVAL_ALIGN, sizeof(t)) +#define ALIGN(b,t) ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)]) + +JSAtom * +js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) +{ + jsdouble *dp; + JSHashNumber keyHash; + jsval key; + JSAtomState *state; + JSHashTable *table; + JSHashEntry *he, **hep; + JSAtom *atom; + char buf[2 * ALIGNMENT(double)]; + + dp = ALIGN(buf, double); + *dp = d; + keyHash = HASH_DOUBLE(dp); + key = DOUBLE_TO_JSVAL(dp); + state = &cx->runtime->atomState; + JS_LOCK(&state->lock, cx); + table = state->table; + hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) == NULL) { +#ifdef JS_THREADSAFE + uint32 gen = state->tablegen; +#endif + JS_UNLOCK(&state->lock,cx); + if (!js_NewDoubleValue(cx, d, &key)) + return NULL; + JS_LOCK(&state->lock, cx); +#ifdef JS_THREADSAFE + if (state->tablegen != gen) { + hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) != NULL) { + atom = (JSAtom *)he; + goto out; + } + } +#endif + he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + atom = NULL; + goto out; + } + } + + atom = (JSAtom *)he; + atom->flags |= flags; + cx->lastAtom = atom; +out: + JS_UNLOCK(&state->lock,cx); + return atom; +} + +JSAtom * +js_AtomizeString(JSContext *cx, JSString *str, uintN flags) +{ + JSHashNumber keyHash; + jsval key; + JSAtomState *state; + JSHashTable *table; + JSHashEntry *he, **hep; + JSAtom *atom; + + keyHash = js_HashString(str); + key = STRING_TO_JSVAL(str); + state = &cx->runtime->atomState; + JS_LOCK(&state->lock, cx); + table = state->table; + hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) == NULL) { +#ifdef JS_THREADSAFE + uint32 gen = state->tablegen; + JS_UNLOCK(&state->lock, cx); +#endif + + if (flags & ATOM_TMPSTR) { + str = (flags & ATOM_NOCOPY) + ? js_NewString(cx, str->chars, str->length, 0) + : js_NewStringCopyN(cx, str->chars, str->length, 0); + if (!str) + return NULL; + key = STRING_TO_JSVAL(str); + } else { + if (!JS_MakeStringImmutable(cx, str)) + return NULL; + } + +#ifdef JS_THREADSAFE + JS_LOCK(&state->lock, cx); + if (state->tablegen != gen) { + hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + if ((he = *hep) != NULL) { + atom = (JSAtom *)he; + if (flags & ATOM_NOCOPY) + str->chars = NULL; + goto out; + } + } +#endif + + he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + atom = NULL; + goto out; + } + } + + atom = (JSAtom *)he; + atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED); + cx->lastAtom = atom; +out: + JS_UNLOCK(&state->lock,cx); + return atom; +} + +JS_FRIEND_API(JSAtom *) +js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) +{ + jschar *chars; + JSString *str; + JSAtom *atom; + char buf[2 * ALIGNMENT(JSString)]; + + /* + * 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]; + + if (length < ATOMIZE_BUF_MAX) { + js_InflateStringToBuffer(inflated, bytes, length); + chars = inflated; + } else { + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + flags |= ATOM_NOCOPY; + } + + str = ALIGN(buf, JSString); + + str->chars = chars; + str->length = length; + atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); + if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars)) + JS_free(cx, chars); + return atom; +} + +JS_FRIEND_API(JSAtom *) +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) +{ + JSString *str; + char buf[2 * ALIGNMENT(JSString)]; + + str = ALIGN(buf, JSString); + str->chars = (jschar *)chars; + str->length = length; + return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); +} + +JSAtom * +js_AtomizeValue(JSContext *cx, jsval value, uintN flags) +{ + if (JSVAL_IS_STRING(value)) + return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags); + if (JSVAL_IS_INT(value)) + return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags); + if (JSVAL_IS_DOUBLE(value)) + return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags); + if (JSVAL_IS_OBJECT(value)) + return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags); + if (JSVAL_IS_BOOLEAN(value)) + return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags); + return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags); +} + +JSAtom * +js_ValueToStringAtom(JSContext *cx, jsval v) +{ + JSString *str; + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + return js_AtomizeString(cx, str, 0); +} + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_atom_ptr(const void *key) +{ + const JSAtom *atom = key; + return atom->number; +} + +JS_STATIC_DLL_CALLBACK(void *) +js_alloc_temp_space(void *priv, size_t size) +{ + JSContext *cx = priv; + void *space; + + JS_ARENA_ALLOCATE(space, &cx->tempPool, size); + if (!space) + JS_ReportOutOfMemory(cx); + return space; +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_temp_space(void *priv, void *item) +{ +} + +JS_STATIC_DLL_CALLBACK(JSHashEntry *) +js_alloc_temp_entry(void *priv, const void *key) +{ + JSContext *cx = priv; + JSAtomListElement *ale; + + JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); + if (!ale) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return &ale->entry; +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) +{ +} + +static JSHashAllocOps temp_alloc_ops = { + js_alloc_temp_space, js_free_temp_space, + js_alloc_temp_entry, js_free_temp_entry +}; + +JSAtomListElement * +js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) +{ + JSAtomListElement *ale, *ale2, *next; + JSHashEntry **hep; + + ATOM_LIST_LOOKUP(ale, hep, al, atom); + if (!ale) { + if (al->count <= 5) { + /* Few enough for linear search, no hash table needed. */ + JS_ASSERT(!al->table); + ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom); + if (!ale) + return NULL; + ALE_SET_ATOM(ale, atom); + ALE_SET_NEXT(ale, al->list); + al->list = ale; + } else { + /* We want to hash. Have we already made a hash table? */ + if (!al->table) { + /* No hash table yet, so hep had better be null! */ + JS_ASSERT(!hep); + al->table = JS_NewHashTable(8, js_hash_atom_ptr, + JS_CompareValues, JS_CompareValues, + &temp_alloc_ops, cx); + if (!al->table) + return NULL; + + /* Insert each ale on al->list into the new hash table. */ + for (ale2 = al->list; ale2; ale2 = next) { + next = ALE_NEXT(ale2); + ale2->entry.keyHash = ALE_ATOM(ale2)->number; + hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash, + ale2->entry.key); + ALE_SET_NEXT(ale2, *hep); + *hep = &ale2->entry; + } + al->list = NULL; + + /* Set hep for insertion of atom's ale, immediately below. */ + hep = JS_HashTableRawLookup(al->table, atom->number, atom); + } + + /* Finally, add an entry for atom into the hash bucket at hep. */ + ale = (JSAtomListElement *) + JS_HashTableRawAdd(al->table, hep, atom->number, atom, NULL); + if (!ale) + return NULL; + } + + ALE_SET_INDEX(ale, al->count++); + } + return ale; +} + +JS_FRIEND_API(JSAtom *) +js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) +{ + JSAtom *atom; + static JSAtom dummy; + + JS_ASSERT(map->vector && i < map->length); + if (!map->vector || i >= map->length) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ATOMIC_NUMBER, numBuf); + return &dummy; + } + atom = map->vector[i]; + JS_ASSERT(atom); + return atom; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_map_atom(JSHashEntry *he, intN i, void *arg) +{ + JSAtomListElement *ale = (JSAtomListElement *)he; + JSAtom **vector = arg; + + vector[ALE_INDEX(ale)] = ALE_ATOM(ale); + return HT_ENUMERATE_NEXT; +} + +#ifdef DEBUG +jsrefcount js_atom_map_count; +jsrefcount js_atom_map_hash_table_count; +#endif + +JS_FRIEND_API(JSBool) +js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) +{ + JSAtom **vector; + JSAtomListElement *ale; + uint32 count; + +#ifdef DEBUG + JS_ATOMIC_INCREMENT(&js_atom_map_count); +#endif + ale = al->list; + if (!ale && !al->table) { + map->vector = NULL; + map->length = 0; + return JS_TRUE; + } + + count = al->count; + if (count >= ATOM_INDEX_LIMIT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_LITERALS); + return JS_FALSE; + } + vector = (JSAtom **) JS_malloc(cx, (size_t) count * sizeof *vector); + if (!vector) + return JS_FALSE; + + 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); + } + ATOM_LIST_INIT(al); + + map->vector = vector; + map->length = (jsatomid)count; + return JS_TRUE; +} + +JS_FRIEND_API(void) +js_FreeAtomMap(JSContext *cx, JSAtomMap *map) +{ + if (map->vector) { + JS_free(cx, map->vector); + map->vector = NULL; + } + map->length = 0; +} diff --git a/src/extension/script/js/jsatom.h b/src/extension/script/js/jsatom.h new file mode 100644 index 000000000..ec9b52f80 --- /dev/null +++ b/src/extension/script/js/jsatom.h @@ -0,0 +1,409 @@ +/* -*- 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 "jstypes.h" +#include "jshash.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#ifdef JS_THREADSAFE +#include "jslock.h" +#endif + +JS_BEGIN_EXTERN_C + +#define ATOM_PINNED 0x01 /* atom is pinned against GC */ +#define ATOM_INTERNED 0x02 /* pinned variant for JS_Intern* API */ +#define ATOM_MARK 0x04 /* atom is reachable via GC */ +#define ATOM_NOCOPY 0x40 /* don't copy atom string bytes */ +#define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ + +struct JSAtom { + JSHashEntry entry; /* key is jsval, value keyword info */ + uint32 flags; /* pinned, interned, and mark flags */ + jsatomid number; /* atom serial number and hash code */ +}; + +#define ATOM_KEY(atom) ((jsval)(atom)->entry.key) +#define ATOM_IS_OBJECT(atom) JSVAL_IS_OBJECT(ATOM_KEY(atom)) +#define ATOM_TO_OBJECT(atom) JSVAL_TO_OBJECT(ATOM_KEY(atom)) +#define ATOM_IS_INT(atom) JSVAL_IS_INT(ATOM_KEY(atom)) +#define ATOM_TO_INT(atom) JSVAL_TO_INT(ATOM_KEY(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)) +#define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) +#define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) + +/* + * 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 JS_FRIEND_API(const char *) +js_AtomToPrintableString(JSContext *cx, JSAtom *atom); + +#define ATOM_KEYWORD(atom) ((struct keyword *)(atom)->entry.value) +#define ATOM_SET_KEYWORD(atom,kw) ((atom)->entry.value = (kw)) + +struct JSAtomListElement { + JSHashEntry entry; +}; + +#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) +#define ALE_INDEX(ale) ((jsatomid) (ale)->entry.value) +#define ALE_JSOP(ale) ((JSOp) (ale)->entry.value) +#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) + +#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) +#define ALE_SET_INDEX(ale,index)((ale)->entry.value = (void *)(index)) +#define ALE_SET_JSOP(ale,op) ((ale)->entry.value = (void *)(op)) +#define ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link)) + +struct JSAtomList { + JSAtomListElement *list; /* literals indexed for mapping */ + JSHashTable *table; /* hash table if list gets too long */ + jsuint count; /* count of indexed literals */ +}; + +#define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \ + (al)->count = 0) + +#define ATOM_LIST_SEARCH(_ale,_al,_atom) \ + JS_BEGIN_MACRO \ + JSHashEntry **_hep; \ + ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \ + JS_END_MACRO + +#define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \ + JS_BEGIN_MACRO \ + if ((_al)->table) { \ + _hep = JS_HashTableRawLookup((_al)->table, _atom->number, _atom); \ + _ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \ + } else { \ + JSAtomListElement **_alep = &(_al)->list; \ + _hep = NULL; \ + while ((_ale = *_alep) != NULL) { \ + if (ALE_ATOM(_ale) == (_atom)) { \ + /* Hit, move atom's element to the front of the list. */ \ + *_alep = ALE_NEXT(_ale); \ + ALE_SET_NEXT(_ale, (_al)->list); \ + (_al)->list = _ale; \ + break; \ + } \ + _alep = (JSAtomListElement **)&_ale->entry.next; \ + } \ + } \ + JS_END_MACRO + +struct JSAtomMap { + JSAtom **vector; /* array of ptrs to indexed atoms */ + jsatomid length; /* count of (to-be-)indexed atoms */ +}; + +struct JSAtomState { + JSRuntime *runtime; /* runtime that owns us */ + JSHashTable *table; /* hash table containing all atoms */ + jsatomid number; /* one beyond greatest atom number */ + jsatomid liveAtoms; /* number of live atoms after last GC */ + + /* Type names and value literals. */ + JSAtom *typeAtoms[JSTYPE_LIMIT]; + JSAtom *booleanAtoms[2]; + JSAtom *nullAtom; + + /* Various built-in or commonly-used atoms, pinned on first context. */ + JSAtom *ArgumentsAtom; + JSAtom *ArrayAtom; + JSAtom *BooleanAtom; + JSAtom *CallAtom; + JSAtom *DateAtom; + JSAtom *ErrorAtom; + JSAtom *FunctionAtom; + JSAtom *MathAtom; + JSAtom *NumberAtom; + JSAtom *ObjectAtom; + JSAtom *RegExpAtom; + JSAtom *ScriptAtom; + JSAtom *StringAtom; + JSAtom *anonymousAtom; + JSAtom *argumentsAtom; + JSAtom *arityAtom; + JSAtom *calleeAtom; + JSAtom *callerAtom; + JSAtom *classPrototypeAtom; + JSAtom *constructorAtom; + JSAtom *countAtom; + JSAtom *evalAtom; + JSAtom *getAtom; + JSAtom *getterAtom; + JSAtom *indexAtom; + JSAtom *inputAtom; + JSAtom *lengthAtom; + JSAtom *nameAtom; + JSAtom *noSuchMethodAtom; + JSAtom *parentAtom; + JSAtom *protoAtom; + JSAtom *setAtom; + JSAtom *setterAtom; + JSAtom *toLocaleStringAtom; + JSAtom *toSourceAtom; + JSAtom *toStringAtom; + JSAtom *valueOfAtom; + + /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ + struct { + JSAtom *EvalErrorAtom; + JSAtom *InfinityAtom; + JSAtom *InternalErrorAtom; + JSAtom *NaNAtom; + JSAtom *RangeErrorAtom; + JSAtom *ReferenceErrorAtom; + JSAtom *SyntaxErrorAtom; + JSAtom *TypeErrorAtom; + JSAtom *URIErrorAtom; + JSAtom *decodeURIAtom; + JSAtom *decodeURIComponentAtom; + JSAtom *defineGetterAtom; + JSAtom *defineSetterAtom; + JSAtom *encodeURIAtom; + JSAtom *encodeURIComponentAtom; + JSAtom *escapeAtom; + JSAtom *hasOwnPropertyAtom; + JSAtom *isFiniteAtom; + JSAtom *isNaNAtom; + JSAtom *isPrototypeOfAtom; + JSAtom *lookupGetterAtom; + JSAtom *lookupSetterAtom; + JSAtom *parseFloatAtom; + JSAtom *parseIntAtom; + JSAtom *propertyIsEnumerableAtom; + JSAtom *unescapeAtom; + JSAtom *unevalAtom; + JSAtom *unwatchAtom; + JSAtom *watchAtom; + } lazy; + +#ifdef JS_THREADSAFE + JSThinLock lock; + volatile uint32 tablegen; +#endif +}; + +/* Well-known predefined strings and their atoms. */ +extern const char *js_type_str[]; +extern const char *js_boolean_str[]; + +extern const char js_Arguments_str[]; +extern const char js_Array_str[]; +extern const char js_Boolean_str[]; +extern const char js_Call_str[]; +extern const char js_Date_str[]; +extern const char js_Function_str[]; +extern const char js_Math_str[]; +extern const char js_Number_str[]; +extern const char js_Object_str[]; +extern const char js_RegExp_str[]; +extern const char js_Script_str[]; +extern const char js_String_str[]; +extern const char js_anonymous_str[]; +extern const char js_arguments_str[]; +extern const char js_arity_str[]; +extern const char js_callee_str[]; +extern const char js_caller_str[]; +extern const char js_class_prototype_str[]; +extern const char js_constructor_str[]; +extern const char js_count_str[]; +extern const char js_eval_str[]; +extern const char js_getter_str[]; +extern const char js_get_str[]; +extern const char js_index_str[]; +extern const char js_input_str[]; +extern const char js_length_str[]; +extern const char js_name_str[]; +extern const char js_noSuchMethod_str[]; +extern const char js_parent_str[]; +extern const char js_proto_str[]; +extern const char js_setter_str[]; +extern const char js_set_str[]; +extern const char js_toSource_str[]; +extern const char js_toString_str[]; +extern const char js_toLocaleString_str[]; +extern const char js_valueOf_str[]; + +/* + * Initialize atom state. Return true on success, false with an out of + * memory error report on failure. + */ +extern JSBool +js_InitAtomState(JSContext *cx, JSAtomState *state); + +/* + * Free and clear atom state (except for any interned string atoms). + */ +extern void +js_FreeAtomState(JSContext *cx, JSAtomState *state); + +/* + * Interned strings are atoms that live until state's runtime is destroyed. + * This function frees all interned string atoms, and then frees and clears + * state's members (just as js_FreeAtomState does), unless there aren't any + * interned strings in state -- in which case state must be "free" already. + * + * NB: js_FreeAtomState is called for each "last" context being destroyed in + * a runtime, where there may yet be another context created in the runtime; + * whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know + * that no more contexts will be created. Thus we minimize garbage during + * context-free episodes on a runtime, while preserving atoms created by the + * JS_Intern*String APIs for the life of the runtime. + */ +extern void +js_FinishAtomState(JSAtomState *state); + +/* + * Atom garbage collection hooks. + */ +typedef void +(*JSGCThingMarker)(void *thing, void *data); + +extern void +js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark, + void *data); + +extern void +js_SweepAtomState(JSAtomState *state); + +extern JSBool +js_InitPinnedAtoms(JSContext *cx, JSAtomState *state); + +extern void +js_UnpinPinnedAtoms(JSAtomState *state); + +/* + * Find or create the atom for an object. If we create a new atom, give it the + * type indicated in flags. Return 0 on failure to allocate memory. + */ +extern JSAtom * +js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags); + +/* + * Find or create the atom for a Boolean value. If we create a new atom, give + * it the type indicated in flags. Return 0 on failure to allocate memory. + */ +extern JSAtom * +js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags); + +/* + * Find or create the atom for an integer value. If we create a new atom, give + * it the type indicated in flags. Return 0 on failure to allocate memory. + */ +extern JSAtom * +js_AtomizeInt(JSContext *cx, jsint i, uintN flags); + +/* + * Find or create the atom for a double value. If we create a new atom, give + * it the type indicated in flags. Return 0 on failure to allocate memory. + */ +extern JSAtom * +js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags); + +/* + * Find or create the atom for a string. If we create a new atom, give it the + * type indicated in flags. Return 0 on failure to allocate memory. + */ +extern JSAtom * +js_AtomizeString(JSContext *cx, JSString *str, uintN flags); + +extern JS_FRIEND_API(JSAtom *) +js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); + +extern JS_FRIEND_API(JSAtom *) +js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); + +/* + * This variant handles all value tag types. + */ +extern JSAtom * +js_AtomizeValue(JSContext *cx, jsval value, uintN flags); + +/* + * Convert v to an atomized string. + */ +extern JSAtom * +js_ValueToStringAtom(JSContext *cx, jsval v); + +/* + * Assign atom an index and insert it on al. + */ +extern JSAtomListElement * +js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al); + +/* + * Get the atom with index i from map. + */ +extern JS_FRIEND_API(JSAtom *) +js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i); + +/* + * For all unmapped atoms recorded in al, add a mapping from the atom's index + * to its address. The GC must not run until all indexed atoms in atomLists + * have been mapped by scripts connected to live objects (Function and Script + * class objects have scripts as/in their private data -- the GC knows about + * these two classes). + */ +extern JS_FRIEND_API(JSBool) +js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); + +/* + * Free map->vector and clear map. + */ +extern JS_FRIEND_API(void) +js_FreeAtomMap(JSContext *cx, JSAtomMap *map); + +JS_END_EXTERN_C + +#endif /* jsatom_h___ */ diff --git a/src/extension/script/js/jsautocfg.h b/src/extension/script/js/jsautocfg.h new file mode 100644 index 000000000..a7f5d6bc8 --- /dev/null +++ b/src/extension/script/js/jsautocfg.h @@ -0,0 +1,50 @@ +#ifndef js_cpucfg___ +#define js_cpucfg___ + +/* AUTOMATICALLY GENERATED - DO NOT EDIT */ + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 4L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define JS_WORDS_PER_DWORD_LOG2 1L + +#endif /* js_cpucfg___ */ diff --git a/src/extension/script/js/jsbit.h b/src/extension/script/js/jsbit.h new file mode 100644 index 000000000..db41acf9f --- /dev/null +++ b/src/extension/script/js/jsbit.h @@ -0,0 +1,113 @@ +/* -*- 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" +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_TEST_BIT(_map,_bit) \ + ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) +#define JS_SET_BIT(_map,_bit) \ + ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) +#define JS_CLEAR_BIT(_map,_bit) \ + ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= ~(1L << ((_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); + +/* +** 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. +*/ +#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 + +/* +** 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. +*/ +#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 + +JS_END_EXTERN_C +#endif /* jsbit_h___ */ diff --git a/src/extension/script/js/jsbool.c b/src/extension/script/js/jsbool.c new file mode 100644 index 000000000..05f596556 --- /dev/null +++ b/src/extension/script/js/jsbool.c @@ -0,0 +1,244 @@ +/* -*- 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 "jsstddef.h" +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsstr.h" + +static JSClass boolean_class = { + "Boolean", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#if JS_HAS_TOSOURCE +#include "jsprf.h" + +static JSBool +bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + char buf[32]; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_BOOLEAN(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + JS_snprintf(buf, sizeof buf, "(new %s(%s))", + boolean_class.name, + js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +static JSBool +bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + JSAtom *atom; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_BOOLEAN(v)) + return js_obj_toString(cx, obj, argc, argv, rval); + atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; + str = ATOM_TO_STRING(atom); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + return JS_FALSE; + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + return JS_TRUE; +} + +static JSFunctionSpec boolean_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, bool_toSource, 0,0,0}, +#endif + {js_toString_str, bool_toString, 0,0,0}, + {js_valueOf_str, bool_valueOf, 0,0,0}, + {0,0,0,0,0} +}; + +#ifdef XP_MAC +#undef Boolean +#define Boolean js_Boolean +#endif + +static JSBool +Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool b; + jsval bval; + + if (argc != 0) { + if (!js_ValueToBoolean(cx, argv[0], &b)) + return JS_FALSE; + bval = BOOLEAN_TO_JSVAL(b); + } else { + bval = JSVAL_FALSE; + } + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + *rval = bval; + return JS_TRUE; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); + return JS_TRUE; +} + +JSObject * +js_InitBooleanClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1, + NULL, boolean_methods, NULL, NULL); + if (!proto) + return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); + return proto; +} + +JSObject * +js_BooleanToObject(JSContext *cx, JSBool b) +{ + JSObject *obj; + + obj = js_NewObject(cx, &boolean_class, NULL, NULL); + if (!obj) + return NULL; + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); + return obj; +} + +JSString * +js_BooleanToString(JSContext *cx, JSBool b) +{ + return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); +} + +JSBool +js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) +{ + JSBool b; + jsdouble d; + +#if defined _MSC_VER && _MSC_VER <= 800 + /* MSVC1.5 coredumps */ + if (!bp) + return JS_TRUE; + /* This should be an if-else chain, but MSVC1.5 crashes if it is. */ +#define ELSE +#else +#define ELSE else +#endif + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + /* Must return early to avoid falling thru to JSVAL_IS_OBJECT case. */ + *bp = JS_FALSE; + return JS_TRUE; + } + if (JSVAL_IS_OBJECT(v)) { + if (!JSVERSION_IS_ECMA(cx->version)) { + if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v)) + return JS_FALSE; + if (!JSVAL_IS_BOOLEAN(v)) + v = JSVAL_TRUE; /* non-null object is true */ + b = JSVAL_TO_BOOLEAN(v); + } else { + b = JS_TRUE; + } + } ELSE + if (JSVAL_IS_STRING(v)) { + b = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) ? JS_TRUE : JS_FALSE; + } ELSE + if (JSVAL_IS_INT(v)) { + b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE; + } ELSE + if (JSVAL_IS_DOUBLE(v)) { + d = *JSVAL_TO_DOUBLE(v); + b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE; + } ELSE +#if defined _MSC_VER && _MSC_VER <= 800 + if (JSVAL_IS_BOOLEAN(v)) { + b = JSVAL_TO_BOOLEAN(v); + } +#else + { + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + b = JSVAL_TO_BOOLEAN(v); + } +#endif + +#undef ELSE + *bp = b; + return JS_TRUE; +} diff --git a/src/extension/script/js/jsbool.h b/src/extension/script/js/jsbool.h new file mode 100644 index 000000000..c07910f59 --- /dev/null +++ b/src/extension/script/js/jsbool.h @@ -0,0 +1,62 @@ +/* -*- 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. + */ + +JS_BEGIN_EXTERN_C + +extern JSObject * +js_InitBooleanClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_BooleanToObject(JSContext *cx, JSBool b); + +extern JSString * +js_BooleanToString(JSContext *cx, JSBool b); + +extern JSBool +js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); + +JS_END_EXTERN_C + +#endif /* jsbool_h___ */ diff --git a/src/extension/script/js/jsclist.h b/src/extension/script/js/jsclist.h new file mode 100644 index 000000000..2eafe8e40 --- /dev/null +++ b/src/extension/script/js/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/src/extension/script/js/jscntxt.c b/src/extension/script/js/jscntxt.c new file mode 100644 index 000000000..85496b89b --- /dev/null +++ b/src/extension/script/js/jscntxt.c @@ -0,0 +1,702 @@ +/* -*- 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 execution context. + */ +#include "jsstddef.h" +#include +#include +#include +#include "jstypes.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 "jsconfig.h" +#include "jsdbgapi.h" +#include "jsexn.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscan.h" +#include "jsscript.h" +#include "jsstr.h" + +JSContext * +js_NewContext(JSRuntime *rt, size_t stackChunkSize) +{ + JSContext *cx; + JSBool ok, first; + + cx = (JSContext *) malloc(sizeof *cx); + if (!cx) + return NULL; + memset(cx, 0, sizeof *cx); + + cx->runtime = rt; +#if JS_STACK_GROWTH_DIRECTION > 0 + cx->stackLimit = (jsuword)-1; +#endif +#ifdef JS_THREADSAFE + js_InitContextForLocking(cx); +#endif + + JS_LOCK_GC(rt); + for (;;) { + first = (rt->contextList.next == &rt->contextList); + if (rt->state == JSRTS_UP) { + JS_ASSERT(!first); + break; + } + if (rt->state == JSRTS_DOWN) { + JS_ASSERT(first); + rt->state = JSRTS_LAUNCHING; + break; + } + JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); + } + JS_APPEND_LINK(&cx->links, &rt->contextList); + JS_UNLOCK_GC(rt); + + /* + * First we do the infallible, every-time per-context initializations. + * Should a later, fallible initialization (js_InitRegExpStatics, e.g., + * or the stuff under 'if (first)' below) fail, at least the version + * and arena-pools will be valid and safe to use (say, from the last GC + * done by js_DestroyContext). + */ + cx->version = JSVERSION_DEFAULT; + cx->jsop_eq = JSOP_EQ; + cx->jsop_ne = JSOP_NE; + JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval)); + JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble)); + +#if JS_HAS_REGEXPS + if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { + js_DestroyContext(cx, JS_NO_GC); + return NULL; + } +#endif +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_FALSE; +#endif + + /* + * 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) { + ok = (rt->atomState.liveAtoms == 0) + ? js_InitAtomState(cx, &rt->atomState) + : js_InitPinnedAtoms(cx, &rt->atomState); + if (ok) + ok = js_InitScanner(cx); + if (ok) + ok = js_InitRuntimeNumberState(cx); + if (ok) + ok = js_InitRuntimeStringState(cx); + if (!ok) { + js_DestroyContext(cx, JS_NO_GC); + return NULL; + } + + JS_LOCK_GC(rt); + rt->state = JSRTS_UP; + JS_NOTIFY_ALL_CONDVAR(rt->stateChange); + JS_UNLOCK_GC(rt); + } + + return cx; +} + +void +js_DestroyContext(JSContext *cx, JSGCMode gcmode) +{ + JSRuntime *rt; + JSBool last; + JSArgumentFormatMap *map; + + rt = cx->runtime; + + /* Remove cx from context list first. */ + JS_LOCK_GC(rt); + JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); + JS_REMOVE_LINK(&cx->links); + last = (rt->contextList.next == &rt->contextList); + if (last) + rt->state = JSRTS_LANDING; + 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 + + /* Unpin all pinned atoms before final GC. */ + js_UnpinPinnedAtoms(&rt->atomState); + + /* Unlock and clear GC things held by runtime pointers. */ + js_FinishRuntimeNumberState(cx); + js_FinishRuntimeStringState(cx); + + /* Clear debugging state to remove GC roots. */ + JS_ClearAllTraps(cx); + JS_ClearAllWatchPoints(cx); + } + +#if JS_HAS_REGEXPS + /* + * Remove more GC roots in regExpStatics, then collect garbage. + * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within + * XXX this function call to wait for any racing GC to complete, in the + * XXX case where JS_DestroyContext is called outside of a request on cx + */ + js_FreeRegExpStatics(cx, &cx->regExpStatics); +#endif + +#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. + * + * At this point, cx must be inaccessible to other threads. It's off the + * rt->contextList, and it should not be reachable via any object private + * data structure. + */ + while (cx->requestDepth != 0) + JS_EndRequest(cx); +#endif + + if (last) { + /* Always force, so we wait for any racing GC to finish. */ + js_ForceGC(cx, GC_LAST_CONTEXT); + + /* Iterate until no finalizer removes a GC root or lock. */ + while (rt->gcPoke) + js_GC(cx, GC_LAST_CONTEXT); + + /* Try to free atom state, now that no unrooted scripts survive. */ + if (rt->atomState.liveAtoms == 0) + js_FreeAtomState(cx, &rt->atomState); + + /* 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); + JS_UNLOCK_GC(rt); + } else { + if (gcmode == JS_FORCE_GC) + js_ForceGC(cx, 0); + else if (gcmode == JS_MAYBE_GC) + JS_MaybeGC(cx); + } + + /* Free the stuff hanging off of cx. */ + JS_FinishArenaPool(&cx->stackPool); + JS_FinishArenaPool(&cx->tempPool); + if (cx->lastMessage) + free(cx->lastMessage); + + /* Remove any argument formatters. */ + map = cx->argumentFormatMap; + while (map) { + JSArgumentFormatMap *temp = map; + map = map->next; + JS_free(cx, temp); + } + + /* Destroy the resolve recursion damper. */ + if (cx->resolvingTable) { + JS_DHashTableDestroy(cx->resolvingTable); + cx->resolvingTable = NULL; + } + + /* Finally, free cx itself. */ + 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->links) + 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); + if (!cx) + cx = (JSContext *)&rt->contextList; + cx = (JSContext *)cx->links.next; + if (&cx->links == &rt->contextList) + cx = NULL; + *iterp = cx; + if (unlocked) + JS_UNLOCK_GC(rt); + return cx; +} + +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. + */ + if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) + reportp->flags |= JSREPORT_EXCEPTION; + +#if JS_HAS_ERROR_EXCEPTIONS + /* + * 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_ErrorToException(cx, message, reportp)) { + js_ReportErrorAgain(cx, message, reportp); + } else if (cx->runtime->debugErrorHook && cx->errorReporter) { + JSDebugErrorHook hook = cx->runtime->debugErrorHook; + /* test local in case debugErrorHook changed on another thread */ + if (hook) + hook(cx, message, reportp, cx->runtime->debugErrorHookData); + } +#else + js_ReportErrorAgain(cx, message, reportp); +#endif +} + +/* + * 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, JSErrorCallback callback) +{ + JSStackFrame *fp; + JSErrorReport report; + JSErrorReporter onError = cx->errorReporter; + + /* Get the message for this error, but we won't expand any arguments. */ + const JSErrorFormatString *efs = callback(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; + + /* + * Walk stack until we find a frame that is associated with some script + * rather than a native frame. + */ + for (fp = cx->fp; fp; fp = fp->down) { + if (fp->script && fp->pc) { + report.filename = fp->script->filename; + report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + break; + } + } + + /* + * 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->runtime->debugErrorHook; + if (hook && + !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) { + onError = NULL; + } + } + + if (onError) + onError(cx, msg, &report); +} + +JSBool +js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) +{ + char *last; + JSStackFrame *fp; + JSErrorReport report; + JSBool warning; + + if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) + return JS_TRUE; + + last = JS_vsmprintf(format, ap); + if (!last) + return JS_FALSE; + + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = flags; + + /* Find the top-most active script frame, for best line number blame. */ + for (fp = cx->fp; fp; fp = fp->down) { + if (fp->script && fp->pc) { + report.filename = fp->script->filename; + report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + break; + } + } + + warning = JSREPORT_IS_WARNING(report.flags); + if (warning && JS_HAS_WERROR_OPTION(cx)) { + report.flags &= ~JSREPORT_WARNING; + warning = JS_FALSE; + } + + ReportError(cx, last, &report); + free(last); + 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, + JSBool *warningp, JSBool charArgs, va_list ap) +{ + const JSErrorFormatString *efs; + int i; + int argCount; + + *warningp = JSREPORT_IS_WARNING(reportp->flags); + if (*warningp && JS_HAS_WERROR_OPTION(cx)) { + reportp->flags &= ~JSREPORT_WARNING; + *warningp = JS_FALSE; + } + + *messagep = NULL; + if (callback) { + 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 **) + JS_malloc(cx, 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 *); + reportp->messageArgs[i] + = js_InflateString(cx, charArg, strlen(charArg)); + 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) { + const char *fmt; + const jschar *arg; + jschar *out; + int expandedArgs = 0; + size_t expandedLength + = strlen(efs->format) + - (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 *) + JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); + if (!out) + goto error; + fmt = efs->format; + while (*fmt) { + if (*fmt == '{') { + if (isdigit(fmt[1])) { + int d = JS7_UNDEC(fmt[1]); + JS_ASSERT(expandedArgs < argCount); + arg = reportp->messageArgs[d]; + js_strncpy(out, arg, argLengths[d]); + out += argLengths[d]; + fmt += 3; + expandedArgs++; + continue; + } + } + /* + * is this kosher? + */ + *out++ = (unsigned char)(*fmt++); + } + JS_ASSERT(expandedArgs == argCount); + *out = 0; + *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) { + *messagep = JS_strdup(cx, efs->format); + if (!*messagep) + goto error; + reportp->ucmessage + = js_InflateString(cx, *messagep, strlen(*messagep)); + 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 *)JS_malloc(cx, nbytes); + if (!*messagep) + goto error; + JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); + } + return JS_TRUE; + +error: + if (reportp->messageArgs) { + i = 0; + while (reportp->messageArgs[i]) + JS_free(cx, (void *)reportp->messageArgs[i++]); + JS_free(cx, (void *)reportp->messageArgs); + reportp->messageArgs = NULL; + } + if (reportp->ucmessage) { + JS_free(cx, (void *)reportp->ucmessage); + reportp->ucmessage = NULL; + } + if (*messagep) { + JS_free(cx, (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) +{ + JSStackFrame *fp; + JSErrorReport report; + char *message; + JSBool warning; + + if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) + return JS_TRUE; + + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = flags; + report.errorNumber = errorNumber; + + /* + * If we can't find out where the error was based on the current frame, + * see if the next frame has a script/pc combo we can use. + */ + for (fp = cx->fp; fp; fp = fp->down) { + if (fp->script && fp->pc) { + report.filename = fp->script->filename; + report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + break; + } + } + + if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, + &message, &report, &warning, charArgs, ap)) { + return JS_FALSE; + } + + ReportError(cx, message, &report); + + if (message) + JS_free(cx, message); + if (report.messageArgs) { + int i = 0; + while (report.messageArgs[i]) + JS_free(cx, (void *)report.messageArgs[i++]); + JS_free(cx, (void *)report.messageArgs); + } + if (report.ucmessage) + JS_free(cx, (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) + 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->runtime->debugErrorHook; + if (hook && + !hook(cx, cx->lastMessage, reportp, + cx->runtime->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); +} + +#if defined DEBUG && defined XP_UNIX +/* For gdb usage. */ +void js_traceon(JSContext *cx) { cx->tracefp = stderr; } +void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } +#endif + +JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { +#if JS_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 "js.msg" +#undef MSG_DEF +}; + +const JSErrorFormatString * +js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) +{ + if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) + return &js_ErrorFormatString[errorNumber]; + return NULL; +} diff --git a/src/extension/script/js/jscntxt.h b/src/extension/script/js/jscntxt.h new file mode 100644 index 000000000..f9aab41a8 --- /dev/null +++ b/src/extension/script/js/jscntxt.h @@ -0,0 +1,496 @@ +/* -*- 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 jscntxt_h___ +#define jscntxt_h___ +/* + * JS execution context. + */ +#include "jsarena.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jslong.h" +#include "jsatom.h" +#include "jsconfig.h" +#include "jsdhash.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsregexp.h" + +JS_BEGIN_EXTERN_C + +typedef enum JSGCMode { JS_NO_GC, JS_MAYBE_GC, JS_FORCE_GC } JSGCMode; + +typedef enum JSRuntimeState { + JSRTS_DOWN, + JSRTS_LAUNCHING, + JSRTS_UP, + JSRTS_LANDING +} JSRuntimeState; + +typedef struct JSPropertyTreeEntry { + JSDHashEntryHdr hdr; + JSScopeProperty *child; +} JSPropertyTreeEntry; + +struct JSRuntime { + /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ + JSRuntimeState state; + + /* Garbage collector state, used by jsgc.c. */ + JSArenaPool gcArenaPool; + JSDHashTable gcRootsHash; + JSDHashTable *gcLocksHash; + JSGCThing *gcFreeList; + jsrefcount gcKeepAtoms; + uint32 gcBytes; + uint32 gcLastBytes; + uint32 gcMaxBytes; + uint32 gcLevel; + uint32 gcNumber; + JSPackedBool gcPoke; + JSPackedBool gcRunning; + JSGCCallback gcCallback; + uint32 gcMallocBytes; +#ifdef JS_GCMETER + JSGCStats gcStats; +#endif + + /* Literal table maintained by jsatom.c functions. */ + JSAtomState atomState; + + /* Random number generator state, used by jsmath.c. */ + JSBool rngInitialized; + int64 rngMultiplier; + int64 rngAddend; + int64 rngMask; + int64 rngSeed; + jsdouble rngDscale; + + /* Well-known numbers held for use by this runtime's contexts. */ + jsdouble *jsNaN; + jsdouble *jsNegativeInfinity; + jsdouble *jsPositiveInfinity; + + /* Empty string held for use by this runtime's contexts. */ + JSString *emptyString; + + /* List of active contexts sharing this runtime; protected by gcLock. */ + JSCList contextList; + + /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */ + JSTrapHandler interruptHandler; + void *interruptHandlerData; + JSNewScriptHook newScriptHook; + void *newScriptHookData; + JSDestroyScriptHook destroyScriptHook; + void *destroyScriptHookData; + JSTrapHandler debuggerHandler; + void *debuggerHandlerData; + JSSourceHandler sourceHandler; + void *sourceHandlerData; + JSInterpreterHook executeHook; + void *executeHookData; + JSInterpreterHook callHook; + void *callHookData; + JSObjectHook objectHook; + void *objectHookData; + JSTrapHandler throwHook; + void *throwHookData; + JSDebugErrorHook debugErrorHook; + void *debugErrorHookData; + + /* More debugging state, see jsdbgapi.c. */ + JSCList trapList; + JSCList watchPointList; + + /* Weak links to properties, indexed by quickened get/set opcodes. */ + /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */ + JSPropertyCache propertyCache; + + /* Client opaque pointer */ + void *data; + +#ifdef JS_THREADSAFE + /* These combine to interlock the GC and new requests. */ + PRLock *gcLock; + PRCondVar *gcDone; + PRCondVar *requestDone; + uint32 requestCount; + jsword 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; + + /* Used to serialize cycle checks when setting __proto__ or __parent__. */ + PRLock *setSlotLock; + PRCondVar *setSlotDone; + JSBool setSlotBusy; + JSScope *setSlotScope; /* deadlock avoidance, see jslock.c */ + + /* + * State for sharing single-threaded scopes, once a second thread tries to + * lock a scope. The scopeSharingDone condvar is protected by rt->gcLock, + * to minimize number of locks taken in JS_EndRequest. + * + * The scopeSharingTodo linked list is likewise "global" per runtime, not + * one-list-per-context, to conserve space over all contexts, optimizing + * for the likely case that scopes become shared rarely, and among a very + * small set of threads (contexts). + */ + PRCondVar *scopeSharingDone; + JSScope *scopeSharingTodo; + +/* + * Magic terminator for the rt->scopeSharingTodo linked list, threaded through + * scope->u.link. This hack allows us to test whether a scope is on the list + * by asking whether scope->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_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef) +#endif /* JS_THREADSAFE */ + + /* + * Check property accessibility for objects of arbitrary class. Used at + * present to check f.caller accessibility for any function object f. + */ + JSCheckAccessOp checkObjectAccess; + + /* Security principals serialization support. */ + JSPrincipalsTranscoder principalsTranscoder; + + /* Shared scope property tree, and allocator for its nodes. */ + JSDHashTable propertyTreeHash; + JSScopeProperty *propertyFreeList; + JSArenaPool propertyArenaPool; + +#ifdef DEBUG + /* Function invocation metering. */ + jsrefcount inlineCalls; + jsrefcount nativeCalls; + jsrefcount nonInlineCalls; + jsrefcount constructs; + + /* Scope lock and property metering. */ + jsrefcount claimAttempts; + jsrefcount claimedScopes; + jsrefcount deadContexts; + jsrefcount deadlocksAvoided; + jsrefcount liveScopes; + jsrefcount sharedScopes; + jsrefcount totalScopes; + jsrefcount badUndependStrings; + jsrefcount liveScopeProps; + jsrefcount totalScopeProps; + jsrefcount livePropTreeNodes; + jsrefcount duplicatePropTreeNodes; + jsrefcount totalPropTreeNodes; + jsrefcount propTreeKidsChunks; + jsrefcount middleDeleteFixups; + + /* String instrumentation. */ + jsrefcount liveStrings; + jsrefcount totalStrings; + jsrefcount liveDependentStrings; + jsrefcount totalDependentStrings; + double lengthSum; + double lengthSquaredSum; + double strdepLengthSum; + double strdepLengthSquaredSum; +#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 */ + +struct JSContext { + JSCList links; + + /* Interpreter activation count. */ + uintN interpLevel; + + /* Limit pointer for checking stack consumption during recursion. */ + jsuword stackLimit; + + /* Runtime version control identifier and equality operators. */ + JSVersion version; + jsbytecode jsop_eq; + jsbytecode jsop_ne; + + /* Data shared by threads in an address space. */ + JSRuntime *runtime; + + /* Stack arena pool and frame pointer register. */ + JSArenaPool stackPool; + 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; + + /* Most recently created things by type, members of the GC's root set. */ + JSGCThing *newborn[GCX_NTYPES]; + + /* Atom root for the last-looked-up atom on this context. */ + JSAtom *lastAtom; + + /* Regular expression class statics (XXX not shared globally). */ + JSRegExpStatics regExpStatics; + + /* State for object and array toSource conversion. */ + JSSharpObjectMap sharpObjectMap; + + /* 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; +#endif + + /* Per-context optional user callbacks. */ + JSBranchCallback branchCallback; + JSErrorReporter errorReporter; + + /* Client opaque pointer */ + void *data; + + /* GC and thread-safe state. */ + JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ +#ifdef JS_THREADSAFE + jsword thread; + jsrefcount requestDepth; + JSScope *scopeToShare; /* weak reference, see jslock.c */ + JSScope *lockedSealedScope; /* weak ref, for low-cost sealed + scope locking */ +#endif + +#if JS_HAS_LVALUE_RETURN + /* + * Secondary return value from native method called on the left-hand side + * of an assignment operator. The native should store the object in which + * to set a property in *rval, and return the property's id expressed as a + * jsval by calling JS_SetCallReturnValue2(cx, idval). + */ + jsval rval2; + JSPackedBool rval2set; +#endif + + /* + * True if creating an exception object, to prevent runaway recursion. + * NB: creatingException packs with rval2set, #if JS_HAS_LVALUE_RETURN, + * and with throwing, below. + */ + JSPackedBool creatingException; + + /* + * Exception state -- the exception member is a GC root by definition. + * NB: throwing packs with creatingException and rval2set, above. + */ + JSPackedBool throwing; /* is there a pending exception? */ + jsval exception; /* most-recently-thrown exception */ + + /* 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; + + /* PDL of stack headers describing stack slots not rooted by argv, etc. */ + JSStackHeader *stackHeaders; + + /* Optional hook to find principals for an object being accessed on cx. */ + JSObjectPrincipalsFinder findObjectPrincipals; +}; + +/* Slightly more readable macros, also to hide bitset implementation detail. */ +#define JS_HAS_STRICT_OPTION(cx) ((cx)->options & JSOPTION_STRICT) +#define JS_HAS_WERROR_OPTION(cx) ((cx)->options & JSOPTION_WERROR) + +extern JSContext * +js_NewContext(JSRuntime *rt, size_t stackChunkSize); + +extern void +js_DestroyContext(JSContext *cx, JSGCMode gcmode); + +/* + * Return true if cx points to a context in rt->contextList, else return false. + * NB: the caller (see jslock.c:ClaimScope) must hold rt->gcLock. + */ +extern JSBool +js_ValidContextPointer(JSRuntime *rt, JSContext *cx); + +/* + * 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); + +/* + * 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 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, + JSBool *warningp, JSBool charArgs, va_list ap); +#endif + +extern void +js_ReportOutOfMemory(JSContext *cx, JSErrorCallback errorCallback); + +/* + * 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); + +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 + +JS_END_EXTERN_C + +#endif /* jscntxt_h___ */ diff --git a/src/extension/script/js/jscompat.h b/src/extension/script/js/jscompat.h new file mode 100644 index 000000000..6ba036a5e --- /dev/null +++ b/src/extension/script/js/jscompat.h @@ -0,0 +1,57 @@ +/* ***** 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 ***** */ + +/* -*- Mode: C; tab-width: 8 -*- + * Copyright © 1996-1999 Netscape Communications Corporation, All Rights Reserved. + */ +#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/src/extension/script/js/jsconfig.h b/src/extension/script/js/jsconfig.h new file mode 100644 index 000000000..fae86d3a7 --- /dev/null +++ b/src/extension/script/js/jsconfig.h @@ -0,0 +1,489 @@ +/* -*- 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 150 +#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 + * ^ ^ + * | | + * basis for ECMAv1 close to ECMAv2 + * + * where ECMAv3 stands for ECMA-262 Edition 3. 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 unlikely event that SpiderMonkey ever implements JavaScript 2.0, or + * ECMA-262 Edition 4 (JS2 without certain extensions), 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. + */ +#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_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, array.pop, etc */ +#define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has fun.apply(obj, argArray) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#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_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 1 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 1 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 1 /* numbers have formatting methods */ +#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_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 100 + +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 1 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 0 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 0 /* has fun.apply(obj, argArray) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#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_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 110 + +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 1 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 1 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 1 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 0 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#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_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 120 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 1 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#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_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#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_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 130 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#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_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#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_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 140 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#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_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* rt errors reflected as exceptions */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#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_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ + +#elif JS_VERSION == 150 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#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_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#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_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 1 /* rt errors reflected as exceptions */ +#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 1 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 1 /* numbers have formatting methods */ +#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_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ + +#else + +#error "unknown JS_VERSION" + +#endif diff --git a/src/extension/script/js/jscpucfg.c b/src/extension/script/js/jscpucfg.c new file mode 100644 index 000000000..aa6c04c4d --- /dev/null +++ b/src/extension/script/js/jscpucfg.c @@ -0,0 +1,377 @@ +/* -*- 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 +#define INT64 PRInt64 +#else + +#ifdef __MWERKS__ +#define XP_MAC 1 +#endif + +/************************************************************************/ + +/* Generate cpucfg.h */ +#ifdef XP_MAC +#include +#define INT64 UnsignedWide +#else +#if defined(XP_WIN) || defined(XP_OS2) +#ifdef WIN32 +#if defined(__GNUC__) +#define INT64 long long +#else +#define INT64 _int64 +#endif /* __GNUC__ */ +#else +#define INT64 long +#endif +#else +#if defined(HPUX) || defined(__QNX__) || defined(_SCO_DS) || defined(UNIXWARE) +#define INT64 long +#else +#define INT64 long long +#endif +#endif +#endif + +#endif /* CROSS_COMPILE */ + +typedef void *prword; + +struct align_short { + char c; + short a; +}; +struct align_int { + char c; + int a; +}; +struct align_long { + char c; + long a; +}; +struct align_int64 { + char c; + INT64 a; +}; +struct align_fakelonglong { + char c; + struct { + long hi, lo; + } a; +}; +struct align_float { + char c; + float a; +}; +struct align_double { + char c; + double a; +}; +struct align_pointer { + char c; + void *a; +}; +struct align_prword { + char c; + prword a; +}; + +#define ALIGN_OF(type) \ + (((char*)&(((struct align_##type *)0)->a)) - ((char*)0)) + +unsigned int bpb; + +static int Log2(unsigned int n) +{ + int log2 = 0; + + if (n & (n-1)) + log2++; + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; + return log2; +} + +/* + * Conceivably this could actually be used, but there is lots of code out + * there with ands and shifts in it that assumes a byte is exactly 8 bits, + * so forget about porting THIS code to all those non 8 bit byte machines. + */ +static void BitsPerByte(void) +{ + bpb = 8; +} + +static int StackGrowthDirection(int *dummy1addr) +{ + int dummy2; + + return (&dummy2 < dummy1addr) ? -1 : 1; +} + +int main(int argc, char **argv) +{ + int sizeof_char, sizeof_short, sizeof_int, sizeof_int64, sizeof_long, + sizeof_float, sizeof_double, sizeof_word, sizeof_dword; + int bits_per_int64_log2, align_of_short, align_of_int, align_of_long, + align_of_int64, align_of_float, align_of_double, align_of_pointer, + align_of_word; + int dummy1; + + BitsPerByte(); + + printf("#ifndef js_cpucfg___\n"); + printf("#define js_cpucfg___\n\n"); + + printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); + +#ifdef CROSS_COMPILE +#if 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 + + sizeof_char = PR_BYTES_PER_BYTE; + sizeof_short = PR_BYTES_PER_SHORT; + sizeof_int = PR_BYTES_PER_INT; + sizeof_int64 = PR_BYTES_PER_INT64; + sizeof_long = PR_BYTES_PER_LONG; + sizeof_float = PR_BYTES_PER_FLOAT; + sizeof_double = PR_BYTES_PER_DOUBLE; + sizeof_word = PR_BYTES_PER_WORD; + sizeof_dword = PR_BYTES_PER_DWORD; + + bits_per_int64_log2 = PR_BITS_PER_INT64_LOG2; + + align_of_short = PR_ALIGN_OF_SHORT; + align_of_int = PR_ALIGN_OF_INT; + align_of_long = PR_ALIGN_OF_LONG; + align_of_int64 = PR_ALIGN_OF_INT64; + align_of_float = PR_ALIGN_OF_FLOAT; + align_of_double = PR_ALIGN_OF_DOUBLE; + align_of_pointer = PR_ALIGN_OF_POINTER; + align_of_word = PR_ALIGN_OF_WORD; + +#else /* !CROSS_COMPILE */ + + /* + * 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; + } + } + + sizeof_char = sizeof(char); + sizeof_short = sizeof(short); + sizeof_int = sizeof(int); + sizeof_int64 = 8; + sizeof_long = sizeof(long); + sizeof_float = sizeof(float); + sizeof_double = sizeof(double); + sizeof_word = sizeof(prword); + sizeof_dword = 8; + + bits_per_int64_log2 = 6; + + align_of_short = ALIGN_OF(short); + align_of_int = ALIGN_OF(int); + align_of_long = ALIGN_OF(long); + if (sizeof(INT64) < 8) { + /* this machine doesn't actually support int64's */ + align_of_int64 = ALIGN_OF(fakelonglong); + } else { + align_of_int64 = ALIGN_OF(int64); + } + align_of_float = ALIGN_OF(float); + align_of_double = ALIGN_OF(double); + align_of_pointer = ALIGN_OF(pointer); + align_of_word = ALIGN_OF(prword); + +#endif /* CROSS_COMPILE */ + + printf("#define JS_BYTES_PER_BYTE %dL\n", sizeof_char); + printf("#define JS_BYTES_PER_SHORT %dL\n", sizeof_short); + printf("#define JS_BYTES_PER_INT %dL\n", sizeof_int); + printf("#define JS_BYTES_PER_INT64 %dL\n", sizeof_int64); + printf("#define JS_BYTES_PER_LONG %dL\n", sizeof_long); + printf("#define JS_BYTES_PER_FLOAT %dL\n", sizeof_float); + printf("#define JS_BYTES_PER_DOUBLE %dL\n", sizeof_double); + printf("#define JS_BYTES_PER_WORD %dL\n", sizeof_word); + printf("#define JS_BYTES_PER_DWORD %dL\n", sizeof_dword); + printf("\n"); + + printf("#define JS_BITS_PER_BYTE %dL\n", bpb); + printf("#define JS_BITS_PER_SHORT %dL\n", bpb * sizeof_short); + printf("#define JS_BITS_PER_INT %dL\n", bpb * sizeof_int); + printf("#define JS_BITS_PER_INT64 %dL\n", bpb * sizeof_int64); + printf("#define JS_BITS_PER_LONG %dL\n", bpb * sizeof_long); + printf("#define JS_BITS_PER_FLOAT %dL\n", bpb * sizeof_float); + printf("#define JS_BITS_PER_DOUBLE %dL\n", bpb * sizeof_double); + printf("#define JS_BITS_PER_WORD %dL\n", bpb * sizeof_word); + printf("\n"); + + printf("#define JS_BITS_PER_BYTE_LOG2 %dL\n", Log2(bpb)); + printf("#define JS_BITS_PER_SHORT_LOG2 %dL\n", Log2(bpb * sizeof_short)); + printf("#define JS_BITS_PER_INT_LOG2 %dL\n", Log2(bpb * sizeof_int)); + printf("#define JS_BITS_PER_INT64_LOG2 %dL\n", bits_per_int64_log2); + printf("#define JS_BITS_PER_LONG_LOG2 %dL\n", Log2(bpb * sizeof_long)); + printf("#define JS_BITS_PER_FLOAT_LOG2 %dL\n", Log2(bpb * sizeof_float)); + printf("#define JS_BITS_PER_DOUBLE_LOG2 %dL\n", Log2(bpb * sizeof_double)); + printf("#define JS_BITS_PER_WORD_LOG2 %dL\n", Log2(bpb * sizeof_word)); + printf("\n"); + + printf("#define JS_ALIGN_OF_SHORT %dL\n", align_of_short); + printf("#define JS_ALIGN_OF_INT %dL\n", align_of_int); + printf("#define JS_ALIGN_OF_LONG %dL\n", align_of_long); + printf("#define JS_ALIGN_OF_INT64 %dL\n", align_of_int64); + printf("#define JS_ALIGN_OF_FLOAT %dL\n", align_of_float); + printf("#define JS_ALIGN_OF_DOUBLE %dL\n", align_of_double); + printf("#define JS_ALIGN_OF_POINTER %dL\n", align_of_pointer); + printf("#define JS_ALIGN_OF_WORD %dL\n", align_of_word); + printf("\n"); + + printf("#define JS_BYTES_PER_WORD_LOG2 %dL\n", Log2(sizeof_word)); + printf("#define JS_BYTES_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword)); + printf("#define JS_WORDS_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword/sizeof_word)); + printf("\n"); + + printf("#define JS_STACK_GROWTH_DIRECTION (%d)\n", StackGrowthDirection(&dummy1)); + printf("\n"); + + printf("#endif /* js_cpucfg___ */\n"); + + return EXIT_SUCCESS; +} + diff --git a/src/extension/script/js/jscpucfg.h b/src/extension/script/js/jscpucfg.h new file mode 100644 index 000000000..e93f5531d --- /dev/null +++ b/src/extension/script/js/jscpucfg.h @@ -0,0 +1,200 @@ +/* -*- 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___ + +#include "jsosdep.h" + +#ifdef XP_MAC +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 2L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L + +#elif defined(XP_WIN) || defined(XP_OS2) + +#if defined( _WIN32) || defined(XP_OS2) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 4L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 32L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 5L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 4L +#define JS_ALIGN_OF_LONG 4L +#define JS_ALIGN_OF_INT64 8L +#define JS_ALIGN_OF_FLOAT 4L +#define JS_ALIGN_OF_DOUBLE 4L +#define JS_ALIGN_OF_POINTER 4L +#define JS_ALIGN_OF_WORD 4L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L +#endif /* _WIN32 || XP_OS2 */ + +#if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define JS_BYTES_PER_BYTE 1L +#define JS_BYTES_PER_SHORT 2L +#define JS_BYTES_PER_INT 2L +#define JS_BYTES_PER_INT64 8L +#define JS_BYTES_PER_LONG 4L +#define JS_BYTES_PER_FLOAT 4L +#define JS_BYTES_PER_DOUBLE 8L +#define JS_BYTES_PER_WORD 4L +#define JS_BYTES_PER_DWORD 8L + +#define JS_BITS_PER_BYTE 8L +#define JS_BITS_PER_SHORT 16L +#define JS_BITS_PER_INT 16L +#define JS_BITS_PER_INT64 64L +#define JS_BITS_PER_LONG 32L +#define JS_BITS_PER_FLOAT 32L +#define JS_BITS_PER_DOUBLE 64L +#define JS_BITS_PER_WORD 32L + +#define JS_BITS_PER_BYTE_LOG2 3L +#define JS_BITS_PER_SHORT_LOG2 4L +#define JS_BITS_PER_INT_LOG2 4L +#define JS_BITS_PER_INT64_LOG2 6L +#define JS_BITS_PER_LONG_LOG2 5L +#define JS_BITS_PER_FLOAT_LOG2 5L +#define JS_BITS_PER_DOUBLE_LOG2 6L +#define JS_BITS_PER_WORD_LOG2 5L + +#define JS_ALIGN_OF_SHORT 2L +#define JS_ALIGN_OF_INT 2L +#define JS_ALIGN_OF_LONG 2L +#define JS_ALIGN_OF_INT64 2L +#define JS_ALIGN_OF_FLOAT 2L +#define JS_ALIGN_OF_DOUBLE 2L +#define JS_ALIGN_OF_POINTER 2L +#define JS_ALIGN_OF_WORD 2L + +#define JS_BYTES_PER_WORD_LOG2 2L +#define JS_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L +#endif /* defined(_WINDOWS) && !defined(_WIN32) */ + +#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_MAC, 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/src/extension/script/js/jsdate.c b/src/extension/script/js/jsdate.c new file mode 100644 index 000000000..a88df1830 --- /dev/null +++ b/src/extension/script/js/jsdate.c @@ -0,0 +1,2234 @@ +/* -*- 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 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 "jsstddef.h" +#include +#include +#include +#include +#include "jstypes.h" +#include "jsprf.h" +#include "prmjtime.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsconfig.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][12] = { + {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.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} +}; + +#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]; + +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; +} + +/* 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; + + /* 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; +} + + +#define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay) + +#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 MakeTime(hour, min, sec, ms) \ +(((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms) + +static jsdouble +MakeDay(jsdouble year, jsdouble month, jsdouble date) +{ + jsdouble result; + 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); + + result = yearday + + monthday + + date - 1; + return result; +} + +#define MakeDate(day, time) (day * msPerDay + time) + +#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ + && !((d < 0 ? -d : d) > HalfTimeDomain)) \ + ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) + +/** + * end of ECMA 'support' functions + */ + +/* + * Other Support routines and definitions + */ + +static JSClass date_class = { + js_Date_str, + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + 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; +} + +/* + * See ECMA 15.9.4.[3-10]; + */ +/* XXX this function must be above date_parseString to avoid a + horrid bug in the Win16 1.52 compiler */ +#define MAXARGS 7 +static JSBool +date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble array[MAXARGS]; + uintN loop; + jsdouble d; + + for (loop = 0; loop < MAXARGS; loop++) { + if (loop < argc) { + if (!js_ValueToNumber(cx, argv[loop], &d)) + return JS_FALSE; + /* return NaN if any arg is NaN */ + if (!JSDOUBLE_IS_FINITE(d)) { + return js_NewNumberValue(cx, d, rval); + } + array[loop] = floor(d); + } else { + array[loop] = 0; + } + } + + /* adjust 2-digit years into the 20th century */ + if (array[0] >= 0 && array[0] <= 99) + array[0] += 1900; + + /* if we got a 0 for 'date' (which is out of range) + * pretend it's a 1. (So Date.UTC(1972, 5) works) */ + if (array[2] < 1) + array[2] = 1; + + d = date_msecFromDate(array[0], array[1], array[2], + array[3], array[4], array[5], array[6]); + d = TIMECLIP(d); + + return js_NewNumberValue(cx, d, rval); +} + +static JSBool +date_parseString(JSString *str, jsdouble *result) +{ + jsdouble msec; + + const jschar *s = JSSTRING_CHARS(str); + size_t limit = JSSTRING_LENGTH(str); + 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; + jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */ + int prevc = 0; + JSBool seenplusminus = JS_FALSE; + + 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 (n >= 70 || + (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) { + if (year >= 0) + goto syntax; + else if (c <= ' ' || c == ',' || c == '/' || i >= limit) + year = n < 100 ? n + 1900 : 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 == '/') { + if (mon < 0) + mon = /*byte*/ n-1; + else if (mday < 0) + mday = /*byte*/ n; + else + goto syntax; + } else if (i < limit && 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 (min >= 0 && sec < 0) { + sec = /*byte*/ n; + } else if (mday < 0) { + mday = /*byte*/ 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 = (sizeof(wtb)/sizeof(char*)); --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! */ + if (mon < 0) { + mon = /*byte*/ (action - 2); + } else { + goto syntax; + } + } else { + tzoffset = action - 10000; + } + } + break; + } + if (k < 0) + goto syntax; + prevc = 0; + } + } + if (year < 0 || mon < 0 || mday < 0) + goto syntax; + if (sec < 0) + sec = 0; + if (min < 0) + min = 0; + if (hour < 0) + hour = 0; + if (tzoffset == -1) { /* no time zone specified, have to use local */ + jsdouble msec_time; + msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); + + *result = UTC(msec_time); + return JS_TRUE; + } + + msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); + msec += tzoffset * msPerMinute; + *result = msec; + return JS_TRUE; + +syntax: + /* syntax error */ + *result = 0; + return JS_FALSE; +} + +static JSBool +date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble result; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + if (!date_parseString(str, &result)) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + + result = TIMECLIP(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + int64 us, ms, us2ms; + jsdouble msec_time; + + us = PRMJ_Now(); + JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); + JSLL_DIV(ms, us, us2ms); + JSLL_L2D(msec_time, ms); + + return js_NewDoubleValue(cx, msec_time, rval); +} + +/* + * Check that obj is an object of class Date, and get the date value. + * Return NULL on failure. + */ +static jsdouble * +date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) +{ + if (!JS_InstanceOf(cx, obj, &date_class, argv)) + return NULL; + return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); +} + +/* + * See ECMA 15.9.5.4 thru 15.9.5.23 + */ +static JSBool +date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + return js_NewNumberValue(cx, *date, rval); +} + +static JSBool +date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = YearFromTime(LocalTime(result)); + + /* + * During the great date rewrite of 1.3, we tried to track the evolving ECMA + * standard, which then had a definition of getYear which always subtracted + * 1900. Which we implemented, not realizing that it was incompatible with + * the old behavior... now, rather than thrash the behavior yet again, + * we've decided to leave it with the - 1900 behavior and point people to + * the getFullYear method. But we try to protect existing scripts that + * have specified a version... + */ + if (cx->version == JSVERSION_1_0 || + cx->version == JSVERSION_1_1 || + cx->version == JSVERSION_1_2) + { + if (result >= 1900 && result < 2000) + result -= 1900; + } else { + result -= 1900; + } + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = YearFromTime(LocalTime(result)); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = YearFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = MonthFromTime(LocalTime(result)); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = MonthFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = LocalTime(result); + result = DateFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = DateFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = LocalTime(result); + result = WeekDay(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = WeekDay(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = HourFromTime(LocalTime(result)); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = HourFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = MinFromTime(LocalTime(result)); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = MinFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +/* Date.getSeconds is mapped to getUTCSeconds */ + +static JSBool +date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = SecFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +/* Date.getMilliseconds is mapped to getUTCMilliseconds */ + +static JSBool +date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + result = msFromTime(result); + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + result = *date; + + /* + * 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 = (result - LocalTime(result)) / msPerMinute; + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble result; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + if (!js_ValueToNumber(cx, argv[0], &result)) + return JS_FALSE; + + result = TIMECLIP(result); + + *date = result; + return js_NewNumberValue(cx, result, rval); +} + +static JSBool +date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + uintN maxargs, JSBool local, jsval *rval) +{ + 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; + + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + result = *date; + + /* just return NaN if the date is already NaN */ + if (!JSDOUBLE_IS_FINITE(result)) + return js_NewNumberValue(cx, result, rval); + + /* 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) + argc = 1; /* should be safe, because length of all setters is 1 */ + else if (argc > maxargs) + argc = maxargs; /* clamp argc */ + + for (i = 0; i < argc; i++) { + if (!js_ValueToNumber(cx, argv[i], &args[i])) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(args[i])) { + *date = *cx->runtime->jsNaN; + return js_NewNumberValue(cx, *date, rval); + } + 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); */ + + *date = TIMECLIP(result); + return js_NewNumberValue(cx, *date, rval); +} + +static JSBool +date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval); +} + +static JSBool +date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval); +} + +static JSBool +date_setSeconds(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval); +} + +static JSBool +date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval); +} + +static JSBool +date_setMinutes(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval); +} + +static JSBool +date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval); +} + +static JSBool +date_setHours(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval); +} + +static JSBool +date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval); +} + +static JSBool +date_makeDate(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, uintN maxargs, JSBool local, jsval *rval) +{ + uintN i; + jsdouble lorutime; /* local or UTC version of *date */ + jsdouble args[3], *argp, *stop; + jsdouble year, month, day; + jsdouble result; + + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + result = *date; + + /* see complaint about ECMA in date_MakeTime */ + if (argc == 0) + argc = 1; /* should be safe, because length of all setters is 1 */ + else if (argc > maxargs) + argc = maxargs; /* clamp argc */ + + for (i = 0; i < argc; i++) { + if (!js_ValueToNumber(cx, argv[i], &args[i])) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(args[i])) { + *date = *cx->runtime->jsNaN; + return js_NewNumberValue(cx, *date, rval); + } + 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 (argc < 3) + return js_NewNumberValue(cx, result, rval); + else + lorutime = +0.; + } else { + if (local) + lorutime = LocalTime(result); + else + lorutime = 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); + + *date = TIMECLIP(result); + return js_NewNumberValue(cx, *date, rval); +} + +static JSBool +date_setDate(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval); +} + +static JSBool +date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval); +} + +static JSBool +date_setMonth(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval); +} + +static JSBool +date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval); +} + +static JSBool +date_setFullYear(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval); +} + +static JSBool +date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval); +} + +static JSBool +date_setYear(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + jsdouble t; + jsdouble year; + jsdouble day; + jsdouble result; + + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + result = *date; + + if (!js_ValueToNumber(cx, argv[0], &year)) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(year)) { + *date = *cx->runtime->jsNaN; + return js_NewNumberValue(cx, *date, rval); + } + + year = js_DoubleToInteger(year); + + if (!JSDOUBLE_IS_FINITE(result)) { + t = +0.0; + } else { + t = LocalTime(result); + } + + if (year >= 0 && year <= 99) + year += 1900; + + day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + result = MakeDate(day, TimeWithinDay(t)); + result = UTC(result); + + *date = TIMECLIP(result); + return js_NewNumberValue(cx, *date, rval); +} + +/* 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" +}; + +static JSBool +date_toGMTString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + char buf[100]; + JSString *str; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + if (!JSDOUBLE_IS_FINITE(*date)) { + JS_snprintf(buf, sizeof buf, js_NaN_date_str); + } else { + jsdouble temp = *date; + + /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it + * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. + */ + JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", + days[WeekDay(temp)], + DateFromTime(temp), + months[MonthFromTime(temp)], + YearFromTime(temp), + HourFromTime(temp), + MinFromTime(temp), + SecFromTime(temp)); + } + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* for Date.toLocaleString; interface to PRMJTime date struct. + * If findEquivalent is true, then try to map the year to an equivalent year + * that's in range. + */ +static void +new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent) +{ + jsint year = YearFromTime(timeval); + int16 adjustedYear; + + /* If the year doesn't fit in a PRMJTime, find something to do about it. */ + if (year > 32767 || year < -32768) { + if (findEquivalent) { + /* We're really just trying to get a timezone string; map the year + * to some equivalent year in the range 0 to 2800. Borrowed from + * A. D. Olsen. + */ + jsint cycles; +#define CYCLE_YEARS 2800L + cycles = (year >= 0) ? year / CYCLE_YEARS + : -1 - (-1 - year) / CYCLE_YEARS; + adjustedYear = (int16)(year - cycles * CYCLE_YEARS); + } else { + /* Clamp it to the nearest representable year. */ + adjustedYear = (int16)((year > 0) ? 32767 : - 32768); + } + } else { + adjustedYear = (int16)year; + } + + 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 = (int16) adjustedYear; + 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, JS_TRUE); + 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, JSObject *obj, uintN argc, + jsval *argv, jsval *rval, char *format) +{ + char buf[100]; + JSString *str; + PRMJTime split; + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + if (!JSDOUBLE_IS_FINITE(*date)) { + JS_snprintf(buf, sizeof buf, js_NaN_date_str); + } else { + intN result_len; + jsdouble local = LocalTime(*date); + new_explode(local, &split, JS_FALSE); + + /* 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, *date, FORMATSPEC_FULL, rval); + + /* Hacked check against undesired 2-digit year 00/00/00 form. */ + if (buf[result_len - 3] == '/' && + isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) { + JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), + "%d", js_DateGetYear(cx, obj)); + } + + } + + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + /* 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, obj, argc, argv, rval, +#if defined(_WIN32) && !defined(__MWERKS__) + "%#c" +#else + "%c" +#endif + ); +} + +static JSBool +date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + /* 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, obj, argc, argv, rval, +#if defined(_WIN32) && !defined(__MWERKS__) + "%#x" +#else + "%x" +#endif + ); +} + +static JSBool +date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X"); +} + +static JSBool +date_toTimeString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + return date_format(cx, *date, FORMATSPEC_TIME, rval); +} + +static JSBool +date_toDateString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + return date_format(cx, *date, FORMATSPEC_DATE, rval); +} + +#if JS_HAS_TOSOURCE +#include +#include "jsdtoa.h" + +static JSBool +date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble *date; + char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; + JSString *str; + + date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + + numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr); + if (!bytes) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + str = JS_NewString(cx, bytes, strlen(bytes)); + if (!str) { + free(bytes); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +static JSBool +date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsdouble *date = date_getProlog(cx, obj, argv); + if (!date) + return JS_FALSE; + return date_format(cx, *date, FORMATSPEC_FULL, rval); +} + +#if JS_HAS_VALUEOF_HINT +static JSBool +date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + /* 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 + * date_getProlog, which does the check. + */ + + /* If called directly with no arguments, convert to a time number. */ + if (argc == 0) + return date_getTime(cx, obj, argc, argv, rval); + + /* Convert to number only if the hint was given, otherwise favor string. */ + if (argc == 1) { + JSString *str, *str2; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); + if (!js_CompareStrings(str, str2)) + return date_getTime(cx, obj, argc, argv, rval); + } + return date_toString(cx, obj, argc, argv, rval); +} +#else +#define date_valueOf date_getTime +#endif + + +/* + * creation and destruction + */ + +static JSFunctionSpec date_static_methods[] = { + {"UTC", date_UTC, MAXARGS,0,0 }, + {"parse", date_parse, 1,0,0 }, + {"now", date_now, 0,0,0 }, + {0,0,0,0,0} +}; + +static JSFunctionSpec date_methods[] = { + {"getTime", date_getTime, 0,0,0 }, + {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 }, + {"getYear", date_getYear, 0,0,0 }, + {"getFullYear", date_getFullYear, 0,0,0 }, + {"getUTCFullYear", date_getUTCFullYear, 0,0,0 }, + {"getMonth", date_getMonth, 0,0,0 }, + {"getUTCMonth", date_getUTCMonth, 0,0,0 }, + {"getDate", date_getDate, 0,0,0 }, + {"getUTCDate", date_getUTCDate, 0,0,0 }, + {"getDay", date_getDay, 0,0,0 }, + {"getUTCDay", date_getUTCDay, 0,0,0 }, + {"getHours", date_getHours, 0,0,0 }, + {"getUTCHours", date_getUTCHours, 0,0,0 }, + {"getMinutes", date_getMinutes, 0,0,0 }, + {"getUTCMinutes", date_getUTCMinutes, 0,0,0 }, + {"getSeconds", date_getUTCSeconds, 0,0,0 }, + {"getUTCSeconds", date_getUTCSeconds, 0,0,0 }, + {"getMilliseconds", date_getUTCMilliseconds,0,0,0 }, + {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 }, + {"setTime", date_setTime, 1,0,0 }, + {"setYear", date_setYear, 1,0,0 }, + {"setFullYear", date_setFullYear, 3,0,0 }, + {"setUTCFullYear", date_setUTCFullYear, 3,0,0 }, + {"setMonth", date_setMonth, 2,0,0 }, + {"setUTCMonth", date_setUTCMonth, 2,0,0 }, + {"setDate", date_setDate, 1,0,0 }, + {"setUTCDate", date_setUTCDate, 1,0,0 }, + {"setHours", date_setHours, 4,0,0 }, + {"setUTCHours", date_setUTCHours, 4,0,0 }, + {"setMinutes", date_setMinutes, 3,0,0 }, + {"setUTCMinutes", date_setUTCMinutes, 3,0,0 }, + {"setSeconds", date_setSeconds, 2,0,0 }, + {"setUTCSeconds", date_setUTCSeconds, 2,0,0 }, + {"setMilliseconds", date_setMilliseconds, 1,0,0 }, + {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 }, + {"toUTCString", date_toGMTString, 0,0,0 }, + {js_toLocaleString_str, date_toLocaleString, 0,0,0 }, + {"toLocaleDateString", date_toLocaleDateString,0,0,0 }, + {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 }, + {"toDateString", date_toDateString, 0,0,0 }, + {"toTimeString", date_toTimeString, 0,0,0 }, +#if JS_HAS_TOSOURCE + {js_toSource_str, date_toSource, 0,0,0 }, +#endif + {js_toString_str, date_toString, 0,0,0 }, + {js_valueOf_str, date_valueOf, 0,0,0 }, + {0,0,0,0,0} +}; + +static jsdouble * +date_constructor(JSContext *cx, JSObject* obj) +{ + jsdouble *date; + + date = js_NewDouble(cx, 0.0); + if (!date) + return NULL; + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); + return date; +} + +static JSBool +Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble *date; + JSString *str; + jsdouble d; + + /* Date called as function */ + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + int64 us, ms, us2ms; + jsdouble msec_time; + + /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', + * so compute ms from PRMJ_Now. + */ + us = PRMJ_Now(); + JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); + JSLL_DIV(ms, us, us2ms); + JSLL_L2D(msec_time, ms); + + return date_format(cx, msec_time, FORMATSPEC_FULL, rval); + } + + /* Date called as constructor */ + if (argc == 0) { + int64 us, ms, us2ms; + jsdouble msec_time; + + date = date_constructor(cx, obj); + if (!date) + return JS_FALSE; + + us = PRMJ_Now(); + JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); + JSLL_DIV(ms, us, us2ms); + JSLL_L2D(msec_time, ms); + + *date = msec_time; + } else if (argc == 1) { + if (!JSVAL_IS_STRING(argv[0])) { + /* the argument is a millisecond number */ + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + date = date_constructor(cx, obj); + if (!date) + return JS_FALSE; + *date = TIMECLIP(d); + } else { + /* the argument is a string; parse it. */ + date = date_constructor(cx, obj); + if (!date) + return JS_FALSE; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + if (!date_parseString(str, date)) + *date = *cx->runtime->jsNaN; + *date = TIMECLIP(*date); + } + } else { + jsdouble array[MAXARGS]; + uintN loop; + jsdouble double_arg; + jsdouble day; + jsdouble msec_time; + + for (loop = 0; loop < MAXARGS; loop++) { + if (loop < argc) { + if (!js_ValueToNumber(cx, argv[loop], &double_arg)) + return JS_FALSE; + /* if any arg is NaN, make a NaN date object + and return */ + if (!JSDOUBLE_IS_FINITE(double_arg)) { + date = date_constructor(cx, obj); + if (!date) + return JS_FALSE; + *date = *cx->runtime->jsNaN; + return JS_TRUE; + } + array[loop] = js_DoubleToInteger(double_arg); + } else { + if (loop == 2) { + array[loop] = 1; /* Default the date argument to 1. */ + } else { + array[loop] = 0; + } + } + } + + date = date_constructor(cx, obj); + if (!date) + return JS_FALSE; + + /* adjust 2-digit years into the 20th century */ + if (array[0] >= 0 && array[0] <= 99) + array[0] += 1900; + + day = MakeDay(array[0], array[1], array[2]); + msec_time = MakeTime(array[3], array[4], array[5], array[6]); + msec_time = MakeDate(day, msec_time); + msec_time = UTC(msec_time); + *date = TIMECLIP(msec_time); + } + return JS_TRUE; +} + +JSObject * +js_InitDateClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + jsdouble *proto_date; + + /* set static LocalTZA */ + LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); + proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS, + NULL, date_methods, NULL, date_static_methods); + if (!proto) + return NULL; + + /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ + if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) + return NULL; + + /* Set the value of the Date.prototype date to NaN */ + proto_date = date_constructor(cx, proto); + if (!proto_date) + return NULL; + *proto_date = *cx->runtime->jsNaN; + + return proto; +} + +JS_FRIEND_API(JSObject *) +js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) +{ + JSObject *obj; + jsdouble *date; + + obj = js_NewObject(cx, &date_class, NULL, NULL); + if (!obj) + return NULL; + + date = date_constructor(cx, obj); + if (!date) + return NULL; + + *date = msec_time; + 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; + + 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 *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return JS_FALSE; + else + return JS_TRUE; +} + +JS_FRIEND_API(int) +js_DateGetYear(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + /* Preserve legacy API behavior of returning 0 for invalid dates. */ + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) YearFromTime(LocalTime(*date)); +} + +JS_FRIEND_API(int) +js_DateGetMonth(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) MonthFromTime(LocalTime(*date)); +} + +JS_FRIEND_API(int) +js_DateGetDate(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) DateFromTime(LocalTime(*date)); +} + +JS_FRIEND_API(int) +js_DateGetHours(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) HourFromTime(LocalTime(*date)); +} + +JS_FRIEND_API(int) +js_DateGetMinutes(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) MinFromTime(LocalTime(*date)); +} + +JS_FRIEND_API(int) +js_DateGetSeconds(JSContext *cx, JSObject* obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (int) SecFromTime(*date); +} + +JS_FRIEND_API(void) +js_DateSetYear(JSContext *cx, JSObject *obj, int year) +{ + jsdouble local; + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date) + return; + local = LocalTime(*date); + /* 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)); + *date = UTC(local); +} + +JS_FRIEND_API(void) +js_DateSetMonth(JSContext *cx, JSObject *obj, int month) +{ + jsdouble local; + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date) + return; + local = LocalTime(*date); + /* 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)); + *date = UTC(local); +} + +JS_FRIEND_API(void) +js_DateSetDate(JSContext *cx, JSObject *obj, int date) +{ + jsdouble local; + jsdouble *datep = date_getProlog(cx, obj, NULL); + if (!datep) + return; + local = LocalTime(*datep); + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + date, + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + *datep = UTC(local); +} + +JS_FRIEND_API(void) +js_DateSetHours(JSContext *cx, JSObject *obj, int hours) +{ + jsdouble local; + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date) + return; + local = LocalTime(*date); + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + hours, + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); + *date = UTC(local); +} + +JS_FRIEND_API(void) +js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) +{ + jsdouble local; + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date) + return; + local = LocalTime(*date); + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + minutes, + SecFromTime(local), + msFromTime(local)); + *date = UTC(local); +} + +JS_FRIEND_API(void) +js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) +{ + jsdouble local; + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date) + return; + local = LocalTime(*date); + if (JSDOUBLE_IS_NaN(local)) + return; + local = date_msecFromDate(YearFromTime(local), + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + seconds, + msFromTime(local)); + *date = UTC(local); +} + +JS_FRIEND_API(jsdouble) +js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) +{ + jsdouble *date = date_getProlog(cx, obj, NULL); + if (!date || JSDOUBLE_IS_NaN(*date)) + return 0; + return (*date); +} diff --git a/src/extension/script/js/jsdate.h b/src/extension/script/js/jsdate.h new file mode 100644 index 000000000..790b4daf6 --- /dev/null +++ b/src/extension/script/js/jsdate.h @@ -0,0 +1,118 @@ +/* -*- 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 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. + */ +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 year); + +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); + +JS_END_EXTERN_C + +#endif /* jsdate_h___ */ diff --git a/src/extension/script/js/jsdbgapi.c b/src/extension/script/js/jsdbgapi.c new file mode 100644 index 000000000..2aa684941 --- /dev/null +++ b/src/extension/script/js/jsdbgapi.c @@ -0,0 +1,1240 @@ +/* -*- 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 debugging API. + */ +#include "jsstddef.h" +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsclist.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" + +typedef struct JSTrap { + JSCList links; + JSScript *script; + jsbytecode *pc; + JSOp op; + JSTrapHandler handler; + void *closure; +} JSTrap; + +static JSTrap * +FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) +{ + JSTrap *trap; + + for (trap = (JSTrap *)rt->trapList.next; + trap != (JSTrap *)&rt->trapList; + trap = (JSTrap *)trap->links.next) { + if (trap->script == script && trap->pc == pc) + return trap; + } + return NULL; +} + +void +js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op) +{ + JSTrap *trap; + + trap = FindTrap(cx->runtime, script, pc); + if (trap) + trap->op = op; + else + *pc = (jsbytecode)op; +} + +JS_PUBLIC_API(JSBool) +JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler handler, void *closure) +{ + JSRuntime *rt; + JSTrap *trap; + + rt = cx->runtime; + trap = FindTrap(rt, script, pc); + if (trap) { + JS_ASSERT(trap->script == script && trap->pc == pc); + JS_ASSERT(*pc == JSOP_TRAP); + } else { + trap = (JSTrap *) JS_malloc(cx, sizeof *trap); + if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { + if (trap) + JS_free(cx, trap); + return JS_FALSE; + } + JS_APPEND_LINK(&trap->links, &rt->trapList); + trap->script = script; + trap->pc = pc; + trap->op = (JSOp)*pc; + *pc = JSOP_TRAP; + } + trap->handler = handler; + trap->closure = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSOp) +JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSTrap *trap; + + trap = FindTrap(cx->runtime, script, pc); + if (!trap) { + JS_ASSERT(0); /* XXX can't happen */ + return JSOP_LIMIT; + } + return trap->op; +} + +static void +DestroyTrap(JSContext *cx, JSTrap *trap) +{ + JS_REMOVE_LINK(&trap->links); + *trap->pc = (jsbytecode)trap->op; + js_RemoveRoot(cx->runtime, &trap->closure); + JS_free(cx, trap); +} + +JS_PUBLIC_API(void) +JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, + JSTrapHandler *handlerp, void **closurep) +{ + JSTrap *trap; + + trap = FindTrap(cx->runtime, script, pc); + if (handlerp) + *handlerp = trap ? trap->handler : NULL; + if (closurep) + *closurep = trap ? trap->closure : NULL; + if (trap) + DestroyTrap(cx, trap); +} + +JS_PUBLIC_API(void) +JS_ClearScriptTraps(JSContext *cx, JSScript *script) +{ + JSRuntime *rt; + JSTrap *trap, *next; + + rt = cx->runtime; + for (trap = (JSTrap *)rt->trapList.next; + trap != (JSTrap *)&rt->trapList; + trap = next) { + next = (JSTrap *)trap->links.next; + if (trap->script == script) + DestroyTrap(cx, trap); + } +} + +JS_PUBLIC_API(void) +JS_ClearAllTraps(JSContext *cx) +{ + JSRuntime *rt; + JSTrap *trap, *next; + + rt = cx->runtime; + for (trap = (JSTrap *)rt->trapList.next; + trap != (JSTrap *)&rt->trapList; + trap = next) { + next = (JSTrap *)trap->links.next; + DestroyTrap(cx, trap); + } +} + +JS_PUBLIC_API(JSTrapStatus) +JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) +{ + JSTrap *trap; + JSTrapStatus status; + jsint op; + + trap = FindTrap(cx->runtime, script, pc); + if (!trap) { + JS_ASSERT(0); /* XXX can't happen */ + return JSTRAP_ERROR; + } + /* + * 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; +} + +JS_PUBLIC_API(JSBool) +JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) +{ + rt->interruptHandler = handler; + rt->interruptHandlerData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) +{ + if (handlerp) + *handlerp = (JSTrapHandler)rt->interruptHandler; + if (closurep) + *closurep = rt->interruptHandlerData; + rt->interruptHandler = 0; + rt->interruptHandlerData = 0; + return JS_TRUE; +} + +/************************************************************************/ + +typedef struct JSWatchPoint { + JSCList links; + JSObject *object; /* weak link, see js_FinalizeObject */ + JSScopeProperty *sprop; + JSPropertyOp setter; + JSWatchPointHandler handler; + void *closure; + jsrefcount nrefs; +} JSWatchPoint; + +#define HoldWatchPoint(wp) ((wp)->nrefs++) + +static JSBool +DropWatchPoint(JSContext *cx, JSWatchPoint *wp) +{ + JSScopeProperty *sprop; + + if (--wp->nrefs != 0) + return JS_TRUE; + + /* + * Remove wp from the list, then if there are no other watchpoints for + * wp->sprop in any scope, restore wp->sprop->setter from wp. + */ + JS_REMOVE_LINK(&wp->links); + sprop = wp->sprop; + if (!js_GetWatchedSetter(cx->runtime, NULL, sprop)) { + sprop = js_ChangeNativePropertyAttrs(cx, wp->object, sprop, + 0, sprop->attrs, + sprop->getter, wp->setter); + if (!sprop) + return JS_FALSE; + } + js_RemoveRoot(cx->runtime, &wp->closure); + JS_free(cx, wp); + return JS_TRUE; +} + +void +js_MarkWatchPoints(JSRuntime *rt) +{ + JSWatchPoint *wp; + + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + MARK_SCOPE_PROPERTY(wp->sprop); + } +} + +static JSWatchPoint * +FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) +{ + JSWatchPoint *wp; + + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if (wp->object == scope->object && wp->sprop->id == id) + return wp; + } + return NULL; +} + +JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) +{ + JSWatchPoint *wp; + + wp = FindWatchPoint(rt, scope, id); + if (!wp) + return NULL; + return wp->sprop; +} + +JSPropertyOp +js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, + const JSScopeProperty *sprop) +{ + JSWatchPoint *wp; + + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + if ((!scope || wp->object == scope->object) && wp->sprop == sprop) + return wp->setter; + } + return NULL; +} + +JSBool JS_DLL_CALLBACK +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSRuntime *rt; + JSWatchPoint *wp; + JSScopeProperty *sprop; + jsval userid; + JSScope *scope; + JSBool ok; + + rt = cx->runtime; + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = (JSWatchPoint *)wp->links.next) { + sprop = wp->sprop; + if (wp->object == obj && SPROP_USERID(sprop) == id) { + JS_LOCK_OBJ(cx, obj); + userid = SPROP_USERID(sprop); + scope = OBJ_SCOPE(obj); + JS_UNLOCK_OBJ(cx, obj); + HoldWatchPoint(wp); + ok = wp->handler(cx, obj, userid, + SPROP_HAS_VALID_SLOT(sprop, scope) + ? OBJ_GET_SLOT(cx, obj, wp->sprop->slot) + : JSVAL_VOID, + vp, wp->closure); + if (ok) { + /* + * Create pseudo-frame for call to setter so that any + * stack-walking security code in the setter will correctly + * identify the guilty party. + */ + JSObject *funobj = (JSObject *) wp->closure; + JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, funobj); + JSStackFrame frame; + + memset(&frame, 0, sizeof(frame)); + frame.script = fun->script; + frame.fun = fun; + frame.down = cx->fp; + cx->fp = &frame; + ok = !wp->setter || + ((sprop->attrs & JSPROP_SETTER) + ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter), + 1, vp, vp) + : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp)); + cx->fp = frame.down; + } + return DropWatchPoint(cx, wp); + } + } + JS_ASSERT(0); /* XXX can't happen */ + return JS_FALSE; +} + +JSBool JS_DLL_CALLBACK +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 = (JSFunction *) JS_GetPrivate(cx, funobj); + userid = ATOM_KEY(wrapper->atom); + *rval = argv[0]; + return js_watch_set(cx, obj, userid, rval); +} + +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 (!JSVAL_IS_INT(id)) { + atom = (JSAtom *)id; + } else { + atom = js_AtomizeInt(cx, JSVAL_TO_INT(id), 0); + if (!atom) + return NULL; + } + wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, + OBJ_GET_PARENT(cx, (JSObject *)setter), + atom); + if (!wrapper) + return NULL; + return (JSPropertyOp) wrapper->object; +} + +JS_PUBLIC_API(JSBool) +JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler handler, void *closure) +{ + JSAtom *atom; + jsid propid; + JSObject *pobj; + JSScopeProperty *sprop; + JSRuntime *rt; + JSWatchPoint *wp; + JSPropertyOp watcher; + + if (!OBJ_IS_NATIVE(obj)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, + OBJ_GET_CLASS(cx, obj)->name); + return JS_FALSE; + } + + if (JSVAL_IS_INT(id)) { + propid = (jsid)id; + atom = NULL; + } else { + atom = js_ValueToStringAtom(cx, id); + if (!atom) + return JS_FALSE; + propid = (jsid)atom; + } + + if (!js_LookupProperty(cx, obj, propid, &pobj, (JSProperty **)&sprop)) + return JS_FALSE; + 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_DefineProperty(cx, obj, propid, JSVAL_VOID, + NULL, NULL, JSPROP_ENUMERATE, + (JSProperty **)&sprop)) { + sprop = NULL; + } + } + } else if (pobj != obj) { + /* Clone the prototype property so we can watch the right object. */ + jsval value; + JSPropertyOp getter, setter; + uintN attrs; + + 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; + } else { + if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) { + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + return JS_FALSE; + } + getter = setter = JS_PropertyStub; + attrs = JSPROP_ENUMERATE; + } + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + + if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs, + (JSProperty **)&sprop)) { + sprop = NULL; + } + } + if (!sprop) + return JS_FALSE; + + wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); + if (!wp) { + watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); + if (!watcher) + return JS_FALSE; + + wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); + if (!wp) + return JS_FALSE; + wp->handler = NULL; + wp->closure = NULL; + if (!js_AddRoot(cx, &wp->closure, "wp->closure")) { + JS_free(cx, wp); + return JS_FALSE; + } + JS_APPEND_LINK(&wp->links, &rt->watchPointList); + wp->object = obj; + wp->sprop = sprop; + JS_ASSERT(sprop->setter != js_watch_set); + wp->setter = sprop->setter; + wp->nrefs = 1; + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, + sprop->getter, watcher); + if (!sprop) + return DropWatchPoint(cx, wp); + } + wp->handler = handler; + wp->closure = closure; + OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler *handlerp, void **closurep) +{ + JSRuntime *rt; + JSWatchPoint *wp; + + rt = cx->runtime; + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&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 DropWatchPoint(cx, wp); + } + } + 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; + + rt = cx->runtime; + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = next) { + next = (JSWatchPoint *)wp->links.next; + if (wp->object == obj && !DropWatchPoint(cx, wp)) + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ClearAllWatchPoints(JSContext *cx) +{ + JSRuntime *rt; + JSWatchPoint *wp, *next; + + rt = cx->runtime; + for (wp = (JSWatchPoint *)rt->watchPointList.next; + wp != (JSWatchPoint *)&rt->watchPointList; + wp = next) { + next = (JSWatchPoint *)wp->links.next; + if (!DropWatchPoint(cx, wp)) + return JS_FALSE; + } + 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; +} + +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) ? cx->fp : (*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->pc; +} + +JS_PUBLIC_API(JSStackFrame *) +JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) +{ + if (!fp) + fp = cx->fp; + while ((fp = fp->down) != NULL) { + if (fp->script) + return fp; + } + return NULL; +} + +JS_PUBLIC_API(JSPrincipals *) +JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) +{ + if (fp->fun && cx->findObjectPrincipals) { + JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]); + + if (fp->fun->object != callee) + return cx->findObjectPrincipals(cx, callee); + /* FALL THROUGH */ + } + if (fp->script) + return fp->script->principals; + return NULL; +} + +JS_PUBLIC_API(JSPrincipals *) +JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) +{ + if (cx->findObjectPrincipals) + return cx->findObjectPrincipals(cx, JSVAL_TO_OBJECT(fp->argv[-2])); + if (!caller) + return NULL; + return JS_StackFramePrincipals(cx, caller); +} + +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 fp->scopeChain; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) +{ + if (! fp->fun) + return NULL; +#if JS_HAS_ARGS_OBJECT + /* Force creation of argument object if not yet created */ + (void) js_GetArgsObject(cx, fp); +#endif +#if JS_HAS_CALL_OBJECT + /* + * XXX ill-defined: null return here means error was reported, unlike a + * null returned above or in the #else + */ + return js_GetCallObject(cx, fp, NULL); +#else + return NULL; +#endif /* JS_HAS_CALL_OBJECT */ +} + + +JS_PUBLIC_API(JSObject *) +JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) +{ + return fp->thisp; +} + +JS_PUBLIC_API(JSFunction *) +JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) +{ + return fp->fun; +} + +JS_PUBLIC_API(JSObject *) +JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp) +{ + return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; +} + +JS_PUBLIC_API(JSBool) +JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) +{ + return (fp->flags & JSFRAME_CONSTRUCTING) != 0; +} + +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 script->version; +} + +/***************************************************************************/ + +JS_PUBLIC_API(void) +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) +{ + rt->newScriptHook = hook; + rt->newScriptHookData = callerdata; +} + +JS_PUBLIC_API(void) +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, + void *callerdata) +{ + rt->destroyScriptHook = hook; + rt->destroyScriptHookData = callerdata; +} + +/***************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, + const jschar *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + uint32 flags; + JSScript *script; + JSBool ok; + + /* + * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL + * flags to the code generator (see js_EmitTree's TOK_SEMI case). + */ + flags = fp->flags; + fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; + script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain, + JS_StackFramePrincipals(cx, fp), + bytes, length, filename, lineno); + fp->flags = flags; + if (!script) + return JS_FALSE; + + ok = js_Execute(cx, fp->scopeChain, script, fp, + JSFRAME_DEBUGGER | JSFRAME_EVAL, rval); + 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; + + chars = js_InflateString(cx, bytes, length); + if (!chars) + return JS_FALSE; + ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, + rval); + JS_free(cx, 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 */ + if (!sprop) { + sprop = SCOPE_LAST_PROP(scope); + } else { + while ((sprop = sprop->parent) != NULL) { + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + if (SCOPE_HAS_PROPERTY(scope, sprop)) + break; + } + } + *iteratorp = sprop; + return sprop; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, + JSPropertyDesc *pd) +{ + JSPropertyOp getter; + JSScope *scope; + JSScopeProperty *aprop; + jsval lastException; + JSBool wasThrowing; + + pd->id = ID_TO_VALUE(sprop->id); + + wasThrowing = cx->throwing; + if (wasThrowing) { + lastException = cx->exception; + if (JSVAL_IS_GCTHING(lastException) && + !js_AddRoot(cx, &lastException, "lastException")) { + return JS_FALSE; + } + 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; + if (JSVAL_IS_GCTHING(lastException)) + js_RemoveRoot(cx->runtime, &lastException); + } + + getter = sprop->getter; + pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) + | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) + | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) +#if JS_HAS_CALL_OBJECT + | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) +#endif /* JS_HAS_CALL_OBJECT */ + | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) + | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); +#if JS_HAS_CALL_OBJECT + /* for Call Object 'real' getter isn't passed in to us */ + if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && + getter == js_CallClass.getProperty) { + /* + * Property of a heavyweight function's variable object having the + * class-default getter. It's either an argument if permanent, or a + * nested function if impermanent. Local variables have a special + * getter (js_GetCallVariable, tested above) and setter, and not the + * class default. + */ + pd->flags |= (sprop->attrs & JSPROP_PERMANENT) + ? JSPD_ARGUMENT + : JSPD_VARIABLE; + } +#endif /* JS_HAS_CALL_OBJECT */ + pd->spare = 0; + pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) + ? sprop->shortid + : 0; + pd->alias = JSVAL_VOID; + scope = OBJ_SCOPE(obj); + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + for (aprop = SCOPE_LAST_PROP(scope); 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->object != obj || scope->entryCount == 0) { + pda->length = 0; + pda->array = NULL; + return JS_TRUE; + } + + n = scope->entryCount; + if (n > scope->map.nslots) + n = scope->map.nslots; + pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); + if (!pd) + return JS_FALSE; + i = 0; + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) + continue; + 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); + } + JS_free(cx, pd); +} + +/************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) +{ + rt->debuggerHandler = handler; + rt->debuggerHandlerData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) +{ + rt->sourceHandler = handler; + rt->sourceHandlerData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) +{ + rt->executeHook = hook; + rt->executeHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) +{ + rt->callHook = hook; + rt->callHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) +{ + rt->objectHook = hook; + rt->objectHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure) +{ + rt->throwHook = hook; + rt->throwHookData = closure; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) +{ + rt->debugErrorHook = hook; + rt->debugErrorHookData = closure; + return JS_TRUE; +} + +/************************************************************************/ + +JS_PUBLIC_API(size_t) +JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) +{ + size_t nbytes; + JSScope *scope; + + nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0]; + if (OBJ_IS_NATIVE(obj)) { + scope = OBJ_SCOPE(obj); + if (scope->object == obj) { + nbytes += sizeof *scope; + nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *); + } + } + return nbytes; +} + +static size_t +GetAtomTotalSize(JSContext *cx, JSAtom *atom) +{ + size_t nbytes; + + nbytes = sizeof *atom; + if (ATOM_IS_STRING(atom)) { + nbytes += sizeof(JSString); + nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar); + } else if (ATOM_IS_DOUBLE(atom)) { + nbytes += sizeof(jsdouble); + } else if (ATOM_IS_OBJECT(atom)) { + nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom)); + } + return nbytes; +} + +JS_PUBLIC_API(size_t) +JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) +{ + size_t nbytes, obytes; + JSObject *obj; + JSAtom *atom; + + nbytes = sizeof *fun; + JS_ASSERT(fun->nrefs); + obj = fun->object; + if (obj) { + obytes = JS_GetObjectTotalSize(cx, obj); + if (fun->nrefs > 1) + obytes = JS_HOWMANY(obytes, fun->nrefs); + nbytes += obytes; + } + if (fun->script) + nbytes += JS_GetScriptTotalSize(cx, fun->script); + atom = fun->atom; + if (atom) + nbytes += GetAtomTotalSize(cx, atom); + return nbytes; +} + +#include "jsemit.h" + +JS_PUBLIC_API(size_t) +JS_GetScriptTotalSize(JSContext *cx, JSScript *script) +{ + size_t nbytes, pbytes; + JSObject *obj; + jsatomid i; + jssrcnote *sn, *notes; + JSTryNote *tn, *tnotes; + JSPrincipals *principals; + + nbytes = sizeof *script; + obj = script->object; + if (obj) + nbytes += JS_GetObjectTotalSize(cx, obj); + + 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(script); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nbytes += (sn - notes + 1) * sizeof *sn; + + tnotes = script->trynotes; + if (tnotes) { + for (tn = tnotes; tn->catchStart; tn++) + continue; + nbytes += (tn - tnotes + 1) * sizeof *tn; + } + + 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; +} diff --git a/src/extension/script/js/jsdbgapi.h b/src/extension/script/js/jsdbgapi.h new file mode 100644 index 000000000..90215a275 --- /dev/null +++ b/src/extension/script/js/jsdbgapi.h @@ -0,0 +1,345 @@ +/* -*- 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 jsdbgapi_h___ +#define jsdbgapi_h___ +/* + * JS debugger API. + */ +#include "jsapi.h" +#include "jsopcode.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +extern void +js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); + +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 "jsconfig.h" has been included. + */ +extern void +js_MarkWatchPoints(JSRuntime *rt); + +extern JSScopeProperty * +js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); + +extern JSPropertyOp +js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, + const JSScopeProperty *sprop); + +extern JSBool JS_DLL_CALLBACK +js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool JS_DLL_CALLBACK +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(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); + +/* + * Like JS_StackFramePrincipals(cx, caller), but if cx->findObjectPrincipals + * is non-null, return the object principals for fp's callee function object + * (fp->argv[-2]), which is eval, Function, or a similar eval-like method. + * The caller parameter should be the result of 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); + +/************************************************************************/ + +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 *bytes, 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); + +JS_END_EXTERN_C + +#endif /* jsdbgapi_h___ */ diff --git a/src/extension/script/js/jsdhash.c b/src/extension/script/js/jsdhash.c new file mode 100644 index 000000000..736de04ad --- /dev/null +++ b/src/extension/script/js/jsdhash.c @@ -0,0 +1,763 @@ +/* -*- 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 + * + * 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 "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 + +JS_PUBLIC_API(void *) +JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) +{ + return malloc(nbytes); +} + +JS_PUBLIC_API(void) +JS_DHashFreeTable(JSDHashTable *table, void *ptr) +{ + free(ptr); +} + +JS_PUBLIC_API(JSDHashNumber) +JS_DHashStringKey(JSDHashTable *table, const void *key) +{ + JSDHashNumber h; + const unsigned char *s; + + h = 0; + for (s = key; *s != '\0'; s++) + h = (h >> (JS_DHASH_BITS - 4)) ^ (h << 4) ^ *s; + return h; +} + +JS_PUBLIC_API(const void *) +JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry) +{ + JSDHashEntryStub *stub = (JSDHashEntryStub *)entry; + + return stub->key; +} + +JS_PUBLIC_API(JSDHashNumber) +JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) +{ + return (JSDHashNumber)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(stub->key, 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; + + 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_DHashGetKeyStub, + 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 *) malloc(sizeof *table); + if (!table) + return NULL; + if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) { + free(table); + return NULL; + } + return table; +} + +JS_PUBLIC_API(void) +JS_DHashTableDestroy(JSDHashTable *table) +{ + JS_DHashTableFinish(table); + 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; + log2 = JS_CeilingLog2(capacity); + capacity = JS_BIT(log2); + if (capacity >= JS_DHASH_SIZE_LIMIT) + return JS_FALSE; + table->hashShift = JS_DHASH_BITS - log2; + table->maxAlphaFrac = 0xC0; /* .75 */ + table->minAlphaFrac = 0x40; /* .25 */ + table->entrySize = entrySize; + table->entryCount = table->removedCount = 0; + table->generation = 0; + nbytes = capacity * entrySize; + + table->entryStore = ops->allocTable(table, nbytes); + if (!table->entryStore) + return JS_FALSE; + memset(table->entryStore, 0, nbytes); + METER(memset(&table->stats, 0, sizeof table->stats)); + 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 + + /* 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; + } + + /* Free entry storage last. */ + table->ops->freeTable(table, table->entryStore); +} + +static JSDHashEntryHdr * +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. */ + if (ENTRY_IS_REMOVED(entry)) { + firstRemoved = entry; + } else { + firstRemoved = NULL; + if (op == JS_DHASH_ADD) + entry->keyHash |= COLLISION_FLAG; + } + + for (;;) { + 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; + } + + if (ENTRY_IS_REMOVED(entry)) { + if (!firstRemoved) + firstRemoved = entry; + } else { + if (op == JS_DHASH_ADD) + entry->keyHash |= COLLISION_FLAG; + } + } + + /* 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; + JSDHashGetKey getKey; + JSDHashMoveEntry moveEntry; + + /* 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 = table->ops->allocTable(table, nbytes); + if (!newEntryStore) + return JS_FALSE; + + /* We can't fail from here on, so update table parameters. */ + 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; + getKey = table->ops->getKey; + moveEntry = table->ops->moveEntry; + + /* 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 = SearchTable(table, getKey(table, oldEntry), + oldEntry->keyHash, JS_DHASH_ADD); + 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_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) +{ + JSDHashNumber keyHash; + JSDHashEntryHdr *entry; + uint32 size; + int deltaLog2; + + 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++); + return NULL; + } + } + + /* + * 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); + return NULL; + } + 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; + } + + return entry; +} + +JS_PUBLIC_API(void) +JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) +{ + JSDHashNumber keyHash; /* load first in case clearEntry goofs it */ + + 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; + JSBool didRemove; + JSDHashEntryHdr *entry; + JSDHashOperator op; + + 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; + } + + /* + * 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; + (void) ChangeTable(table, + JS_CeilingLog2(capacity) + - (JS_DHASH_BITS - table->hashShift)); + } + return i; +} + +#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/src/extension/script/js/jsdhash.h b/src/extension/script/js/jsdhash.h new file mode 100644 index 000000000..39982ce05 --- /dev/null +++ b/src/extension/script/js/jsdhash.h @@ -0,0 +1,573 @@ +/* -*- 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 + +#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, + * and asks each entry for its key when necessary via a getKey callback, used + * when growing or shrinking the table. Other 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 * +(* JS_DLL_CALLBACK JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); + +typedef void +(* JS_DLL_CALLBACK JSDHashFreeTable) (JSDHashTable *table, void *ptr); + +/* + * When a table grows or shrinks, each entry is queried for its key using this + * callback. NB: in that event, entry is not in table any longer; it's in the + * old entryStore vector, which is due to be freed once all entries have been + * moved via moveEntry callbacks. + */ +typedef const void * +(* JS_DLL_CALLBACK JSDHashGetKey) (JSDHashTable *table, + JSDHashEntryHdr *entry); + +/* + * 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 +(* JS_DLL_CALLBACK 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 +(* JS_DLL_CALLBACK 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 +(* JS_DLL_CALLBACK 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 +(* JS_DLL_CALLBACK 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 +(* JS_DLL_CALLBACK 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 +(* JS_DLL_CALLBACK 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; + JSDHashGetKey getKey; + 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(const void *) +JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry); + +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. + */ +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))) + +/* + * 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_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 +(* JS_DLL_CALLBACK JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg); + +extern JS_PUBLIC_API(uint32) +JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); + +#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/src/extension/script/js/jsdtoa.c b/src/extension/script/js/jsdtoa.c new file mode 100644 index 000000000..2bd163d75 --- /dev/null +++ b/src/extension/script/js/jsdtoa.c @@ -0,0 +1,3155 @@ +/* -*- 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 "jsstddef.h" +#include "jslibmath.h" +#include "jstypes.h" +#include "jsdtoa.h" +#include "jsprf.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jspubtd.h" +#include "jsnum.h" + +#ifdef JS_THREADSAFE +#include "prlock.h" +#endif + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991 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 + Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-0636 + U.S.A. + dmg@bell-labs.com + */ + +/* 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-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets err to JS_DTOA_ERANGE or JS_DTOA_ENOMEM). 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 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 Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of js_dtoa. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #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 JS_HAVE_LONG_LONG on machines that have a "long long" + * integer type (of >= 64 bits). 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 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. + * #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: 2000 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. + * #define INFNAN_CHECK on IEEE systems to cause strtod to check 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.) + * #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() and released + * by RELEASE_DTOA_LOCK(). (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. + */ +#ifdef IS_LITTLE_ENDIAN +#define IEEE_8087 +#else +#define IEEE_MC68k +#endif + +#ifndef Long +#define Long int32 +#endif + +#ifndef ULong +#define ULong uint32 +#endif + +#define Bug(errorMessageString) JS_ASSERT(!errorMessageString) + +#include "stdlib.h" +#include "string.h" + +#ifdef MALLOC +extern void *MALLOC(size_t); +#else +#define MALLOC malloc +#endif + +#define Omit_Private_Memory +/* Private memory currently doesn't work with JS_THREADSAFE */ +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2000 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#ifdef Bad_float_h +#undef __STDC__ + +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7976931348623157e+308 + + + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +/* + * MacOS 10.2 defines the macro FLT_ROUNDS to an internal function + * which does not exist on 10.1. We can safely #define it to 1 here + * to allow 10.2 builds to run on 10.1, since we can't use fesetround() + * (which does not exist on 10.1 either). + */ +#if defined(MACOS_DEPLOYMENT_TARGET) && (MACOS_DEPLOYMENT_TARGET < 100200) +#undef FLT_ROUNDS +#define FLT_ROUNDS 1 +#endif +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifndef CONST +#define CONST const +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 +Exactly one of IEEE_8087 or IEEE_MC68k should be defined. +#endif + +#define word0(x) JSDOUBLE_HI32(x) +#define set_word0(x, y) JSDOUBLE_SET_HI32(x, y) +#define word1(x) JSDOUBLE_LO32(x) +#define set_word1(x, y) JSDOUBLE_SET_LO32(x, y) + +#define Storeinc(a,b,c) (*(a)++ = (b) << 16 | (c) & 0xffff) + +/* #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) */ + +#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 +#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#endif + + + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#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 JS_HAVE_LONG_LONG +#undef ULLong +#else /* long long available */ +#ifndef Llong +#define Llong JSInt64 +#endif +#ifndef ULLong +#define ULLong JSUint64 +#endif +#endif /* JS_HAVE_LONG_LONG */ + +#ifdef JS_THREADSAFE +#define MULTIPLE_THREADS +static PRLock *freelist_lock; +#define ACQUIRE_DTOA_LOCK() \ + JS_BEGIN_MACRO \ + if (!initialized) \ + InitDtoa(); \ + PR_Lock(freelist_lock); \ + JS_END_MACRO +#define RELEASE_DTOA_LOCK() PR_Unlock(freelist_lock) +#else +#undef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK() /*nothing*/ +#define RELEASE_DTOA_LOCK() /*nothing*/ +#endif + +#define Kmax 15 + +struct Bigint { + struct Bigint *next; /* Free list link */ + int32 k; /* lg2(maxwds) */ + int32 maxwds; /* Number of words allocated for x */ + int32 sign; /* Zero if positive, 1 if negative. Ignored by most Bigint routines! */ + int32 wds; /* Actual number of words. If value is nonzero, the most significant word must be nonzero. */ + ULong x[1]; /* wds words of number in little endian order */ +}; + +#ifdef ENABLE_OOM_TESTING +/* Out-of-memory testing. Use a good testcase (over and over) and then use + * these routines to cause a memory failure on every possible Balloc allocation, + * to make sure that all out-of-memory paths can be followed. See bug 14044. + */ + +static int allocationNum; /* which allocation is next? */ +static int desiredFailure; /* which allocation should fail? */ + +/** + * js_BigintTestingReset + * + * Call at the beginning of a test run to set the allocation failure position. + * (Set to 0 to just have the engine count allocations without failing.) + */ +JS_PUBLIC_API(void) +js_BigintTestingReset(int newFailure) +{ + allocationNum = 0; + desiredFailure = newFailure; +} + +/** + * js_BigintTestingWhere + * + * Report the current allocation position. This is really only useful when you + * want to learn how many allocations a test run has. + */ +JS_PUBLIC_API(int) +js_BigintTestingWhere() +{ + return allocationNum; +} + + +/* + * So here's what you do: Set up a fantastic test case that exercises the + * elements of the code you wish. Set the failure point at 0 and run the test, + * then get the allocation position. This number is the number of allocations + * your test makes. Now loop from 1 to that number, setting the failure point + * at each loop count, and run the test over and over, causing failures at each + * step. Any memory failure *should* cause a Out-Of-Memory exception; if it + * doesn't, then there's still an error here. + */ +#endif + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +/* + * Allocate a Bigint with 2^k words. + * This is not threadsafe. The caller must use thread locks + */ +static Bigint *Balloc(int32 k) +{ + int32 x; + Bigint *rv; +#ifndef Omit_Private_Memory + uint32 len; +#endif + +#ifdef ENABLE_OOM_TESTING + if (++allocationNum == desiredFailure) { + printf("Forced Failing Allocation number %d\n", allocationNum); + return NULL; + } +#endif + + if ((rv = freelist[k]) != NULL) + freelist[k] = rv->next; + if (rv == NULL) { + 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 (pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + if (!rv) + return NULL; + rv->k = k; + rv->maxwds = x; + } + rv->sign = rv->wds = 0; + return rv; +} + +static void Bfree(Bigint *v) +{ + if (v) { + v->next = freelist[v->k]; + freelist[v->k] = v; + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ + y->wds*sizeof(Long) + 2*sizeof(int32)) + +/* Return b*m + a. Deallocate the old b. Both a and m must be between 0 and + * 65535 inclusive. NOTE: old b is deallocated on memory failure. + */ +static Bigint *multadd(Bigint *b, int32 m, int32 a) +{ + int32 i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; + ULong xi, z; +#endif + Bigint *b1; + +#ifdef ENABLE_OOM_TESTING + if (++allocationNum == desiredFailure) { + /* Faux allocation, because I'm not getting all of the failure paths + * without it. + */ + printf("Forced Failing Allocation number %d\n", allocationNum); + Bfree(b); + return NULL; + } +#endif + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = (ULong)(y & 0xffffffffUL); +#else + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + if (!b1) { + Bfree(b); + return NULL; + } + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = (ULong)carry; + b->wds = wds; + } + return b; +} + +static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9) +{ + Bigint *b; + int32 i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; + b = Balloc(k); + if (!b) + return NULL; + b->x[0] = y9; + b->wds = 1; + + i = 9; + if (9 < nd0) { + s += 9; + do { + b = multadd(b, 10, *s++ - '0'); + if (!b) + return NULL; + } while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) { + b = multadd(b, 10, *s++ - '0'); + if (!b) + return NULL; + } + return b; +} + + +/* Return the number (0 through 32) of most significant zero bits in x. */ +static int32 hi0bits(register ULong x) +{ + register int32 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; +} + + +/* Return the number (0 through 32) of least significant zero bits in y. + * Also shift y to the right past these 0 through 32 zeros so that y's + * least significant bit will be set unless y was originally zero. */ +static int32 lo0bits(ULong *y) +{ + register int32 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 & 1) + return 32; + } + *y = x; + return k; +} + +/* Return a new Bigint with the given integer value, which must be nonnegative. */ +static Bigint *i2b(int32 i) +{ + Bigint *b; + + b = Balloc(1); + if (!b) + return NULL; + b->x[0] = i; + b->wds = 1; + return b; +} + +/* Return a newly allocated product of a and b. */ +static Bigint *mult(CONST Bigint *a, CONST Bigint *b) +{ + CONST Bigint *t; + Bigint *c; + int32 k, wa, wb, wc; + ULong y; + ULong *xc, *xc0, *xce; + CONST ULong *x, *xa, *xae, *xb, *xbe; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; + ULong z2; +#endif + + if (a->wds < b->wds) { + t = a; + a = b; + b = t; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + if (!c) + return NULL; + for(xc = c->x, xce = xc + wc; xc < xce; xc++) + *xc = 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++) != 0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = (ULong)(z & 0xffffffffUL); + } + while(x < xae); + *xc = (ULong)carry; + } + } +#else + for(; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff) != 0) { + 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) != 0) { + 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; + } + } +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +/* + * 'p5s' points to a linked list of Bigints that are powers of 5. + * This list grows on demand, and it can only grow: it won't change + * in any other way. So if we read 'p5s' or the 'next' field of + * some Bigint on the list, and it is not NULL, we know it won't + * change to NULL or some other value. Only when the value of + * 'p5s' or 'next' is NULL do we need to acquire the lock and add + * a new Bigint to the list. + */ + +static Bigint *p5s; + +#ifdef JS_THREADSAFE +static PRLock *p5s_lock; +#endif + +/* Return b * 5^k. Deallocate the old b. k must be nonnegative. */ +/* NOTE: old b is deallocated on memory failure. */ +static Bigint *pow5mult(Bigint *b, int32 k) +{ + Bigint *b1, *p5, *p51; + int32 i; + static CONST int32 p05[3] = { 5, 25, 125 }; + + if ((i = k & 3) != 0) { + b = multadd(b, p05[i-1], 0); + if (!b) + return NULL; + } + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { +#ifdef JS_THREADSAFE + /* + * We take great care to not call i2b() and Bfree() + * while holding the lock. + */ + Bigint *wasted_effort = NULL; + p5 = i2b(625); + if (!p5) { + Bfree(b); + return NULL; + } + /* lock and check again */ + PR_Lock(p5s_lock); + if (!p5s) { + /* first time */ + p5s = p5; + p5->next = 0; + } else { + /* some other thread just beat us */ + wasted_effort = p5; + p5 = p5s; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } +#else + /* first time */ + p5 = p5s = i2b(625); + if (!p5) { + Bfree(b); + return NULL; + } + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + if (!b1) + return NULL; + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef JS_THREADSAFE + Bigint *wasted_effort = NULL; + p51 = mult(p5, p5); + if (!p51) { + Bfree(b); + return NULL; + } + PR_Lock(p5s_lock); + if (!p5->next) { + p5->next = p51; + p51->next = 0; + } else { + wasted_effort = p51; + p51 = p5->next; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } +#else + p51 = mult(p5,p5); + if (!p51) { + Bfree(b); + return NULL; + } + p51->next = 0; + p5->next = p51; +#endif + } + p5 = p51; + } + return b; +} + +/* Return b * 2^k. Deallocate the old b. k must be nonnegative. + * NOTE: on memory failure, old b is deallocated. */ +static Bigint *lshift(Bigint *b, int32 k) +{ + int32 i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + + n = k >> 5; + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + if (!b1) + goto done; + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z) != 0) + ++n1; + } + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; +done: + Bfree(b); + return b1; +} + +/* Return -1, 0, or 1 depending on whether ab, respectively. */ +static int32 cmp(Bigint *a, Bigint *b) +{ + ULong *xa, *xa0, *xb, *xb0; + int32 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(Bigint *a, Bigint *b) +{ + Bigint *c; + int32 i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; + ULong z; +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + if (!c) + return NULL; + 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); + if (!c) + return NULL; + 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 & 1UL; + *xc++ = (ULong)(y & 0xffffffffUL); + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = (ULong)(y & 0xffffffffUL); + } +#else + 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); + } +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; +} + +/* Return the absolute difference between x and the adjacent greater-magnitude double number (ignoring exponent overflows). */ +static double ulp(double x) +{ + register Long L; + double a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif + set_word0(a, L); + set_word1(a, 0); +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + set_word0(a, 0x80000 >> L); + set_word1(a, 0); + } + else { + set_word0(a, 0); + L -= Exp_shift; + set_word1(a, L >= 31 ? 1 : 1 << (31 - L)); + } + } +#endif + return a; +} + + +static double b2d(Bigint *a, int32 *e) +{ + ULong *xa, *xa0, w, y, z; + int32 k; + double d; +#define d0 word0(d) +#define d1 word1(d) +#define set_d0(x) set_word0(d, x) +#define set_d1(x) set_word1(d, x) + + 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; + if (k < Ebits) { + set_d0(Exp_1 | y >> (Ebits - k)); + w = xa > xa0 ? *--xa : 0; + set_d1(y << (32-Ebits + k) | w >> (Ebits - k)); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + set_d0(Exp_1 | y << k | z >> (32 - k)); + y = xa > xa0 ? *--xa : 0; + set_d1(z << k | y >> (32 - k)); + } + else { + set_d0(Exp_1 | y); + set_d1(z); + } + ret_d: +#undef d0 +#undef d1 +#undef set_d0 +#undef set_d1 + return d; +} + + +/* Convert d into the form b*2^e, where b is an odd integer. b is the returned + * Bigint and e is the returned binary exponent. Return the number of significant + * bits in b in bits. d must be finite and nonzero. */ +static Bigint *d2b(double d, int32 *e, int32 *bits) +{ + Bigint *b; + int32 de, i, k; + ULong *x, y, z; +#define d0 word0(d) +#define d1 word1(d) +#define set_d0(x) set_word0(d, x) +#define set_d1(x) set_word1(d, x) + + b = Balloc(1); + if (!b) + return NULL; + x = b->x; + + z = d0 & Frac_mask; + set_d0(d0 & 0x7fffffff); /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int32)(d0 >> Exp_shift); + z |= Exp_msk11; +#else + if ((de = (int32)(d0 >> Exp_shift)) != 0) + z |= Exp_msk1; +#endif + if ((y = d1) != 0) { + if ((k = lo0bits(&y)) != 0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; + i = b->wds = (x[1] = z) ? 2 : 1; + } + else { + JS_ASSERT(z); + k = lo0bits(&z); + x[0] = z; + i = b->wds = 1; + k += 32; + } +#ifndef Sudden_Underflow + if (de) { +#endif + *e = de - Bias - (P-1) + k; + *bits = P - k; +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; + *bits = 32*i - hi0bits(x[i-1]); + } +#endif + return b; +} +#undef d0 +#undef d1 +#undef set_d0 +#undef set_d1 + + +static double ratio(Bigint *a, Bigint *b) +{ + double da, db; + int32 k, ka, kb; + + da = b2d(a, &ka); + db = b2d(b, &kb); + k = ka - kb + 32*(a->wds - b->wds); + if (k > 0) + set_word0(da, word0(da) + k*Exp_msk1); + else { + k = -k; + set_word0(db, word0(db) + k*Exp_msk1); + } + return da / 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 +}; + +static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.e-256 +#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 + + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + +static int match(CONST char **sp, char *t) +{ + 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; + } +#endif /* INFNAN_CHECK */ + + +#ifdef JS_THREADSAFE +static JSBool initialized = JS_FALSE; + +/* hacked replica of nspr _PR_InitDtoa */ +static void InitDtoa(void) +{ + freelist_lock = PR_NewLock(); + p5s_lock = PR_NewLock(); + initialized = JS_TRUE; +} +#endif + +void js_FinishDtoa(void) +{ + int count; + Bigint *temp; + +#ifdef JS_THREADSAFE + if (initialized == JS_TRUE) { + PR_DestroyLock(freelist_lock); + PR_DestroyLock(p5s_lock); + initialized = JS_FALSE; + } +#endif + + /* clear down the freelist array and p5s */ + + /* static Bigint *freelist[Kmax+1]; */ + for (count = 0; count <= Kmax; count++) { + Bigint **listp = &freelist[count]; + while ((temp = *listp) != NULL) { + *listp = temp->next; + free(temp); + } + freelist[count] = NULL; + } + + /* static Bigint *p5s; */ + while (p5s) { + temp = p5s; + p5s = p5s->next; + free(temp); + } +} + +/* nspr2 watcom bug ifdef omitted */ + +JS_FRIEND_API(double) +JS_strtod(CONST char *s00, char **se, int *err) +{ + int32 scale; + int32 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, aadj1, adj, rv, rv0; + Long L; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + + SET_FPU(); + + *err = 0; + + bb = bd = bs = delta = NULL; + sign = nz0 = nz = 0; + rv = 0.; + + /* Locking for Balloc's shared buffers that will be used in this block */ + ACQUIRE_DTOA_LOCK(); + + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + s = s00; + goto ret; + 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; + 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) { + s = s00; + goto ret; + } + 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 = (int32)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,"nfinity")) { + 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; + goto ret; + } + } +#endif /* INFNAN_CHECK */ + s = s00; + } + 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; + rv = y; + if (k > 9) + rv = tens[k - 9] * rv + z; + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT + && FLT_ROUNDS == 1 +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { + /* rv = */ rounded_product(rv, tens[e]); + goto ret; + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + rv *= tens[i]; + /* rv = */ rounded_product(rv, tens[e]); + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(rv, tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + + scale = 0; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15) != 0) + rv *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + *err = JS_DTOA_ERANGE; +#ifdef __STDC__ + rv = HUGE_VAL; +#else + /* Can't trust HUGE_VAL */ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif + if (bd0) + goto retfree; + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= bigtens[j]; + /* The last multiplication could overflow. */ + set_word0(rv, word0(rv) - P*Exp_msk1); + 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) */ + set_word0(rv, Big0); + set_word1(rv, Big1); + } + else + set_word0(rv, word0(rv) + P*Exp_msk1); + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15) != 0) + rv /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + if (scale && (j = P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + set_word1(rv, 0); + set_word0(rv, word0(rv) & (0xffffffff << (j-32))); + if (!word0(rv)) + set_word0(rv, 1); + } + else + set_word1(rv, word1(rv) & (0xffffffff << j)); + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + /* The last multiplication could underflow. */ + rv0 = rv; + rv *= tinytens[j]; + if (!rv) { + rv = 2.*rv0; + rv *= tinytens[j]; +#endif + if (!rv) { + undfl: + rv = 0.; + *err = JS_DTOA_ERANGE; + if (bd0) + goto retfree; + goto ret; + } +#ifndef Avoid_Underflow + set_word0(rv, Tiny0); + set_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); + if (!bd0) + goto nomem; + + for(;;) { + bd = Balloc(bd0->k); + if (!bd) + goto nomem; + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + if (!bb) + goto nomem; + bs = i2b(1); + if (!bs) + goto nomem; + + 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 Sudden_Underflow + j = P + 1 - bbbits; +#else +#ifdef Avoid_Underflow + j = bbe - scale; +#else + j = bbe; +#endif + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif + 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); + if (!bs) + goto nomem; + bb1 = mult(bs, bb); + if (!bb1) + goto nomem; + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) { + bb = lshift(bb, bb2); + if (!bb) + goto nomem; + } + if (bd5 > 0) { + bd = pow5mult(bd, bd5); + if (!bd) + goto nomem; + } + if (bd2 > 0) { + bd = lshift(bd, bd2); + if (!bd) + goto nomem; + } + if (bs2 > 0) { + bs = lshift(bs, bs2); + if (!bs) + goto nomem; + } + delta = diff(bb, bd); + if (!delta) + goto nomem; + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); + 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 Avoid_Underflow + || (word0(rv) & Exp_mask) <= Exp_msk1 + P*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif + ) { +#ifdef Avoid_Underflow + if (!delta->x[0] && delta->wds == 1) + dsign = 2; +#endif + break; + } + delta = lshift(delta,Log2P); + if (!delta) + goto nomem; + 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) == 0xffffffff) { + /*boundary case -- increment exponent*/ + set_word0(rv, (word0(rv) & Exp_mask) + Exp_msk1); + set_word1(rv, 0); +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { +#ifdef Avoid_Underflow + dsign = 2; +#endif + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow + L = word0(rv) & Exp_mask; + if (L <= Exp_msk1) + goto undfl; + L -= Exp_msk1; +#else + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif + set_word0(rv, L | Bndry_mask1); + set_word1(rv, 0xffffffff); + break; + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + rv += ulp(rv); +#ifndef ROUND_BIASED + else { + rv -= ulp(rv); +#ifndef Sudden_Underflow + if (!rv) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + 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; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(FLT_ROUNDS) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (FLT_ROUNDS == 0) + aadj1 += 0.5; +#endif + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + rv0 = rv; + set_word0(rv, word0(rv) - P*Exp_msk1); + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + set_word0(rv, Big0); + set_word1(rv, Big1); + goto cont; + } + else + set_word0(rv, word0(rv) + P*Exp_msk1); + } + else { +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + rv0 = rv; + set_word0(rv, word0(rv) + P*Exp_msk1); + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + set_word0(rv, Tiny0); + set_word1(rv, Tiny1); + goto cont; + } + else + set_word0(rv, word0(rv) - P*Exp_msk1); + } + else { + adj = aadj1 * ulp(rv); + rv += adj; + } +#else + /* 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 . + */ +#ifdef Avoid_Underflow + if (y <= P*Exp_msk1 && aadj > 1.) +#else + if (y <= (P-1)*Exp_msk1 && aadj > 1.) +#endif + { + aadj1 = (double)(int32)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } +#ifdef Avoid_Underflow + if (scale && y <= P*Exp_msk1) + set_word0(aadj1, word0(aadj1) + (P+1)*Exp_msk1 - y); +#endif + adj = aadj1 * ulp(rv); + rv += adj; +#endif + } + z = word0(rv) & Exp_mask; +#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; + } + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + bb = bd = bs = delta = NULL; + } +#ifdef Avoid_Underflow + if (scale) { + set_word0(rv0, Exp_1 - P*Exp_msk1); + set_word1(rv0, 0); + if ((word0(rv) & Exp_mask) <= P*Exp_msk1 + && word1(rv) & 1 + && dsign != 2) { + if (dsign) { +#ifdef Sudden_Underflow + /* rv will be 0, but this would give the */ + /* right result if only rv *= rv0 worked. */ + set_word0(rv, word0(rv) + P*Exp_msk1); + set_word0(rv0, Exp_1 - 2*P*Exp_msk1); +#endif + rv += ulp(rv); + } + else + set_word1(rv, word1(rv) & ~1); + } + rv *= rv0; + } +#endif /* Avoid_Underflow */ +retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +ret: + RELEASE_DTOA_LOCK(); + if (se) + *se = (char *)s; + rv0 = sign ? -rv : rv; + goto ret1; + +nomem: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + *err = JS_DTOA_ENOMEM; + rv0 = 0; + +ret1: + RESTORE_FPU(); + return rv0; +} + + +/* 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; +} + +/* Return floor(b/S) and set b to be the remainder. As added restrictions, b must not have + * more words than S, the most significant word of S must not start with a 1 bit, and the + * returned quotient must be less than 36. */ +static int32 quorem(Bigint *b, Bigint *S) +{ + int32 n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; + ULong si, z, zs; +#endif + + n = S->wds; + JS_ASSERT(b->wds <= n); + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + JS_ASSERT(*sxe <= 0x7FFFFFFF); + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ + JS_ASSERT(q < 36); + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = (ULong)(y & 0xffffffffUL); +#else + 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); +#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 & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = (ULong)(y & 0xffffffffUL); +#else + 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); +#endif + } while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return (int32)q; +} + +/* 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. 92-101]. + * + * 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. + */ + +/* Always emits at least one digit. */ +/* If biasUp is set, then rounding in modes 2 and 3 will round away from zero + * when the number is exactly halfway between two representable values. For example, + * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. + * 2.49 will still round to 2, and 2.51 will still round to 3. */ +/* bufsize should be at least 20 for modes 0 and 1. For the other modes, + * bufsize should be two greater than the maximum number of output characters expected. */ +static JSBool +js_dtoa(double d, int mode, JSBool biasUp, int ndigits, + int *decpt, int *sign, char **rve, char *buf, size_t bufsize) +{ + /* 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-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> 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. + */ + + int32 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 + int32 denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s; + JSBool ok; + + SET_FPU(); + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + set_word0(d, word0(d) & ~Sign_bit); /* clear sign bit */ + } + else + *sign = 0; + + if ((word0(d) & Exp_mask) == Exp_mask) { + /* Infinity or NaN */ + *decpt = 9999; + s = !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"; + if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { + JS_ASSERT(JS_FALSE); +/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ + ok = JS_FALSE; + goto ret2; + } + strcpy(buf, s); + if (rve) { + *rve = buf[3] ? buf + 8 : buf + 3; + JS_ASSERT(**rve == '\0'); + } + ok = JS_TRUE; + goto ret2; + } + + b = NULL; /* initialize for abort protection */ + S = NULL; + mlo = mhi = NULL; + + if (!d) { + no_digits: + *decpt = 1; + if (bufsize < 2) { + JS_ASSERT(JS_FALSE); +/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ + ok = JS_FALSE; + goto ret2; + } + buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ + if (rve) + *rve = buf + 1; + /* We might have jumped to "no_digits" from below, so we need + * to be sure to free the potentially allocated Bigints to avoid + * memory leaks. */ + Bfree(b); + Bfree(S); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + ok = JS_TRUE; + goto ret2; + } + + b = d2b(d, &be, &bbits); + if (!b) + goto nomem; +#ifdef Sudden_Underflow + i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { +#endif + d2 = d; + set_word0(d2, word0(d2) & Frac_mask1); + set_word0(d2, word0(d2) | Exp_11); + + /* 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; +#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); + d2 = x; + set_word0(d2, word0(d2) - 31*Exp_msk1); /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int32)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. + If k_check is zero, we're guaranteed that k = floor(log10(d)). */ + j = bbits - i - 1; + /* At this point d = b/2^j, where b is an odd integer. */ + 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; + } + /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, + b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = 0; + 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; + } + /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ + /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, + when it turns out that k was computed too high by one. */ + + /* Ensure space for at least i+1 characters, including trailing null. */ + if (bufsize <= (size_t)i) { + Bfree(b); + JS_ASSERT(JS_FALSE); + ok = JS_FALSE; + goto ret2; + } + s = buf; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if ((j1 = -k) != 0) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + /* Check that k was computed correctly. */ + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + /* eps bounds the cumulative error. */ + eps = ieps*d + 7.; + set_word0(eps, word0(eps) - (P-1)*Exp_msk1); + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (Long)d; + d -= L; + *s++ = '0' + (char)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.) { + L = (Long)d; + d -= L; + *s++ = '0' + (char)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while(*--s == '0') ; + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = buf; + d = 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 || d < 5*ds || (!biasUp && d == 5*ds)) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = (Long) (d / ds); + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (char)L; + if (i == ilim) { + d += d; + if ((d > ds) || (d == ds && (L & 1 || biasUp))) { + bump_up: + while(*--s == '9') + if (s == buf) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif + 1 + P - bbits; + /* i is 1 plus the number of trailing zero bits in d's significand. Thus, + (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ + } + b2 += i; + s2 += i; + mhi = i2b(1); + if (!mhi) + goto nomem; + /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or + input (when mode < 2) significant digit, divided by 10^k. */ + } + /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in + b2, m2, and s2 without changing the equalities. */ + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + + /* Fold b5 into b and m5 into mhi. */ + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + if (!mhi) + goto nomem; + b1 = mult(mhi, b); + if (!b1) + goto nomem; + Bfree(b); + b = b1; + } + if ((j = b5 - m5) != 0) { + b = pow5mult(b, j); + if (!b) + goto nomem; + } + } + else { + b = pow5mult(b, b5); + if (!b) + goto nomem; + } + } + /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and + (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ + + S = i2b(1); + if (!S) + goto nomem; + if (s5 > 0) { + S = pow5mult(S, s5); + if (!S) + goto nomem; + } + /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and + (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ + + /* Check for special case that d is a normalized power of 2. */ + spec_case = 0; + if (mode < 2) { + 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 decimal output string's value is less than d. */ + 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. + */ + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; + /* i is the number of leading zero bits in the most significant word of S*2^s2. */ + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ + if (b2 > 0) { + b = lshift(b, b2); + if (!b) + goto nomem; + } + if (s2 > 0) { + S = lshift(S, s2); + if (!S) + goto nomem; + } + /* Now we have d/10^k = b/S and + (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (!b) + goto nomem; + if (leftright) { + mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + ilim = ilim1; + } + } + /* At this point 1 <= d/10^k = b/S < 10. */ + + if (ilim <= 0 && mode > 2) { + /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. + Output either zero or the minimum nonzero output depending on which is closer to d. */ + if (ilim < 0) + goto no_digits; + S = multadd(S,5,0); + if (!S) + goto nomem; + i = cmp(b,S); + if (i < 0 || (i == 0 && !biasUp)) { + /* Always emit at least one digit. If the number appears to be zero + using the current mode, then emit one '0' digit and set decpt to 1. */ + /*no_digits: + k = -1 - ndigits; + goto ret; */ + goto no_digits; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) { + mhi = lshift(mhi, m2); + if (!mhi) + goto nomem; + } + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + if (!mhi) + goto nomem; + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + if (!mhi) + goto nomem; + } + /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ + /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ + + 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); + /* j is b/S compared with mlo/S. */ + delta = diff(S, mhi); + if (!delta) + goto nomem; + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); + /* j1 is b/S compared with 1 - mhi/S. */ +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = (char)dig; + goto ret; + } +#endif + if ((j < 0) || (j == 0 && !mode +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant decimal digit. + Use whichever would produce a decimal value closer to d. */ + b = lshift(b, 1); + if (!b) + goto nomem; + j1 = cmp(b, S); + if (((j1 > 0) || (j1 == 0 && (dig & 1 || biasUp))) + && (dig++ == '9')) + goto round_9_up; + } + *s++ = (char)dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = (char)dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (!b) + goto nomem; + if (mlo == mhi) { + mlo = mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + else { + mlo = multadd(mlo, 10, 0); + if (!mlo) + goto nomem; + mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + } + } + else + for(i = 1;; i++) { + *s++ = (char)(dig = quorem(b,S) + '0'); + if (i >= ilim) + break; + b = multadd(b, 10, 0); + if (!b) + goto nomem; + } + + /* Round off last digit */ + + b = lshift(b, 1); + if (!b) + goto nomem; + j = cmp(b, S); + if ((j > 0) || (j == 0 && (dig & 1 || biasUp))) { + roundoff: + while(*--s == '9') + if (s == buf) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + /* Strip trailing zeros */ + while(*--s == '0') ; + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: + Bfree(b); + JS_ASSERT(s < buf + bufsize); + *s = '\0'; + if (rve) + *rve = s; + *decpt = k + 1; + ok = JS_TRUE; + goto ret2; + +nomem: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + Bfree(b); + ok = JS_FALSE; + +ret2: + RESTORE_FPU(); + return ok; +} + + +/* Mapping of JSDToStrMode -> js_dtoa mode */ +static const int dtoaModes[] = { + 0, /* DTOSTR_STANDARD */ + 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ + 3, /* DTOSTR_FIXED, */ + 2, /* DTOSTR_EXPONENTIAL, */ + 2}; /* DTOSTR_PRECISION */ + +JS_FRIEND_API(char *) +JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double d) +{ + int decPt; /* Position of decimal point relative to first digit returned by js_dtoa */ + int sign; /* Nonzero if the sign bit was set in d */ + int nDigits; /* Number of significand digits returned by js_dtoa */ + char *numBegin = buffer+2; /* Pointer to the digits returned by js_dtoa; the +2 leaves space for */ + /* the sign and/or decimal point */ + char *numEnd; /* Pointer past the digits returned by js_dtoa */ + JSBool dtoaRet; + + JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : + DTOSTR_VARIABLE_BUFFER_SIZE(precision))); + + if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) + mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ + + /* Locking for Balloc's shared buffers */ + ACQUIRE_DTOA_LOCK(); + dtoaRet = js_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2); + RELEASE_DTOA_LOCK(); + if (!dtoaRet) + return 0; + + nDigits = numEnd - numBegin; + + /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ + if (decPt != 9999) { + JSBool exponentialNotation = JS_FALSE; + int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ + 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, pad it with zeros at the end */ + 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; +} + + +/* "-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 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; + double di; /* d truncated to an integer */ + double df; /* The fractional part of d */ + + JS_ASSERT(base >= 2 && base <= 36); + + buffer = (char*) malloc(DTOBASESTR_BUFFER_SIZE); + if (buffer) { + p = buffer; + if (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++ = '-'; + d = -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; + } + + /* Locking for Balloc's shared buffers */ + ACQUIRE_DTOA_LOCK(); + + /* Output the integer part of d with the digits in reverse order. */ + pInt = p; + di = fd_floor(d); + if (di <= 4294967295.0) { + uint32 n = (uint32)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 { + int32 e; + int32 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); + 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; + } + + df = d - di; + if (df != 0.0) { + /* We have a fraction. */ + int32 e, bbits, 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); + 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'; + RELEASE_DTOA_LOCK(); + } + return buffer; +} diff --git a/src/extension/script/js/jsdtoa.h b/src/extension/script/js/jsdtoa.h new file mode 100644 index 000000000..409f45454 --- /dev/null +++ b/src/extension/script/js/jsdtoa.h @@ -0,0 +1,130 @@ +/* -*- 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. + */ +extern void js_FinishDtoa(void); + +JS_END_EXTERN_C + +#endif /* jsdtoa_h___ */ diff --git a/src/extension/script/js/jsemit.c b/src/extension/script/js/jsemit.c new file mode 100644 index 000000000..13e105c07 --- /dev/null +++ b/src/extension/script/js/jsemit.c @@ -0,0 +1,4471 @@ +/* -*- 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 bytecode generation. + */ +#include "jsstddef.h" +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include "jstypes.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 "jscntxt.h" +#include "jsconfig.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.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)) + +JS_FRIEND_API(JSBool) +js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, + JSArenaPool *codePool, JSArenaPool *notePool, + const char *filename, uintN lineno, + JSPrincipals *principals) +{ + memset(cg, 0, sizeof *cg); + TREE_CONTEXT_INIT(&cg->treeContext); + cg->treeContext.flags |= TCF_COMPILING; + cg->codePool = codePool; + cg->notePool = notePool; + cg->codeMark = JS_ARENA_MARK(codePool); + cg->noteMark = JS_ARENA_MARK(notePool); + cg->tempMark = JS_ARENA_MARK(&cx->tempPool); + cg->current = &cg->main; + cg->filename = filename; + cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno; + cg->principals = principals; + ATOM_LIST_INIT(&cg->atomList); + cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1; + return JS_TRUE; +} + +JS_FRIEND_API(void) +js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg) +{ + TREE_CONTEXT_FINISH(&cg->treeContext); + JS_ARENA_RELEASE(cg->codePool, cg->codeMark); + JS_ARENA_RELEASE(cg->notePool, cg->noteMark); + JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark); +} + +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 = PTRDIFF(next, base, jsbytecode); + 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(PTRDIFF(limit, base, jsbytecode)); + incr -= size; + JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + } + if (!base) { + JS_ReportOutOfMemory(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; + const JSCodeSpec *cs; + intN nuses; + + pc = CG_CODE(cg, target); + cs = &js_CodeSpec[pc[0]]; + nuses = cs->nuses; + if (nuses < 0) + nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ + cg->stackDepth -= nuses; + if (cg->stackDepth < 0) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", target); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_STACK_UNDERFLOW, + cg->filename ? cg->filename : "stdin", numBuf); + } + cg->stackDepth += cs->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; + 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"; + +static const char *statementName[] = { + "block", /* BLOCK */ + "label statement", /* LABEL */ + "if statement", /* IF */ + "else statement", /* ELSE */ + "switch statement", /* SWITCH */ + js_with_statement_str, /* WITH */ + "try statement", /* TRY */ + "catch block", /* CATCH */ + "finally statement", /* FINALLY */ + "do loop", /* DO_LOOP */ + "for loop", /* FOR_LOOP */ + "for/in loop", /* FOR_IN_LOOP */ + "while loop", /* WHILE_LOOP */ +}; + +static const char * +StatementName(JSCodeGenerator *cg) +{ + if (!cg->treeContext.topStmt) + return "script"; + return statementName[cg->treeContext.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.enteract.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_PUSH for jumps that push a + result on the interpreter's stack, namely JSOP_GOSUB; or 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_ReportOutOfMemory(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)) { + if (!sdbase) { + size = SPANDEPS_SIZE_MIN; + JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size); + } else { + size = SPANDEPS_SIZE(index); + JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size); + } + if (!sdbase) + return JS_FALSE; + cg->spanDeps = sdbase; + } + + cg->numSpanDeps = index + 1; + sd = cg->spanDeps + index; + sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode); + sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode); + + 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, NULL); + } 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 JSBool +BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) +{ + jsbytecode *pc, *end; + JSOp op; + const JSCodeSpec *cs; + ptrdiff_t len, off; + + pc = CG_BASE(cg); + end = CG_NEXT(cg); + while (pc < end) { + op = (JSOp)*pc; + cs = &js_CodeSpec[op]; + len = (ptrdiff_t)cs->length; + + switch (cs->format & JOF_TYPEMASK) { + case JOF_JUMP: + off = GET_JUMP_OFFSET(pc); + if (!AddSpanDep(cx, cg, pc, pc, off)) + return JS_FALSE; + break; + +#if JS_HAS_SWITCH_STATEMENT + case JOF_TABLESWITCH: + { + jsbytecode *pc2; + jsint i, low, high; + + pc2 = pc; + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return JS_FALSE; + pc2 += JUMP_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + for (i = low; i <= high; i++) { + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return JS_FALSE; + pc2 += JUMP_OFFSET_LEN; + } + len = 1 + pc2 - pc; + break; + } + + case JOF_LOOKUPSWITCH: + { + jsbytecode *pc2; + jsint npairs; + + pc2 = pc; + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return JS_FALSE; + pc2 += JUMP_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + while (npairs) { + pc2 += ATOM_INDEX_LEN; + off = GET_JUMP_OFFSET(pc2); + if (!AddSpanDep(cx, cg, pc, pc2, off)) + return JS_FALSE; + pc2 += JUMP_OFFSET_LEN; + npairs--; + } + len = 1 + pc2 - pc; + break; + } +#endif /* JS_HAS_SWITCH_STATEMENT */ + } + + pc += len; + } + + 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 = PTRDIFF(pc, CG_BASE(cg), jsbytecode); + 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; + JSTryNote *tn, *tnlimit; +#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 = (js_CodeSpec[op].format & JOF_TYPEMASK); + 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_TARGET_OFFSET(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: JS_ASSERT(0); + } + *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 + printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", + cg->filename ? cg->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(PTRDIFF(limit, base, jsbytecode)); + incr = BYTECODE_SIZE(length) - size; + JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); + if (!base) { + JS_ReportOutOfMemory(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 = (js_CodeSpec[op].format & JOF_TYPEMASK); + + 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_TARGET_OFFSET(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; + } + } + } + + /* + * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's + * not clear how we can beat that). + */ + for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) { + /* + * 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 = tn->start; + sd = FindNearestSpanDep(cg, offset, 0, &guard); + delta = sd->offset - sd->before; + tn->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 = tn->length; + sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); + if (sd2 != sd) + tn->length = length + sd2->offset - sd2->before - delta; + + /* + * Finally, adjust tn->catchStart upward only if it is non-zero, + * and provided there are spandeps below it that grew. + */ + offset = tn->catchStart; + if (offset != 0) { + sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard); + tn->catchStart = offset + sd->offset - sd->before; + } + } + } + +#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 = (js_CodeSpec[op].format & JOF_TYPEMASK); + 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_TARGET_OFFSET(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))); + JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps, + JS_MAX(size, SPANDEPS_SIZE_MIN)); + cg->spanDeps = NULL; + FreeJumpTargets(cg, cg->jumpTargets); + cg->jumpTargets = NULL; + cg->numSpanDeps = cg->numJumpTargets = 0; + return JS_TRUE; +} + +static JSBool +EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) +{ + ptrdiff_t jmp; + jsbytecode *pc; + + if (off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off) { + if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) + return JS_FALSE; + } + + jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); + if (jmp >= 0 && cg->spanDeps) { + pc = CG_CODE(cg, jmp); + if (!AddSpanDep(cx, cg, pc, pc, off)) + return JS_FALSE; + } + 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); +} + +JSBool +js_InWithStatement(JSTreeContext *tc) +{ + JSStmtInfo *stmt; + + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (stmt->type == STMT_WITH) + return JS_TRUE; + } + return JS_FALSE; +} + +JSBool +js_InCatchBlock(JSTreeContext *tc, JSAtom *atom) +{ + JSStmtInfo *stmt; + + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (stmt->type == STMT_CATCH && stmt->label == atom) + return JS_TRUE; + } + return JS_FALSE; +} + +void +js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, + ptrdiff_t top) +{ + stmt->type = type; + SET_STATEMENT_TOP(stmt, top); + stmt->label = NULL; + stmt->down = tc->topStmt; + tc->topStmt = stmt; +} + +/* + * 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. + */ +#define EMIT_BACKPATCH_OP(cx, cg, last, op, jmp) \ + JS_BEGIN_MACRO \ + ptrdiff_t offset, delta; \ + offset = CG_OFFSET(cg); \ + delta = offset - (last); \ + last = offset; \ + JS_ASSERT(delta > 0); \ + jmp = EmitJump((cx), (cg), (op), (delta)); \ + JS_END_MACRO + +/* Emit additional bytecode(s) for non-local jumps. */ +static JSBool +EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, + JSOp *returnop) +{ + intN depth; + JSStmtInfo *stmt; + ptrdiff_t jmp; + + /* + * Return from within a try block that has a finally clause must be split + * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval; + * and JSOP_RETRVAL, which makes control flow go back to the caller, who + * picks up fp->rval as usual. Otherwise, the stack will be unbalanced + * when executing the finally clause. + * + * We mutate *returnop once only if we find an enclosing try-block (viz, + * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one + * or more JSOP_GOSUBs and other fixup opcodes emitted by this function. + * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop. + * The fixup opcodes and gosubs must interleave in the proper order, from + * inner statement to outer, so that finally clauses run at the correct + * stack depth. + */ + if (returnop) { + JS_ASSERT(*returnop == JSOP_RETURN); + for (stmt = cg->treeContext.topStmt; stmt != toStmt; + stmt = stmt->down) { + if (stmt->type == STMT_FINALLY) { + if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) + return JS_FALSE; + *returnop = JSOP_RETRVAL; + break; + } + } + + /* + * If there are no try-with-finally blocks open around this return + * statement, we can generate a return forthwith and skip generating + * any fixup code. + */ + if (*returnop == JSOP_RETURN) + return JS_TRUE; + } + + /* + * 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; + for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { + switch (stmt->type) { + case STMT_FINALLY: + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_BACKPATCH_OP(cx, cg, stmt->gosub, JSOP_BACKPATCH_PUSH, jmp); + if (jmp < 0) + return JS_FALSE; + break; + + case STMT_WITH: + case STMT_CATCH: + /* There's a With object on the stack that we need to pop. */ + 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. + * JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN. + */ + if (js_Emit1(cx, cg, JSOP_POP2) < 0) + return JS_FALSE; + break; + + case STMT_SUBROUTINE: + /* There's a retsub pc-offset on the stack that we need to pop. */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + break; + + default:; + } + } + + cg->stackDepth = depth; + return JS_TRUE; +} + +static ptrdiff_t +EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, + ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType) +{ + intN index; + ptrdiff_t jmp; + + if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) + return -1; + + if (label) { + index = js_NewSrcNote(cx, cg, noteType); + if (index < 0) + return -1; + if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0, + (ptrdiff_t) ALE_INDEX(label))) { + return -1; + } + } else if (noteType != SRC_NULL) { + if (js_NewSrcNote(cx, cg, noteType) < 0) + return -1; + } + + EMIT_BACKPATCH_OP(cx, cg, *last, JSOP_BACKPATCH, jmp); + return jmp; +} + +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 = PTRDIFF(target, pc, jsbytecode); + 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) +{ + tc->topStmt = tc->topStmt->down; +} + +JSBool +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) +{ + JSStmtInfo *stmt; + + stmt = cg->treeContext.topStmt; + if (!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->treeContext); + return JS_TRUE; +} + +/* + * Emit a bytecode and its 2-byte constant (atom) index immediate operand. + * NB: We use cx and cg from our caller's lexical environment, and return + * false on error. + */ +#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ + JS_BEGIN_MACRO \ + if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex), \ + ATOM_INDEX_LO(atomIndex)) < 0) { \ + return JS_FALSE; \ + } \ + JS_END_MACRO + +static JSBool +EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSAtomListElement *ale; + + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale)); + return JS_TRUE; +} + +/* + * This routine tries to optimize name gets and sets to stack slot loads and + * stores, given the variables object and scope chain in cx's top frame, the + * compile-time context in tc, and a TOK_NAME node pn. It returns false on + * error, true on success. + * + * The caller can inspect pn->pn_slot for a non-negative slot number to tell + * whether optimization occurred, in which case LookupArgOrVar also updated + * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless + * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether + * or not pn->pn_op was modified, if this function finds an argument or local + * variable name, pn->pn_attrs will contain the property's attributes after a + * successful return. + */ +static JSBool +LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) +{ + JSObject *obj, *pobj; + JSClass *clasp; + JSAtom *atom; + JSScopeProperty *sprop; + JSOp op; + + JS_ASSERT(pn->pn_type == TOK_NAME); + if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) + return JS_TRUE; + + /* + * We can't optimize if var and closure (a local function not in a larger + * expression and not at top-level within another's body) collide. + * XXX suboptimal: keep track of colliding names and deoptimize only those + */ + if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) + return JS_TRUE; + + /* + * We can't optimize if we're not compiling a function body, whether via + * eval, or directly when compiling a function statement or expression. + */ + obj = cx->fp->varobj; + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp != &js_FunctionClass && clasp != &js_CallClass) + return JS_TRUE; + + /* + * We can't optimize if we're in an eval called inside a with statement, + * or we're compiling a with statement and its body, or we're in a catch + * block whose exception variable has the same name as pn. + */ + atom = pn->pn_atom; + if (cx->fp->scopeChain != obj || + js_InWithStatement(tc) || + js_InCatchBlock(tc, atom)) { + return JS_TRUE; + } + + /* + * Ok, we may be able to optimize name to stack slot. Look for an argument + * or variable property in the function, or its call object, not found in + * any prototype object. Rewrite pn_op and update pn accordingly. NB: We + * know that JSOP_DELNAME on an argument or variable must evaluate to + * false, due to JSPROP_PERMANENT. + */ + if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop)) + return JS_FALSE; + op = pn->pn_op; + if (sprop) { + if (pobj == obj) { + JSPropertyOp getter = sprop->getter; + + if (getter == js_GetArgument) { + 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; + case JSOP_DELNAME: op = JSOP_FALSE; break; + default: JS_ASSERT(0); + } + } else if (getter == js_GetLocalVariable || + getter == js_GetCallVariable) + { + switch (op) { + case JSOP_NAME: op = JSOP_GETVAR; break; + case JSOP_SETNAME: op = JSOP_SETVAR; break; + case JSOP_SETCONST: op = JSOP_SETVAR; break; + case JSOP_INCNAME: op = JSOP_INCVAR; break; + case JSOP_NAMEINC: op = JSOP_VARINC; break; + case JSOP_DECNAME: op = JSOP_DECVAR; break; + case JSOP_NAMEDEC: op = JSOP_VARDEC; break; + case JSOP_FORNAME: op = JSOP_FORVAR; break; + case JSOP_DELNAME: op = JSOP_FALSE; break; + default: JS_ASSERT(0); + } + } + if (op != pn->pn_op) { + pn->pn_op = op; + pn->pn_slot = sprop->shortid; + } + pn->pn_attrs = sprop->attrs; + } + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + } + + if (pn->pn_slot < 0) { + /* + * We couldn't optimize it, so it's not an arg or local var name. Now + * we must check for the predefined arguments variable. It may be + * overridden by assignment, in which case the function is heavyweight + * and the interpreter will look up 'arguments' in the function's call + * object. + */ + if (pn->pn_op == JSOP_NAME && + atom == cx->runtime->atomState.argumentsAtom) { + pn->pn_op = JSOP_ARGUMENTS; + return JS_TRUE; + } + + tc->flags |= TCF_FUN_USES_NONLOCALS; + } + 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, JSTreeContext *tc, JSParseNode *pn, + JSBool *answer) +{ + JSBool ok; + JSFunction *fun; + JSParseNode *pn2; + + ok = JS_TRUE; + if (!pn || *answer) + return ok; + + switch (pn->pn_arity) { + case PN_FUNC: + /* + * A named function is presumed useful: we can't yet know that it is + * not called. The side effects are the creation of a scope object + * to parent this function object, and the binding of the function's + * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: + * in jsinterp.c. + */ + fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); + if (fun->atom) + *answer = JS_TRUE; + break; + + case PN_LIST: + if (pn->pn_type == TOK_NEW || + pn->pn_type == TOK_LP || + pn->pn_type == TOK_LB) { + /* + * 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). + */ + *answer = JS_TRUE; + } else { + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) + ok &= CheckSideEffects(cx, tc, pn2, answer); + } + break; + + case PN_TERNARY: + ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && + CheckSideEffects(cx, tc, pn->pn_kid2, answer) && + CheckSideEffects(cx, tc, 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. + */ + *answer = JS_TRUE; + } else { + if (pn->pn_type == TOK_LB) { + pn2 = pn->pn_left; + if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2)) + return JS_FALSE; + if (pn2->pn_op != JSOP_ARGUMENTS) { + /* + * Any indexed property reference could call a getter with + * side effects, except for arguments[i] where arguments is + * unambiguous. + */ + *answer = JS_TRUE; + } + } + ok = CheckSideEffects(cx, tc, pn->pn_left, answer) && + CheckSideEffects(cx, tc, pn->pn_right, answer); + } + break; + + case PN_UNARY: + if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || + pn->pn_type == TOK_DELETE || + pn->pn_type == TOK_THROW || + pn->pn_type == TOK_DEFSHARP) { + /* All these operations have effects that we must commit. */ + *answer = JS_TRUE; + } else { + ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); + } + break; + + case PN_NAME: + if (pn->pn_type == TOK_NAME) { + if (!LookupArgOrVar(cx, tc, pn)) + return JS_FALSE; + if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { + /* + * Not an argument or local variable use, so this expression + * could invoke a getter that has side effects. + */ + *answer = JS_TRUE; + } + } + pn2 = pn->pn_expr; + if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { + if (!LookupArgOrVar(cx, tc, 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, tc, pn2, answer); + break; + + case PN_NULLARY: + if (pn->pn_type == TOK_DEBUGGER) + *answer = JS_TRUE; + break; + } + return ok; +} + +static JSBool +EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + JSParseNode *pn2, *pndot, *pnup, *pndown; + ptrdiff_t top; + JSAtomListElement *ale; + + pn2 = pn->pn_expr; + if (op == JSOP_GETPROP && + pn->pn_type == TOK_DOT && + pn2->pn_type == TOK_NAME) { + /* Try to optimize arguments.length into JSOP_ARGCNT. */ + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) + return JS_FALSE; + if (pn2->pn_op == JSOP_ARGUMENTS && + pn->pn_atom == cx->runtime->atomState.lengthAtom) { + return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; + } + } + + /* + * 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; + 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; + } + ale = js_IndexAtom(cx, pndot->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(pndot->pn_op, ALE_INDEX(ale)); + + /* 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; + if (!pn->pn_atom) { + JS_ASSERT(op == JSOP_IMPORTALL); + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else { + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale)); + } + return JS_TRUE; +} + +static JSBool +EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) +{ + ptrdiff_t top; + JSParseNode *left, *right, *next; + 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(pn); + 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 (!LookupArgOrVar(cx, &cg->treeContext, left)) + return JS_FALSE; + if (left->pn_op == JSOP_ARGUMENTS && + JSDOUBLE_IS_INT(next->pn_dval, slot) && + (jsuint)slot < ATOM_INDEX_LIMIT) { + left->pn_offset = next->pn_offset = top; + EMIT_ATOM_INDEX_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 { + 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 (!LookupArgOrVar(cx, &cg->treeContext, left)) + return JS_FALSE; + if (left->pn_op == JSOP_ARGUMENTS && + JSDOUBLE_IS_INT(right->pn_dval, slot) && + (jsuint)slot < ATOM_INDEX_LIMIT) { + left->pn_offset = right->pn_offset = top; + EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot); + return JS_TRUE; + } + } + + if (!js_EmitTree(cx, cg, left)) + return JS_FALSE; + } + 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; + jsatomid atomIndex; + 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 ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) { + atomIndex = (jsatomid)ival; + EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex); + return JS_TRUE; + } + atom = js_AtomizeInt(cx, ival, 0); + } else { + atom = js_AtomizeDouble(cx, dval, 0); + } + if (!atom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ALE_INDEX(ale)); + return JS_TRUE; +} + +#if JS_HAS_SWITCH_STATEMENT +static JSBool +EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, + JSStmtInfo *stmtInfo) +{ + JSOp switchop; + JSBool ok, hasDefault; + ptrdiff_t top, off, defaultOffset; + JSParseNode *pn2, *pn3, *pn4; + uint32 ncases, tablen; + jsdouble d; + jsint i, low, high; + JSAtom *atom; + JSAtomListElement *ale; + intN noteIndex; + size_t switchsize, tablesize; + JSParseNode **table; + jsbytecode *pc; + + /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ + switchop = JSOP_TABLESWITCH; + ok = JS_TRUE; + hasDefault = JS_FALSE; + defaultOffset = -1; + + /* Emit code for the discriminant first. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + + /* Switch bytecodes run from here till end of final case. */ + top = CG_OFFSET(cg); + js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); + + pn2 = pn->pn_kid2; + ncases = pn2->pn_count; + tablen = 0; + + if (ncases == 0 || + (ncases == 1 && + (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { + ncases = 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; + ncases--; /* one of the "cases" was the default */ + continue; + } + + JS_ASSERT(pn3->pn_type == TOK_CASE); + if (switchop == JSOP_CONDSWITCH) + continue; + + pn4 = pn3->pn_left; + 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, 0); + 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_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_NUMBER(pn3->pn_val) || + JSVAL_IS_STRING(pn3->pn_val) || + JSVAL_IS_BOOLEAN(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 *) + JS_malloc(cx, + (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) + JS_free(cx, intmap); + if (!ok) + return JS_FALSE; + + /* + * Compute table length and select lookup instead if overlarge or + * more than half-sparse. + */ + if (switchop == JSOP_TABLESWITCH) { + tablen = (uint32)(high - low + 1); + if (tablen >= JS_BIT(16) || tablen > 2 * ncases) + switchop = JSOP_LOOKUPSWITCH; + } + } + + /* + * 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 + tablen)); + } 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 + ATOM_INDEX_LEN + + (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * ncases); + } + + /* + * 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; + + /* 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 (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 (pn3 == pn2->pn_head) { + /* Switch note's second offset is to first JSOP_CASE. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, + off - top)) { + 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 if (switchop == JSOP_TABLESWITCH) { + /* Fill in switch bounds, which we know fit in 16-bit offsets. */ + pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); + SET_JUMP_OFFSET(pc, low); + pc += JUMP_OFFSET_LEN; + SET_JUMP_OFFSET(pc, high); + pc += JUMP_OFFSET_LEN; + } else { + JS_ASSERT(switchop == JSOP_LOOKUPSWITCH); + + /* Fill in the number of cases. */ + pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); + SET_ATOM_INDEX(pc, ncases); + } + + /* 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(cx, cg, pn3->pn_offset); + pn4 = pn3->pn_right; + if (!js_EmitTree(cx, cg, pn4)) + return JS_FALSE; + 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). */ + pc = NULL; + if (switchop == JSOP_CONDSWITCH) { + JS_ASSERT(defaultOffset != -1); + if (!js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), + off - (defaultOffset - top))) { + return JS_FALSE; + } + } else { + pc = CG_CODE(cg, top); + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + pc += JUMP_OFFSET_LEN; + } + + /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ + off = CG_OFFSET(cg) - top; + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off)) + return JS_FALSE; + + 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. */ + if (tablen) { + /* Avoid bloat for a compilation unit with many switches. */ + tablesize = (size_t)tablen * sizeof *table; + table = (JSParseNode **) JS_malloc(cx, 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 < tablen); + table[i] = pn3; + } + for (i = 0; i < (jsint)tablen; i++) { + pn3 = table[i]; + off = pn3 ? pn3->pn_offset - top : 0; + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + break; + pc += JUMP_OFFSET_LEN; + } + JS_free(cx, table); + if (!ok) + return JS_FALSE; + } + } else if (switchop == JSOP_LOOKUPSWITCH) { + /* Skip over the already-initialized number of cases. */ + pc += ATOM_INDEX_LEN; + + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) + continue; + atom = js_AtomizeValue(cx, pn3->pn_val, 0); + if (!atom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + SET_ATOM_INDEX(pc, ALE_INDEX(ale)); + pc += ATOM_INDEX_LEN; + + off = pn3->pn_offset - top; + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + pc += JUMP_OFFSET_LEN; + } + } + + return js_PopStatementCG(cx, cg); +} +#endif /* JS_HAS_SWITCH_STATEMENT */ + +JSBool +js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, + JSFunction *fun) +{ + JSStackFrame *fp, frame; + JSObject *funobj; + JSBool ok; + + if (!js_AllocTryNotes(cx, cg)) + return JS_FALSE; + + fp = cx->fp; + funobj = fun->object; + if (!fp || fp->fun != fun || fp->varobj != funobj || + fp->scopeChain != funobj) { + memset(&frame, 0, sizeof frame); + frame.fun = fun; + frame.varobj = frame.scopeChain = funobj; + frame.down = fp; + cx->fp = &frame; + } + ok = js_EmitTree(cx, cg, body); + cx->fp = fp; + if (!ok) + return JS_FALSE; + + fun->script = js_NewScriptFromCG(cx, cg, fun); + if (!fun->script) + return JS_FALSE; + if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + return JS_TRUE; +} + +/* A macro for inlining at the top of js_EmitTree (whence it came). */ +#define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ + JS_BEGIN_MACRO \ + uintN line_ = (pn)->pn_pos.begin.lineno; \ + 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, JSParseNode *pn) +{ + UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); + return JS_TRUE; +} + +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; + intN noteIndex; + JSSrcNoteType noteType; + jsbytecode *pc; + JSOp op; + uint32 argc; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + 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); + + switch (pn->pn_type) { + case TOK_FUNCTION: + { + void *cg2mark; + JSCodeGenerator *cg2; + JSFunction *fun; + + /* Generate code for the function's body. */ + cg2mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); + if (!cg2) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool, + cg->filename, pn->pn_pos.begin.lineno, + cg->principals)) { + return JS_FALSE; + } + cg2->treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION; + cg2->treeContext.tryCount = pn->pn_tryCount; + fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); + if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) + return JS_FALSE; + + /* + * We need an activation object if an inner peeks out, or if such + * inner-peeking caused one of our inners to become heavyweight. + */ + if (cg2->treeContext.flags & + (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) { + cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT; + } + js_FinishCodeGenerator(cx, cg2); + JS_ARENA_RELEASE(&cx->tempPool, cg2mark); + + /* Make the function object a literal in the outer script's pool. */ + ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + +#if JS_HAS_LEXICAL_CLOSURE + /* Emit a bytecode pointing to the closure object in its immediate. */ + if (pn->pn_op != JSOP_NOP) { + EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); + break; + } +#endif + + /* Top-level named functions need a nop for decompilation. */ + noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex); + if (noteIndex < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* + * Top-levels also need a prolog op to predefine their names in the + * variable object, or if local, to fill their stack slots. + */ + CG_SWITCH_TO_PROLOG(cg); + +#if JS_HAS_LEXICAL_CLOSURE + if (cg->treeContext.flags & TCF_IN_FUNCTION) { + JSObject *obj, *pobj; + JSScopeProperty *sprop; + uintN slot; + + obj = OBJ_GET_PARENT(cx, fun->object); + if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj, + (JSProperty **)&sprop)) { + return JS_FALSE; + } + JS_ASSERT(sprop && pobj == obj); + slot = sprop->shortid; + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + + /* Emit [JSOP_DEFLOCALFUN, local variable slot, atomIndex]. */ + off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN+ATOM_INDEX_LEN); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_VARNO(pc, slot); + pc += VARNO_LEN; + SET_ATOM_INDEX(pc, atomIndex); + } else +#endif + EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex); + + CG_SWITCH_TO_MAIN(cg); + break; + } + +#if JS_HAS_EXPORT_IMPORT + case TOK_EXPORT: + pn2 = pn->pn_head; + if (pn2->pn_type == TOK_STAR) { + /* + * 'export *' must have no other elements in the list (what would + * be the point?). + */ + if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) + return JS_FALSE; + } else { + /* + * If not 'export *', the list consists of NAME nodes identifying + * properties of the variables object to flag as exported. + */ + do { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale)); + } while ((pn2 = pn2->pn_next) != NULL); + } + break; + + case TOK_IMPORT: + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Each subtree on an import list is rooted by a DOT or LB node. + * A DOT may have a null pn_atom member, in which case pn_op must + * be JSOP_IMPORTALL -- see EmitPropOp above. + */ + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + 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; + if (stmtInfo.type == STMT_IF) { + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, + CG_OFFSET(cg)); + } 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. + */ + JS_ASSERT(stmtInfo.type == STMT_ELSE); + stmtInfo.type = STMT_IF; + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) + 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; + +#if JS_HAS_SWITCH_STATEMENT + case TOK_SWITCH: + /* Out of line to avoid bloating js_EmitTree's stack frame size. */ + ok = EmitSwitch(cx, cg, pn, &stmtInfo); + break; +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case TOK_WHILE: + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); + if (jmp < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) + return JS_FALSE; + ok = js_PopStatementCG(cx, cg); + break; + +#if JS_HAS_DO_WHILE_LOOP + case TOK_DO: + /* Emit an annotated nop so we know to decompile a 'do' keyword. */ + if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Compile the loop body. */ + top = CG_OFFSET(cg); + js_PushStatement(&cg->treeContext, &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; + + /* + * No source note needed, because JSOP_IFNE is used only for do-while. + * If we ever use JSOP_IFNE for other purposes, we can still avoid yet + * another note here, by storing (jmp - top) in the SRC_WHILE note's + * offset, and fetching that delta in order to decompile recursively. + */ + if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0) + return JS_FALSE; + ok = js_PopStatementCG(cx, cg); + break; +#endif /* JS_HAS_DO_WHILE_LOOP */ + + case TOK_FOR: + beq = 0; /* suppress gcc warnings */ + pn2 = pn->pn_left; + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); + + if (pn2->pn_type == TOK_IN) { + /* Set stmtInfo type for later testing. */ + stmtInfo.type = STMT_FOR_IN_LOOP; + noteIndex = -1; + + /* If the left part is var x = i, bind x, evaluate i, and pop. */ + pn3 = pn2->pn_left; + if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + /* Set pn3 to the variable name, to avoid another var note. */ + pn3 = pn3->pn_head; + JS_ASSERT(pn3->pn_type == TOK_NAME); + } + + /* Emit a push to allocate the iterator. */ + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + + /* Compile the object expression to the right of 'in'. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0) + return JS_FALSE; + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + + /* Compile a JSOP_FOR* bytecode based on the left hand side. */ + switch (pn3->pn_type) { + case TOK_VAR: + pn3 = pn3->pn_head; + if (js_NewSrcNote(cx, cg, SRC_VAR) < 0) + return JS_FALSE; + /* FALL THROUGH */ + case TOK_NAME: + pn3->pn_op = JSOP_FORNAME; + if (!LookupArgOrVar(cx, &cg->treeContext, pn3)) + return JS_FALSE; + op = pn3->pn_op; + if (pn3->pn_slot >= 0) { + if (pn3->pn_attrs & JSPROP_READONLY) + op = JSOP_GETVAR; + atomIndex = (jsatomid) pn3->pn_slot; + EMIT_ATOM_INDEX_OP(op, atomIndex); + } else { + if (!EmitAtomOp(cx, pn3, op, cg)) + return JS_FALSE; + } + break; + + case TOK_DOT: + if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) + return JS_FALSE; + break; + + case TOK_LB: + /* + * We separate the first/next bytecode from the enumerator + * variable binding to avoid any side-effects in the index + * expression (e.g., for (x[i++] in {}) should not bind x[i] + * or increment i at all). + */ + if (!js_Emit1(cx, cg, JSOP_FORELEM)) + return JS_FALSE; + + /* + * Emit a SRC_WHILE note with offset telling the distance to + * the loop-closing jump (we can't reckon from the branch at + * the top of the loop, because the loop-closing jump might + * need to be an extended jump, independent of whether the + * branch is short or long). + */ + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0) + return JS_FALSE; + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0) + return JS_FALSE; + + /* Now that we're safely past the IFEQ, commit side effects. */ + if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) + return JS_FALSE; + break; + + default: + JS_ASSERT(0); + } + if (pn3->pn_type != TOK_LB) { + /* Annotate so the decompiler can find the loop-closing jump. */ + noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); + if (noteIndex < 0) + return JS_FALSE; + + /* Pop and test the loop condition generated by JSOP_FOR*. */ + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0) + return JS_FALSE; + } + } else { + if (!pn2->pn_kid1) { + /* No initializer: emit an annotated nop for the decompiler. */ + op = JSOP_NOP; + } else { + if (!js_EmitTree(cx, cg, pn2->pn_kid1)) + return JS_FALSE; + op = JSOP_POP; + } + noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); + if (noteIndex < 0 || + js_Emit1(cx, cg, op) < 0) { + return JS_FALSE; + } + + top = CG_OFFSET(cg); + SET_STATEMENT_TOP(&stmtInfo, top); + if (!pn2->pn_kid2) { + /* No loop condition: flag this fact in the source notes. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0)) + return JS_FALSE; + } else { + if (!js_EmitTree(cx, cg, pn2->pn_kid2)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, + CG_OFFSET(cg) - top)) { + return JS_FALSE; + } + beq = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (beq < 0) + return JS_FALSE; + } + + /* Set pn3 (used below) here to avoid spurious gcc warnings. */ + pn3 = pn2->pn_kid3; + } + + /* Emit code for the loop body. */ + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + + if (pn2->pn_type != TOK_IN) { + /* 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) - top)) { + return JS_FALSE; + } + + if (pn3) { + /* 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); + + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 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; + } + } + + /* The third note offset helps us find the loop-closing jump. */ + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, + CG_OFFSET(cg) - top)) { + return JS_FALSE; + } + } + + /* Emit the loop-closing jump and fixup all jump offsets. */ + jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); + if (jmp < 0) + return JS_FALSE; + if (beq > 0) + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); + if (pn2->pn_type == TOK_IN) { + /* Set the SRC_WHILE note offset so we can find the closing jump. */ + JS_ASSERT(noteIndex != -1); + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq)) + return JS_FALSE; + } + + /* Now fixup all breaks and continues (before for/in's final POP2). */ + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; + + if (pn2->pn_type == TOK_IN) { + /* + * Generate the object and iterator pop opcodes after popping the + * stmtInfo stack, so breaks will go to this pop bytecode. + */ + if (pn3->pn_type != TOK_LB) { + if (js_Emit1(cx, cg, JSOP_POP2) < 0) + return JS_FALSE; + } else { + /* + * With 'for(x[i]...)', there's only the object on the stack, + * so we need to hide the pop. + */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } + } + break; + + case TOK_BREAK: + stmt = cg->treeContext.topStmt; + atom = pn->pn_atom; + if (atom) { + ale = js_IndexAtom(cx, atom, &cg->atomList); + 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 = SRC_NULL; + } + + if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) + return JS_FALSE; + break; + + case TOK_CONTINUE: + stmt = cg->treeContext.topStmt; + atom = pn->pn_atom; + if (atom) { + /* Find the loop statement enclosed by the matching label. */ + JSStmtInfo *loop = NULL; + ale = js_IndexAtom(cx, atom, &cg->atomList); + 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->treeContext, &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; + +#if JS_HAS_EXCEPTIONS + + case TOK_TRY: + { + ptrdiff_t start, end, catchStart, finallyCatch, catchJump; + JSParseNode *iter; + intN depth; + + /* Quell GCC overwarnings. */ + end = catchStart = finallyCatch = catchJump = -1; + +/* Emit JSOP_GOTO that points to the first op after the catch/finally blocks */ +#define EMIT_CATCH_GOTO(cx, cg, jmp) \ + EMIT_BACKPATCH_OP(cx, cg, stmtInfo.catchJump, JSOP_BACKPATCH, jmp) + +/* Emit JSOP_GOSUB that points to the finally block. */ +#define EMIT_FINALLY_GOSUB(cx, cg, jmp) \ + EMIT_BACKPATCH_OP(cx, cg, stmtInfo.gosub, JSOP_BACKPATCH_PUSH, jmp) + + /* + * Push stmtInfo to track jumps-over-catches and gosubs-to-finally + * for later fixup. + * + * When a finally block is `active' (STMT_FINALLY on the treeContext), + * non-local jumps (including jumps-over-catches) result in a GOSUB + * being written into the bytecode stream and fixed-up later (c.f. + * EMIT_BACKPATCH_OP and BackPatch). + */ + js_PushStatement(&cg->treeContext, &stmtInfo, + pn->pn_kid3 ? STMT_FINALLY : STMT_BLOCK, + CG_OFFSET(cg)); + + /* + * About JSOP_SETSP: an exception can be thrown while the stack is in + * an unbalanced state, and this imbalance causes problems with things + * like function invocation later on. + * + * To fix this, we compute the `balanced' stack depth upon try entry, + * and then restore the stack to this depth when we hit the first catch + * or finally block. We can't just zero the stack, because things like + * for/in and with that are active upon entry to the block keep state + * variables on the stack. + */ + depth = cg->stackDepth; + + /* Mark try location for decompilation, then emit try block. */ + if (js_Emit1(cx, cg, JSOP_TRY) < 0) + return JS_FALSE; + start = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, pn->pn_kid1)) + return JS_FALSE; + + /* GOSUB to finally, if present. */ + if (pn->pn_kid3) { + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_FINALLY_GOSUB(cx, cg, jmp); + 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; + EMIT_CATCH_GOTO(cx, cg, jmp); + if (jmp < 0) + return JS_FALSE; + + end = CG_OFFSET(cg); + + /* If this try has a catch block, emit it. */ + iter = pn->pn_kid2; + if (iter) { + catchStart = end; + + /* + * The emitted code for a catch block looks like: + * + * [ popscope ] only if 2nd+ catch block + * name Object + * pushobj + * newinit + * exception + * initcatchvar + * enterwith + * [< catchguard code >] if there's a catchguard + * [ifeq ] " " + * < catch block contents > + * leavewith + * 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 (;;) { + JSStmtInfo stmtInfo2; + JSParseNode *disc; + ptrdiff_t guardnote; + + if (!UpdateLineNumberNotes(cx, cg, iter)) + return JS_FALSE; + + if (catchJump != -1) { + /* Fix up and clean up previous catch block. */ + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump); + + /* Compensate for the [leavewith]. */ + cg->stackDepth++; + JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth); + + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { + return JS_FALSE; + } + } else { + /* Set stack to original depth (see SETSP comment above). */ + EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth); + cg->stackDepth = depth; + } + + /* Non-negative guardnote offset is length of catchguard. */ + guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); + if (guardnote < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { + return JS_FALSE; + } + + /* Construct the scope holder and push it on. */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); + + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 || + js_Emit1(cx, cg, JSOP_NEWINIT) < 0 || + js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) { + return JS_FALSE; + } + + /* initcatchvar */ + disc = iter->pn_kid1; + ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + + EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale)); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) { + return JS_FALSE; + } + + /* boolean_expr */ + if (disc->pn_expr) { + ptrdiff_t guardstart = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, disc->pn_expr)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0, + CG_OFFSET(cg) - guardstart)) { + return JS_FALSE; + } + /* ifeq */ + catchJump = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (catchJump < 0) + return JS_FALSE; + } + + /* Emit catch block. */ + js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH, + CG_OFFSET(cg)); + stmtInfo2.label = disc->pn_atom; + if (!js_EmitTree(cx, cg, iter->pn_kid3)) + return JS_FALSE; + js_PopStatementCG(cx, cg); + + /* + * Jump over the remaining catch blocks. + * This counts as a non-local jump, so do the finally thing. + */ + + /* popscope */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { + return JS_FALSE; + } + + /* gosub , if required */ + if (pn->pn_kid3) { + EMIT_FINALLY_GOSUB(cx, cg, jmp); + if (jmp < 0) + return JS_FALSE; + } + + /* This will get fixed up to jump to after catch/finally. */ + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_CATCH_GOTO(cx, cg, jmp); + if (jmp < 0) + return JS_FALSE; + if (!iter->pn_kid2) /* leave iter at last catch */ + break; + iter = iter->pn_kid2; + } + } + + /* + * We use a [leavewith],[gosub],rethrow block for rethrowing + * when there's no unguarded catch, and also for running finally + * code while letting an uncaught exception pass through. + */ + if (pn->pn_kid3 || + (catchJump != -1 && iter->pn_kid1->pn_expr)) { + /* + * Emit another stack fixup, because the catch could itself + * throw an exception in an unbalanced state, and the finally + * may need to call functions. If there is no finally, only + * guarded catches, the rethrow code below nevertheless needs + * stack fixup. + */ + finallyCatch = CG_OFFSET(cg); + EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth); + cg->stackDepth = depth; + + /* Last discriminant jumps to rethrow if none match. */ + if (catchJump != -1 && iter->pn_kid1->pn_expr) { + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump); + + /* Compensate for the [leavewith]. */ + cg->stackDepth++; + JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth); + + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { + return JS_FALSE; + } + } + + if (pn->pn_kid3) { + EMIT_FINALLY_GOSUB(cx, cg, jmp); + if (jmp < 0) + return JS_FALSE; + cg->stackDepth = depth; + } + + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 || + js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_THROW) < 0) { + return JS_FALSE; + } + JS_ASSERT(cg->stackDepth == depth); + } + + /* + * If we have a finally, it belongs here, and we have to fix up the + * gosubs that might have been emitted before non-local jumps. + */ + if (pn->pn_kid3) { + if (!BackPatch(cx, cg, stmtInfo.gosub, CG_NEXT(cg), JSOP_GOSUB)) + return JS_FALSE; + + /* + * The stack budget must be balanced at this point, and we need + * one more slot for the JSOP_RETSUB return address pushed by a + * JSOP_GOSUB opcode that calls this finally clause. + */ + JS_ASSERT(cg->stackDepth == depth); + if ((uintN)++cg->stackDepth > cg->maxStackDepth) + cg->maxStackDepth = cg->stackDepth; + + /* Now indicate that we're emitting a subroutine body. */ + stmtInfo.type = STMT_SUBROUTINE; + if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3)) + 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_PopStatementCG(cx, cg); + + 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, stmtInfo.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) { + JS_ASSERT(end != -1 && catchStart != -1); + if (!js_NewTryNote(cx, cg, start, end, catchStart)) + 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) { + JS_ASSERT(finallyCatch != -1); + if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch)) + return JS_FALSE; + } + break; + } + +#endif /* JS_HAS_EXCEPTIONS */ + + case TOK_VAR: + off = noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + JS_ASSERT(pn2->pn_type == TOK_NAME); + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) + return JS_FALSE; + op = pn2->pn_op; + if (op == JSOP_ARGUMENTS) { + JS_ASSERT(!pn2->pn_expr); /* JSOP_ARGUMENTS => no initializer */ +#ifdef __GNUC__ + atomIndex = 0; /* quell GCC overwarning */ +#endif + } else { + if (pn2->pn_slot >= 0) { + atomIndex = (jsatomid) pn2->pn_slot; + } else { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + + if (!(cg->treeContext.flags & TCF_IN_FUNCTION) || + (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)) { + /* Emit a prolog bytecode to predefine the variable. */ + CG_SWITCH_TO_PROLOG(cg); + if (!UpdateLineNumberNotes(cx, cg, pn2)) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); + CG_SWITCH_TO_MAIN(cg); + } + } + if (pn2->pn_expr) { + if (op == JSOP_SETNAME) + EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); + if (!js_EmitTree(cx, cg, pn2->pn_expr)) + return JS_FALSE; + } + } + if (pn2 == pn->pn_head && + js_NewSrcNote(cx, cg, + (pn->pn_op == JSOP_DEFCONST) + ? SRC_CONST + : SRC_VAR) < 0) { + return JS_FALSE; + } + if (op == JSOP_ARGUMENTS) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + } else { + EMIT_ATOM_INDEX_OP(op, atomIndex); + } + 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; + } + } + if (pn->pn_extra) { + if (js_Emit1(cx, cg, JSOP_POP) < 0) + 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 mutates op to JSOP_RETRVAL after emitting a + * JSOP_SETRVAL if there are open try blocks having finally clauses. + * We can't simply transfer control flow to our caller in that case, + * because we must gosub to those 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). + */ + op = JSOP_RETURN; + if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op)) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; + + case TOK_LC: + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, 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. + */ + useful = wantval = !cx->fp->fun || + cx->fp->fun->native || + (cx->fp->flags & JSFRAME_SPECIAL); + if (!useful) { + if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) + return JS_FALSE; + } + if (!useful) { + CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; + if (!js_ReportCompileErrorNumber(cx, NULL, cg, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_USELESS_EXPR)) { + return JS_FALSE; + } + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, wantval ? JSOP_POPV : JSOP_POP) < 0) + return JS_FALSE; + } + } + break; + + case TOK_COLON: + /* Emit an annotated nop so we know to decompile a label. */ + atom = pn->pn_atom; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + pn2 = pn->pn_expr; + noteIndex = js_NewSrcNote2(cx, cg, + (pn2->pn_type == TOK_LC) + ? SRC_LABELBRACE + : SRC_LABEL, + (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->treeContext, &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 (pn2->pn_type == TOK_LC) { + 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; + JS_ASSERT(pn2->pn_type != TOK_RP); + atomIndex = (jsatomid) -1; /* Suppress warning. */ + switch (pn2->pn_type) { + case TOK_NAME: + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) + return JS_FALSE; + if (pn2->pn_slot >= 0) { + atomIndex = (jsatomid) pn2->pn_slot; + } else { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); + } + break; + case TOK_DOT: + if (!js_EmitTree(cx, cg, pn2->pn_expr)) + return JS_FALSE; + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + break; + case TOK_LB: + JS_ASSERT(pn->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_LVALUE_RETURN + case TOK_LP: + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + break; +#endif + default: + JS_ASSERT(0); + } + + op = pn->pn_op; +#if JS_HAS_GETTER_SETTER + if (op == JSOP_GETTER || op == JSOP_SETTER) { + /* 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->pn_op != JSOP_SETNAME) { + EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG) + ? JSOP_GETARG + : JSOP_GETVAR, + atomIndex); + break; + } + /* FALL THROUGH */ + case TOK_DOT: + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex); + break; + case TOK_LB: +#if JS_HAS_LVALUE_RETURN + case TOK_LP: +#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) { + if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 || + 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_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->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { + case TOK_DOT: + EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); + } + break; + case TOK_LB: +#if JS_HAS_LVALUE_RETURN + case TOK_LP: +#endif + if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) + return JS_FALSE; + break; + default:; + } + 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); + 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; + + /* + * Because each branch pushes a single value, but our stack budgeting + * analysis ignores branches, we now have two values accounted for in + * cg->stackDepth. Execution will follow only one path, so we must + * decrement cg->stackDepth here. 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). + */ + JS_ASSERT(cg->stackDepth > 1); + cg->stackDepth--; + 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. + * + * Avoid tail recursion for long ||...|| expressions and long &&...&& + * expressions or long mixtures of ||'s and &&'s that can easily blow + * the stack, by forward-linking and then backpatching all the JSOP_OR + * and JSOP_AND bytecodes' immediate jump-offset operands. + */ + pn3 = pn; + 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; + jmp = top; + pn2 = pn->pn_right; + while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) { + pn = pn2; + if (!js_EmitTree(cx, cg, pn->pn_left)) + 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; + pn2 = pn->pn_right; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + 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 = pn3->pn_op; + top += tmp; + } while ((pn3 = pn3->pn_right) != pn2); + break; + + case TOK_BITOR: + case TOK_BITXOR: + case TOK_BITAND: + case TOK_EQOP: + case TOK_RELOP: +#if JS_HAS_IN_OPERATOR + case TOK_IN: +#endif +#if JS_HAS_INSTANCEOF + case TOK_INSTANCEOF: +#endif + case TOK_SHOP: + case TOK_PLUS: + 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->pn_op; + 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 { + /* 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_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + } + break; + +#if JS_HAS_EXCEPTIONS + case TOK_THROW: +#endif + case TOK_UNARYOP: + /* Unary op, including unary +/-. */ + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, pn->pn_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->pn_op; + switch (pn2->pn_type) { + case TOK_NAME: + pn2->pn_op = op; + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) + return JS_FALSE; + op = pn2->pn_op; + if (pn2->pn_slot >= 0) { + if (pn2->pn_attrs & JSPROP_READONLY) + op = JSOP_GETVAR; + atomIndex = (jsatomid) pn2->pn_slot; + EMIT_ATOM_INDEX_OP(op, atomIndex); + } else { + if (!EmitAtomOp(cx, pn2, op, cg)) + return JS_FALSE; + } + break; + case TOK_DOT: + if (!EmitPropOp(cx, pn2, op, cg)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn2, op, cg)) + return JS_FALSE; + break; +#if JS_HAS_LVALUE_RETURN + 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; +#endif + default: + JS_ASSERT(0); + } + break; + + case TOK_DELETE: + /* Under ECMA 3, deleting a non-reference returns true. */ + pn2 = pn->pn_kid; + switch (pn2->pn_type) { + case TOK_NAME: + pn2->pn_op = JSOP_DELNAME; + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) + return JS_FALSE; + op = pn2->pn_op; + 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)) + return JS_FALSE; + break; + case TOK_LB: + if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) + return JS_FALSE; + break; + default: + if (js_Emit1(cx, cg, JSOP_TRUE) < 0) + return JS_FALSE; + } + break; + + 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). This bytecode sets the virtual machine's + * "obj" register to the left operand's ToObject conversion result, + * for use by JSOP_PUSHOBJ. + */ + ok = EmitPropOp(cx, pn, pn->pn_op, cg); + break; + + case TOK_LB: + /* + * 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->pn_op, cg); + break; + + case TOK_NEW: + case TOK_LP: + /* + * Emit function call or operator new (constructor call) code. + * First, emit code for the left operand to evaluate the callable or + * constructable object expression. + */ + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + + /* Remember start of callable-object bytecode for decompilation hint. */ + off = pn2->pn_offset; + + /* + * Push the virtual machine's "obj" register, which was set by a name, + * property, or element get (or set) bytecode. + */ + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + + /* + * 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. + */ + for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + 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->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) + return JS_FALSE; + break; + +#if JS_HAS_INITIALIZERS + case TOK_RB: + /* + * Emit code for [a, b, c] of the form: + * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; + * but use a stack slot for t and avoid dup'ing and popping it via + * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. + */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) + return JS_FALSE; + + pn2 = pn->pn_head; +#if JS_HAS_SHARP_VARS + if (pn2 && pn2->pn_type == TOK_DEFSHARP) { + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); + pn2 = pn2->pn_next; + } +#endif + + for (atomIndex = 0; pn2; pn2 = pn2->pn_next) { + /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */ + JS_ASSERT(atomIndex < ATOM_INDEX_LIMIT); + if (atomIndex == 0) { + if (js_Emit1(cx, cg, JSOP_ZERO) < 0) + return JS_FALSE; + } else if (atomIndex == 1) { + if (js_Emit1(cx, cg, JSOP_ONE) < 0) + return JS_FALSE; + } else { + EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex); + } + + /* Sub-optimal: holes in a sparse initializer are void-filled. */ + if (pn2->pn_type == TOK_COMMA) { + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) + return JS_FALSE; + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + } + if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + + atomIndex++; + } + + if (pn->pn_extra) { + /* Emit a source note so we know to decompile an extra comma. */ + if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) + return JS_FALSE; + } + + /* Emit an op for sharp array cleanup and decompilation. */ + if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) + return JS_FALSE; + break; + + case TOK_RC: + /* + * Emit code for {p:a, '%q':b, 2:c} of the form: + * t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; + * but use a stack slot for t and avoid dup'ing and popping it via + * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. + */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); + + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) + return JS_FALSE; + + pn2 = pn->pn_head; +#if JS_HAS_SHARP_VARS + if (pn2 && pn2->pn_type == TOK_DEFSHARP) { + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); + pn2 = pn2->pn_next; + } +#endif + + for (; pn2; pn2 = pn2->pn_next) { + /* Emit an index for t[2], else map an atom for t.p or t['%q']. */ + pn3 = pn2->pn_left; + switch (pn3->pn_type) { + case TOK_NUMBER: + if (!EmitNumberOp(cx, pn3->pn_dval, cg)) + return JS_FALSE; + break; + case TOK_NAME: + case TOK_STRING: + ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + break; + default: + JS_ASSERT(0); + } + + /* Emit code for the property initializer. */ + if (!js_EmitTree(cx, cg, pn2->pn_right)) + return JS_FALSE; + +#if JS_HAS_GETTER_SETTER + op = pn2->pn_op; + 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_LABEL) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) + return JS_FALSE; + } else { + EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); + } + } + + /* Emit an op for sharpArray cleanup and decompilation. */ + if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) + return JS_FALSE; + break; + +#if JS_HAS_SHARP_VARS + case TOK_DEFSHARP: + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); + break; + + case TOK_USESHARP: + EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + + case TOK_RP: + /* + * The node for (e) has e as its kid, enabling users who want to nest + * assignment expressions in conditions to avoid the error correction + * done by Condition (from x = y to x == y) by double-parenthesizing. + */ + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_GROUP) < 0) + return JS_FALSE; + break; + + case TOK_NAME: + if (!LookupArgOrVar(cx, &cg->treeContext, pn)) + return JS_FALSE; + op = pn->pn_op; + if (op == JSOP_ARGUMENTS) { + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; + } + if (pn->pn_slot >= 0) { + atomIndex = (jsatomid) pn->pn_slot; + EMIT_ATOM_INDEX_OP(op, atomIndex); + break; + } + /* FALL THROUGH */ + case TOK_STRING: + case TOK_OBJECT: + /* + * The scanner and parser associate JSOP_NAME with TOK_NAME, although + * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME, + * JSOP_FORNAME, etc.). Among JSOP_*NAME* variants, only JSOP_NAME + * may generate the first operand of a call or new expression, so only + * it sets the "obj" virtual machine register to the object along the + * scope chain in which the name was found. + * + * Token types for STRING and OBJECT have corresponding bytecode ops + * in pn_op and emit the same format as NAME, so they share this code. + */ + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + break; + + case TOK_NUMBER: + ok = EmitNumberOp(cx, pn->pn_dval, cg); + break; + + case TOK_PRIMARY: + if (js_Emit1(cx, cg, pn->pn_op) < 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 */ + + default: + JS_ASSERT(0); + } + + if (ok && --cg->emitLevel == 0 && cg->spanDeps) + ok = OptimizeSpanDeps(cx, cg); + + return ok; +} + +JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { + {"null", 0, 0, 0}, + {"if", 0, 0, 0}, + {"if-else", 1, 0, 1}, + {"while", 1, 0, 1}, + {"for", 3, 1, 1}, + {"continue", 0, 0, 0}, + {"var", 0, 0, 0}, + {"pcdelta", 1, 0, 1}, + {"assignop", 0, 0, 0}, + {"cond", 1, 0, 1}, + {"reserved0", 0, 0, 0}, + {"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, 11, 1}, + {"const", 0, 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_ReportOutOfMemory(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_ReportOutOfMemory(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; +} + +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(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(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 = PTRDIFF(sn, CG_NOTES(cg), jssrcnote); + + /* + * Simultaneously test to see if the source note array must grow to + * accomodate 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_brendan +#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; + setlinebuf(fp); + } + 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) { + /* 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_brendan + { int bin = JS_CeilingLog2(totalCount); + if (bin >= NBINS) + bin = NBINS - 1; + ++hist[bin]; + } +#endif + return JS_TRUE; +} + +JSBool +js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) +{ + size_t size, incr; + ptrdiff_t delta; + + size = TRYNOTE_SIZE(cg->treeContext.tryCount); + if (size <= cg->tryNoteSpace) + return JS_TRUE; + + /* + * Allocate trynotes from cx->tempPool. + * XXX Too much growing and we bloat, as other tempPool allocators block + * in-place growth, and we never recycle old free space in an arena. + * YYY But once we consume an entire arena, we'll realloc it, letting the + * malloc heap recycle old space, while still freeing _en masse_ via the + * arena pool. + */ + if (!cg->tryBase) { + size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK)); + JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size); + if (!cg->tryBase) + return JS_FALSE; + cg->tryNoteSpace = size; + cg->tryNext = cg->tryBase; + } else { + delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char); + incr = size - cg->tryNoteSpace; + incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK)); + size = cg->tryNoteSpace; + JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr); + if (!cg->tryBase) + return JS_FALSE; + cg->tryNoteSpace = size + incr; + cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta); + } + return JS_TRUE; +} + +JSTryNote * +js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, + ptrdiff_t end, ptrdiff_t catchStart) +{ + JSTryNote *tn; + + JS_ASSERT(cg->tryBase <= cg->tryNext); + JS_ASSERT(catchStart >= 0); + tn = cg->tryNext++; + tn->start = start; + tn->length = end - start; + tn->catchStart = catchStart; + return tn; +} + +void +js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes) +{ + uintN count; + + count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote); + if (!count) + return; + + memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count)); + notes[count].start = 0; + notes[count].length = CG_OFFSET(cg); + notes[count].catchStart = 0; +} diff --git a/src/extension/script/js/jsemit.h b/src/extension/script/js/jsemit.h new file mode 100644 index 000000000..43b520c6e --- /dev/null +++ b/src/extension/script/js/jsemit.h @@ -0,0 +1,547 @@ +/* -*- 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 jsemit_h___ +#define jsemit_h___ +/* + * JS bytecode generation. + */ + +#include "jsstddef.h" +#include "jstypes.h" +#include "jsatom.h" +#include "jsopcode.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * NB: If you add non-loop STMT_* enumerators, do so before STMT_DO_LOOP or + * you will break the STMT_IS_LOOP macro, just below this enum. + */ +typedef enum JSStmtType { + STMT_BLOCK = 0, /* compound statement: { s1[;... sN] } */ + STMT_LABEL = 1, /* labeled statement: L: s */ + STMT_IF = 2, /* if (then) statement */ + STMT_ELSE = 3, /* else clause of if statement */ + STMT_SWITCH = 4, /* switch statement */ + STMT_WITH = 5, /* with statement */ + STMT_TRY = 6, /* try statement */ + STMT_CATCH = 7, /* catch block */ + STMT_FINALLY = 8, /* finally statement */ + STMT_SUBROUTINE = 9, /* gosub-target subroutine body */ + STMT_DO_LOOP = 10, /* do/while loop statement */ + STMT_FOR_LOOP = 11, /* for loop statement */ + STMT_FOR_IN_LOOP = 12, /* for/in loop statement */ + STMT_WHILE_LOOP = 13 /* while loop statement */ +} JSStmtType; + +#define STMT_IS_LOOP(stmt) ((stmt)->type >= STMT_DO_LOOP) + +typedef struct JSStmtInfo JSStmtInfo; + +struct JSStmtInfo { + JSStmtType type; /* statement type */ + 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 */ + ptrdiff_t gosub; /* offset of last GOSUB for this finally */ + ptrdiff_t catchJump; /* offset of last end-of-catch jump */ + JSAtom *label; /* name of LABEL or CATCH var */ + JSStmtInfo *down; /* info for enclosing statement */ +}; + +#define SET_STATEMENT_TOP(stmt, top) \ + ((stmt)->update = (top), (stmt)->breaks = \ + (stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1)) + +struct JSTreeContext { /* tree context for semantic checks */ + uint32 flags; /* statement state flags, see below */ + uint32 tryCount; /* total count of try statements parsed */ + JSStmtInfo *topStmt; /* top of statement info stack */ + JSAtomList decls; /* function, const, and var declarations */ + JSParseNode *nodeList; /* list of recyclable parse-node structs */ +}; + +#define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ +#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_CLOSURE_VS_VAR 0x20 /* function and var with same name */ +#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ +#define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ +#define TCF_FUN_FLAGS 0xE0 /* flags to propagate from FunctionBody */ + +#define TREE_CONTEXT_INIT(tc) \ + ((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL, \ + ATOM_LIST_INIT(&(tc)->decls), (tc)->nodeList = NULL) + +#define TREE_CONTEXT_FINISH(tc) \ + ((void)0) + +/* + * 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_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)) +#define SD_TARGET_OFFSET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ + JT_CLR_TAG((sd)->target)->offset) + +struct JSCodeGenerator { + JSTreeContext treeContext; /* base state: statement info stack, etc. */ + 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 */ + void *tempMark; /* low watermark in cx->tempPool */ + 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; + const char *filename; /* null or weak link to source filename */ + uintN firstLine; /* first line, for js_NewScriptFromCG */ + JSPrincipals *principals; /* principals for constant folding eval */ + JSAtomList atomList; /* literals indexed for mapping */ + intN stackDepth; /* current stack depth in script frame */ + uintN maxStackDepth; /* maximum stack depth so far */ + JSTryNote *tryBase; /* first exception handling note */ + JSTryNote *tryNext; /* next available note */ + size_t tryNoteSpace; /* # of bytes allocated at tryBase */ + 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 */ + uintN emitLevel; /* js_EmitTree recursion level */ +}; + +#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) PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode) + +#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) PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\ + jsbytecode) + +#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) +#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) + +/* + * Initialize cg to allocate bytecode space from codePool, source note space + * from notePool, and all other arena-allocated temporaries from cx->tempPool. + * Return true on success. Report an error and return false if the initial + * code segment can't be allocated. + */ +extern JS_FRIEND_API(JSBool) +js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, + JSArenaPool *codePool, JSArenaPool *notePool, + const char *filename, uintN lineno, + JSPrincipals *principals); + +/* + * Release cg->codePool, cg->notePool, and cx->tempPool to marks set by + * js_InitCodeGenerator. 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 JS_FinishCodeGenerator. + */ +extern JS_FRIEND_API(void) +js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg); + +/* + * 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(cx,cg,pc,off) \ + JS_BEGIN_MACRO \ + if (!js_SetJumpOffset(cx, cg, pc, off)) \ + return JS_FALSE; \ + JS_END_MACRO + +#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ + CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off)) + +extern JSBool +js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, + ptrdiff_t off); + +/* Test whether we're in a with statement. */ +extern JSBool +js_InWithStatement(JSTreeContext *tc); + +/* Test whether we're in a catch block with exception named by atom. */ +extern JSBool +js_InCatchBlock(JSTreeContext *tc, JSAtom *atom); + +/* + * 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); + +/* + * 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->treeContext), also patch breaks and continues. + * May fail if a jump offset overflows. + */ +extern JSBool +js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); + +/* + * Emit code into cg for the tree rooted at pn. + */ +extern JSBool +js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); + +/* + * Emit code into cg for the tree rooted at body, then create a persistent + * script for fun from cg. + */ +extern JSBool +js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, + JSFunction *fun); + +/* + * 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. + */ +typedef enum JSSrcNoteType { + SRC_NULL = 0, /* terminates a note vector */ + SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ + SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ + SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */ + SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */ + 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,,] */ + SRC_VAR = 6, /* JSOP_NAME/SETNAME/FORNAME in a var decl */ + SRC_PCDELTA = 7, /* offset from comma-operator to next POP, + or from CONDSWITCH to first CASE opcode */ + SRC_ASSIGNOP = 8, /* += or another assign-op follows */ + SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ + SRC_RESERVED0 = 10, /* reserved for future use */ + SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ + SRC_PCBASE = 12, /* offset of first obj.prop.subprop bytecode */ + 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_CONST = 21, /* JSOP_SETCONST in a const decl */ + 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; + +#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) (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 */ + uint8 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); + +/* + * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel) + * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount + * js_NewTryNote calls. The storage is freed by js_FinishCodeGenerator. + */ +extern JSBool +js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); + +/* + * Grab the next trynote slot in cg, filling it in appropriately. + */ +extern JSTryNote * +js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, + ptrdiff_t end, ptrdiff_t catchStart); + +/* + * Finish generating exception information into the space at notes. As with + * js_FinishTakingSrcNotes, the caller must use CG_COUNT_FINAL_TRYNOTES(cg) to + * preallocate enough space in a JSTryNote[] to pass as the notes parameter of + * js_FinishTakingTryNotes. + */ +#define CG_COUNT_FINAL_TRYNOTES(cg, cnt) \ + JS_BEGIN_MACRO \ + cnt = ((cg)->tryNext > (cg)->tryBase) \ + ? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1 \ + : 0; \ + JS_END_MACRO + +extern void +js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes); + +JS_END_EXTERN_C + +#endif /* jsemit_h___ */ diff --git a/src/extension/script/js/jsexn.c b/src/extension/script/js/jsexn.c new file mode 100644 index 000000000..c407c1628 --- /dev/null +++ b/src/extension/script/js/jsexn.c @@ -0,0 +1,1081 @@ +/* -*- 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 standard exception implementation. + */ + +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jsopcode.h" +#include "jsnum.h" +#include "jsscript.h" + +#if JS_HAS_ERROR_EXCEPTIONS +#if !JS_HAS_EXCEPTIONS +# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS" +#endif + +/* XXX consider adding rt->atomState.messageAtom */ +static char js_message_str[] = "message"; +static char js_filename_str[] = "fileName"; +static char js_lineno_str[] = "lineNumber"; +static char js_stack_str[] = "stack"; + +/* Forward declarations for ExceptionClass's initializer. */ +static JSBool +Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + +static void +exn_finalize(JSContext *cx, JSObject *obj); + +static JSClass ExceptionClass = { + "Error", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize, + NULL, NULL, NULL, Exception, + NULL, NULL, NULL, 0 +}; + +/* + * A copy of the JSErrorReport originally generated. + */ +typedef struct JSExnPrivate { + JSErrorReport *errorReport; +} JSExnPrivate; + +/* + * Undo all the damage done by exn_newPrivate. + */ +static void +exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData) +{ + JSErrorReport *report; + const jschar **args; + + if (!privateData) + return; + report = privateData->errorReport; + if (report) { + if (report->uclinebuf) + JS_free(cx, (void *)report->uclinebuf); + if (report->filename) + JS_free(cx, (void *)report->filename); + if (report->ucmessage) + JS_free(cx, (void *)report->ucmessage); + if (report->messageArgs) { + args = report->messageArgs; + while (*args != NULL) + JS_free(cx, (void *)*args++); + JS_free(cx, (void *)report->messageArgs); + } + JS_free(cx, report); + } + JS_free(cx, privateData); +} + +/* + * Copy everything interesting about an error into allocated memory. + */ +static JSExnPrivate * +exn_newPrivate(JSContext *cx, JSErrorReport *report) +{ + intN i; + JSExnPrivate *newPrivate; + JSErrorReport *newReport; + size_t capacity; + + newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate)); + if (!newPrivate) + return NULL; + memset(newPrivate, 0, sizeof (JSExnPrivate)); + + /* Copy the error report */ + newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport)); + if (!newReport) + goto error; + memset(newReport, 0, sizeof (JSErrorReport)); + newPrivate->errorReport = newReport; + + if (report->filename != NULL) { + newReport->filename = JS_strdup(cx, report->filename); + if (!newReport->filename) + goto error; + } else { + newReport->filename = NULL; + } + + newReport->lineno = report->lineno; + + /* + * We don't need to copy linebuf and tokenptr, because they + * point into the deflated string cache. (currently?) + */ + newReport->linebuf = report->linebuf; + newReport->tokenptr = report->tokenptr; + + /* + * But we do need to copy uclinebuf, uctokenptr, because they're + * pointers into internal tokenstream structs, and may go away. + */ + if (report->uclinebuf != NULL) { + capacity = js_strlen(report->uclinebuf) + 1; + newReport->uclinebuf = + (const jschar *)JS_malloc(cx, capacity * sizeof(jschar)); + if (!newReport->uclinebuf) + goto error; + js_strncpy((jschar *)newReport->uclinebuf, report->uclinebuf, capacity); + newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr - + report->uclinebuf); + } else { + newReport->uclinebuf = newReport->uctokenptr = NULL; + } + + if (report->ucmessage != NULL) { + capacity = js_strlen(report->ucmessage) + 1; + newReport->ucmessage = (const jschar *) + JS_malloc(cx, capacity * sizeof(jschar)); + if (!newReport->ucmessage) + goto error; + js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, capacity); + + if (report->messageArgs) { + for (i = 0; report->messageArgs[i] != NULL; i++) + continue; + JS_ASSERT(i); + newReport->messageArgs = + (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *)); + if (!newReport->messageArgs) + goto error; + for (i = 0; report->messageArgs[i] != NULL; i++) { + capacity = js_strlen(report->messageArgs[i]) + 1; + newReport->messageArgs[i] = + (const jschar *)JS_malloc(cx, capacity * sizeof(jschar)); + if (!newReport->messageArgs[i]) + goto error; + js_strncpy((jschar *)(newReport->messageArgs[i]), + report->messageArgs[i], capacity); + } + newReport->messageArgs[i] = NULL; + } else { + newReport->messageArgs = NULL; + } + } else { + newReport->ucmessage = NULL; + newReport->messageArgs = NULL; + } + newReport->errorNumber = report->errorNumber; + + /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ + newReport->flags = report->flags; + + return newPrivate; +error: + exn_destroyPrivate(cx, newPrivate); + return NULL; +} + +static void +exn_finalize(JSContext *cx, JSObject *obj) +{ + JSExnPrivate *privateData; + jsval privateValue; + + privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + + if (!JSVAL_IS_VOID(privateValue)) { + privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); + if (privateData) + exn_destroyPrivate(cx, privateData); + } +} + +JSErrorReport * +js_ErrorFromException(JSContext *cx, jsval exn) +{ + JSObject *obj; + JSExnPrivate *privateData; + jsval privateValue; + + if (JSVAL_IS_PRIMITIVE(exn)) + return NULL; + obj = JSVAL_TO_OBJECT(exn); + if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass) + return NULL; + privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (JSVAL_IS_VOID(privateValue)) + return NULL; + privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); + if (!privateData) + return NULL; + + JS_ASSERT(privateData->errorReport); + return privateData->errorReport; +} + +/* + * This must be kept in synch with the exceptions array below. + * XXX use a jsexn.tbl file a la jsopcode.tbl + */ +typedef enum JSExnType { + JSEXN_NONE = -1, + JSEXN_ERR, + JSEXN_INTERNALERR, + JSEXN_EVALERR, + JSEXN_RANGEERR, + JSEXN_REFERENCEERR, + JSEXN_SYNTAXERR, + JSEXN_TYPEERR, + JSEXN_URIERR, + JSEXN_LIMIT +} JSExnType; + +struct JSExnSpec { + int protoIndex; + const char *name; + JSNative native; +}; + +/* + * All *Error constructors share the same JSClass, ExceptionClass. But each + * constructor function for an *Error class must have a distinct native 'call' + * function pointer, in order for instanceof to work properly across multiple + * standard class sets. See jsfun.c:fun_hasInstance. + */ +#define MAKE_EXCEPTION_CTOR(name) \ +const char js_##name##_str[] = #name; \ +static JSBool \ +name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ +{ \ + return Exception(cx, obj, argc, argv, rval); \ +} + +MAKE_EXCEPTION_CTOR(Error) +MAKE_EXCEPTION_CTOR(InternalError) +MAKE_EXCEPTION_CTOR(EvalError) +MAKE_EXCEPTION_CTOR(RangeError) +MAKE_EXCEPTION_CTOR(ReferenceError) +MAKE_EXCEPTION_CTOR(SyntaxError) +MAKE_EXCEPTION_CTOR(TypeError) +MAKE_EXCEPTION_CTOR(URIError) + +#undef MAKE_EXCEPTION_CTOR + +static struct JSExnSpec exceptions[] = { + { JSEXN_NONE, js_Error_str, Error }, + { JSEXN_ERR, js_InternalError_str, InternalError }, + { JSEXN_ERR, js_EvalError_str, EvalError }, + { JSEXN_ERR, js_RangeError_str, RangeError }, + { JSEXN_ERR, js_ReferenceError_str, ReferenceError }, + { JSEXN_ERR, js_SyntaxError_str, SyntaxError }, + { JSEXN_ERR, js_TypeError_str, TypeError }, + { JSEXN_ERR, js_URIError_str, URIError }, + {0,NULL,NULL} +}; + +static JSBool +InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, + JSString *filename, uintN lineno) +{ + JSCheckAccessOp checkAccess; + JSErrorReporter older; + JSExceptionState *state; + jschar *stackbuf; + size_t stacklen, stackmax; + JSStackFrame *fp; + jsval callerid, v; + JSBool ok; + JSString *argsrc, *stack; + uintN i, ulineno; + const char *cp; + char ulnbuf[11]; + + if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, js_filename_str, + STRING_TO_JSVAL(filename), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, js_lineno_str, + INT_TO_JSVAL(lineno), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + /* + * Set the 'stack' property. + * + * First, 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. + */ + checkAccess = cx->runtime->checkObjectAccess; + if (checkAccess) { + older = JS_SetErrorReporter(cx, NULL); + state = JS_SaveExceptionState(cx); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else { + older = NULL; + state = NULL; + } +#endif + callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); + + /* + * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes + * the next free jschar slot, and with room for at most stackmax non-null + * jschars. If stackbuf is non-null, it always contains an extra slot for + * the null terminator we'll store at the end, as a backstop. + * + * All early returns must goto done after this point, till the after-loop + * cleanup code has run! + */ + stackbuf = NULL; + stacklen = stackmax = 0; + ok = JS_TRUE; + +#define APPEND_CHAR_TO_STACK(c) \ + JS_BEGIN_MACRO \ + if (stacklen == stackmax) { \ + void *ptr_; \ + stackmax = stackmax ? 2 * stackmax : 64; \ + ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ + if (!ptr_) { \ + ok = JS_FALSE; \ + goto done; \ + } \ + stackbuf = ptr_; \ + } \ + stackbuf[stacklen++] = (c); \ + JS_END_MACRO + +#define APPEND_STRING_TO_STACK(str) \ + JS_BEGIN_MACRO \ + JSString *str_ = str; \ + size_t length_ = JSSTRING_LENGTH(str_); \ + if (stacklen + length_ > stackmax) { \ + void *ptr_; \ + stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ + ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ + if (!ptr_) { \ + ok = JS_FALSE; \ + goto done; \ + } \ + stackbuf = ptr_; \ + } \ + js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ + stacklen += length_; \ + JS_END_MACRO + + for (fp = cx->fp; fp; fp = fp->down) { + if (checkAccess) { + v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + ok = checkAccess(cx, fp->fun->object, callerid, JSACC_READ, &v); + if (!ok) { + ok = JS_TRUE; + break; + } + } + } + + if (fp->fun) { + if (fp->fun->atom) + APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom)); + + APPEND_CHAR_TO_STACK('('); + for (i = 0; i < fp->argc; i++) { + /* Avoid toSource bloat and fallibility for object types. */ + v = fp->argv[i]; + if (JSVAL_IS_PRIMITIVE(v)) { + argsrc = js_ValueToSource(cx, v); + } else if (JSVAL_IS_FUNCTION(cx, v)) { + /* XXX Avoid function decompilation bloat for now. */ + argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v)); + if (!argsrc) + argsrc = js_ValueToSource(cx, v); + } 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); + argsrc = JS_NewStringCopyZ(cx, buf); + } + if (!argsrc) { + ok = JS_FALSE; + goto done; + } + if (i > 0) + APPEND_CHAR_TO_STACK(','); + APPEND_STRING_TO_STACK(argsrc); + } + APPEND_CHAR_TO_STACK(')'); + } + + APPEND_CHAR_TO_STACK('@'); + if (fp->script && fp->script->filename) { + for (cp = fp->script->filename; *cp; cp++) + APPEND_CHAR_TO_STACK(*cp); + } + APPEND_CHAR_TO_STACK(':'); + if (fp->script && fp->pc) { + ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); + JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno); + for (cp = ulnbuf; *cp; cp++) + APPEND_CHAR_TO_STACK(*cp); + } else { + APPEND_CHAR_TO_STACK('0'); + } + APPEND_CHAR_TO_STACK('\n'); + } + +#undef APPEND_CHAR_TO_STACK +#undef APPEND_STRING_TO_STACK + +done: + if (checkAccess) { + if (ok) + JS_RestoreExceptionState(cx, state); + else + JS_DropExceptionState(cx, state); + JS_SetErrorReporter(cx, older); + } + if (!ok) { + JS_free(cx, stackbuf); + return JS_FALSE; + } + + if (!stackbuf) { + stack = cx->runtime->emptyString; + } else { + /* NB: if stackbuf was allocated, it has room for the terminator. */ + JS_ASSERT(stacklen <= stackmax); + 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 = realloc(stackbuf, (stacklen+1) * sizeof(jschar)); + if (shrunk) + stackbuf = shrunk; + } + stackbuf[stacklen] = 0; + stack = js_NewString(cx, stackbuf, stacklen, 0); + if (!stack) { + JS_free(cx, stackbuf); + return JS_FALSE; + } + } + return JS_DefineProperty(cx, obj, js_stack_str, + STRING_TO_JSVAL(stack), + NULL, NULL, JSPROP_ENUMERATE); +} + +static JSBool +Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSBool ok; + jsval pval; + int32 lineno; + JSString *message, *filename; + + if (cx->creatingException) + return JS_FALSE; + cx->creatingException = JS_TRUE; + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* + * 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. + */ + ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), + (jsid)cx->runtime->atomState.classPrototypeAtom, + &pval); + if (!ok) + goto out; + obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL); + if (!obj) { + ok = JS_FALSE; + goto out; + } + *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) == &ExceptionClass) + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); + + /* Set the 'message' property. */ + if (argc != 0) { + message = js_ValueToString(cx, argv[0]); + if (!message) { + ok = JS_FALSE; + goto out; + } + } else { + message = cx->runtime->emptyString; + } + + /* Set the 'fileName' property. */ + if (argc > 1) { + filename = js_ValueToString(cx, argv[1]); + if (!filename) { + ok = JS_FALSE; + goto out; + } + } else { + filename = cx->runtime->emptyString; + } + + /* Set the 'lineNumber' property. */ + if (argc > 2) { + ok = js_ValueToInt32(cx, argv[2], &lineno); + if (!ok) + goto out; + } else { + lineno = 0; + } + + ok = InitExceptionObject(cx, obj, message, filename, lineno); + +out: + cx->creatingException = JS_FALSE; + return ok; +} + +/* + * 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, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *name, *message, *result; + jschar *chars, *cp; + size_t name_length, message_length, length; + + if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) + return JS_FALSE; + name = js_ValueToString(cx, v); + if (!name) + return JS_FALSE; + + if (!JS_GetProperty(cx, obj, js_message_str, &v) || + !(message = js_ValueToString(cx, v))) { + return JS_FALSE; + } + + if (JSSTRING_LENGTH(message) != 0) { + name_length = JSSTRING_LENGTH(name); + message_length = JSSTRING_LENGTH(message); + length = name_length + message_length + 2; + cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + + js_strncpy(cp, JSSTRING_CHARS(name), name_length); + cp += name_length; + *cp++ = ':'; *cp++ = ' '; + js_strncpy(cp, JSSTRING_CHARS(message), message_length); + cp += message_length; + *cp = 0; + + result = js_NewString(cx, chars, length, 0); + if (!result) { + JS_free(cx, chars); + return JS_FALSE; + } + } else { + result = name; + } + + *rval = 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, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *name, *message, *filename, *lineno_as_str, *result; + int32 lineno; + size_t lineno_length, name_length, message_length, filename_length, length; + jschar *chars, *cp; + + if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) + return JS_FALSE; + name = js_ValueToString(cx, v); + if (!name) + return JS_FALSE; + + if (!JS_GetProperty(cx, obj, js_message_str, &v) || + !(message = js_ValueToSource(cx, v))) { + return JS_FALSE; + } + + if (!JS_GetProperty(cx, obj, js_filename_str, &v) || + !(filename = js_ValueToSource(cx, v))) { + return JS_FALSE; + } + + if (!JS_GetProperty(cx, obj, js_lineno_str, &v) || + !js_ValueToInt32 (cx, v, &lineno)) { + return JS_FALSE; + } + + if (lineno != 0) { + if (!(lineno_as_str = js_ValueToString(cx, v))) { + return JS_FALSE; + } + lineno_length = JSSTRING_LENGTH(lineno_as_str); + } else { + lineno_as_str = NULL; + lineno_length = 0; + } + + /* Magic 8, for the characters in ``(new ())''. */ + name_length = JSSTRING_LENGTH(name); + message_length = JSSTRING_LENGTH(message); + length = 8 + name_length + message_length; + + filename_length = JSSTRING_LENGTH(filename); + 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*) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + + *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; + js_strncpy(cp, JSSTRING_CHARS(name), name_length); + cp += name_length; + *cp++ = '('; + if (message_length != 0) { + js_strncpy(cp, JSSTRING_CHARS(message), message_length); + cp += message_length; + } + + if (filename_length != 0) { + /* append filename as ``, {filename}'' */ + *cp++ = ','; *cp++ = ' '; + js_strncpy(cp, JSSTRING_CHARS(filename), 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, JSSTRING_CHARS(lineno_as_str), lineno_length); + cp += lineno_length; + } + + *cp++ = ')'; *cp++ = ')'; *cp = 0; + + result = js_NewString(cx, chars, length, 0); + if (!result) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(result); + return JS_TRUE; +} +#endif + +static JSFunctionSpec exception_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, exn_toSource, 0,0,0}, +#endif + {js_toString_str, exn_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSObject * +js_InitExceptionClasses(JSContext *cx, JSObject *obj) +{ + int i; + JSObject *protos[JSEXN_LIMIT]; + + /* Initialize the prototypes first. */ + for (i = 0; exceptions[i].name != 0; i++) { + JSAtom *atom; + JSFunction *fun; + JSString *nameString; + int protoIndex = exceptions[i].protoIndex; + + /* Make the prototype for the current constructor name. */ + protos[i] = js_NewObject(cx, &ExceptionClass, + (protoIndex != JSEXN_NONE) + ? protos[protoIndex] + : NULL, + obj); + if (!protos[i]) + return NULL; + + /* So exn_finalize knows whether to destroy private data. */ + OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID); + + atom = js_Atomize(cx, exceptions[i].name, strlen(exceptions[i].name), 0); + if (!atom) + return NULL; + + /* Make a constructor function for the current name. */ + fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); + if (!fun) + return NULL; + + /* Make this constructor make objects of class Exception. */ + fun->clasp = &ExceptionClass; + + /* Make the prototype and constructor links. */ + if (!js_SetClassPrototype(cx, fun->object, protos[i], + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + + /* proto bootstrap bit from JS_InitClass omitted. */ + nameString = JS_NewStringCopyZ(cx, exceptions[i].name); + if (!nameString) + return NULL; + + /* Add the name property to the prototype. */ + if (!JS_DefineProperty(cx, protos[i], js_name_str, + STRING_TO_JSVAL(nameString), + NULL, NULL, + JSPROP_ENUMERATE)) { + return NULL; + } + } + + /* + * Add an empty message property. (To Exception.prototype only, + * because this property will be the same for all the exception + * protos.) + */ + if (!JS_DefineProperty(cx, protos[0], js_message_str, + STRING_TO_JSVAL(cx->runtime->emptyString), + NULL, NULL, JSPROP_ENUMERATE)) { + return NULL; + } + if (!JS_DefineProperty(cx, protos[0], js_filename_str, + STRING_TO_JSVAL(cx->runtime->emptyString), + NULL, NULL, JSPROP_ENUMERATE)) { + return NULL; + } + if (!JS_DefineProperty(cx, protos[0], js_lineno_str, + INT_TO_JSVAL(0), + NULL, NULL, JSPROP_ENUMERATE)) { + return NULL; + } + + /* + * Add methods only to Exception.prototype, because ostensibly all + * exception types delegate to that. + */ + if (!JS_DefineFunctions(cx, protos[0], exception_methods)) + return NULL; + + return protos[0]; +} + +static JSExnType errorToExceptionNum[] = { +#define MSG_DEF(name, number, count, exception, format) \ + exception, +#include "js.msg" +#undef MSG_DEF +}; + +#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; + JSExnType exn; + JSBool ok; + JSObject *errProto, *errObject; + JSString *messageStr, *filenameStr; + uintN lineno; + JSExnPrivate *privateData; + + /* + * Tell our caller to report immediately if cx has no active frames, or if + * this report is just a warning. + */ + JS_ASSERT(reportp); + if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) + return JS_FALSE; + + /* Find the exception index associated with this error. */ + errorNumber = (JSErrNum) reportp->errorNumber; + exn = errorToExceptionNum[errorNumber]; + 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, just as the Exception native constructor + * must do, via cx->creatingException. 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->creatingException) + return JS_FALSE; + cx->creatingException = JS_TRUE; + + /* + * 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. + * + * XXXbe hack around JSCLASS_NEW_RESOLVE code in js_LookupProperty that + * checks cx->fp, cx->fp->pc, and js_CodeSpec[*cx->fp->pc] in order + * to compute resolve flags such as JSRESOLVE_ASSIGNING. The bug + * is that this "internal" js_GetClassPrototype call may trigger a + * resolve of exceptions[exn].name if the global object uses a lazy + * standard class resolver (see JS_ResolveStandardClass), but the + * current frame and bytecode end up affecting the resolve flags. + */ + { + JSStackFrame *fp = cx->fp; + jsbytecode *pc = NULL; + + if (fp) { + pc = fp->pc; + fp->pc = NULL; + } + ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto); + if (pc) + fp->pc = pc; + if (!ok) + goto out; + } + + errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL); + if (!errObject) { + ok = JS_FALSE; + goto out; + } + + /* + * Set the generated Exception object early, so it won't be GC'd by a last + * ditch attempt to collect garbage, or a GC that otherwise nests or races + * under any of the following calls. If one of the following calls fails, + * it will overwrite this exception object with one of its own (except in + * case of OOM errors, of course). + */ + JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); + + messageStr = JS_NewStringCopyZ(cx, message); + if (!messageStr) { + ok = JS_FALSE; + goto out; + } + + if (reportp) { + filenameStr = JS_NewStringCopyZ(cx, reportp->filename); + if (!filenameStr) { + ok = JS_FALSE; + goto out; + } + lineno = reportp->lineno; + } else { + filenameStr = cx->runtime->emptyString; + lineno = 0; + } + ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno); + if (!ok) + goto out; + + /* + * Construct a new copy of the error report struct, and store it in the + * exception object's private data. We can't use the error report struct + * that was passed in, because it's stack-allocated, and also because it + * may point to transient data in the JSTokenStream. + */ + privateData = exn_newPrivate(cx, reportp); + if (!privateData) { + ok = JS_FALSE; + goto out; + } + OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData)); + + /* Flag the error report passed in to indicate an exception was raised. */ + reportp->flags |= JSREPORT_EXCEPTION; + +out: + cx->creatingException = JS_FALSE; + return ok; +} +#endif /* JS_HAS_ERROR_EXCEPTIONS */ + +#if JS_HAS_EXCEPTIONS + +JSBool +js_ReportUncaughtException(JSContext *cx) +{ + JSObject *exnObject; + JSString *str; + jsval exn; + JSErrorReport *reportp; + const char *bytes; + + if (!JS_IsExceptionPending(cx)) + return JS_FALSE; + + if (!JS_GetPendingException(cx, &exn)) + return JS_FALSE; + + /* + * Because js_ValueToString below could error and an exception object + * could become unrooted, we root it here. + */ + if (JSVAL_IS_OBJECT(exn) && exn != JSVAL_NULL) { + exnObject = JSVAL_TO_OBJECT(exn); + if (!js_AddRoot(cx, &exnObject, "exn.report.root")) + return JS_FALSE; + } else { + exnObject = NULL; + } + +#if JS_HAS_ERROR_EXCEPTIONS + reportp = js_ErrorFromException(cx, exn); +#else + reportp = NULL; +#endif + + str = js_ValueToString(cx, exn); + bytes = str ? js_GetStringBytes(str) : "null"; + + if (reportp == NULL) { + /* + * XXXmccabe todo: Instead of doing this, synthesize an error report + * struct that includes the filename, lineno where the exception was + * originally thrown. + */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_UNCAUGHT_EXCEPTION, bytes); + } else { + /* Flag the error as an exception. */ + reportp->flags |= JSREPORT_EXCEPTION; + js_ReportErrorAgain(cx, bytes, reportp); + } + + if (exnObject != NULL) + js_RemoveRoot(cx->runtime, &exnObject); + return JS_TRUE; +} + +#endif /* JS_HAS_EXCEPTIONS */ diff --git a/src/extension/script/js/jsexn.h b/src/extension/script/js/jsexn.h new file mode 100644 index 000000000..aeaab0a58 --- /dev/null +++ b/src/extension/script/js/jsexn.h @@ -0,0 +1,102 @@ +/* -*- 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 + +/* + * Initialize the exception constructor/prototype hierarchy. + */ +extern JSObject * +js_InitExceptionClasses(JSContext *cx, JSObject *obj); + +/* + * String constants naming the exception classes. + */ +extern const char js_Error_str[]; +extern const char js_InternalError_str[]; +extern const char js_EvalError_str[]; +extern const char js_RangeError_str[]; +extern const char js_ReferenceError_str[]; +extern const char js_SyntaxError_str[]; +extern const char js_TypeError_str[]; +extern const char js_URIError_str[]; + +/* + * 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); + +JS_END_EXTERN_C + +#endif /* jsexn_h___ */ diff --git a/src/extension/script/js/jsfile.c b/src/extension/script/js/jsfile.c new file mode 100644 index 000000000..ef5e93b29 --- /dev/null +++ b/src/extension/script/js/jsfile.c @@ -0,0 +1,2610 @@ +/* -*- 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 File object + */ +#if JS_HAS_FILE_OBJECT + +#include "jsstddef.h" + +/* ----------------- Platform-specific includes and defines ----------------- */ +#ifdef XP_MAC +# define FILESEPARATOR ':' +# define FILESEPARATOR2 '\0' +# define CURRENT_DIR "HARD DISK:Desktop Folder" +/* TODO: #include */ +#elif 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(XP_UNIX) || defined(XP_BEOS) +# 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" + +#define MAX_PATH_LENGTH 1024 +#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"); \ + }else \ + 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"); \ + }else \ + 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_CLOSED, op); \ + goto out; \ + } + +#define JSFILE_CHECK_CLOSED(op) \ + if(file->isOpen){ \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_FILE_MUST_BE_OPEN, 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) +*/ +#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; + JSString *linebuffer; /* temp buffer used by readln. */ + 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... */ +static JSClass file_class; +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) +{ +#ifdef XP_MAC + /* pipes are not supported on the MAC */ + return JS_FALSE; +#else + if(!filename) return JS_FALSE; + return filename[0]==PIPE_SYMBOL || + filename[strlen(filename)-1]==PIPE_SYMBOL; +#endif +} + +static JSBool +js_isAbsolute(const char *name) +{ +#if defined(XP_WIN) || defined(XP_OS2) + return (strlen(name)>1)?((name[1]==':')?JS_TRUE:JS_FALSE):JS_FALSE; +#else + return (name[0] +# if defined(XP_UNIX) || defined(XP_BEOS) + == +# else + != +# endif + FILESEPARATOR)?JS_TRUE:JS_FALSE; +#endif +} + +/* + Concatinates 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 = (char*)JS_malloc(cx, len+strlen(name)+2); + + if (!result) return NULL; + + strcpy(result, base); + + if (base[len-1]!=FILESEPARATOR +#if defined(XP_WIN) || defined(XP_OS2) + && base[len-1]!=FILESEPARATOR2 +#endif + ) { + 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; + +#if defined(XP_WIN) || defined(XP_OS2) + /* First, get rid of the drive selector */ + if ((strlen(pathname)>=2)&&(pathname[1]==':')) { + pathname = &pathname[2]; + } +#endif + index = strlen(pathname)-1; + /* + remove trailing separators -- don't necessarily need to check for + FILESEPARATOR2, but that's fine + */ + 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 = (char*)JS_malloc(cx, aux-index+1); + if (!result) return NULL; + strncpy(result, &pathname[index+1], aux-index); + result[aux-index] = '\0'; + return result; +} + +/* + Returns everytynig but the last component from a path name. + Returned string must be freed. Returned string must be freed. +*/ +static char * +js_fileDirectoryName(JSContext *cx, const char *pathname) +{ + jsint index; + char *result; + +#if defined(XP_WIN) || defined(XP_OS2) + char drive = '\0'; + const char *oldpathname = pathname; + + /* First, get rid of the drive selector */ + if ((strlen(pathname)>=2)&&(pathname[1]==':')) { + drive = pathname[0]; + pathname = &pathname[2]; + } +#endif + index = strlen(pathname)-1; + while ((index>0)&&((pathname[index]==FILESEPARATOR)|| + (pathname[index]==FILESEPARATOR2))) index--; + while ((index>0)&&(pathname[index]!=FILESEPARATOR)&& + (pathname[index]!=FILESEPARATOR2)) index--; + + if (index>=0){ + result = (char*)JS_malloc(cx, index+4); + if (!result) return NULL; +#if defined(XP_WIN) || defined(XP_OS2) + if (drive!='\0') { + result[0] = toupper(drive); + result[1] = ':'; + strncpy(&result[2], pathname, index); + result[index+3] = '\0'; + }else +#endif + { + strncpy(result, pathname, index); + result[index] = '\0'; + } + + /* add terminating separator */ + index = strlen(result)-1; + result[index] = FILESEPARATOR; + result[index+1] = '\0'; + } else{ +#if defined(XP_WIN) || defined(XP_OS2) + result = JS_strdup(cx, oldpathname); /* may include drive selector */ +#else + result = JS_strdup(cx, pathname); +#endif + } + + 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=0 && path[j]==' ') j--; + + tmp = JS_malloc(cx, j-i+2); + strncpy(tmp, &path[i], j-i+1); + tmp[j-i+1] = '\0'; + + path = tmp; + + /* pipe support */ + if(js_filenameHasAPipe(path)) + return JS_strdup(cx, path); + /* file:// support */ + if(!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) + return js_canonicalPath(cx, &path[strlen(URL_PREFIX)-1]); + + if (!js_isAbsolute(path)) + path = js_absolutePath(cx, path); + else + path = JS_strdup(cx, path); + + result = JS_strdup(cx, ""); + + current = path; + + base = js_fileBaseName(cx, current); + dir = js_fileDirectoryName(cx, current); + + /* TODO: MAC -- not going to work??? */ + while (strcmp(dir, current)) { + if (!strcmp(base, "..")) { + back++; + } else + if(!strcmp(base, ".")){ + /* ??? */ + } else { + if (back>0) + back--; + else { + tmp = result; + result = JS_malloc(cx, strlen(base)+1+strlen(tmp)+1); + if (!result) { + JS_free(cx, dir); + JS_free(cx, base); + JS_free(cx, current); + return NULL; + } + strcpy(result, base); + c = strlen(result); + if (*tmp) { + result[c] = FILESEPARATOR; + result[c+1] = '\0'; + strcat(result, tmp); + } + JS_free(cx, tmp); + } + } + JS_free(cx, current); + JS_free(cx, base); + current = dir; + base = js_fileBaseName(cx, current); + dir = js_fileDirectoryName(cx, current); + } + + tmp = result; + result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1); + if (!result) { + JS_free(cx, dir); + JS_free(cx, base); + JS_free(cx, current); + return NULL; + } + 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); + } + JS_free(cx, tmp); + JS_free(cx, dir); + JS_free(cx, base); + JS_free(cx, 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 if (onechar <= MAX_UCS2) { numUTF8bytes = 3; + } else { numUTF8bytes = 2; + onechar = DEFAULT_CHAR; + } + + 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; + } + JS_free(cx, options); + return found; +} + +/* empty the buffer */ +static void +js_ResetBuffers(JSFile * file) +{ + file->charBufferUsed = JS_FALSE; + file->nbBytesInBuf = 0; + file->linebuffer = NULL; /* TODO: check for mem. leak? */ +} + +/* 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, 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, 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*)JS_malloc(cx, len); + if (!aux) { + return 0; + } + count = js_BufferedRead(file, aux, len); + if (count==-1) { + JS_free(cx, aux); + return 0; + } + for (i = 0;i0) { + file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; + file->nbBytesInBuf++; + utfbuf[0] = utfbuf[1]; + utfbuf[1] = utfbuf[2]; + remainder--; + } + break; + case UCS2: + count = js_BufferedRead(file, (char*)buf, len*2)>>1; + if (count==-1) { + return 0; + } + break; + } + + 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, 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; + } + + 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, i, j; + unsigned char *utfbuf; + + switch (mode) { + case ASCII: + aux = (unsigned char*)JS_malloc(cx, len); + if (!aux) return 0; + + for (i = 0; iisNative)? + PR_Write(file->handle, aux, len): + fwrite(aux, 1, len, file->nativehandle); + + if (count==-1) { + JS_free(cx, aux); + return 0; + } + JS_free(cx, aux); + break; + case UTF8: + utfbuf = (unsigned char*)JS_malloc(cx, len*3); + if (!utfbuf) return 0; + i = 0; + for (count = 0;countisNative)? + PR_Write(file->handle, utfbuf, i): + fwrite(utfbuf, 1, i, file->nativehandle); + + if (jisNative)? + PR_Write(file->handle, buf, len*2)>>1: + fwrite(buf, 1, len*2, file->nativehandle)>>1; + + if (count==-1) { + return 0; + } + break; + } + 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){ + return (PR_Access(file->path, PR_ACCESS_EXISTS)==PR_SUCCESS); + }else{ + /* doesn't make sense for a pipe of stdstream */ + return JS_FALSE; + } +} + +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); + }else{ + if(file->isPipe){ + /* pipe open for reading */ + return file->path[0]==PIPE_SYMBOL; + }else{ + 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); + }else{ + if(file->isPipe){ + /* pipe open for writing */ + return file->path[strlen(file->path)-1]==PIPE_SYMBOL; + }else{ + 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; + }else + return (info.type==PR_FILE_FILE); + }else{ + /* 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; + }else + return (info.type==PR_FILE_DIRECTORY); + }else{ + /* 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); + goto out; + }else + return INT_TO_JSVAL(info.size); +out: + return JSVAL_VOID; +} + +/* Return the parent object */ +static jsval +js_parent(JSContext *cx, JSFile *file) +{ + char *str; + + /* since we only care about pipes and native files, return NULL */ + if(file->isNative) return JSVAL_VOID; + + str = js_fileDirectoryName(cx, file->path); + /* root.parent = null ??? */ + if(!strcmp(file->path, str) || + (!strncmp(str, file->path, strlen(str)-1)&& + file->path[strlen(file->path)]-1)==FILESEPARATOR){ + return JSVAL_NULL; + }else{ + return OBJECT_TO_JSVAL(js_NewFileObject(cx, str)); + JS_free(cx, str); + } +} + +static jsval +js_name(JSContext *cx, JSFile *file){ + return file->isPipe? + JSVAL_VOID: + STRING_TO_JSVAL(JS_NewStringCopyZ(cx, js_fileBaseName(cx, file->path))); +} + +/* ------------------------------ File object methods ---------------------------- */ +static JSBool +file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); + JSString *strmode, *strtype; + char *ctype, *mode; + int32 mask, type; + int len; + + 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, BTW, 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{ + 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[0] = 'r'; + pipemode[1] = file->type==UTF8?'b':'t'; + pipemode[2] = '\0'; + file->nativehandle = POPEN(&file->path[1], pipemode); + }else + if(file->path[len-1] == PIPE_SYMBOL){ + char *command = JS_malloc(cx, len); + + strncpy(command, file->path, len-1); + command[len-1] = '\0'; + /* open(STATUS, "netstat -an 2>&1 |") */ + pipemode[0] = 'w'; + pipemode[1] = file->type==UTF8?'b':'t'; + pipemode[2] = '\0'; + file->nativehandle = POPEN(command, pipemode); + JS_free(cx, 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); + JS_free(cx, 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; + }else + goto good; +good: + file->isOpen = JS_TRUE; + *rval = JSVAL_TRUE; + return JS_TRUE; +out: + if(mode) JS_free(cx, mode); + *rval = JSVAL_VOID; + return JS_FALSE; +} + +static JSBool +file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, 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: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + + +static JSBool +file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, 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, &file_class, 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; + + dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + + /* 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 = JS_malloc(cx, size); + + count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); + + /* reading panic */ + if (count!=size) { + JS_free(cx, 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) { + JS_free(cx, buffer); + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_COPY_WRITE_ERROR, file->path); + goto out; + } + + JS_free(cx, 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, &file_class, 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"); + + dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); + + if (PR_Rename(file->path, dest)==PR_SUCCESS){ + /* copy the new filename */ + JS_free(cx, 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, &file_class, 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, &file_class, 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, &file_class, 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, &file_class, 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 = JS_malloc(cx, 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); + JS_free(cx, buf); + return JS_TRUE; + } else { + JS_free(cx, 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, &file_class, NULL); + JSString *str; + jschar *buf; + int32 offset; + intN room; + jschar data, data2; + JSBool endofline; + + SECURITY_CHECK(cx, NULL, "readln", file); + JSFILE_CHECK_READ; + + if (!file->linebuffer) { + buf = JS_malloc(cx, MAX_LINE_LENGTH*(sizeof data)); + if (!buf) goto out; + file->linebuffer = JS_NewUCString(cx, buf, MAX_LINE_LENGTH); + } + room = JS_GetStringLength(file->linebuffer); + offset = 0; + + /* XXX TEST ME!! TODO: yes, please do */ + for(;;) { + if (!js_FileRead(cx, file, &data, 1, file->type)) { + endofline = JS_FALSE; + goto loop; + } + switch (data) { + case '\n' : + endofline = JS_TRUE; + goto loop; + case '\r' : + if (!js_FileRead(cx, file, &data2, 1, file->type)) { + endofline = JS_TRUE; + goto loop; + } + if (data2!='\n') { /* We read one char too far. Buffer it. */ + file->charBuffer = data2; + file->charBufferUsed = JS_TRUE; + } + endofline = JS_TRUE; + goto loop; + default: + if (--room < 0) { + buf = JS_malloc(cx, (offset+MAX_LINE_LENGTH)*sizeof data); + if (!buf) return JS_FALSE; + room = MAX_LINE_LENGTH-1; + memcpy(buf, JS_GetStringChars(file->linebuffer), + JS_GetStringLength(file->linebuffer)); + /* what follows may not be the cleanest way. */ + file->linebuffer->chars = buf; + file->linebuffer->length = offset+MAX_LINE_LENGTH; + } + file->linebuffer->chars[offset++] = data; + break; + } + } +loop: + file->linebuffer->chars[offset] = 0; + if ((endofline==JS_TRUE)) { + str = JS_NewUCStringCopyN(cx, JS_GetStringChars(file->linebuffer), + offset); + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + }else{ + goto out; + } +out: + *rval = JSVAL_NULL; + return JS_FALSE; +} + +static JSBool +file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); + JSObject *array; + jsint len; + jsval line; + + SECURITY_CHECK(cx, NULL, "readAll", file); + JSFILE_CHECK_READ; + + array = JS_NewArrayObject(cx, 0, NULL); + len = 0; + + while(file_readln(cx, obj, 0, NULL, &line)){ + JS_SetElement(cx, array, len, &line); + len++; + } + + *rval = OBJECT_TO_JSVAL(array); + return JS_TRUE; +out: + *rval = JSVAL_FALSE; + return JS_FALSE; +} + +static JSBool +file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, 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, &file_class, 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 (JSVAL_IS_REGEXP(cx, argv[0])) { + re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + }else + if (JSVAL_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); + JS_free(cx, 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, &file_class, 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); + + JS_free(cx, 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{ + char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + char *fullName; + + fullName = js_combinePath(cx, file->path, dirName); + if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ + *rval = JSVAL_TRUE; + JS_free(cx, fullName); + return JS_TRUE; + }else{ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, + JSFILEMSG_OP_FAILED, "mkdir", fullName); + JS_free(cx, 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, &file_class, NULL); + + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path)); + return JS_TRUE; +} + +static JSBool +file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); + char url[MAX_PATH_LENGTH]; + jschar *urlChars; + + JSFILE_CHECK_NATIVE("toURL"); + + sprintf(url, "file://%s", file->path); + /* TODO: js_escape in jsstr.h may go away at some point */ + + urlChars = js_InflateString(cx, url, strlen(url)); + if (urlChars == NULL) return JS_FALSE; + *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0)); + if (!js_str_escape(cx, obj, 0, rval, rval)) return JS_FALSE; + + return JS_TRUE; +out: + *rval = JSVAL_VOID; + return JS_FALSE; +} + + +static void +file_finalize(JSContext *cx, JSObject *obj) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, 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) + JS_free(cx, file->path); + + JS_free(cx, file); + } +} + +/* + Allocates memory for the file object, sets fields to defaults. +*/ +static JSFile* +file_init(JSContext *cx, JSObject *obj, char *bytes) +{ + JSFile *file; + + file = JS_malloc(cx, 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); + JS_free(cx, file); + return NULL; + }else + 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, &file_class, 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; +#ifdef XP_MAC + JS_ReportWarning(cx, "Native files are not fully supported on the MAC"); +#endif + + obj = JS_NewObject(cx, &file_class, 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); + JS_free(cx, 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; + + 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]); + goto out; + } + + file = file_init(cx, obj, JS_GetStringBytes(str)); + if (!file) goto out; + + SECURITY_CHECK(cx, NULL, "constructor", file); + + return JS_TRUE; +out: + *rval = JSVAL_VOID; + return JS_FALSE; +} + +/* -------------------- 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, &file_class, NULL); + char *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); + *vp = js_parent(cx, file); + break; + case FILE_PATH: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path)); + break; + case FILE_NAME: + *vp = js_name(cx, file); + 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"); + str = (char*)JS_malloc(cx, MODE_SIZE); + str[0] = '\0'; + flag = JS_FALSE; + + if ((file->mode&PR_RDONLY)==PR_RDONLY) { + if (flag) strcat(str, ","); + strcat(str, "read"); + flag = JS_TRUE; + } + if ((file->mode&PR_WRONLY)==PR_WRONLY) { + if (flag) strcat(str, ","); + strcat(str, "write"); + flag = JS_TRUE; + } + if ((file->mode&PR_RDWR)==PR_RDWR) { + if (flag) strcat(str, ","); + strcat(str, "readWrite"); + flag = JS_TRUE; + } + if ((file->mode&PR_APPEND)==PR_APPEND) { + if (flag) strcat(str, ","); + strcat(str, "append"); + flag = JS_TRUE; + } + if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { + if (flag) strcat(str, ","); + strcat(str, "create"); + flag = JS_TRUE; + } + if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { + if (flag) strcat(str, ","); + strcat(str, "replace"); + flag = JS_TRUE; + } + if (file->hasAutoflush) { + if (flag) strcat(str, ","); + strcat(str, "hasAutoFlush"); + flag = JS_TRUE; + } + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); + JS_free(cx, str); + 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 = JS_GetStringBytes(JS_ValueToString(cx, id)); + + /* 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)){ + str = js_combinePath(cx, file->path, prop_name); + *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, str)); + JS_free(cx, str); + return JS_TRUE; + } + } + } + } + return JS_TRUE; +out: + *vp = JSVAL_VOID; + return JS_FALSE; +} + +static JSBool +file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, 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: + *vp = JSVAL_VOID; + return JS_FALSE; +} + +/* + File.currentDir = new File("D:\") or File.currentDir = "D:\" +*/ +static JSBool +file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSObject *rhsObject; + char *path; + JSFile *file = JS_GetInstancePrivate(cx, rhsObject, &file_class, NULL); + + /* Look at the rhs and extract a file object from it */ + if (JSVAL_IS_OBJECT(*vp)){ + if (JS_InstanceOf(cx, rhsObject, &file_class, 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); + goto out; + }else{ + rhsObject = JSVAL_TO_OBJECT(*vp); + chdir(file->path); + return JS_TRUE; + } + }else + goto out; + }else{ + path = JS_GetStringBytes(JS_ValueToString(cx, *vp)); + rhsObject = js_NewFileObject(cx, path); + if (!rhsObject) goto out; + + 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; +out: + *vp = JSVAL_VOID; + return JS_FALSE; +} + +/* Declare class */ +static JSClass file_class = { + FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE, + 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, JSBool initStandardStreams) +{ + JSObject *file, *ctor, *afile; + jsval vp; + char *currentdir; + char separator[2]; + + file = JS_InitClass(cx, obj, NULL, &file_class, 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 = JS_malloc(cx, MAX_PATH_LENGTH); + currentdir = getcwd(currentdir, MAX_PATH_LENGTH); + afile = js_NewFileObject(cx, currentdir); + JS_free(cx, currentdir); + vp = OBJECT_TO_JSVAL(afile); + JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, + JS_PropertyStub, file_currentDirSetter, + JSPROP_ENUMERATE | JSPROP_READONLY ); + + if(initStandardStreams){ + /* Code to create stdin, stdout, and stderr. Insert in the appropriate place. */ + /* 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/src/extension/script/js/jsfile.h b/src/extension/script/js/jsfile.h new file mode 100644 index 000000000..47a8692d8 --- /dev/null +++ b/src/extension/script/js/jsfile.h @@ -0,0 +1,50 @@ +/* -*- 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 +extern JS_PUBLIC_API(JSObject*) +js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams); + +extern JS_PUBLIC_API(JSObject*) +js_NewFileObject(JSContext *cx, char *bytes); +#endif /* JS_HAS_FILE_OBJECT */ +#endif /* _jsfile_h__ */ diff --git a/src/extension/script/js/jsfun.c b/src/extension/script/js/jsfun.c new file mode 100644 index 000000000..93cd4009d --- /dev/null +++ b/src/extension/script/js/jsfun.c @@ -0,0 +1,2059 @@ +/* -*- 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 function support. + */ +#include "jsstddef.h" +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.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" + +/* Generic function/call/arguments tinyids -- also reflected bit numbers. */ +enum { + CALL_ARGUMENTS = -1, /* predefined arguments local variable */ + CALL_CALLEE = -2, /* reference to active function's object */ + ARGS_LENGTH = -3, /* number of actual args, arity if inactive */ + ARGS_CALLEE = -4, /* reference from arguments to active funobj */ + FUN_ARITY = -5, /* number of formal parameters; desired argc */ + FUN_NAME = -6, /* function name, "" if anonymous */ + FUN_CALLER = -7 /* Function.prototype.caller, backward compat */ +}; + +#if JSFRAME_OVERRIDE_BITS < 8 +# error "not enough override bits in JSStackFrame.flags!" +#endif + +#define TEST_OVERRIDE_BIT(fp, tinyid) \ + ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) + +#define SET_OVERRIDE_BIT(fp, tinyid) \ + ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) + +#if JS_HAS_ARGS_OBJECT + +JSBool +js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) +{ + JSObject *argsobj; + + if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { + JS_ASSERT(fp->callobj); + return OBJ_GET_PROPERTY(cx, fp->callobj, + (jsid) cx->runtime->atomState.argumentsAtom, + vp); + } + argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); + return JS_TRUE; +} + +static JSBool +MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) +{ + JSObject *argsobj; + jsval bmapval, bmapint; + size_t nbits, nbytes; + jsbitmap *bitmap; + + argsobj = fp->argsobj; + (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); + nbits = JS_MAX(fp->argc, fp->fun->nargs); + JS_ASSERT(slot < nbits); + if (JSVAL_IS_VOID(bmapval)) { + if (nbits <= JSVAL_INT_BITS) { + bmapint = 0; + bitmap = (jsbitmap *) &bmapint; + } else { + nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap); + bitmap = (jsbitmap *) JS_malloc(cx, nbytes); + if (!bitmap) + return JS_FALSE; + memset(bitmap, 0, nbytes); + bmapval = PRIVATE_TO_JSVAL(bitmap); + JS_SetReservedSlot(cx, argsobj, 0, bmapval); + } + } else { + if (nbits <= JSVAL_INT_BITS) { + bmapint = JSVAL_TO_INT(bmapval); + bitmap = (jsbitmap *) &bmapint; + } else { + bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); + } + } + JS_SET_BIT(bitmap, slot); + if (bitmap == (jsbitmap *) &bmapint) { + bmapval = INT_TO_JSVAL(bmapint); + JS_SetReservedSlot(cx, argsobj, 0, bmapval); + } + return JS_TRUE; +} + +/* NB: Infallible predicate, false does not mean error/exception. */ +static JSBool +ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) +{ + JSObject *argsobj; + jsval bmapval, bmapint; + jsbitmap *bitmap; + + argsobj = fp->argsobj; + (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); + if (JSVAL_IS_VOID(bmapval)) + return JS_FALSE; + if (JS_MAX(fp->argc, fp->fun->nargs) <= JSVAL_INT_BITS) { + bmapint = JSVAL_TO_INT(bmapval); + bitmap = (jsbitmap *) &bmapint; + } else { + bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); + } + return JS_TEST_BIT(bitmap, slot) != 0; +} + +JSBool +js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, + JSObject **objp, jsval *vp) +{ + jsval val; + JSObject *obj; + uintN slot; + + if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { + JS_ASSERT(fp->callobj); + if (!OBJ_GET_PROPERTY(cx, fp->callobj, + (jsid) cx->runtime->atomState.argumentsAtom, + &val)) { + return JS_FALSE; + } + if (JSVAL_IS_PRIMITIVE(val)) { + obj = js_ValueToNonNullObject(cx, val); + if (!obj) + return JS_FALSE; + } else { + obj = JSVAL_TO_OBJECT(val); + } + *objp = obj; + return OBJ_GET_PROPERTY(cx, obj, id, vp); + } + + *objp = NULL; + *vp = JSVAL_VOID; + if (JSVAL_IS_INT(id)) { + slot = (uintN) JSVAL_TO_INT(id); + if (slot < JS_MAX(fp->argc, fp->fun->nargs)) { + if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) + return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); + *vp = fp->argv[slot]; + } + } else { + if (id == (jsid) cx->runtime->atomState.lengthAtom) { + if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) + return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); + *vp = INT_TO_JSVAL((jsint) fp->argc); + } + } + return JS_TRUE; +} + +JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *argsobj; + + /* Create an arguments object for fp only if it lacks one. */ + JS_ASSERT(fp->fun); + argsobj = fp->argsobj; + if (argsobj) + return argsobj; + + /* Link the new object to fp so it can get actual argument values. */ + argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); + if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + fp->argsobj = argsobj; + return argsobj; +} + +static JSBool +args_enumerate(JSContext *cx, JSObject *obj); + +JSBool +js_PutArgsObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *argsobj; + jsval bmapval, rval; + JSBool ok; + JSRuntime *rt; + + /* + * Reuse args_enumerate here to reflect fp's actual arguments as indexed + * elements of argsobj. Do this first, before clearing and freeing the + * deleted argument slot bitmap, because args_enumerate depends on that. + */ + argsobj = fp->argsobj; + ok = args_enumerate(cx, argsobj); + + /* + * Now clear the deleted argument number bitmap slot and free the bitmap, + * if one was actually created due to 'delete arguments[0]' or similar. + */ + (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); + if (!JSVAL_IS_VOID(bmapval)) { + JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID); + if (JS_MAX(fp->argc, fp->fun->nargs) > JSVAL_INT_BITS) + JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); + } + + /* + * Now get the prototype properties so we snapshot fp->fun and fp->argc + * before fp goes away. + */ + rt = cx->runtime; + ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); + ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); + ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); + ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); + + /* + * Clear the private pointer to fp, which is about to go away (js_Invoke). + * Do this last because the args_enumerate and js_GetProperty calls above + * need to follow the private slot to find fp. + */ + ok &= JS_SetPrivate(cx, argsobj, NULL); + fp->argsobj = NULL; + return ok; +} + +static JSBool +args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSStackFrame *fp; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->argsobj); + JS_ASSERT(fp->fun); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case ARGS_CALLEE: + case ARGS_LENGTH: + SET_OVERRIDE_BIT(fp, slot); + break; + + default: + if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) && + !MarkArgDeleted(cx, fp, slot)) { + return JS_FALSE; + } + break; + } + return JS_TRUE; +} + +static JSBool +args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSStackFrame *fp; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->argsobj); + JS_ASSERT(fp->fun); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case ARGS_CALLEE: + if (!TEST_OVERRIDE_BIT(fp, slot)) + *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); + break; + + case ARGS_LENGTH: + if (!TEST_OVERRIDE_BIT(fp, slot)) + *vp = INT_TO_JSVAL((jsint)fp->argc); + break; + + default: + if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) && + !ArgWasDeleted(cx, fp, slot)) { + *vp = fp->argv[slot]; + } + break; + } + return JS_TRUE; +} + +static JSBool +args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSStackFrame *fp; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->argsobj); + JS_ASSERT(fp->fun); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case ARGS_CALLEE: + case ARGS_LENGTH: + SET_OVERRIDE_BIT(fp, slot); + break; + + default: + if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) && + !ArgWasDeleted(cx, fp, slot)) { + fp->argv[slot] = *vp; + } + break; + } + return JS_TRUE; +} + +static JSBool +args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSStackFrame *fp; + uintN slot; + JSString *str; + JSAtom *atom; + intN tinyid; + jsval value; + + *objp = NULL; + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->argsobj); + JS_ASSERT(fp->fun); + + if (JSVAL_IS_INT(id)) { + slot = JSVAL_TO_INT(id); + if (slot < JS_MAX(fp->argc, fp->fun->nargs) && + !ArgWasDeleted(cx, fp, slot)) { + /* XXX ECMA specs DontEnum, contrary to other array-like objects */ + if (!js_DefineProperty(cx, obj, (jsid) id, fp->argv[slot], + args_getProperty, args_setProperty, + JSVERSION_IS_ECMA(cx->version) + ? 0 + : JSPROP_ENUMERATE, + NULL)) { + return JS_FALSE; + } + *objp = obj; + } + } else { + str = JSVAL_TO_STRING(id); + atom = cx->runtime->atomState.lengthAtom; + if (str == ATOM_TO_STRING(atom)) { + tinyid = ARGS_LENGTH; + value = INT_TO_JSVAL(fp->argc); + } else { + atom = cx->runtime->atomState.calleeAtom; + if (str == ATOM_TO_STRING(atom)) { + tinyid = ARGS_CALLEE; + value = fp->argv ? fp->argv[-2] + : OBJECT_TO_JSVAL(fp->fun->object); + } else { + atom = NULL; + + /* Quell GCC overwarnings. */ + tinyid = 0; + value = JSVAL_NULL; + } + } + + if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { + if (!js_DefineNativeProperty(cx, obj, (jsid) atom, value, + args_getProperty, args_setProperty, 0, + SPROP_HAS_SHORTID, tinyid, NULL)) { + return JS_FALSE; + } + *objp = obj; + } + } + + return JS_TRUE; +} + +static JSBool +args_enumerate(JSContext *cx, JSObject *obj) +{ + JSStackFrame *fp; + JSObject *pobj; + JSProperty *prop; + uintN slot, nargs; + + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->argsobj); + JS_ASSERT(fp->fun); + + /* + * Trigger reflection with value snapshot in args_resolve using a series + * of js_LookupProperty calls. We handle length, callee, and the indexed + * argument properties. We know that args_resolve covers all these cases + * and creates direct properties of obj, but that it may fail to resolve + * length or callee if overridden. + */ + if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.lengthAtom, + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + + if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.calleeAtom, + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + + nargs = JS_MAX(fp->argc, fp->fun->nargs); + for (slot = 0; slot < nargs; slot++) { + if (!js_LookupProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot), + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + return JS_TRUE; +} + +/* + * 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(1), + JS_PropertyStub, args_delProperty, + args_getProperty, args_setProperty, + args_enumerate, (JSResolveOp) args_resolve, + JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#endif /* JS_HAS_ARGS_OBJECT */ + +#if JS_HAS_CALL_OBJECT + +JSObject * +js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) +{ + JSObject *callobj, *funobj; + + /* Create a call object for fp only if it lacks one. */ + JS_ASSERT(fp->fun); + callobj = fp->callobj; + if (callobj) + return callobj; + JS_ASSERT(fp->fun); + + /* The default call parent is its function's parent (static link). */ + if (!parent) { + funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; + if (funobj) + parent = OBJ_GET_PARENT(cx, funobj); + } + + /* Create the call object and link it to its stack frame. */ + callobj = js_NewObject(cx, &js_CallClass, NULL, parent); + if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + fp->callobj = callobj; + + /* Make callobj be the scope chain and the variables object. */ + fp->scopeChain = callobj; + fp->varobj = callobj; + return callobj; +} + +static JSBool +call_enumerate(JSContext *cx, JSObject *obj); + +JSBool +js_PutCallObject(JSContext *cx, JSStackFrame *fp) +{ + JSObject *callobj; + JSBool ok; + jsid argsid; + jsval aval; + + /* + * Reuse call_enumerate here to reflect all actual args and vars into the + * call object from fp. + */ + callobj = fp->callobj; + if (!callobj) + return JS_TRUE; + ok = call_enumerate(cx, callobj); + + /* + * Get the arguments object to snapshot fp's actual argument values. + */ + if (fp->argsobj) { + argsid = (jsid) cx->runtime->atomState.argumentsAtom; + ok &= js_GetProperty(cx, callobj, argsid, &aval); + ok &= js_SetProperty(cx, callobj, argsid, &aval); + ok &= js_PutArgsObject(cx, fp); + } + + /* + * Clear the private pointer to fp, which is about to go away (js_Invoke). + * Do this last because the call_enumerate and js_GetProperty calls above + * need to follow the private slot to find fp. + */ + ok &= JS_SetPrivate(cx, callobj, NULL); + fp->callobj = NULL; + return ok; +} + +static JSPropertySpec call_props[] = { + {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,0,0}, + {"__callee__", CALL_CALLEE, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSStackFrame *fp; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->fun); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case CALL_ARGUMENTS: + if (!TEST_OVERRIDE_BIT(fp, slot)) { + JSObject *argsobj = js_GetArgsObject(cx, fp); + if (!argsobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(argsobj); + } + break; + + case CALL_CALLEE: + if (!TEST_OVERRIDE_BIT(fp, slot)) + *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); + break; + + default: + if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) + *vp = fp->argv[slot]; + break; + } + return JS_TRUE; +} + +static JSBool +call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSStackFrame *fp; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->fun); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case CALL_ARGUMENTS: + case CALL_CALLEE: + SET_OVERRIDE_BIT(fp, slot); + break; + + default: + if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) + fp->argv[slot] = *vp; + break; + } + return JS_TRUE; +} + +JSBool +js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSStackFrame *fp; + + JS_ASSERT(JSVAL_IS_INT(id)); + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (fp) { + /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ + if ((uintN)JSVAL_TO_INT(id) < fp->nvars) + *vp = fp->vars[JSVAL_TO_INT(id)]; + } + return JS_TRUE; +} + +JSBool +js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSStackFrame *fp; + + JS_ASSERT(JSVAL_IS_INT(id)); + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (fp) { + /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ + jsint slot = JSVAL_TO_INT(id); + if ((uintN)slot < fp->nvars) + fp->vars[slot] = *vp; + } + return JS_TRUE; +} + +static JSBool +call_enumerate(JSContext *cx, JSObject *obj) +{ + JSStackFrame *fp; + JSObject *funobj; + JSScope *scope; + JSScopeProperty *sprop, *cprop; + JSPropertyOp getter; + jsval *vec; + JSProperty *prop; + + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + + /* + * Do not enumerate a cloned function object at fp->argv[-2], it may have + * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets + * the clone's prototype property). We must enumerate the function object + * that was decorated with parameter and local variable properties by the + * compiler when the compiler created fp->fun, namely fp->fun->object. + * + * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll + * use js_LookupProperty to find any overridden properties in that object, + * if it was a mutated clone; and if not, we will search its prototype, + * fp->fun->object, to find compiler-created params and locals. + */ + funobj = fp->fun->object; + if (!funobj) + return JS_TRUE; + + /* + * Reflect actual args from fp->argv for formal parameters, and local vars + * and functions in fp->vars for declared variables and nested-at-top-level + * local functions. + */ + scope = OBJ_SCOPE(funobj); + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + getter = sprop->getter; + if (getter == js_GetArgument) + vec = fp->argv; + else if (getter == js_GetLocalVariable) + vec = fp->vars; + else + continue; + + /* Trigger reflection in call_resolve by doing a lookup. */ + if (!js_LookupProperty(cx, obj, sprop->id, &obj, &prop)) + return JS_FALSE; + JS_ASSERT(obj && prop); + cprop = (JSScopeProperty *)prop; + LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[sprop->shortid]); + OBJ_DROP_PROPERTY(cx, obj, prop); + } + + return JS_TRUE; +} + +static JSBool +call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSStackFrame *fp; + JSObject *funobj; + JSString *str; + JSAtom *atom; + JSObject *obj2; + JSScopeProperty *sprop; + jsid propid; + JSPropertyOp getter, setter; + uintN attrs, slot, nslots, spflags; + jsval *vp, value; + intN shortid; + + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + JS_ASSERT(fp->fun); + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + + funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; + if (!funobj) + return JS_TRUE; + + str = JSVAL_TO_STRING(id); + atom = js_AtomizeString(cx, str, 0); + if (!atom) + return JS_FALSE; + if (!js_LookupProperty(cx, funobj, (jsid)atom, &obj2, + (JSProperty **)&sprop)) { + return JS_FALSE; + } + + if (sprop && OBJ_IS_NATIVE(obj2)) { + propid = sprop->id; + getter = sprop->getter; + attrs = sprop->attrs & ~JSPROP_SHARED; + slot = (uintN) sprop->shortid; + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + if (getter == js_GetArgument || getter == js_GetLocalVariable) { + if (getter == js_GetArgument) { + vp = fp->argv; + nslots = JS_MAX(fp->argc, fp->fun->nargs); + getter = setter = NULL; + } else { + vp = fp->vars; + nslots = fp->nvars; + getter = js_GetCallVariable; + setter = js_SetCallVariable; + } + if (slot < nslots) { + value = vp[slot]; + spflags = SPROP_HAS_SHORTID; + shortid = (intN) slot; + } else { + value = JSVAL_VOID; + spflags = 0; + shortid = 0; + } + if (!js_DefineNativeProperty(cx, obj, propid, value, + getter, setter, attrs, + spflags, shortid, NULL)) { + return JS_FALSE; + } + *objp = obj; + } + } + return JS_TRUE; +} + +static JSBool +call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + JSStackFrame *fp; + + if (type == JSTYPE_FUNCTION) { + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (fp) { + JS_ASSERT(fp->fun); + *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); + } + } + return JS_TRUE; +} + +JSClass js_CallClass = { + js_Call_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + call_getProperty, call_setProperty, + call_enumerate, (JSResolveOp)call_resolve, + call_convert, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#endif /* JS_HAS_CALL_OBJECT */ + +/* SHARED because fun_getProperty always computes a new value. */ +#define FUNCTION_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) + +static JSPropertySpec function_props[] = { + {js_arguments_str, CALL_ARGUMENTS, FUNCTION_PROP_ATTRS,0,0}, + {js_arity_str, FUN_ARITY, FUNCTION_PROP_ATTRS,0,0}, + {js_length_str, ARGS_LENGTH, FUNCTION_PROP_ATTRS,0,0}, + {js_name_str, FUN_NAME, FUNCTION_PROP_ATTRS,0,0}, + {js_caller_str, FUN_CALLER, FUNCTION_PROP_ATTRS,0,0}, + {0,0,0,0,0} +}; + +static JSBool +fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSFunction *fun; + JSStackFrame *fp; +#if defined _MSC_VER &&_MSC_VER <= 800 + /* MSVC1.5 coredumps */ + jsval bogus = *vp; +#endif + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + + /* No valid function object should lack private data, but check anyway. */ + fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); + if (!fun) + return JS_TRUE; + + /* Find fun's top-most activation record. */ + for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); + fp = fp->down) { + continue; + } + + switch (slot) { + case CALL_ARGUMENTS: +#if JS_HAS_ARGS_OBJECT + /* 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; +#else /* !JS_HAS_ARGS_OBJECT */ + *vp = OBJECT_TO_JSVAL(fp ? obj : NULL); + break; +#endif /* !JS_HAS_ARGS_OBJECT */ + + case ARGS_LENGTH: + if (!JSVERSION_IS_ECMA(cx->version)) + *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); + else + 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: + while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down) + fp = fp->down; + if (fp && fp->down && fp->down->fun && fp->down->argv) + *vp = fp->down->argv[-2]; + else + *vp = JSVAL_NULL; + if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { + id = ATOM_KEY(cx->runtime->atomState.callerAtom); + if (!cx->runtime->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) +#if defined _MSC_VER &&_MSC_VER <= 800 + /* MSVC1.5 coredumps */ + if (bogus == *vp) +#endif + *vp = fp->argv[slot]; + break; + } + + return JS_TRUE; +} + +static JSBool +fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + JSFunction *fun; + JSString *str; + JSAtom *prototypeAtom; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + + /* No valid function object should lack private data, but check anyway. */ + fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); + if (!fun || !fun->object) + return JS_TRUE; + + /* No need to reflect fun.prototype in 'fun.prototype = ...'. */ + if (flags & JSRESOLVE_ASSIGNING) + return JS_TRUE; + + /* + * Ok, check whether id is 'prototype' and bootstrap the function object's + * prototype property. + */ + str = JSVAL_TO_STRING(id); + prototypeAtom = cx->runtime->atomState.classPrototypeAtom; + if (str == ATOM_TO_STRING(prototypeAtom)) { + JSObject *proto, *parentProto; + jsval pval; + + proto = parentProto = NULL; + if (fun->object != obj && fun->object) { + /* + * Clone of a function: make its prototype property value have the + * same class as the clone-parent's prototype. + */ + if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval)) + return JS_FALSE; + if (JSVAL_IS_OBJECT(pval)) + parentProto = JSVAL_TO_OBJECT(pval); + } + + /* + * 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 (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom) + return JS_TRUE; + + /* + * If resolving "prototype" in a clone, clone the parent's prototype. + * Pass the constructor's (obj's) parent as the prototype parent, to + * avoid defaulting to parentProto.constructor.__parent__. + */ + proto = js_NewObject(cx, &js_ObjectClass, parentProto, + OBJ_GET_PARENT(cx, obj)); + if (!proto) + return JS_FALSE; + + /* + * ECMA says that constructor.prototype is DontEnum | 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)) { + cx->newborn[GCX_OBJECT] = NULL; + return JS_FALSE; + } + *objp = obj; + } + + 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); + } +} + +static void +fun_finalize(JSContext *cx, JSObject *obj) +{ + JSFunction *fun; + + /* No valid function object should lack private data, but check anyway. */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + if (!fun) + return; + if (fun->object == obj) + fun->object = NULL; + JS_ATOMIC_DECREMENT(&fun->nrefs); + if (fun->nrefs) + return; + if (fun->script) + js_DestroyScript(cx, fun->script); + JS_free(cx, fun); +} + +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +enum { + JSXDR_FUNARG = 1, + JSXDR_FUNVAR = 2, + JSXDR_FUNCONST = 3 +}; + +/* XXX store parent and proto, if defined */ +static JSBool +fun_xdrObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + JSFunction *fun; + JSString *atomstr; + char *propname; + JSScopeProperty *sprop; + uint32 userid; /* NB: holds a signed int-tagged jsval */ + JSAtom *atom; + uintN i, n, dupflag; + uint32 type; +#ifdef DEBUG + uintN nvars = 0, nargs = 0; +#endif + + cx = xdr->cx; + if (xdr->mode == JSXDR_ENCODE) { + /* + * No valid function object should lack private data, but fail soft + * (return true, no error report) in case one does due to API pilot + * or internal error. + */ + fun = (JSFunction *) JS_GetPrivate(cx, *objp); + if (!fun) + return JS_TRUE; + atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + } else { + fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); + if (!fun) + return JS_FALSE; + atomstr = NULL; + } + + if (!JS_XDRStringOrNull(xdr, &atomstr) || + !JS_XDRUint16(xdr, &fun->nargs) || + !JS_XDRUint16(xdr, &fun->extra) || + !JS_XDRUint16(xdr, &fun->nvars) || + !JS_XDRUint8(xdr, &fun->flags)) { + return JS_FALSE; + } + + /* do arguments and local vars */ + if (fun->object) { + n = fun->nargs + fun->nvars; + if (xdr->mode == JSXDR_ENCODE) { + JSScope *scope; + JSScopeProperty **spvec, *auto_spvec[8]; + void *mark; + + if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { + spvec = auto_spvec; + mark = NULL; + } else { + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, + n * sizeof(JSScopeProperty *)); + if (!spvec) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + scope = OBJ_SCOPE(fun->object); + for (sprop = SCOPE_LAST_PROP(scope); sprop; + sprop = sprop->parent) { + if (sprop->getter == js_GetArgument) { + JS_ASSERT(nargs++ <= fun->nargs); + spvec[sprop->shortid] = sprop; + } else if (sprop->getter == js_GetLocalVariable) { + JS_ASSERT(nvars++ <= fun->nvars); + spvec[fun->nargs + sprop->shortid] = sprop; + } + } + for (i = 0; i < n; i++) { + sprop = spvec[i]; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + type = (i < fun->nargs) + ? JSXDR_FUNARG + : (sprop->attrs & JSPROP_READONLY) + ? JSXDR_FUNCONST + : JSXDR_FUNVAR; + userid = INT_TO_JSVAL(sprop->shortid); + /* XXX lossy conversion, need new XDR version for ECMAv3 */ + propname = JS_GetStringBytes(ATOM_TO_STRING((JSAtom *)sprop->id)); + if (!propname || + !JS_XDRUint32(xdr, &type) || + !JS_XDRUint32(xdr, &userid) || + !JS_XDRCString(xdr, &propname)) { + if (mark) + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + } + if (mark) + JS_ARENA_RELEASE(&cx->tempPool, mark); + } else { + JSPropertyOp getter, setter; + + for (i = n; i != 0; i--) { + uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT; + + if (!JS_XDRUint32(xdr, &type) || + !JS_XDRUint32(xdr, &userid) || + !JS_XDRCString(xdr, &propname)) { + return JS_FALSE; + } + JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || + type == JSXDR_FUNCONST); + if (type == JSXDR_FUNARG) { + getter = js_GetArgument; + setter = js_SetArgument; + JS_ASSERT(nargs++ <= fun->nargs); + } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { + getter = js_GetLocalVariable; + setter = js_SetLocalVariable; + if (type == JSXDR_FUNCONST) + attrs |= JSPROP_READONLY; + JS_ASSERT(nvars++ <= fun->nvars); + } else { + getter = NULL; + setter = NULL; + } + atom = js_Atomize(cx, propname, strlen(propname), 0); + JS_free(cx, propname); + if (!atom) + return JS_FALSE; + + /* Flag duplicate argument if atom is bound in fun->object. */ + dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), (jsid)atom) + ? SPROP_IS_DUPLICATE + : 0; + + if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + getter, setter, SPROP_INVALID_SLOT, + attrs | JSPROP_SHARED, + SPROP_HAS_SHORTID | dupflag, + JSVAL_TO_INT(userid))) { + return JS_FALSE; + } + } + } + } + + if (!js_XDRScript(xdr, &fun->script, NULL)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + *objp = fun->object; + if (atomstr) { + fun->atom = js_AtomizeString(cx, atomstr, 0); + if (!fun->atom) + return JS_FALSE; + + if (!OBJ_DEFINE_PROPERTY(cx, cx->globalObject, + (jsid)fun->atom, OBJECT_TO_JSVAL(*objp), + NULL, NULL, JSPROP_ENUMERATE, + NULL)) { + return JS_FALSE; + } + } + + js_CallNewScriptHook(cx, fun->script, fun); + } + + return JS_TRUE; +} + +#else /* !JS_HAS_XDR */ + +#define fun_xdrObject NULL + +#endif /* !JS_HAS_XDR */ + +#if JS_HAS_INSTANCEOF + +/* + * [[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, cval; + JSString *str; + JSObject *proto, *obj2; + JSFunction *cfun, *ofun; + + if (!OBJ_GET_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.classPrototypeAtom, + &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. + */ + str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str)); + } + return JS_FALSE; + } + + proto = JSVAL_TO_OBJECT(pval); + if (!js_IsDelegate(cx, proto, v, bp)) + return JS_FALSE; + + if (!*bp && !JSVAL_IS_PRIMITIVE(v)) { + /* + * Extension for "brutal sharing" of standard class constructors: if + * a script is compiled using a single, shared set of constructors, in + * particular Function and RegExp, but executed many times using other + * sets of standard constructors, then (/re/ instanceof RegExp), e.g., + * will be false. + * + * We extend instanceof in this case to look for a matching native or + * script underlying the function object found in the 'constructor' + * property of the object in question (which is JSVAL_TO_OBJECT(v)), + * or found in the 'constructor' property of one of its prototypes. + * + * See also jsexn.c, where the *Error constructors are defined, each + * with its own native function, to satisfy (e instanceof Error) even + * when exceptions cross standard-class sharing boundaries. Note that + * Error.prototype may not lie on e's __proto__ chain in that case. + */ + obj2 = JSVAL_TO_OBJECT(v); + do { + if (!OBJ_GET_PROPERTY(cx, obj2, + (jsid)cx->runtime->atomState.constructorAtom, + &cval)) { + return JS_FALSE; + } + + if (JSVAL_IS_FUNCTION(cx, cval)) { + cfun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(cval)); + ofun = (JSFunction *) JS_GetPrivate(cx, obj); + if (cfun->native == ofun->native && + cfun->script == ofun->script) { + *bp = JS_TRUE; + break; + } + } + } while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL); + } + + return JS_TRUE; +} + +#else /* !JS_HAS_INSTANCEOF */ + +#define fun_hasInstance NULL + +#endif /* !JS_HAS_INSTANCEOF */ + +static uint32 +fun_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSFunction *fun; + + fun = (JSFunction *) JS_GetPrivate(cx, obj); + if (fun) { + if (fun->atom) + GC_MARK_ATOM(cx, fun->atom, arg); + if (fun->script) + js_MarkScript(cx, fun->script, arg); + } + return 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. + */ +JSClass js_FunctionClass = { + js_Function_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2), + JS_PropertyStub, JS_PropertyStub, + fun_getProperty, JS_PropertyStub, + JS_EnumerateStub, (JSResolveOp)fun_resolve, + fun_convert, fun_finalize, + NULL, NULL, + NULL, NULL, + fun_xdrObject, fun_hasInstance, + fun_mark, 0 +}; + +JSBool +js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, + uintN argc, jsval *argv, jsval *rval) +{ + jsval fval; + JSFunction *fun; + JSString *str; + + if (!argv) { + JS_ASSERT(JS_ObjectIsFunction(cx, obj)); + } else { + fval = argv[-1]; + if (!JSVAL_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_OBJECT(fval)) { + obj = JSVAL_TO_OBJECT(fval); + if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, + &fval)) { + return JS_FALSE; + } + } + if (!JSVAL_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); + } + + fun = (JSFunction *) JS_GetPrivate(cx, obj); + if (!fun) + return JS_TRUE; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + str = JS_DecompileFunction(cx, fun, (uintN)indent); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return js_fun_toString(cx, obj, 0, argc, argv, rval); +} + +#if JS_HAS_TOSOURCE +static JSBool +fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval); +} +#endif + +static const char js_call_str[] = "call"; + +#if JS_HAS_CALL_FUNCTION +static JSBool +fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval fval, *sp, *oldsp; + void *mark; + uintN i; + JSStackFrame *fp; + JSBool ok; + + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) + return JS_FALSE; + fval = argv[-1]; + + if (!JSVAL_IS_FUNCTION(cx, fval)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_call_str, + JS_GetStringBytes(JS_ValueToString(cx, fval))); + return JS_FALSE; + } + + if (argc == 0) { + /* Call fun with its parent as the 'this' parameter if no args. */ + obj = OBJ_GET_PARENT(cx, obj); + } else { + /* Otherwise convert the first arg to 'this' and skip over it. */ + if (!js_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + argc--; + argv++; + } + + /* Allocate stack space for fval, obj, and the args. */ + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push fval, obj, and the args. */ + *sp++ = fval; + *sp++ = OBJECT_TO_JSVAL(obj); + for (i = 0; i < argc; i++) + *sp++ = argv[i]; + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); + + /* Store rval and pop stack back to our frame's sp. */ + *rval = fp->sp[-1]; + fp->sp = oldsp; + js_FreeStack(cx, mark); + return ok; +} +#endif /* JS_HAS_CALL_FUNCTION */ + +#if JS_HAS_APPLY_FUNCTION +static JSBool +fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval fval, *sp, *oldsp; + JSObject *aobj; + jsuint length; + void *mark; + uintN i; + JSBool ok; + JSStackFrame *fp; + + if (argc == 0) { + /* Will get globalObject as 'this' and no other agurments. */ + return fun_call(cx, obj, argc, argv, rval); + } + + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) + return JS_FALSE; + fval = argv[-1]; + + if (!JSVAL_IS_FUNCTION(cx, fval)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, "apply", + JS_GetStringBytes(JS_ValueToString(cx, fval))); + 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(argv[1]) || JSVAL_IS_VOID(argv[1])) { + argc = 0; + } else { + /* The second arg must be an array (or arguments object). */ + if (JSVAL_IS_PRIMITIVE(argv[1]) || + (aobj = JSVAL_TO_OBJECT(argv[1]), + OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass && + OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass)) + { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_APPLY_ARGS); + return JS_FALSE; + } + if (!js_GetLengthProperty(cx, aobj, &length)) + return JS_FALSE; + } + } + + /* Convert the first arg to 'this' and skip over it. */ + if (!js_ValueToObject(cx, argv[0], &obj)) + return JS_FALSE; + + /* Allocate stack space for fval, obj, and the args. */ + argc = (uintN)JS_MIN(length, ARGC_LIMIT - 1); + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push fval, obj, and aobj's elements as args. */ + *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++; + } + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); + + /* Store rval and pop stack back to our frame's sp. */ + *rval = fp->sp[-1]; + fp->sp = oldsp; +out: + js_FreeStack(cx, mark); + return ok; +} +#endif /* JS_HAS_APPLY_FUNCTION */ + +static JSFunctionSpec function_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, fun_toSource, 0,0,0}, +#endif + {js_toString_str, fun_toString, 1,0,0}, +#if JS_HAS_APPLY_FUNCTION + {"apply", fun_apply, 2,0,0}, +#endif +#if JS_HAS_CALL_FUNCTION + {js_call_str, fun_call, 1,0,0}, +#endif + {0,0,0,0,0} +}; + +JSBool +js_IsIdentifier(JSString *str) +{ + size_t n; + jschar *s, c; + + n = JSSTRING_LENGTH(str); + if (n == 0) + return JS_FALSE; + s = JSSTRING_CHARS(str); + c = *s; + if (!JS_ISIDENT_START(c)) + return JS_FALSE; + for (n--; n != 0; n--) { + c = *++s; + if (!JS_ISIDENT(c)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp, *caller; + JSFunction *fun; + JSObject *parent; + uintN i, n, lineno, dupflag; + JSAtom *atom; + const char *filename; + JSObject *obj2; + JSScopeProperty *sprop; + JSString *str, *arg; + void *mark; + JSTokenStream *ts; + JSPrincipals *principals; + jschar *collected_args, *cp; + size_t arg_length, args_length; + JSTokenType tt; + JSBool ok; + + fp = cx->fp; + if (fp && !(fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + fun = (JSFunction *) JS_GetPrivate(cx, obj); + if (fun) + return JS_TRUE; + +#if JS_HAS_CALL_OBJECT + /* + * 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])); +#else + /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */ + parent = NULL; +#endif + + fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, + JSVERSION_IS_ECMA(cx->version) + ? cx->runtime->atomState.anonymousAtom + : NULL); + + 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. + */ + JS_ASSERT(!fp->script && fp->fun && fp->fun->native == Function); + caller = JS_GetScriptedCaller(cx, fp); + if (caller) { + filename = caller->script->filename; + lineno = js_PCToLineNumber(cx, caller->script, caller->pc); + principals = JS_EvalFramePrincipals(cx, fp, caller); + } else { + filename = NULL; + lineno = 0; + principals = NULL; + } + + n = argc ? argc - 1 : 0; + if (n > 0) { + /* + * 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. + */ + 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); + args_length += JSSTRING_LENGTH(arg); + } + /* Add 1 for each joining comma. */ + args_length += n - 1; + + /* + * 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) + 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 = JSSTRING_LENGTH(arg); + (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); + cp += arg_length; + + /* Add separating comma or terminating 0. */ + *cp++ = (i + 1 < n) ? ',' : 0; + } + + /* + * Make a tokenstream (allocated from cx->tempPool) that reads from + * the given string. + */ + ts = js_NewTokenStream(cx, collected_args, args_length, filename, + lineno, principals); + if (!ts) { + 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 bad_formal; + + /* + * Get the atom corresponding to the name from the tokenstream; + * we're assured at this point that it's a valid identifier. + */ + atom = CURRENT_TOKEN(ts).t_atom; + if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2, + (JSProperty **)&sprop)) { + goto bad_formal; + } + dupflag = 0; + if (sprop) { + ok = JS_TRUE; + if (obj2 == obj) { + const char *name = js_AtomToPrintableString(cx, atom); + + /* + * A duplicate parameter name. We force a duplicate + * node on the SCOPE_LAST_PROP(scope) list with the + * same id, distinguished by the SPROP_IS_DUPLICATE + * flag, and not mapped by an entry in scope. + */ + JS_ASSERT(sprop->getter == js_GetArgument); + ok = name && + js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DUPLICATE_FORMAL, + name); + + dupflag = SPROP_IS_DUPLICATE; + } + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + if (!ok) + goto bad_formal; + sprop = NULL; + } + if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_SHARED, + SPROP_HAS_SHORTID | dupflag, + fun->nargs)) { + goto bad_formal; + } + fun->nargs++; + + /* + * 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 bad_formal; + tt = js_GetToken(cx, ts); + } + } + + /* Clean up. */ + ok = js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!ok) + return JS_FALSE; + } + + if (argc) { + str = js_ValueToString(cx, argv[argc-1]); + } else { + /* Can't use cx->runtime->emptyString because we're called too early. */ + str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); + } + if (!str) + return JS_FALSE; + if (argv) { + /* Use the last arg (or this if argc == 0) as a local GC root. */ + argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); + } + + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + filename, lineno, principals); + if (!ts) { + ok = JS_FALSE; + } else { + ok = js_CompileFunctionBody(cx, ts, fun) && + js_CloseTokenStream(cx, ts); + } + JS_ARENA_RELEASE(&cx->tempPool, mark); + return ok; + +bad_formal: + /* + * Report "malformed formal parameter" iff no illegal char or similar + * scanner error was already reported. + */ + if (!(ts->flags & TSF_ERROR)) + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); + + /* + * Clean up the arguments string and tokenstream if we failed to parse + * the arguments. + */ + (void)js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; +} + +JSObject * +js_InitFunctionClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + JSAtom *atom; + JSFunction *fun; + + proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, + function_props, function_methods, NULL, NULL); + if (!proto) + return NULL; + atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), + 0); + if (!atom) + goto bad; + fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); + if (!fun) + goto bad; + fun->script = js_NewScript(cx, 0, 0, 0); + if (!fun->script) + goto bad; + return proto; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +#if JS_HAS_CALL_OBJECT +JSObject * +js_InitCallClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, + call_props, NULL, NULL, NULL); +} +#endif + +JSFunction * +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom) +{ + JSFunction *fun; + + /* Allocate a function struct. */ + fun = (JSFunction *) JS_malloc(cx, sizeof *fun); + if (!fun) + return NULL; + + /* If funobj is null, allocate an object for it. */ + if (funobj) { + OBJ_SET_PARENT(cx, funobj, parent); + } else { + funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); + if (!funobj) { + JS_free(cx, fun); + return NULL; + } + } + + /* Initialize all function members. */ + fun->nrefs = 0; + fun->object = NULL; + fun->native = native; + fun->script = NULL; + fun->nargs = nargs; + fun->extra = 0; + fun->nvars = 0; + fun->flags = flags & JSFUN_FLAGS_MASK; + fun->spare = 0; + fun->atom = atom; + fun->clasp = NULL; + + /* Link fun to funobj and vice versa. */ + if (!js_LinkFunctionObject(cx, fun, funobj)) { + cx->newborn[GCX_OBJECT] = NULL; + JS_free(cx, fun); + return NULL; + } + return fun; +} + +JSObject * +js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) +{ + JSObject *newfunobj; + JSFunction *fun; + + JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); + newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); + if (!newfunobj) + return NULL; + fun = (JSFunction *) JS_GetPrivate(cx, funobj); + if (!js_LinkFunctionObject(cx, fun, newfunobj)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + return newfunobj; +} + +JSBool +js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) +{ + if (!fun->object) + fun->object = funobj; + if (!JS_SetPrivate(cx, funobj, fun)) + return JS_FALSE; + JS_ATOMIC_INCREMENT(&fun->nrefs); + return JS_TRUE; +} + +JSFunction * +js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, + uintN nargs, uintN attrs) +{ + JSFunction *fun; + + fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); + if (!fun) + return NULL; + if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object), + NULL, NULL, attrs & ~JSFUN_FLAGS_MASK, NULL)) { + 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_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) + return NULL; + obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; + } + } + if (!obj) { + js_ReportIsNotFunction(cx, vp, flags); + return NULL; + } + return (JSFunction *) JS_GetPrivate(cx, obj); +} + +void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) +{ + JSType type; + JSString *fallback; + JSString *str; + + /* + * We provide the typename as the fallback to handle the case when + * valueOf is not a function, which prevents ValueToString from being + * called as the default case inside js_DecompileValueGenerator (and + * so recursing back to here). + */ + type = JS_TypeOfValue(cx, *vp); + fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); + str = js_DecompileValueGenerator(cx, + (flags & JSV2F_SEARCH_STACK) + ? JSDVG_SEARCH_STACK + : cx->fp + ? vp - cx->fp->sp + : JSDVG_IGNORE_STACK, + *vp, + fallback); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + (uintN)((flags & JSV2F_CONSTRUCT) + ? JSMSG_NOT_CONSTRUCTOR + : JSMSG_NOT_FUNCTION), + JS_GetStringBytes(str)); + } +} diff --git a/src/extension/script/js/jsfun.h b/src/extension/script/js/jsfun.h new file mode 100644 index 000000000..0a3237921 --- /dev/null +++ b/src/extension/script/js/jsfun.h @@ -0,0 +1,151 @@ +/* -*- 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" + +JS_BEGIN_EXTERN_C + +struct JSFunction { + jsrefcount nrefs; /* number of referencing objects */ + JSObject *object; /* back-pointer to GC'ed object header */ + JSNative native; /* native method pointer or null */ + JSScript *script; /* interpreted bytecode descriptor or null */ + uint16 nargs; /* minimum number of actual arguments */ + uint16 extra; /* number of arg slots for local GC roots */ + uint16 nvars; /* number of local variables */ + uint8 flags; /* bound method and other flags, see jsapi.h */ + uint8 spare; /* reserved for future use */ + JSAtom *atom; /* name for diagnostics and decompiling */ + JSClass *clasp; /* if non-null, constructor for this class */ +}; + +extern JSClass js_ArgumentsClass; +extern JSClass js_CallClass; + +/* JS_FRIEND_DATA so that JSVAL_IS_FUNCTION is callable from outside */ +extern JS_FRIEND_DATA(JSClass) js_FunctionClass; + +/* + * NB: jsapi.h and jsobj.h must be included before any call to this macro. + */ +#define JSVAL_IS_FUNCTION(cx, v) \ + (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) + +extern JSBool +js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, + uintN argc, jsval *argv, jsval *rval); + +extern JSBool +js_IsIdentifier(JSString *str); + +extern JSObject * +js_InitFunctionClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitArgumentsClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitCallClass(JSContext *cx, JSObject *obj); + +extern JSFunction * +js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, + uintN flags, JSObject *parent, JSAtom *atom); + +extern JSObject * +js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); + +extern JSBool +js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); + +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_SEARCH_STACK 2 + +extern JSFunction * +js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); + +extern void +js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); + +extern JSObject * +js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent); + +extern JSBool +js_PutCallObject(JSContext *cx, JSStackFrame *fp); + +extern JSBool +js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp); + +extern JSBool +js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, + JSObject **objp, jsval *vp); + +extern JSObject * +js_GetArgsObject(JSContext *cx, JSStackFrame *fp); + +extern JSBool +js_PutArgsObject(JSContext *cx, JSStackFrame *fp); + +extern JSBool +js_XDRFunction(JSXDRState *xdr, JSObject **objp); + +JS_END_EXTERN_C + +#endif /* jsfun_h___ */ diff --git a/src/extension/script/js/jsgc.c b/src/extension/script/js/jsgc.c new file mode 100644 index 000000000..a0efec4d7 --- /dev/null +++ b/src/extension/script/js/jsgc.c @@ -0,0 +1,1423 @@ +/* -*- 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 Mark-and-Sweep Garbage Collector. + * + * This GC allocates only fixed-sized things big enough to contain two words + * (pointers) on any host architecture. It allocates from an arena pool (see + * jsarena.h). 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 "jsstddef.h" +#include /* for free, called by JS_ARENA_DESTROY */ +#include /* for memset, called by jsarena.h macros if DEBUG */ +#include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" + +/* + * GC arena sizing depends on amortizing arena overhead using a large number + * of things per arena, and on the thing/flags ratio of 8:1 on most platforms. + * + * On 64-bit platforms, we would have half as many things per arena because + * pointers are twice as big, so we double the bytes for things per arena. + * This preserves the 1024 byte flags sub-arena size, which relates to the + * GC_PAGE_SIZE (see below for why). + */ +#if JS_BYTES_PER_WORD == 8 +# define GC_THINGS_SHIFT 14 /* 16KB for things on Alpha, etc. */ +#else +# define GC_THINGS_SHIFT 13 /* 8KB for things on most platforms */ +#endif +#define GC_THINGS_SIZE JS_BIT(GC_THINGS_SHIFT) +#define GC_FLAGS_SIZE (GC_THINGS_SIZE / sizeof(JSGCThing)) +#define GC_ARENA_SIZE (GC_THINGS_SIZE + GC_FLAGS_SIZE) + +/* + * The private JSGCThing struct, which describes a gcFreeList element. + */ +struct JSGCThing { + JSGCThing *next; + uint8 *flagp; +}; + +/* + * A GC arena contains one flag byte for each thing in its heap, and supports + * O(1) lookup of a flag given its thing's address. + * + * To implement this, we take advantage of the thing/flags numerology: given + * the 8K bytes worth of GC-things, there are 1K flag bytes. We mask a thing's + * address with ~1023 to find a JSGCPageInfo record at the front of a mythical + * "GC page" within the larger 8K thing arena. That JSGCPageInfo contains a + * pointer to the 128 flag bytes corresponding to the things in the page, so we + * index into this flags array using the thing's index within its page. + * + * To align thing pages on 1024-byte boundaries, we must allocate the 9KB of + * flags+things arena payload, then find the first 0 mod 1024 boundary after + * the first payload address. That's where things start, with a JSGCPageInfo + * taking up the first thing-slot, as usual for 0 mod 1024 byte boundaries. + * The effect of this alignment trick is to split the flags into at most 2 + * discontiguous spans, one before the things and one after (if we're really + * lucky, and the arena payload starts on a 0 mod 1024 byte boundary, no need + * to split). + * + * The overhead of this scheme for most platforms is (16+8*(8+1))/(16+9K) or + * .95% (assuming 16 byte JSArena header size, and 8 byte JSGCThing size). + * + * Here's some ASCII art showing an arena: + * + * split + * | + * V + * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ + * |fB| tp0 | tp1 | tp2 | tp3 | tp4 | tp5 | tp6 | tp7 | fA | + * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ + * ^ ^ + * tI ---------+ | + * tJ -------------------------------------------+ + * + * - fB are the "before split" flags, fA are the "after split" flags + * - tp0-tp7 are the 8 thing pages + * - thing tI points into tp1, whose flags are below the split, in fB + * - thing tJ points into tp5, clearly above the split + * + * In general, one of the thing pages will have some of its things' flags on + * the low side of the split, and the rest of its things' flags on the high + * side. All the other pages have flags only below or only above. Therefore + * we'll have to test something to decide whether the split divides flags in + * a given thing's page. So we store the split pointer (the pointer to tp0) + * in each JSGCPageInfo, along with the flags pointer for the 128 flag bytes + * ideally starting, for tp0 things, at the beginning of the arena's payload + * (at the start of fB). + * + * That is, each JSGCPageInfo's flags pointer is 128 bytes from the previous, + * or at the start of the arena if there is no previous page in this arena. + * Thus these ideal 128-byte flag pages run contiguously from the start of the + * arena (right over the split!), and the JSGCPageInfo flags pointers contain + * no discontinuities over the split created by the thing pages. So if, for a + * given JSGCPageInfo *pi, we find that + * + * pi->flags + ((jsuword)thing % 1024) / sizeof(JSGCThing) >= pi->split + * + * then we must add GC_THINGS_SIZE to the nominal flags pointer to jump over + * all the thing pages that split the flags into two discontiguous spans. + * + * (If we need to implement card-marking for an incremental GC write barrier, + * we can use the low byte of the pi->split pointer as the card-mark, for an + * extremely efficient write barrier: when mutating an object obj, just store + * a 1 byte at (uint8 *) ((jsuword)obj & ~1023) for little-endian platforms. + * When finding flags, we'll of course have to mask split with ~255, but it is + * guaranteed to be 1024-byte aligned, so no information is lost by overlaying + * the card-mark byte on split's low byte.) + */ +#define GC_PAGE_SHIFT 10 +#define GC_PAGE_MASK ((jsuword) JS_BITMASK(GC_PAGE_SHIFT)) +#define GC_PAGE_SIZE JS_BIT(GC_PAGE_SHIFT) + +typedef struct JSGCPageInfo { + uint8 *split; + uint8 *flags; +} JSGCPageInfo; + +#define FIRST_THING_PAGE(a) (((a)->base + GC_FLAGS_SIZE) & ~GC_PAGE_MASK) + +static JSGCThing * +gc_new_arena(JSArenaPool *pool) +{ + uint8 *flagp, *split, *pagep, *limit; + JSArena *a; + JSGCThing *thing; + JSGCPageInfo *pi; + + /* Use JS_ArenaAllocate to grab another 9K-net-size hunk of space. */ + flagp = (uint8 *) JS_ArenaAllocate(pool, GC_ARENA_SIZE); + if (!flagp) + return NULL; + a = pool->current; + + /* Reset a->avail to start at the flags split, aka the first thing page. */ + a->avail = FIRST_THING_PAGE(a); + split = pagep = (uint8 *) a->avail; + a->avail += sizeof(JSGCPageInfo); + thing = (JSGCThing *) a->avail; + a->avail += sizeof(JSGCThing); + + /* Initialize the JSGCPageInfo records at the start of every thing page. */ + limit = pagep + GC_THINGS_SIZE; + do { + pi = (JSGCPageInfo *) pagep; + pi->split = split; + pi->flags = flagp; + flagp += GC_PAGE_SIZE >> (GC_THINGS_SHIFT - GC_PAGE_SHIFT); + pagep += GC_PAGE_SIZE; + } while (pagep < limit); + return thing; +} + +uint8 * +js_GetGCThingFlags(void *thing) +{ + JSGCPageInfo *pi; + uint8 *flagp; + + pi = (JSGCPageInfo *) ((jsuword)thing & ~GC_PAGE_MASK); + flagp = pi->flags + ((jsuword)thing & GC_PAGE_MASK) / sizeof(JSGCThing); + if (flagp >= pi->split) + flagp += GC_THINGS_SIZE; + return flagp; +} + +JSBool +js_IsAboutToBeFinalized(JSContext *cx, void *thing) +{ + uint8 flags = *js_GetGCThingFlags(thing); + + return !(flags & (GCF_MARK | GCF_LOCKMASK | GCF_FINAL)); +} + +typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing); + +static GCFinalizeOp gc_finalizers[GCX_NTYPES]; + +intN +js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, + JSStringFinalizeOp newop) +{ + uintN i; + + for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) { + if (gc_finalizers[i] == (GCFinalizeOp) oldop) { + gc_finalizers[i] = (GCFinalizeOp) newop; + return (intN) i; + } + } + return -1; +} + +#ifdef JS_GCMETER +#define METER(x) x +#else +#define METER(x) /* nothing */ +#endif + +/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ +#define GC_ROOTS_SIZE 256 +#define GC_FINALIZE_LEN 1024 + +JSBool +js_InitGC(JSRuntime *rt, uint32 maxbytes) +{ + JS_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); + JS_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); + JS_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); + + if (!gc_finalizers[GCX_OBJECT]) { + gc_finalizers[GCX_OBJECT] = (GCFinalizeOp)js_FinalizeObject; + gc_finalizers[GCX_STRING] = (GCFinalizeOp)js_FinalizeString; +#ifdef DEBUG + gc_finalizers[GCX_DOUBLE] = (GCFinalizeOp)js_FinalizeDouble; +#endif + gc_finalizers[GCX_MUTABLE_STRING] = (GCFinalizeOp)js_FinalizeString; + } + + JS_InitArenaPool(&rt->gcArenaPool, "gc-arena", GC_ARENA_SIZE, + sizeof(JSGCThing)); + 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 */ + rt->gcMaxBytes = maxbytes; + return JS_TRUE; +} + +#ifdef JS_GCMETER +void +js_DumpGCStats(JSRuntime *rt, FILE *fp) +{ + fprintf(fp, "\nGC allocation statistics:\n"); + fprintf(fp, " bytes currently allocated: %lu\n", rt->gcBytes); + fprintf(fp, " alloc attempts: %lu\n", rt->gcStats.alloc); + fprintf(fp, " GC freelist length: %lu\n", rt->gcStats.freelen); + fprintf(fp, " recycles through GC freelist: %lu\n", rt->gcStats.recycle); + fprintf(fp, "alloc retries after running GC: %lu\n", rt->gcStats.retry); + fprintf(fp, " allocation failures: %lu\n", rt->gcStats.fail); + fprintf(fp, " valid lock calls: %lu\n", rt->gcStats.lock); + fprintf(fp, " valid unlock calls: %lu\n", rt->gcStats.unlock); + fprintf(fp, " locks that hit stuck counts: %lu\n", rt->gcStats.stuck); + fprintf(fp, " unlocks that saw stuck counts: %lu\n", rt->gcStats.unstuck); + fprintf(fp, " mark recursion depth: %lu\n", rt->gcStats.depth); + fprintf(fp, " maximum mark recursion depth: %lu\n", rt->gcStats.maxdepth); + fprintf(fp, " maximum GC nesting level: %lu\n", rt->gcStats.maxlevel); + fprintf(fp, " potentially useful GC calls: %lu\n", rt->gcStats.poke); + fprintf(fp, " useless GC calls: %lu\n", rt->gcStats.nopoke); + fprintf(fp, " thing arenas freed so far: %lu\n", rt->gcStats.afree); + fprintf(fp, " extra stack segments scanned: %lu\n", rt->gcStats.stackseg); + fprintf(fp, " stack segment slots scanned: %lu\n", rt->gcStats.segslots); +#ifdef JS_ARENAMETER + JS_DumpArenaStats(fp); +#endif +} +#endif + +#ifdef DEBUG +JS_STATIC_DLL_CALLBACK(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; +} +#endif + +void +js_FinishGC(JSRuntime *rt) +{ +#ifdef JS_ARENAMETER + JS_DumpArenaStats(stdout); +#endif +#ifdef JS_GCMETER + js_DumpGCStats(rt, stdout); +#endif + JS_FinishArenaPool(&rt->gcArenaPool); + JS_ArenaFinish(); + + if (rt->gcRootsHash.ops) { +#ifdef DEBUG + 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.\n" +" This root may point to freed memory. Objects reachable\n" +" through it have not been finalized.\n"); + } else { + fprintf(stderr, +"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" +" These roots may point to freed memory. Objects reachable\n" +" through them have not been finalized.\n", + (unsigned long) leakedroots); + } + } +#endif + + JS_DHashTableFinish(&rt->gcRootsHash); + rt->gcRootsHash.ops = NULL; + } + if (rt->gcLocksHash) { + JS_DHashTableDestroy(rt->gcLocksHash); + rt->gcLocksHash = NULL; + } + rt->gcFreeList = 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). + * + * If the GC is running and we're called on another thread, wait for this + * GC activation to finish. We can safely wait here (in the case where we + * are called within a request on another thread's context) without fear + * of deadlock because the GC doesn't set rt->gcRunning until after it has + * waited for all active requests to end. + */ + JS_LOCK_GC(rt); +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); + if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcLevel > 0); + } +#endif + 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); +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); + if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { + do { + JS_AWAIT_GC_DONE(rt); + } while (rt->gcLevel > 0); + } +#endif + (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); + rt->gcPoke = JS_TRUE; + JS_UNLOCK_GC(rt); + return JS_TRUE; +} + +void * +js_AllocGCThing(JSContext *cx, uintN flags) +{ + JSBool tried_gc; + JSRuntime *rt; + JSGCThing *thing; + uint8 *flagp; + +#ifdef TOO_MUCH_GC + js_GC(cx, GC_KEEP_ATOMS); + tried_gc = JS_TRUE; +#else + tried_gc = JS_FALSE; +#endif + + 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(rt->gcStats.alloc++); +retry: + thing = rt->gcFreeList; + if (thing) { + rt->gcFreeList = thing->next; + flagp = thing->flagp; + METER(rt->gcStats.freelen--); + METER(rt->gcStats.recycle++); + } else { + if (rt->gcBytes < rt->gcMaxBytes && + (tried_gc || rt->gcMallocBytes < rt->gcMaxBytes)) + { + /* + * Inline form of JS_ARENA_ALLOCATE adapted to truncate the current + * arena's limit to a GC_PAGE_SIZE boundary, and to skip over every + * GC_PAGE_SIZE-byte-aligned thing (which is actually not a thing, + * it's a JSGCPageInfo record). + */ + JSArenaPool *pool = &rt->gcArenaPool; + JSArena *a = pool->current; + size_t nb = sizeof(JSGCThing); + jsuword p = a->avail; + jsuword q = p + nb; + + if (q > (a->limit & ~GC_PAGE_MASK)) { + thing = gc_new_arena(pool); + } else { + if ((p & GC_PAGE_MASK) == 0) { + /* Beware, p points to a JSGCPageInfo record! */ + p = q; + q += nb; + JS_ArenaCountAllocation(pool, nb); + } + a->avail = q; + thing = (JSGCThing *)p; + } + JS_ArenaCountAllocation(pool, nb); + } + + /* + * Consider doing a "last ditch" GC if thing couldn't be allocated. + * + * 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 Mozilla bug 162779 + * (http://bugzilla.mozilla.org/show_bug.cgi?id=162779). + */ + if (!thing) { + if (!tried_gc) { + rt->gcPoke = JS_TRUE; + js_GC(cx, GC_KEEP_ATOMS | GC_ALREADY_LOCKED); + tried_gc = JS_TRUE; + METER(rt->gcStats.retry++); + goto retry; + } + METER(rt->gcStats.fail++); + JS_UNLOCK_GC(rt); + JS_ReportOutOfMemory(cx); + return NULL; + } + + /* Find the flags pointer given thing's address. */ + flagp = js_GetGCThingFlags(thing); + } + *flagp = (uint8)flags; + rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8); + cx->newborn[flags & GCF_TYPEMASK] = thing; + + /* + * Clear thing before unlocking in case a GC run is about to scan it, + * finding it via cx->newborn[]. + */ + thing->next = NULL; + thing->flagp = NULL; + JS_UNLOCK_GC(rt); + return thing; +} + +JSBool +js_LockGCThing(JSContext *cx, void *thing) +{ + JSBool ok = js_LockGCThingRT(cx->runtime, thing); + if (!ok) + JS_ReportOutOfMemory(cx); + return ok; +} + +JSBool +js_LockGCThingRT(JSRuntime *rt, void *thing) +{ + uint8 *flagp, flags, lockbits; + JSBool ok; + JSGCLockHashEntry *lhe; + + if (!thing) + return JS_TRUE; + flagp = js_GetGCThingFlags(thing); + flags = *flagp; + + ok = JS_FALSE; + JS_LOCK_GC(rt); + lockbits = (flags & GCF_LOCKMASK); + + if (lockbits != GCF_LOCKMASK) { + if ((flags & GCF_TYPEMASK) == GCX_OBJECT) { + /* Objects may require "deep locking", i.e., rooting by value. */ + if (lockbits == 0) { + if (!rt->gcLocksHash) { + rt->gcLocksHash = + JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSGCLockHashEntry), + GC_ROOTS_SIZE); + if (!rt->gcLocksHash) + goto error; + } else { +#ifdef DEBUG + JSDHashEntryHdr *hdr = + JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_LOOKUP); + JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr)); +#endif + } + lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); + if (!lhe) + goto error; + lhe->thing = thing; + lhe->count = 1; + *flagp = (uint8)(flags + GCF_LOCK); + } else { + JS_ASSERT(lockbits == GCF_LOCK); + lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_LOOKUP); + JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)); + if (JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)) { + JS_ASSERT(lhe->count >= 1); + lhe->count++; + } + } + } else { + *flagp = (uint8)(flags + GCF_LOCK); + } + } else { + METER(rt->gcStats.stuck++); + } + + METER(rt->gcStats.lock++); + ok = JS_TRUE; +error: + JS_UNLOCK_GC(rt); + return ok; +} + +JSBool +js_UnlockGCThingRT(JSRuntime *rt, void *thing) +{ + uint8 *flagp, flags, lockbits; + JSGCLockHashEntry *lhe; + + if (!thing) + return JS_TRUE; + flagp = js_GetGCThingFlags(thing); + flags = *flagp; + + JS_LOCK_GC(rt); + lockbits = (flags & GCF_LOCKMASK); + + if (lockbits != GCF_LOCKMASK) { + if ((flags & GCF_TYPEMASK) == GCX_OBJECT) { + /* Defend against a call on an unlocked object. */ + if (lockbits != 0) { + JS_ASSERT(lockbits == GCF_LOCK); + lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_LOOKUP); + JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr)); + if (JS_DHASH_ENTRY_IS_BUSY(&lhe->hdr) && + --lhe->count == 0) { + (void) JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_REMOVE); + *flagp = (uint8)(flags & ~GCF_LOCKMASK); + } + } + } else { + *flagp = (uint8)(flags - GCF_LOCK); + } + } else { + METER(rt->gcStats.unstuck++); + } + + rt->gcPoke = JS_TRUE; + METER(rt->gcStats.unlock++); + JS_UNLOCK_GC(rt); + return JS_TRUE; +} + +#ifdef GC_MARK_DEBUG + +#include +#include +#include "jsprf.h" + +JS_FRIEND_DATA(FILE *) js_DumpGCHeap; +JS_EXPORT_DATA(void *) js_LiveThingToFind; + +#ifdef HAVE_XPCONNECT +#include "dump_xpc.h" +#endif + +static const char * +gc_object_class_name(void* thing) +{ + uint8 *flagp = js_GetGCThingFlags(thing); + const char *className = ""; + static char depbuf[32]; + + switch (*flagp & GCF_TYPEMASK) { + case GCX_OBJECT: { + JSObject *obj = (JSObject *)thing; + JSClass *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); + className = clasp->name; +#ifdef HAVE_XPCONNECT + if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { + jsval privateValue = obj->slots[JSSLOT_PRIVATE]; + + JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); + if (!JSVAL_IS_VOID(privateValue)) { + void *privateThing = JSVAL_TO_PRIVATE(privateValue); + const char *xpcClassName = GetXPCObjectClassName(privateThing); + + if (xpcClassName) + className = xpcClassName; + } + } +#endif + break; + } + + case GCX_STRING: + case GCX_MUTABLE_STRING: { + JSString *str = (JSString *)thing; + if (JSSTRING_IS_DEPENDENT(str)) { + JS_snprintf(depbuf, sizeof depbuf, "start:%u, length:%u", + JSSTRDEP_START(str), JSSTRDEP_LENGTH(str)); + className = depbuf; + } else { + className = "string"; + } + break; + } + + case GCX_DOUBLE: + className = "double"; + break; + } + + return className; +} + +static void +gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) +{ + GCMarkNode *next = NULL; + char *path = NULL; + + while (prev) { + next = prev; + prev = prev->prev; + } + while (next) { + path = JS_sprintf_append(path, "%s(%s).", + next->name, + gc_object_class_name(next->thing)); + next = next->next; + } + if (!path) + return; + + fprintf(fp, "%08lx ", (long)thing); + switch (flags & GCF_TYPEMASK) { + case GCX_OBJECT: + { + JSObject *obj = (JSObject *)thing; + jsval privateValue = obj->slots[JSSLOT_PRIVATE]; + void *privateThing = JSVAL_IS_VOID(privateValue) + ? NULL + : JSVAL_TO_PRIVATE(privateValue); + const char *className = gc_object_class_name(thing); + fprintf(fp, "object %8p %s", privateThing, className); + break; + } + case GCX_DOUBLE: + fprintf(fp, "double %g", *(jsdouble *)thing); + break; + default: + fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); + break; + } + fprintf(fp, " via %s\n", path); + free(path); +} + +#endif /* !GC_MARK_DEBUG */ + +static void +gc_mark_atom_key_thing(void *thing, void *arg) +{ + JSContext *cx = (JSContext *) arg; + + GC_MARK(cx, thing, "atom", NULL); +} + +void +js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg) +{ + jsval key; + + if (atom->flags & ATOM_MARK) + return; + atom->flags |= ATOM_MARK; + key = ATOM_KEY(atom); + if (JSVAL_IS_GCTHING(key)) { +#ifdef GC_MARK_DEBUG + char name[32]; + + if (JSVAL_IS_STRING(key)) { + JS_snprintf(name, sizeof name, "'%s'", + JS_GetStringBytes(JSVAL_TO_STRING(key))); + } else { + JS_snprintf(name, sizeof name, "<%x>", key); + } +#endif + GC_MARK(cx, JSVAL_TO_GCTHING(key), name, arg); + } +} + +void +js_MarkGCThing(JSContext *cx, void *thing, void *arg) +{ + uint8 flags, *flagp; + JSRuntime *rt; + JSObject *obj; + uint32 nslots; + jsval v, *vp, *end; + JSString *str; +#ifdef GC_MARK_DEBUG + JSScope *scope; + JSScopeProperty *sprop; +#endif + + if (!thing) + return; + + flagp = js_GetGCThingFlags(thing); + flags = *flagp; + JS_ASSERT(flags != GCF_FINAL); +#ifdef GC_MARK_DEBUG + if (js_LiveThingToFind == thing) + gc_dump_thing(thing, flags, arg, stderr); +#endif + + if (flags & GCF_MARK) + return; + + *flagp |= GCF_MARK; + rt = cx->runtime; + METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth) + rt->gcStats.maxdepth = rt->gcStats.depth); + +#ifdef GC_MARK_DEBUG + if (js_DumpGCHeap) + gc_dump_thing(thing, flags, arg, js_DumpGCHeap); +#endif + + switch (flags & GCF_TYPEMASK) { + case GCX_OBJECT: + obj = (JSObject *) thing; + vp = obj->slots; + if (!vp) { + /* If obj->slots is null, obj must be a newborn. */ + JS_ASSERT(!obj->map); + goto out; + } + nslots = (obj->map->ops->mark) + ? obj->map->ops->mark(cx, obj, arg) + : JS_MIN(obj->map->freeslot, obj->map->nslots); +#ifdef GC_MARK_DEBUG + scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL; +#endif + for (end = vp + nslots; vp < end; vp++) { + v = *vp; + if (JSVAL_IS_GCTHING(v)) { +#ifdef GC_MARK_DEBUG + char name[32]; + + if (scope) { + uint32 slot; + jsval nval; + + slot = vp - obj->slots; + for (sprop = SCOPE_LAST_PROP(scope); ; + sprop = sprop->parent) { + if (!sprop) { + switch (slot) { + case JSSLOT_PROTO: + strcpy(name, "__proto__"); + break; + case JSSLOT_PARENT: + strcpy(name, "__parent__"); + break; + case JSSLOT_PRIVATE: + strcpy(name, "__private__"); + break; + default: + JS_snprintf(name, sizeof name, + "**UNKNOWN SLOT %ld**", + (long)slot); + break; + } + break; + } + if (sprop->slot == slot) { + nval = ID_TO_VALUE(sprop->id); + if (JSVAL_IS_INT(nval)) { + JS_snprintf(name, sizeof name, "%ld", + (long)JSVAL_TO_INT(nval)); + } else if (JSVAL_IS_STRING(nval)) { + JS_snprintf(name, sizeof name, "%s", + JS_GetStringBytes(JSVAL_TO_STRING(nval))); + } else { + strcpy(name, "**FINALIZED ATOM KEY**"); + } + break; + } + } + } else { + strcpy(name, "**UNKNOWN OBJECT MAP ENTRY**"); + } +#endif + GC_MARK(cx, JSVAL_TO_GCTHING(v), name, arg); + } + } + break; + +#ifdef DEBUG + case GCX_STRING: + str = (JSString *)thing; + JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); + break; +#endif + + case GCX_MUTABLE_STRING: + str = (JSString *)thing; + if (JSSTRING_IS_DEPENDENT(str)) + GC_MARK(cx, JSSTRDEP_BASE(str), "base", arg); + break; + } + +out: + METER(rt->gcStats.depth--); +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +gc_root_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) +{ + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + jsval *rp = (jsval *)rhe->root; + jsval v = *rp; + + /* Ignore null object and scalar values. */ + if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { + JSContext *cx = (JSContext *)arg; +#ifdef DEBUG + JSArena *a; + jsuword firstpage; + JSBool root_points_to_gcArenaPool = JS_FALSE; + void *thing = JSVAL_TO_GCTHING(v); + + for (a = cx->runtime->gcArenaPool.first.next; a; a = a->next) { + firstpage = FIRST_THING_PAGE(a); + if (JS_UPTRDIFF(thing, firstpage) < a->avail - firstpage) { + root_points_to_gcArenaPool = JS_TRUE; + break; + } + } + if (!root_points_to_gcArenaPool && 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_gcArenaPool); +#endif + + GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root", NULL); + } + return JS_DHASH_NEXT; +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) +{ + JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr; + void *thing = (void *)lhe->thing; + JSContext *cx = (JSContext *)arg; + + GC_MARK(cx, thing, "locked object", NULL); + return JS_DHASH_NEXT; +} + +void +js_ForceGC(JSContext *cx, uintN gcflags) +{ + uintN i; + + for (i = 0; i < GCX_NTYPES; i++) + cx->newborn[i] = NULL; + cx->lastAtom = NULL; + cx->runtime->gcPoke = JS_TRUE; + js_GC(cx, gcflags); + JS_ArenaFinish(); +} + +#define GC_MARK_JSVALS(cx, len, vec, name) \ + JS_BEGIN_MACRO \ + jsval _v, *_vp, *_end; \ + \ + for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ + _v = *_vp; \ + if (JSVAL_IS_GCTHING(_v)) \ + GC_MARK(cx, JSVAL_TO_GCTHING(_v), name, NULL); \ + } \ + JS_END_MACRO + +void +js_GC(JSContext *cx, uintN gcflags) +{ + JSRuntime *rt; + JSContext *iter, *acx; + JSStackFrame *fp, *chain; + uintN i, depth, nslots, type; + JSStackHeader *sh; + JSArena *a, **ap; + uint8 flags, *flagp, *split; + JSGCThing *thing, *limit, **flp, **oflp; + GCFinalizeOp finalizer; + JSBool all_clear; +#ifdef JS_THREADSAFE + jsword currentThread; + uint32 requestDebit; +#endif + + rt = cx->runtime; +#ifdef JS_THREADSAFE + /* Avoid deadlock. */ + JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); +#endif + + /* + * 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 && !(gcflags & GC_LAST_CONTEXT)) + return; + + /* + * Let the API user decide to defer a GC if it wants to (unless this + * is the last context). Invoke the callback regardless. + */ + if (rt->gcCallback) { + if (!rt->gcCallback(cx, JSGC_BEGIN) && !(gcflags & GC_LAST_CONTEXT)) + return; + } + + /* Lock out other GC allocator and collector invocations. */ + if (!(gcflags & GC_ALREADY_LOCKED)) + JS_LOCK_GC(rt); + + /* Do nothing if no assignment has executed since the last GC. */ + if (!rt->gcPoke) { + METER(rt->gcStats.nopoke++); + if (!(gcflags & GC_ALREADY_LOCKED)) + JS_UNLOCK_GC(rt); + return; + } + METER(rt->gcStats.poke++); + +#ifdef JS_THREADSAFE + /* Bump gcLevel and return rather than nest on this thread. */ + currentThread = js_CurrentThreadId(); + if (rt->gcThread == currentThread) { + JS_ASSERT(rt->gcLevel > 0); + rt->gcLevel++; + METER(if (rt->gcLevel > rt->gcStats.maxlevel) + rt->gcStats.maxlevel = rt->gcLevel); + if (!(gcflags & GC_ALREADY_LOCKED)) + JS_UNLOCK_GC(rt); + return; + } + + /* + * 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. NB: if cx->thread is 0, then cx is not using + * the request model, and does not contribute to rt->requestCount. + */ + requestDebit = 0; + if (cx->thread) { + /* + * Check all contexts for any with the same thread-id. XXX should we + * keep a sub-list of contexts having the same id? + */ + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { + if (acx->thread == cx->thread && acx->requestDepth) + requestDebit++; + } + } else { + /* + * We assert, but check anyway, in case someone is misusing the API. + * Avoiding the loop over all of rt's contexts is a win in the event + * that the GC runs only on request-less contexts with 0 thread-ids, + * in a special thread such as might be used by the UI/DOM/Layout + * "mozilla" or "main" thread in Mozilla-the-browser. + */ + JS_ASSERT(cx->requestDepth == 0); + if (cx->requestDepth) + requestDebit = 1; + } + if (requestDebit) { + JS_ASSERT(requestDebit <= rt->requestCount); + rt->requestCount -= requestDebit; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + } + + /* If another thread is already in GC, don't attempt GC; wait instead. */ + if (rt->gcLevel > 0) { + /* Bump gcLevel to restart the current GC, so it finds new garbage. */ + rt->gcLevel++; + METER(if (rt->gcLevel > rt->gcStats.maxlevel) + rt->gcStats.maxlevel = rt->gcLevel); + + /* Wait for the other thread to finish, then resume our request. */ + while (rt->gcLevel > 0) + JS_AWAIT_GC_DONE(rt); + if (requestDebit) + rt->requestCount += requestDebit; + if (!(gcflags & GC_ALREADY_LOCKED)) + JS_UNLOCK_GC(rt); + return; + } + + /* No other thread is in GC, so indicate that we're now in GC. */ + rt->gcLevel = 1; + rt->gcThread = currentThread; + + /* Wait for all other requests to finish. */ + while (rt->requestCount > 0) + JS_AWAIT_REQUEST_DONE(rt); + +#else /* !JS_THREADSAFE */ + + /* Bump gcLevel and return rather than nest; the outer gc will restart. */ + rt->gcLevel++; + METER(if (rt->gcLevel > rt->gcStats.maxlevel) + 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; + JS_UNLOCK_GC(rt); + + /* If a suspended compile is running on another context, keep atoms. */ + if (rt->gcKeepAtoms) + gcflags |= GC_KEEP_ATOMS; + + /* Reset malloc counter. */ + rt->gcMallocBytes = 0; + + /* Drop atoms held by the property cache, and clear property weak links. */ + js_DisablePropertyCache(cx); + js_FlushPropertyCache(cx); +#ifdef DEBUG_brendan + { extern void js_DumpScopeMeters(JSRuntime *rt); + js_DumpScopeMeters(rt); + } +#endif + +restart: + rt->gcNumber++; + + /* + * Mark phase. + */ + JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); + if (rt->gcLocksHash) + JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); + js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx); + js_MarkWatchPoints(rt); + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { + /* + * Iterate frame chain and dormant chains. Temporarily tack current + * frame onto the head of the dormant list to ease iteration. + * + * (NB: see comment on this whole "dormant" thing in js_Execute.) + */ + chain = acx->fp; + if (chain) { + JS_ASSERT(!chain->dormantNext); + chain->dormantNext = acx->dormantFrameChain; + } else { + chain = acx->dormantFrameChain; + } + + for (fp = chain; fp; fp = chain = chain->dormantNext) { + do { + if (fp->callobj) + GC_MARK(cx, fp->callobj, "call object", NULL); + if (fp->argsobj) + GC_MARK(cx, fp->argsobj, "arguments object", NULL); + if (fp->varobj) + GC_MARK(cx, fp->varobj, "variables object", NULL); + if (fp->script) { + js_MarkScript(cx, fp->script, NULL); + if (fp->spbase) { + /* + * Don't mark what has not been pushed yet, or what + * has been popped already. + */ + depth = fp->script->depth; + nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) + < depth * sizeof(jsval)) + ? (uintN)(fp->sp - fp->spbase) + : depth; + GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); + } + } + GC_MARK(cx, fp->thisp, "this", NULL); + if (fp->argv) { + nslots = fp->argc; + if (fp->fun && fp->fun->nargs > nslots) + nslots = fp->fun->nargs; + GC_MARK_JSVALS(cx, nslots, fp->argv, "arg"); + } + if (JSVAL_IS_GCTHING(fp->rval)) + GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval", NULL); + if (fp->vars) + GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); + GC_MARK(cx, fp->scopeChain, "scope chain", NULL); + if (fp->sharpArray) + GC_MARK(cx, fp->sharpArray, "sharp array", NULL); + + if (fp->objAtomMap) { + JSAtom **vector, *atom; + + nslots = fp->objAtomMap->length; + vector = fp->objAtomMap->vector; + for (i = 0; i < nslots; i++) { + atom = vector[i]; + if (atom) + GC_MARK_ATOM(cx, atom, NULL); + } + } + } while ((fp = fp->down) != NULL); + } + + /* Cleanup temporary "dormant" linkage. */ + if (acx->fp) + acx->fp->dormantNext = NULL; + + /* Mark other roots-by-definition in acx. */ + GC_MARK(cx, acx->globalObject, "global object", NULL); + GC_MARK(cx, acx->newborn[GCX_OBJECT], "newborn object", NULL); + GC_MARK(cx, acx->newborn[GCX_STRING], "newborn string", NULL); + GC_MARK(cx, acx->newborn[GCX_DOUBLE], "newborn double", NULL); + GC_MARK(cx, acx->newborn[GCX_MUTABLE_STRING], "newborn mutable string", + NULL); + for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) + GC_MARK(cx, acx->newborn[i], "newborn external string", NULL); + if (acx->lastAtom) + GC_MARK_ATOM(cx, acx->lastAtom, NULL); +#if JS_HAS_EXCEPTIONS + if (acx->throwing && JSVAL_IS_GCTHING(acx->exception)) + GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception", NULL); +#endif +#if JS_HAS_LVALUE_RETURN + if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) + GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2", NULL); +#endif + + for (sh = acx->stackHeaders; sh; sh = sh->down) { + METER(rt->gcStats.stackseg++); + METER(rt->gcStats.segslots += sh->nslots); + GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); + } + } + + if (rt->gcCallback) + (void) rt->gcCallback(cx, JSGC_MARK_END); + + /* + * 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. + */ + js_SweepAtomState(&rt->atomState); + js_SweepScopeProperties(rt); + js_SweepScriptFilenames(rt); + for (a = rt->gcArenaPool.first.next; a; a = a->next) { + flagp = (uint8 *) a->base; + split = (uint8 *) FIRST_THING_PAGE(a); + limit = (JSGCThing *) a->avail; + for (thing = (JSGCThing *) split; thing < limit; thing++) { + if (((jsuword)thing & GC_PAGE_MASK) == 0) { + flagp++; + thing++; + } + flags = *flagp; + if (flags & GCF_MARK) { + *flagp &= ~GCF_MARK; + } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) { + /* Call the finalizer with GCF_FINAL ORed into flags. */ + type = flags & GCF_TYPEMASK; + finalizer = gc_finalizers[type]; + if (finalizer) { + *flagp = (uint8)(flags | GCF_FINAL); + if (type >= GCX_EXTERNAL_STRING) + js_PurgeDeflatedStringCache((JSString *)thing); + finalizer(cx, thing); + } + + /* Set flags to GCF_FINAL, signifying that thing is free. */ + *flagp = GCF_FINAL; + + JS_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8)); + rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8); + } + if (++flagp == split) + flagp += GC_THINGS_SIZE; + } + } + + /* + * Free phase. + * Free any unused arenas and rebuild the JSGCThing freelist. + */ + ap = &rt->gcArenaPool.first.next; + a = *ap; + if (!a) + goto out; + all_clear = JS_TRUE; + flp = oflp = &rt->gcFreeList; + *flp = NULL; + METER(rt->gcStats.freelen = 0); + + do { + flagp = (uint8 *) a->base; + split = (uint8 *) FIRST_THING_PAGE(a); + limit = (JSGCThing *) a->avail; + for (thing = (JSGCThing *) split; thing < limit; thing++) { + if (((jsuword)thing & GC_PAGE_MASK) == 0) { + flagp++; + thing++; + } + if (*flagp != GCF_FINAL) { + all_clear = JS_FALSE; + } else { + thing->flagp = flagp; + *flp = thing; + flp = &thing->next; + METER(rt->gcStats.freelen++); + } + if (++flagp == split) + flagp += GC_THINGS_SIZE; + } + + if (all_clear) { + JS_ARENA_DESTROY(&rt->gcArenaPool, a, ap); + flp = oflp; + METER(rt->gcStats.afree++); + } else { + ap = &a->next; + all_clear = JS_TRUE; + oflp = flp; + } + } while ((a = *ap) != NULL); + + /* Terminate the new freelist. */ + *flp = NULL; + + if (rt->gcCallback) + (void) rt->gcCallback(cx, JSGC_FINALIZE_END); +#ifdef DEBUG_brendan + { extern void DumpSrcNoteSizeHist(); + DumpSrcNoteSizeHist(); + } +#endif + +out: + JS_LOCK_GC(rt); + if (rt->gcLevel > 1) { + rt->gcLevel = 1; + JS_UNLOCK_GC(rt); + goto restart; + } + js_EnablePropertyCache(cx); + rt->gcLevel = 0; + rt->gcLastBytes = rt->gcBytes; + rt->gcPoke = rt->gcRunning = JS_FALSE; + +#ifdef JS_THREADSAFE + /* If we were invoked during a request, pay back the temporary debit. */ + if (requestDebit) + rt->requestCount += requestDebit; + rt->gcThread = 0; + JS_NOTIFY_GC_DONE(rt); + if (!(gcflags & GC_ALREADY_LOCKED)) + JS_UNLOCK_GC(rt); +#endif + + if (rt->gcCallback) { + if (gcflags & GC_ALREADY_LOCKED) + JS_UNLOCK_GC(rt); + (void) rt->gcCallback(cx, JSGC_END); + if (gcflags & GC_ALREADY_LOCKED) + JS_LOCK_GC(rt); + } +} diff --git a/src/extension/script/js/jsgc.h b/src/extension/script/js/jsgc.h new file mode 100644 index 000000000..f501aca23 --- /dev/null +++ b/src/extension/script/js/jsgc.h @@ -0,0 +1,230 @@ +/* -*- 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" + +JS_BEGIN_EXTERN_C + +/* GC thing type indexes. */ +#define GCX_OBJECT 0 /* JSObject */ +#define GCX_STRING 1 /* JSString */ +#define GCX_DOUBLE 2 /* jsdouble */ +#define GCX_MUTABLE_STRING 3 /* JSString that's mutable -- + single-threaded only! */ +#define GCX_EXTERNAL_STRING 4 /* JSString w/ external chars */ +#define GCX_NTYPES_LOG2 3 /* type index bits */ +#define GCX_NTYPES JS_BIT(GCX_NTYPES_LOG2) + +/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */ +#define GCF_TYPEMASK JS_BITMASK(GCX_NTYPES_LOG2) +#define GCF_MARK JS_BIT(GCX_NTYPES_LOG2) +#define GCF_FINAL JS_BIT(GCX_NTYPES_LOG2 + 1) +#define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 2) /* lock bit shift and mask */ +#define GCF_LOCKMASK (JS_BITMASK(8 - GCF_LOCKSHIFT) << GCF_LOCKSHIFT) +#define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */ + +/* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */ +#define GCF_MUTABLE 2 + +#if (GCX_STRING | GCF_MUTABLE) != GCX_MUTABLE_STRING +# error "mutable string type index botch!" +#endif + +extern uint8 * +js_GetGCThingFlags(void *thing); + +/* These are compatible with JSDHashEntryStub. */ +struct JSGCRootHashEntry { + JSDHashEntryHdr hdr; + void *root; + const char *name; +}; + +struct JSGCLockHashEntry { + JSDHashEntryHdr hdr; + const JSGCThing *thing; + uint32 count; +}; + +#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 intN +js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, + JSStringFinalizeOp newop); + +extern JSBool +js_InitGC(JSRuntime *rt, uint32 maxbytes); + +extern void +js_FinishGC(JSRuntime *rt); + +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); + +extern void * +js_AllocGCThing(JSContext *cx, uintN flags); + +extern JSBool +js_LockGCThing(JSContext *cx, void *thing); + +extern JSBool +js_LockGCThingRT(JSRuntime *rt, void *thing); + +extern JSBool +js_UnlockGCThingRT(JSRuntime *rt, void *thing); + +extern JSBool +js_IsAboutToBeFinalized(JSContext *cx, void *thing); + +extern void +js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg); + +/* We avoid a large number of unnecessary calls by doing the flag check first */ +#define GC_MARK_ATOM(cx, atom, arg) \ + JS_BEGIN_MACRO \ + if (!((atom)->flags & ATOM_MARK)) \ + js_MarkAtom(cx, atom, arg); \ + JS_END_MACRO + +extern void +js_MarkGCThing(JSContext *cx, void *thing, void *arg); + +#ifdef GC_MARK_DEBUG + +typedef struct GCMarkNode GCMarkNode; + +struct GCMarkNode { + void *thing; + const char *name; + GCMarkNode *next; + GCMarkNode *prev; +}; + +#define GC_MARK(_cx, _thing, _name, _prev) \ + JS_BEGIN_MACRO \ + GCMarkNode _node; \ + _node.thing = _thing; \ + _node.name = _name; \ + _node.next = NULL; \ + _node.prev = _prev; \ + if (_prev) ((GCMarkNode *)(_prev))->next = &_node; \ + js_MarkGCThing(_cx, _thing, &_node); \ + JS_END_MACRO + +#else /* !GC_MARK_DEBUG */ + +#define GC_MARK(cx, thing, name, prev) js_MarkGCThing(cx, thing, NULL) + +#endif /* !GC_MARK_DEBUG */ + +/* + * Flags to modify how a GC marks and sweeps: + * GC_KEEP_ATOMS Don't sweep unmarked atoms, they may be in use by the + * compiler, or by an API function that calls js_Atomize, + * when the GC is called from js_AllocGCThing, due to a + * malloc failure or runtime GC-thing limit. + * GC_LAST_CONTEXT Called from js_DestroyContext for last JSContext in a + * JSRuntime, when it is imperative that rt->gcPoke gets + * cleared early in js_GC, if it is set. + * GC_ALREADY_LOCKED rt->gcLock is already held on entry to js_GC, and kept + * on return to its caller. + */ +#define GC_KEEP_ATOMS 0x1 +#define GC_LAST_CONTEXT 0x2 +#define GC_ALREADY_LOCKED 0x4 + +extern void +js_ForceGC(JSContext *cx, uintN gcflags); + +extern void +js_GC(JSContext *cx, uintN gcflags); + +#ifdef JS_GCMETER + +typedef struct JSGCStats { + uint32 alloc; /* number of allocation attempts */ + uint32 freelen; /* gcFreeList length */ + uint32 recycle; /* number of things recycled through gcFreeList */ + uint32 retry; /* allocation attempt retries after running the GC */ + uint32 fail; /* allocation failures */ + uint32 finalfail; /* finalizer calls allocator failures */ + uint32 lock; /* valid lock calls */ + uint32 unlock; /* valid unlock calls */ + uint32 stuck; /* stuck reference counts seen by lock calls */ + uint32 unstuck; /* unlock calls that saw a stuck lock count */ + uint32 depth; /* mark recursion depth */ + uint32 maxdepth; /* maximum mark recursion depth */ + uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ + uint32 poke; /* number of potentially useful GC calls */ + uint32 nopoke; /* useless GC calls where js_PokeGC was not set */ + uint32 afree; /* thing arenas freed so far */ + uint32 stackseg; /* total extraordinary stack segments scanned */ + uint32 segslots; /* total stack segment jsval slots scanned */ +} JSGCStats; + +extern void +js_DumpGCStats(JSRuntime *rt, FILE *fp); + +#endif /* JS_GCMETER */ + +JS_END_EXTERN_C + +#endif /* jsgc_h___ */ diff --git a/src/extension/script/js/jshash.c b/src/extension/script/js/jshash.c new file mode 100644 index 000000000..3382daadb --- /dev/null +++ b/src/extension/script/js/jshash.c @@ -0,0 +1,479 @@ +/* -*- 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 "jsstddef.h" +#include +#include +#include "jstypes.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) +{ + 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) + 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); +#if defined _MSC_VER && _MSC_VER <= 800 + if (n > 16000) { + (*allocOps->freeTable)(allocPriv, ht); + return NULL; + } +#endif /* WIN16 */ + nb = n * sizeof(JSHashEntry *); + ht->buckets = (JSHashEntry**) (*allocOps->allocTable)(allocPriv, nb); + if (!ht->buckets) { + (*allocOps->freeTable)(allocPriv, ht); + 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); +#ifdef DEBUG + memset(ht, 0xDB, sizeof *ht); +#endif + (*allocOps->freeTable)(allocPriv, ht); +} + +/* +** Multiplicative hash, from Knuth 6.4. +*/ +JS_PUBLIC_API(JSHashEntry **) +JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) +{ + JSHashEntry *he, **hep, **hep0; + JSHashNumber h; + +#ifdef HASHMETER + ht->nlookups++; +#endif + h = keyHash * JS_GOLDEN_RATIO; + h >>= ht->shift; + hep = hep0 = &ht->buckets[h]; + 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 HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +JS_PUBLIC_API(JSHashEntry *) +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, + JSHashNumber keyHash, const void *key, void *value) +{ + uint32 i, n; + JSHashEntry *he, *next, **oldbuckets; + size_t nb; + + /* Grow the table if it is overloaded */ + n = NBUCKETS(ht); + if (ht->nentries >= OVERLOADED(n)) { + oldbuckets = ht->buckets; +#if defined _MSC_VER && _MSC_VER <= 800 + if (2 * n > 16000) + return NULL; +#endif /* WIN16 */ + nb = 2 * n * sizeof(JSHashEntry *); + ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return NULL; + } + memset(ht->buckets, 0, nb); +#ifdef HASHMETER + ht->ngrows++; +#endif + ht->shift--; + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JS_HashTableRawLookup(ht, he->keyHash, he->key); + JS_ASSERT(*hep == NULL); + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); + 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 i, n; + JSHashEntry *next, **oldbuckets; + size_t nb; + + *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)) { + oldbuckets = ht->buckets; + nb = n * sizeof(JSHashEntry*) / 2; + ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return; + } + memset(ht->buckets, 0, nb); +#ifdef HASHMETER + ht->nshrinks++; +#endif + ht->shift++; + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JS_HashTableRawLookup(ht, he->keyHash, he->key); + JS_ASSERT(*hep == NULL); + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets); + } +} + +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; + uint32 i, nbuckets; + int rv, n = 0; + JSHashEntry *todo = NULL; + + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + hep = &ht->buckets[i]; + while ((he = *hep) != NULL) { + rv = (*f)(he, n, arg); + n++; + if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { + *hep = he->next; + if (rv & HT_ENUMERATE_REMOVE) { + he->next = todo; + todo = he; + } + } else { + hep = &he->next; + } + if (rv & HT_ENUMERATE_STOP) { + goto out; + } + } + } + +out: + hep = &todo; + while ((he = *hep) != NULL) { + JS_HashTableRawRemove(ht, hep, he); + } + return n; +} + +#ifdef HASHMETER +#include +#include + +JS_PUBLIC_API(void) +JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + double sqsum, mean, variance, sigma; + uint32 nchains, nbuckets, nentries; + uint32 i, n, maxChain, maxChainLen; + JSHashEntry *he; + + sqsum = 0; + nchains = 0; + 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; + } + } + nentries = ht->nentries; + mean = (double)nentries / nchains; + variance = nchains * sqsum - nentries * nentries; + if (variance < 0 || nchains == 1) + variance = 0; + else + variance /= nchains * (nchains - 1); + sigma = sqrt(variance); + + 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 /* HASHMETER */ + +JS_PUBLIC_API(int) +JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) +{ + int count; + + count = JS_HashTableEnumerateEntries(ht, dump, fp); +#ifdef 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 = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; + return h; +} + +JS_PUBLIC_API(int) +JS_CompareValues(const void *v1, const void *v2) +{ + return v1 == v2; +} diff --git a/src/extension/script/js/jshash.h b/src/extension/script/js/jshash.h new file mode 100644 index 000000000..85e3cf811 --- /dev/null +++ b/src/extension/script/js/jshash.h @@ -0,0 +1,152 @@ +/* -*- 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 (* JS_DLL_CALLBACK JSHashFunction)(const void *key); +typedef intN (* JS_DLL_CALLBACK JSHashComparator)(const void *v1, const void *v2); +typedef intN (* JS_DLL_CALLBACK 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 */ +#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */ + +typedef struct JSHashAllocOps { + void * (*allocTable)(void *pool, size_t size); + void (*freeTable)(void *pool, void *item); + 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 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); + +extern JS_PUBLIC_API(JSHashEntry *) +JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, + const void *key, void *value); + +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/src/extension/script/js/jsinterp.c b/src/extension/script/js/jsinterp.c new file mode 100644 index 000000000..60f39b3ce --- /dev/null +++ b/src/extension/script/js/jsinterp.c @@ -0,0 +1,4284 @@ +/* -*- 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 ***** */ + +/* build on macs with low memory */ +#if defined(XP_MAC) && defined(MOZ_MAC_LOWMEM) +#pragma optimization_level 1 +#endif + +/* + * JavaScript bytecode interpreter. + */ +#include "jsstddef.h" +#include +#include +#include +#include "jstypes.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 "jsconfig.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" + +#if JS_HAS_JIT +#include "jsjit.h" +#endif + +#ifdef DEBUG +#define ASSERT_CACHE_IS_EMPTY(cache) \ + JS_BEGIN_MACRO \ + JSPropertyCacheEntry *end_, *pce_, entry_; \ + JSPropertyCache *cache_ = (cache); \ + JS_ASSERT(cache_->empty); \ + end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ + for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ + PCE_LOAD(cache_, pce_, entry_); \ + JS_ASSERT(!PCE_OBJECT(entry_)); \ + JS_ASSERT(!PCE_PROPERTY(entry_)); \ + } \ + JS_END_MACRO +#else +#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) +#endif + +void +js_FlushPropertyCache(JSContext *cx) +{ + JSPropertyCache *cache; + + cache = &cx->runtime->propertyCache; + 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 + cache->flushes++; +#endif +} + +void +js_DisablePropertyCache(JSContext *cx) +{ + JS_ASSERT(!cx->runtime->propertyCache.disabled); + cx->runtime->propertyCache.disabled = JS_TRUE; +} + +void +js_EnablePropertyCache(JSContext *cx) +{ + JS_ASSERT(cx->runtime->propertyCache.disabled); + ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); + cx->runtime->propertyCache.disabled = JS_FALSE; +} + +/* + * Class for for/in loop property iterator objects. + */ +#define JSSLOT_ITER_STATE JSSLOT_PRIVATE + +static void +prop_iterator_finalize(JSContext *cx, JSObject *obj) +{ + jsval iter_state; + jsval iteratee; + + /* Protect against stillborn iterators. */ + iter_state = obj->slots[JSSLOT_ITER_STATE]; + iteratee = obj->slots[JSSLOT_PARENT]; + if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) { + OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY, + &iter_state, NULL); + } + js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]); + + /* XXX force the GC to restart so we can collect iteratee, if possible, + during the current collector activation */ + cx->runtime->gcLevel++; +} + +static JSClass prop_iterator_class = { + "PropertyIterator", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iterator_finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +/* + * Stack macros and functions. These all use a local variable, jsval *sp, to + * point to the next free stack slot. SAVE_SP must be called before any call + * to a function that may invoke the interpreter. RESTORE_SP must be called + * only after return from js_Invoke, because only js_Invoke changes fp->sp. + */ +#define PUSH(v) (*sp++ = (v)) +#define POP() (*--sp) +#ifdef DEBUG +#define SAVE_SP(fp) \ + (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ + (fp)->sp = sp) +#else +#define SAVE_SP(fp) ((fp)->sp = sp) +#endif +#define RESTORE_SP(fp) (sp = (fp)->sp) + +/* + * Push the generating bytecode's pc onto the parallel pc stack that runs + * depth slots below the operands. + * + * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See + * js_Interpret for these local variables' declarations and uses. + */ +#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) +#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) +#define POP_OPND() POP() +#define FETCH_OPND(n) (sp[n]) + +/* + * Push the jsdouble d using sp, depth, and pc 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_; \ + jsval v_; \ + \ + if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ + v_ = INT_TO_JSVAL(i_); \ + } else { \ + ok = js_NewDoubleValue(cx, d, &v_); \ + if (!ok) \ + goto out; \ + } \ + STORE_OPND(n, v_); \ + JS_END_MACRO + +#define FETCH_NUMBER(cx, n, d) \ + JS_BEGIN_MACRO \ + jsval v_; \ + \ + v_ = FETCH_OPND(n); \ + VALUE_TO_NUMBER(cx, v_, d); \ + JS_END_MACRO + +#define FETCH_INT(cx, n, i) \ + JS_BEGIN_MACRO \ + jsval v_ = FETCH_OPND(n); \ + if (JSVAL_IS_INT(v_)) { \ + i = JSVAL_TO_INT(v_); \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToECMAInt32(cx, v_, &i); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#define FETCH_UINT(cx, n, ui) \ + JS_BEGIN_MACRO \ + jsval v_ = FETCH_OPND(n); \ + jsint i_; \ + if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ + ui = (uint32) i_; \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToECMAUint32(cx, v_, &ui); \ + if (!ok) \ + goto out; \ + } \ + 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, v, d) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_INT(v)) { \ + d = (jsdouble)JSVAL_TO_INT(v); \ + } else if (JSVAL_IS_DOUBLE(v)) { \ + d = *JSVAL_TO_DOUBLE(v); \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToNumber(cx, v, &d); \ + if (!ok) \ + goto out; \ + } \ + 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 { \ + SAVE_SP(fp); \ + ok = js_ValueToBoolean(cx, v, &b); \ + if (!ok) \ + goto out; \ + } \ + sp--; \ + JS_END_MACRO + +#define VALUE_TO_OBJECT(cx, v, obj) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) { \ + obj = JSVAL_TO_OBJECT(v); \ + } else { \ + SAVE_SP(fp); \ + obj = js_ValueToNonNullObject(cx, v); \ + if (!obj) { \ + ok = JS_FALSE; \ + goto out; \ + } \ + } \ + JS_END_MACRO + +#if JS_BUG_VOID_TOSTRING +#define CHECK_VOID_TOSTRING(cx, v) \ + if (JSVAL_IS_VOID(v)) { \ + JSString *str_; \ + str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \ + v = STRING_TO_JSVAL(str_); \ + } +#else +#define CHECK_VOID_TOSTRING(cx, v) ((void)0) +#endif + +#if JS_BUG_EAGER_TOSTRING +#define CHECK_EAGER_TOSTRING(hint) (hint = JSTYPE_STRING) +#else +#define CHECK_EAGER_TOSTRING(hint) ((void)0) +#endif + +#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_PRIMITIVE(v)) { \ + CHECK_VOID_TOSTRING(cx, v); \ + *vp = v; \ + } else { \ + SAVE_SP(fp); \ + CHECK_EAGER_TOSTRING(hint); \ + ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +JS_FRIEND_API(jsval *) +js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp; + + if (markp) + *markp = JS_ARENA_MARK(&cx->stackPool); + JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); + if (!sp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, + (cx->fp && cx->fp->fun) + ? JS_GetFunctionName(cx->fp->fun) + : "script"); + } + return sp; +} + +JS_FRIEND_API(void) +js_FreeRawStack(JSContext *cx, void *mark) +{ + JS_ARENA_RELEASE(&cx->stackPool, mark); +} + +JS_FRIEND_API(jsval *) +js_AllocStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp, *vp, *end; + JSArena *a; + JSStackHeader *sh; + JSStackFrame *fp; + + /* Callers don't check for zero nslots: we do to avoid empty segments. */ + if (nslots == 0) { + *markp = NULL; + return 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 we must initialize unused slots in the + * current frame. See js_GC, just before marking the "operand" jsvals, + * where we scan from fp->spbase to fp->sp or through fp->script->depth + * (whichever covers fewer slots). + */ + fp = cx->fp; + if (fp && fp->script && fp->spbase) { +#ifdef DEBUG + jsuword depthdiff = fp->script->depth * sizeof(jsval); + JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); + JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); +#endif + end = fp->spbase + fp->script->depth; + for (vp = fp->sp; vp < end; vp++) + *vp = JSVAL_VOID; + } + + /* 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; + } + + return sp; +} + +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); +} + +/* + * To economize on slots space in functions, the compiler records arguments and + * local variables as shared (JSPROP_SHARED) properties with well-known getters + * and setters: js_{Get,Set}Argument, js_{Get,Set}LocalVariable. Now, we could + * record args and vars in lists or hash tables in function-private data, but + * that means more duplication in code, and more data at runtime in the hash + * table case due to round-up to powers of two, just to recapitulate the scope + * machinery in the function object. + * + * What's more, for a long time (to the dawn of "Mocha" in 1995), these getters + * and setters knew how to search active stack frames in a context to find the + * top activation of the function f, in order to satisfy a get or set of f.a, + * for argument a, or f.x, for local variable x. You could use f.a instead of + * just a in function f(a) { return f.a }, for example, to return the actual + * parameter. + * + * ECMA requires that we give up on this ancient extension, because it is not + * compatible with the standard as used by real-world scripts. While Chapter + * 16 does allow for additional properties to be defined on native objects by + * a conforming implementation, these magic getters and setters cause f.a's + * meaning to vary unexpectedly. Real-world scripts set f.A = 42 to define + * "class static" (after Java) constants, for example, but if A also names an + * arg or var in f, the constant is not available while f is active, and any + * non-constant class-static can't be set while f is active. + * + * So, to label arg and var properties in functions without giving them magic + * abilities to affect active frame stack slots, while keeping the properties + * shared (slot-less) to save space in the common case (where no assignment + * sets a function property with the same name as an arg or var), the setters + * for args and vars must handle two special cases here. + * + * XXX functions tend to have few args and vars, so we risk O(n^2) growth here + * XXX ECMA *really* wants args and vars to be stored in function-private data, + * not as function object properties. + */ +static JSBool +SetFunctionSlot(JSContext *cx, JSObject *obj, JSPropertyOp setter, jsid id, + jsval v) +{ + uintN slot; + JSObject *origobj; + JSScope *scope; + JSScopeProperty *sprop; + JSString *str; + JSBool ok; + + slot = (uintN) JSVAL_TO_INT(id); + if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { + /* + * Given a non-function object obj that has a function object in its + * prototype chain, where an argument or local variable property named + * by (setter, slot) is being set, override the shared property in the + * prototype with an unshared property in obj. This situation arises + * in real-world JS due to .prototype setting and collisions among a + * function's "static property" names and arg or var names, believe it + * or not. + */ + origobj = obj; + do { + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) + return JS_TRUE; + } while (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass); + + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (sprop->setter == setter) { + JS_ASSERT(!JSVAL_IS_INT(sprop->id) && + ATOM_IS_STRING((JSAtom *)sprop->id) && + (sprop->flags & SPROP_HAS_SHORTID)); + + if ((uintN) sprop->shortid == slot) { + str = ATOM_TO_STRING((JSAtom *)sprop->id); + JS_UNLOCK_SCOPE(cx, scope); + + return JS_DefineUCProperty(cx, origobj, + JSSTRING_CHARS(str), + JSSTRING_LENGTH(str), + v, NULL, NULL, + JSPROP_ENUMERATE); + } + } + } + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; + } + + /* + * Argument and local variable properties of function objects are shared + * by default (JSPROP_SHARED), therefore slot-less. But if for function + * f(a) {}, f.a = 42 is evaluated, f.a should be 42 after the assignment, + * whether or not f is active. So js_SetArgument and js_SetLocalVariable + * must be prepared to change an arg or var from shared to unshared status, + * allocating a slot in obj to hold v. + */ + ok = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (sprop->setter == setter && (uintN) sprop->shortid == slot) { + if (sprop->attrs & JSPROP_SHARED) { + sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, + 0, ~JSPROP_SHARED, + sprop->getter, setter); + if (!sprop) { + ok = JS_FALSE; + } else { + /* See js_SetProperty, near the bottom. */ + GC_POKE(cx, pval); + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, v); + } + } + break; + } + } + JS_UNLOCK_SCOPE(cx, scope); + return ok; +} + +JSBool +js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return SetFunctionSlot(cx, obj, js_SetArgument, id, *vp); +} + +JSBool +js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return SetFunctionSlot(cx, obj, js_SetLocalVariable, id, *vp); +} + +/* + * Compute the 'this' parameter and store it in frame as frame.thisp. + * Activation objects ("Call" objects not created with "new Call()", i.e., + * "Call" objects that have private data) may not be referred to by 'this', + * as dictated by ECMA. + * + * N.B.: fp->argv must be set, fp->argv[-1] the nominal 'this' paramter as + * a jsval, and fp->argv[-2] must be the callee object reference, usually a + * function object. Also, fp->flags must contain JSFRAME_CONSTRUCTING if we + * are preparing for a constructor call. + */ +static JSBool +ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp) +{ + JSObject *parent; + + if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { + /* Some objects (e.g., With) delegate 'this' to another object. */ + thisp = OBJ_THIS_OBJECT(cx, thisp); + if (!thisp) + return JS_FALSE; + + /* Default return value for a constructor is the new object. */ + if (fp->flags & JSFRAME_CONSTRUCTING) + fp->rval = OBJECT_TO_JSVAL(thisp); + } else { + /* + * ECMA requires "the global object", but in the presence of multiple + * top-level objects (windows, frames, or certain layers in the client + * object model), 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_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); + if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || + !(parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2])))) { + thisp = cx->globalObject; + } else { + /* walk up to find the top-level object */ + thisp = parent; + while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL) + thisp = parent; + } + } + fp->thisp = thisp; + fp->argv[-1] = OBJECT_TO_JSVAL(thisp); + return JS_TRUE; +} + +/* + * 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_FRIEND_API(JSBool) +js_Invoke(JSContext *cx, uintN argc, uintN flags) +{ + void *mark; + JSStackFrame *fp, frame; + jsval *sp, *newsp, *limit; + jsval *vp, v; + JSObject *funobj, *parent, *thisp; + JSBool ok; + JSClass *clasp; + JSObjectOps *ops; + JSNative native; + JSFunction *fun; + JSScript *script; + uintN minargs, nvars; + intN nslots, nalloc, surplus; + JSInterpreterHook hook; + void *hookData; + + /* Mark the top of stack and load frequently-used registers. */ + mark = JS_ARENA_MARK(&cx->stackPool); + fp = cx->fp; + sp = fp->sp; + + /* + * Set vp to the callee value's stack slot (it's where rval goes). + * Once vp is set, control should flow through label out2: to return. + * Set frame.rval early so native class and object ops can throw and + * return false, causing a goto out2 with ok set to false. Also set + * frame.flags to flags so that ComputeThis can test bits in it. + */ + vp = sp - (2 + argc); + v = *vp; + frame.rval = JSVAL_VOID; + frame.flags = flags; + thisp = JSVAL_TO_OBJECT(vp[1]); + + /* + * A callee must be an object reference, unless its |this| parameter + * implements the __noSuchMethod__ method, in which case that method will + * be called like so: + * + * thisp.__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. + */ + if (JSVAL_IS_PRIMITIVE(v)) { +#if JS_HAS_NO_SUCH_METHOD + jsbytecode *pc; + jsatomid atomIndex; + JSAtom *atom; + JSObject *argsobj; + JSArena *a; + + if (!fp->script || (flags & JSINVOKE_INTERNAL)) + goto bad; + + /* + * We must ComputeThis here to censor Call objects; performance hit, + * but at least it's idempotent. + * + * Normally, we call ComputeThis after all frame members have been + * set, and in particular, after any revision of the callee value at + * *vp due to clasp->convert (see below). This matters because + * ComputeThis may access *vp via fp->argv[-2], to follow the parent + * chain to a global object to use as the |this| parameter. + * + * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be + * any such defaulting of |this| to callee (v, *vp) ancestor. + */ + frame.argv = vp + 2; + ok = ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; + thisp = frame.thisp; + + ok = OBJ_GET_PROPERTY(cx, thisp, + (jsid)cx->runtime->atomState.noSuchMethodAtom, + &v); + if (!ok) + goto out2; + if (JSVAL_IS_PRIMITIVE(v)) + goto bad; + + pc = (jsbytecode *) vp[-(intN)fp->script->depth]; + switch ((JSOp) *pc) { + case JSOP_NAME: + case JSOP_GETPROP: + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex); + argsobj = js_NewArrayObject(cx, argc, vp + 2); + if (!argsobj) { + ok = JS_FALSE; + goto out2; + } + + sp = vp + 4; + if (argc < 2) { + a = cx->stackPool.current; + if ((jsuword)sp > a->limit) { + /* + * Arguments must be contiguous, and must include argv[-1] + * and argv[-2], so allocate more stack, advance sp, and + * set newsp[1] to thisp (vp[1]). The other argv elements + * will be set below, using negative indexing from sp. + */ + newsp = js_AllocRawStack(cx, 4, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out2; + } + newsp[1] = OBJECT_TO_JSVAL(thisp); + sp = newsp + 4; + } else if ((jsuword)sp > a->avail) { + /* + * Inline, optimized version of JS_ARENA_ALLOCATE to claim + * the small number of words not already allocated as part + * of the caller's operand stack. + */ + JS_ArenaCountAllocation(pool, (jsuword)sp - a->avail); + a->avail = (jsuword)sp; + } + } + + sp[-4] = v; + JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp)); + sp[-2] = ATOM_KEY(atom); + sp[-1] = OBJECT_TO_JSVAL(argsobj); + fp->sp = sp; + argc = 2; + break; + + default: + goto bad; + } +#else + goto bad; +#endif + } + + funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); + clasp = OBJ_GET_CLASS(cx, funobj); + if (clasp != &js_FunctionClass) { + /* Function is inlined, all other classes use object ops. */ + ops = funobj->map->ops; + + /* + * XXX + * Try converting to function, for closure and API compatibility. + * We attempt the conversion under all circumstances for 1.2, but + * only if there is a call op defined otherwise. + */ + if (cx->version == JSVERSION_1_2 || + ((ops == &js_ObjectOps) ? clasp->call : ops->call)) { + ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); + if (!ok) + goto out2; + + if (JSVAL_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; + minargs = nvars = 0; + + /* Try a call or construct native object op. */ + native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; + if (!native) + goto bad; + } else { +have_fun: + /* Get private data and set derived locals from it. */ + fun = (JSFunction *) JS_GetPrivate(cx, funobj); + native = fun->native; + script = fun->script; + minargs = fun->nargs + fun->extra; + nvars = fun->nvars; + + /* Handle bound method special case. */ + if (fun->flags & JSFUN_BOUND_METHOD) + thisp = parent; + } + + /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ + frame.varobj = NULL; + frame.callobj = frame.argsobj = NULL; + frame.script = script; + frame.fun = fun; + frame.argc = argc; + frame.argv = sp - argc; + frame.nvars = nvars; + frame.vars = sp; + frame.down = fp; + frame.annotation = NULL; + frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ + frame.pc = NULL; + frame.spbase = NULL; + frame.sharpDepth = 0; + frame.sharpArray = NULL; + frame.dormantNext = NULL; + frame.objAtomMap = NULL; + + /* Compute the 'this' parameter and store it in frame as frame.thisp. */ + ok = ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; + + /* From here on, control must flow through label out: to return. */ + cx->fp = &frame; + + /* Init these now in case we goto out before first hook call. */ + hook = cx->runtime->callHook; + hookData = NULL; + + /* Check for missing arguments expected by the function. */ + nslots = (intN)((argc < minargs) ? minargs - argc : 0); + if (nslots) { + /* All arguments must be contiguous, so we may have to copy actuals. */ + nalloc = nslots; + limit = (jsval *) cx->stackPool.current->limit; + if (sp + nslots > limit) { + /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ + nalloc += 2 + argc; + } else { + /* Take advantage of surplus slots in the caller's frame depth. */ + surplus = (jsval *)mark - sp; + JS_ASSERT(surplus >= 0); + nalloc -= surplus; + } + + /* Check whether we have enough space in the caller's frame. */ + if (nalloc > 0) { + /* Need space for actuals plus missing formals minus surplus. */ + newsp = js_AllocRawStack(cx, (uintN)nalloc, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out; + } + + /* If we couldn't allocate contiguous args, copy actuals now. */ + if (newsp != mark) { + JS_ASSERT(sp + nslots > limit); + JS_ASSERT(2 + argc + nslots == (uintN)nalloc); + *newsp++ = vp[0]; + *newsp++ = vp[1]; + if (argc) + memcpy(newsp, frame.argv, argc * sizeof(jsval)); + frame.argv = newsp; + sp = frame.vars = newsp + argc; + } + } + + /* Advance frame.vars to make room for the missing args. */ + frame.vars += nslots; + + /* Push void to initialize missing args. */ + while (--nslots >= 0) + PUSH(JSVAL_VOID); + } + + /* Now allocate stack space for local variables. */ + nslots = (intN)frame.nvars; + if (nslots) { + surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars); + if (surplus < nslots) { + newsp = js_AllocRawStack(cx, (uintN)nslots, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out; + } + if (newsp != sp) { + /* NB: Discontinuity between argv and vars. */ + sp = frame.vars = newsp; + } + } + + /* Push void to initialize local variables. */ + while (--nslots >= 0) + PUSH(JSVAL_VOID); + } + + /* Store the current sp in frame before calling fun. */ + SAVE_SP(&frame); + + /* call the hook if present */ + if (hook && (native || script)) + hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); + + /* Call the function, either a native method or an interpreted script. */ + if (native) { +#if JS_HAS_LVALUE_RETURN + /* Set by JS_SetCallReturnValue2, used to return reference types. */ + cx->rval2set = JS_FALSE; +#endif + + /* If native, use caller varobj and scopeChain for eval. */ + frame.varobj = fp->varobj; + frame.scopeChain = fp->scopeChain; + ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); + JS_RUNTIME_METER(cx->runtime, nativeCalls); + } else if (script) { + /* Use parent scope so js_GetCallObject can find the right "Call". */ + frame.scopeChain = parent; + if (fun->flags & JSFUN_HEAVYWEIGHT) { +#if JS_HAS_CALL_OBJECT + /* Scope with a call object parented by the callee's parent. */ + if (!js_GetCallObject(cx, &frame, parent)) { + ok = JS_FALSE; + goto out; + } +#else + /* Bad old code used the function as a proxy for all calls to it. */ + frame.scopeChain = funobj; +#endif + } + ok = js_Interpret(cx, &v); + } else { + /* fun might be onerror trying to report a syntax error in itself. */ + frame.scopeChain = NULL; + ok = JS_TRUE; + } + +out: + if (hookData) { + hook = cx->runtime->callHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } +#if JS_HAS_CALL_OBJECT + /* If frame has a call object, sync values and clear back-pointer. */ + if (frame.callobj) + ok &= js_PutCallObject(cx, &frame); +#endif +#if JS_HAS_ARGS_OBJECT + /* If frame has an arguments object, sync values and clear back-pointer. */ + if (frame.argsobj) + ok &= js_PutArgsObject(cx, &frame); +#endif + + /* Restore cx->fp now that we're done releasing frame objects. */ + cx->fp = fp; + +out2: + /* Pop everything we may have allocated off the stack. */ + JS_ARENA_RELEASE(&cx->stackPool, mark); + + /* Store the return value and restore sp just above it. */ + *vp = frame.rval; + fp->sp = vp + 1; + + /* + * Store the location of the JSOP_CALL or JSOP_EVAL that generated the + * return value, but only if this is an external (compiled from script + * source) call that has stack budget for the generating pc. + */ + if (fp->script && !(flags & JSINVOKE_INTERNAL)) + vp[-(intN)fp->script->depth] = (jsval)fp->pc; + return ok; + +bad: + js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT); + ok = JS_FALSE; + goto out2; +} + +JSBool +js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, + uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp, *oldfp, frame; + jsval *oldsp, *sp; + void *mark; + uintN i; + JSBool ok; + + fp = oldfp = cx->fp; + if (!fp) { + memset(&frame, 0, sizeof frame); + cx->fp = fp = &frame; + } + oldsp = fp->sp; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) { + ok = JS_FALSE; + goto out; + } + + PUSH(fval); + PUSH(OBJECT_TO_JSVAL(obj)); + for (i = 0; i < argc; i++) + PUSH(argv[i]); + fp->sp = sp; + ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); + if (ok) { + RESTORE_SP(fp); + *rval = POP_OPND(); + } + + js_FreeStack(cx, mark); +out: + fp->sp = oldsp; + if (oldfp != fp) + cx->fp = oldfp; + + return ok; +} + +JSBool +js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, + JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) +{ + /* + * Check general (not object-ops/class-specific) access from the running + * script to obj.id only if id has a scripted getter or setter that we're + * about to invoke. If we don't check this case, nothing else will -- no + * other native code has the chance to check. + * + * Contrast this non-native (scripted) case with native getter and setter + * accesses, where the native itself must do an access check, if security + * policies requires it. We make a checkAccess or checkObjectAccess call + * back to the embedding program only in those cases where we're not going + * to call an embedding-defined native function, getter, setter, or class + * hook anyway. Where we do call such a native, there's no need for the + * engine to impose a separate access check callback on all embeddings -- + * many embeddings have no security policy at all. + */ + JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); + if (cx->runtime->checkObjectAccess && + JSVAL_IS_FUNCTION(cx, fval) && + ((JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->script && + !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, + &fval)) { + return JS_FALSE; + } + + return js_InternalCall(cx, obj, fval, argc, argv, rval); +} + +JSBool +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, + JSStackFrame *down, uintN special, jsval *result) +{ + JSStackFrame *oldfp, frame; + JSObject *obj, *tmp; + JSBool ok; + JSInterpreterHook hook; + void *hookData; + + hook = cx->runtime->executeHook; + hookData = NULL; + oldfp = cx->fp; + frame.callobj = frame.argsobj = NULL; + frame.script = script; + if (down) { + /* Propagate arg/var state for eval and the debugger API. */ + frame.varobj = down->varobj; + frame.fun = down->fun; + frame.thisp = down->thisp; + frame.argc = down->argc; + frame.argv = down->argv; + frame.nvars = down->nvars; + frame.vars = down->vars; + frame.annotation = down->annotation; + frame.sharpArray = down->sharpArray; + } else { + obj = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + } + frame.varobj = obj; + frame.fun = NULL; + frame.thisp = chain; + frame.argc = frame.nvars = 0; + frame.argv = frame.vars = NULL; + frame.annotation = NULL; + frame.sharpArray = NULL; + } + frame.rval = JSVAL_VOID; + frame.down = down; + frame.scopeChain = chain; + frame.pc = NULL; + frame.sp = oldfp ? oldfp->sp : NULL; + frame.spbase = NULL; + frame.sharpDepth = 0; + frame.flags = special; + frame.dormantNext = NULL; + frame.objAtomMap = 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 (hook) + hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); + + ok = js_Interpret(cx, result); + + if (hookData) { + hook = cx->runtime->executeHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } + cx->fp = oldfp; + + if (oldfp && oldfp != down) { + JS_ASSERT(cx->dormantFrameChain == oldfp); + cx->dormantFrameChain = oldfp->dormantNext; + oldfp->dormantNext = NULL; + } + + return ok; +} + +#if JS_HAS_EXPORT_IMPORT +/* + * If id is JSVAL_VOID, import all exported properties from obj. + */ +static JSBool +ImportProperty(JSContext *cx, JSObject *obj, jsid id) +{ + JSBool ok; + JSIdArray *ida; + JSProperty *prop; + JSObject *obj2, *target, *funobj, *closure; + JSString *str; + uintN attrs; + jsint i; + jsval value; + + if (JSVAL_IS_VOID(id)) { + ida = JS_Enumerate(cx, obj); + if (!ida) + return JS_FALSE; + ok = JS_TRUE; + if (ida->length == 0) + goto out; + } else { + ida = NULL; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), NULL); + if (str) + js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); + return JS_FALSE; + } + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + return JS_FALSE; + if (!(attrs & JSPROP_EXPORTED)) { + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NOT_EXPORTED, + JS_GetStringBytes(str)); + } + return JS_FALSE; + } + } + + target = cx->fp->varobj; + i = 0; + do { + if (ida) { + id = ida->vector[i]; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); + if (!ok) + goto out; + if (!(attrs & JSPROP_EXPORTED)) + continue; + } + ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); + if (!ok) + goto out; + if (JSVAL_IS_FUNCTION(cx, value)) { + funobj = JSVAL_TO_OBJECT(value); + closure = js_CloneFunctionObject(cx, funobj, obj); + if (!closure) { + ok = JS_FALSE; + goto out; + } + value = OBJECT_TO_JSVAL(closure); + } + + /* + * Handle the case of importing a property that refers to a local + * variable or formal parameter of a function activation. These + * properties are accessed by opcodes using stack slot numbers + * generated by the compiler rather than runtime name-lookup. These + * local references, therefore, bypass the normal scope chain lookup. + * So, instead of defining a new property in the activation object, + * modify the existing value in the stack slot. + */ + if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { + ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); + if (!ok) + goto out; + } else { + prop = NULL; + } + if (prop && target == obj2) { + ok = OBJ_SET_PROPERTY(cx, target, id, &value); + } else { + ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, + attrs & ~JSPROP_EXPORTED, + NULL); + } + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + goto out; + } while (ida && ++i < ida->length); + +out: + if (ida) + JS_DestroyIdArray(cx, ida); + return ok; +} +#endif /* JS_HAS_EXPORT_IMPORT */ + +JSBool +js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, + JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + uintN oldAttrs, report; + JSBool isFunction; + jsval value; + const char *type, *name; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + *foundp = (prop != NULL); + if (!prop) + return JS_TRUE; + ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs); + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + return JS_FALSE; + + /* If either property is readonly, we have an error. */ + report = ((oldAttrs | attrs) & JSPROP_READONLY) + ? JSREPORT_ERROR + : JSREPORT_WARNING | JSREPORT_STRICT; + + if (report != JSREPORT_ERROR) { + /* + * Allow redeclaration of variables and functions, but insist that the + * new value is not a getter if the old value was, ditto for setters -- + * unless prop is impermanent (in which case anyone could delete it and + * redefine it, willy-nilly). + */ + if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) + return JS_TRUE; + if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) + return JS_TRUE; + if (!(oldAttrs & JSPROP_PERMANENT)) + return JS_TRUE; + report = JSREPORT_ERROR; + } + + isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; + if (!isFunction) { + if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) + return JS_FALSE; + isFunction = JSVAL_IS_FUNCTION(cx, value); + } + type = (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_AtomToPrintableString(cx, (JSAtom *)id); + if (!name) + return JS_FALSE; + return JS_ReportErrorFlagsAndNumber(cx, report, + js_GetErrorMessage, NULL, + JSMSG_REDECLARED_VAR, + type, name); +} + +#ifndef MAX_INTERP_LEVEL +#if defined(XP_OS2) +#define MAX_INTERP_LEVEL 250 +#elif defined _MSC_VER && _MSC_VER <= 800 +#define MAX_INTERP_LEVEL 30 +#else +#define MAX_INTERP_LEVEL 1000 +#endif +#endif + +#define MAX_INLINE_CALL_COUNT 1000 + +JSBool +js_Interpret(JSContext *cx, jsval *result) +{ + JSRuntime *rt; + JSStackFrame *fp; + JSScript *script; + uintN inlineCallCount; + JSObject *obj, *obj2, *proto, *parent; + JSVersion currentVersion, originalVersion; + JSBranchCallback onbranch; + JSBool ok, cond; + JSTrapHandler interruptHandler; + jsint depth, len; + jsval *sp, *newsp; + void *mark; + jsbytecode *pc, *pc2, *endpc; + JSOp op, op2; + const JSCodeSpec *cs; + JSAtom *atom; + uintN argc, slot, attrs; + jsval *vp, lval, rval, ltmp, rtmp; + jsid id; + JSObject *withobj, *origobj, *propobj; + jsval iter_state; + JSProperty *prop; + JSScopeProperty *sprop; + JSString *str, *str2; + jsint i, j; + jsdouble d, d2; + JSClass *clasp, *funclasp; + JSFunction *fun; + JSType type; +#ifdef DEBUG + FILE *tracefp; +#endif +#if JS_HAS_EXPORT_IMPORT + JSIdArray *ida; +#endif +#if JS_HAS_SWITCH_STATEMENT + jsint low, high, off, npairs; + JSBool match; +#endif +#if JS_HAS_GETTER_SETTER + JSPropertyOp getter, setter; +#endif + int stackDummy; + + *result = JSVAL_VOID; + rt = cx->runtime; + + /* Set registerized frame pointer and derived script pointer. */ + fp = cx->fp; + script = fp->script; + + /* Count of JS function calls that nest in this C js_Interpret frame. */ + inlineCallCount = 0; + + /* + * 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 = script->version; + originalVersion = cx->version; + if (currentVersion != originalVersion) + JS_SetVersion(cx, currentVersion); + + /* + * Prepare to call a user-supplied branch handler, and abort the script + * if it returns false. + */ + onbranch = cx->branchCallback; + ok = JS_TRUE; +#define CHECK_BRANCH(len) \ + JS_BEGIN_MACRO \ + if (len <= 0 && onbranch) { \ + SAVE_SP(fp); \ + if (!(ok = (*onbranch)(cx, script))) \ + goto out; \ + } \ + 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. + * XXX if it spills, we still lose + */ +#define LOAD_INTERRUPT_HANDLER(rt) (interruptHandler = (rt)->interruptHandler) + + LOAD_INTERRUPT_HANDLER(rt); + + pc = script->code; + endpc = pc + script->length; + depth = (jsint) script->depth; + len = -1; + + /* Check for too much js_Interpret nesting, or too deep a C stack. */ + if (++cx->interpLevel == MAX_INTERP_LEVEL || + !JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + ok = JS_FALSE; + goto out; + } + + /* + * Allocate operand and pc stack slots for the script's worst-case depth. + */ + newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); + if (!newsp) { + ok = JS_FALSE; + goto out; + } + sp = newsp + depth; + fp->spbase = sp; + SAVE_SP(fp); + + while (pc < endpc) { + fp->pc = pc; + op = (JSOp) *pc; + do_op: + cs = &js_CodeSpec[op]; + len = cs->length; + +#ifdef DEBUG + tracefp = (FILE *) cx->tracefp; + if (tracefp) { + intN nuses, n; + + fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); + js_Disassemble1(cx, script, pc, + PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, + tracefp); + nuses = cs->nuses; + if (nuses) { + SAVE_SP(fp); + for (n = -nuses; n < 0; n++) { + str = js_DecompileValueGenerator(cx, n, sp[n], NULL); + if (str != NULL) { + fprintf(tracefp, "%s %s", + (n == -nuses) ? " inputs:" : ",", + JS_GetStringBytes(str)); + } + } + fprintf(tracefp, " @ %d\n", sp - fp->spbase); + } + } +#endif + + if (interruptHandler) { + SAVE_SP(fp); + switch (interruptHandler(cx, script, pc, &rval, + rt->interruptHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + + switch (op) { + case JSOP_NOP: + break; + + case JSOP_GROUP: + obj = NULL; + break; + + case JSOP_PUSH: + PUSH_OPND(JSVAL_VOID); + break; + + case JSOP_POP: + sp--; + break; + + case JSOP_POP2: + sp -= 2; + break; + + case JSOP_SWAP: + /* + * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs + * for the operands it swaps. + */ + ltmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = ltmp; + break; + + case JSOP_POPV: + *result = POP_OPND(); + break; + + case JSOP_ENTERWITH: + rval = FETCH_OPND(-1); + VALUE_TO_OBJECT(cx, rval, obj); + withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain); + if (!withobj) + goto out; + fp->scopeChain = withobj; + STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); + break; + + case JSOP_LEAVEWITH: + rval = POP_OPND(); + JS_ASSERT(JSVAL_IS_OBJECT(rval)); + withobj = JSVAL_TO_OBJECT(rval); + JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); + + rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); + JS_ASSERT(JSVAL_IS_OBJECT(rval)); + fp->scopeChain = JSVAL_TO_OBJECT(rval); + break; + + case JSOP_SETRVAL: + fp->rval = POP_OPND(); + break; + + case JSOP_RETURN: + CHECK_BRANCH(-1); + fp->rval = POP_OPND(); + /* FALL THROUGH */ + + case JSOP_RETRVAL: /* fp->rval already set */ + if (inlineCallCount) + inline_return: + { + JSInlineFrame *ifp = (JSInlineFrame *) fp; + void *hookData = ifp->hookData; + + if (hookData) { + JSInterpreterHook hook = cx->runtime->callHook; + if (hook) { + hook(cx, fp, JS_FALSE, &ok, hookData); + LOAD_INTERRUPT_HANDLER(rt); + } + } +#if JS_HAS_ARGS_OBJECT + if (fp->argsobj) + ok &= js_PutArgsObject(cx, fp); +#endif + + /* Restore context version only if callee hasn't set version. */ + if (cx->version == currentVersion) { + currentVersion = ifp->callerVersion; + if (currentVersion != cx->version) + JS_SetVersion(cx, currentVersion); + } + + /* Store the return value in the caller's operand frame. */ + vp = fp->argv - 2; + *vp = fp->rval; + + /* Restore cx->fp and release the inline frame's space. */ + cx->fp = fp = fp->down; + JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); + + /* Restore sp to point just above the return value. */ + fp->sp = vp + 1; + RESTORE_SP(fp); + + /* Restore the calling script's interpreter registers. */ + script = fp->script; + depth = (jsint) script->depth; + pc = fp->pc; + endpc = script->code + script->length; + + /* Store the generating pc for the return value. */ + vp[-depth] = (jsval)pc; + + /* Set remaining variables for 'goto advance_pc'. */ + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; + len = cs->length; + + /* Resume execution in the calling frame. */ + inlineCallCount--; + if (ok) + goto advance_pc; + } + goto out; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULT: + (void) POP(); + /* FALL THROUGH */ +#endif + case JSOP_GOTO: + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + break; + + case JSOP_IFEQ: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_IFNE: + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_OR: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMP_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_AND: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULTX: + (void) POP(); + /* FALL THROUGH */ +#endif + case JSOP_GOTOX: + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + break; + + case JSOP_IFEQX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_IFNEX: + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_ORX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMPX_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_ANDX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_TOOBJECT: + SAVE_SP(fp); + ok = js_ValueToObject(cx, FETCH_OPND(-1), &obj); + if (!ok) + goto out; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + +#define FETCH_ELEMENT_ID(n, id) \ + JS_BEGIN_MACRO \ + /* If the index is not a jsint, atomize it. */ \ + id = (jsid) FETCH_OPND(n); \ + if (JSVAL_IS_INT(id)) { \ + atom = NULL; \ + } else { \ + SAVE_SP(fp); \ + atom = js_ValueToStringAtom(cx, (jsval)id); \ + if (!atom) { \ + ok = JS_FALSE; \ + goto out; \ + } \ + id = (jsid)atom; \ + } \ + JS_END_MACRO + +#define POP_ELEMENT_ID(id) \ + JS_BEGIN_MACRO \ + FETCH_ELEMENT_ID(-1, id); \ + sp--; \ + JS_END_MACRO + +#if JS_HAS_IN_OPERATOR + case JSOP_IN: + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + str = js_DecompileValueGenerator(cx, -1, rval, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_IN_NOT_OBJECT, + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + sp--; + obj = JSVAL_TO_OBJECT(rval); + FETCH_ELEMENT_ID(-1, id); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto out; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + break; +#endif /* JS_HAS_IN_OPERATOR */ + + case JSOP_FORPROP: + /* + * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop + * is not paid for the more common cases. + */ + lval = FETCH_OPND(-1); + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + i = -2; + goto do_forinloop; + + case JSOP_FORNAME: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + /* + * ECMA 12.6.3 says to eval the LHS after looking for properties + * to enumerate, and bail without LHS eval if there are no props. + * We do Find here to share the most code at label do_forinloop. + * If looking for enumerable properties could have side effects, + * then we'd have to move this into the common code and condition + * it on op == JSOP_FORNAME. + */ + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + lval = OBJECT_TO_JSVAL(obj); + /* FALL THROUGH */ + + case JSOP_FORARG: + case JSOP_FORVAR: + /* + * JSOP_FORARG and JSOP_FORVAR don't require any lval computation + * here, because they address slots on the stack (in fp->args and + * fp->vars, respectively). + */ + /* FALL THROUGH */ + + case JSOP_FORELEM: + /* + * JSOP_FORELEM simply initializes or updates the iteration state + * and leaves the index expression evaluation and assignment to the + * enumerator until after the next property has been acquired, via + * a JSOP_ENUMELEM bytecode. + */ + i = -1; + + do_forinloop: + /* + * ECMA-compatible for/in evals the object just once, before loop. + * Bad old bytecodes (since removed) did it on every iteration. + */ + obj = JSVAL_TO_OBJECT(sp[i]); + + /* If the thing to the right of 'in' has no properties, break. */ + if (!obj) { + rval = JSVAL_FALSE; + goto end_forinloop; + } + + /* + * Save the thing to the right of 'in' as origobj. Later on, we + * use this variable to suppress enumeration of shadowed prototype + * properties. + */ + origobj = obj; + + /* + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. (An object is used + * rather than a native struct so that the iteration state is + * cleaned up via GC if the for-in loop terminates abruptly.) + */ + vp = &sp[i - 1]; + rval = *vp; + + /* Is this the first iteration ? */ + if (JSVAL_IS_VOID(rval)) { + /* Yes, create a new JSObject to hold the iterator state */ + propobj = js_NewObject(cx, &prop_iterator_class, NULL, obj); + if (!propobj) { + ok = JS_FALSE; + goto out; + } + propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; + + /* + * Root the parent slot so we can get it even in our finalizer + * (otherwise, it would live as long as we do, but it might be + * finalized first). + */ + ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT], + "propobj->parent"); + if (!ok) + goto out; + + /* + * Rewrite the iterator so we know to do the next case. + * Do this before calling the enumerator, which could + * displace cx->newborn and cause GC. + */ + *vp = OBJECT_TO_JSVAL(propobj); + + ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + + /* + * Stash private iteration state into property iterator object. + * We do this before checking 'ok' to ensure that propobj is + * in a valid state even if OBJ_ENUMERATE returned JS_FALSE. + * NB: This code knows that the first slots are pre-allocated. + */ +#if JS_INITIAL_NSLOTS < 5 +#error JS_INITIAL_NSLOTS must be greater than or equal to 5. +#endif + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + if (!ok) + goto out; + } else { + /* This is not the first iteration. Recover iterator state. */ + propobj = JSVAL_TO_OBJECT(rval); + obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]); + iter_state = propobj->slots[JSSLOT_ITER_STATE]; + } + + enum_next_property: + /* Get the next jsid to be enumerated and store it in rval. */ + OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval); + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + + /* No more jsids to iterate in obj? */ + if (iter_state == JSVAL_NULL) { + /* Enumerate the properties on obj's prototype chain. */ + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) { + /* End of property list -- terminate loop. */ + rval = JSVAL_FALSE; + goto end_forinloop; + } + + ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + + /* + * Stash private iteration state into property iterator object. + * We do this before checking 'ok' to ensure that propobj is + * in a valid state even if OBJ_ENUMERATE returned JS_FALSE. + * NB: This code knows that the first slots are pre-allocated. + */ + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + if (!ok) + goto out; + + /* + * Update the iterator JSObject's parent link to refer to the + * current object. This is used in the iterator JSObject's + * finalizer. + */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + goto enum_next_property; + } + + /* Skip properties not owned by obj, and leave next id in rval. */ + ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop); + if (!ok) + goto out; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* Yes, don't enumerate again. Go to the next property. */ + if (obj2 != obj) + goto enum_next_property; + } + + /* Make sure rval is a string for uniformity and compatibility. */ + if (!JSVAL_IS_INT(rval)) { + rval = ATOM_KEY((JSAtom *)rval); + } else if (cx->version != JSVERSION_1_2) { + str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval)); + if (!str) { + ok = JS_FALSE; + goto out; + } + + rval = STRING_TO_JSVAL(str); + } + + switch (op) { + case JSOP_FORARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + fp->argv[slot] = rval; + break; + + case JSOP_FORVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + fp->vars[slot] = rval; + break; + + case JSOP_FORELEM: + /* FORELEM is not a SET operation, it's more like BINDNAME. */ + PUSH_OPND(rval); + break; + + default: + /* Convert lval to a non-null object containing id. */ + VALUE_TO_OBJECT(cx, lval, obj); + + /* Set the variable obj[id] to refer to rval. */ + fp->flags |= JSFRAME_ASSIGNING; + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto out; + break; + } + + /* Push true to keep looping through properties. */ + rval = JSVAL_TRUE; + + end_forinloop: + sp += i + 1; + PUSH_OPND(rval); + break; + + case JSOP_DUP: + JS_ASSERT(sp > fp->spbase); + rval = sp[-1]; + PUSH_OPND(rval); + break; + + case JSOP_DUP2: + JS_ASSERT(sp - 1 > fp->spbase); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + PUSH_OPND(lval); + PUSH_OPND(rval); + break; + +#define PROPERTY_OP(n, call) \ + JS_BEGIN_MACRO \ + /* Pop the left part and resolve it to a non-null object. */ \ + lval = FETCH_OPND(n); \ + VALUE_TO_OBJECT(cx, lval, obj); \ + \ + /* Get or set the property, set ok false if error, true if success. */\ + SAVE_SP(fp); \ + call; \ + if (!ok) \ + goto out; \ + JS_END_MACRO + +#define ELEMENT_OP(n, call) \ + JS_BEGIN_MACRO \ + FETCH_ELEMENT_ID(n, id); \ + PROPERTY_OP(n-1, call); \ + JS_END_MACRO + +/* + * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls + * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just + * in case a getter or setter function is invoked. + */ +#define CACHED_GET(call) \ + JS_BEGIN_MACRO \ + if (!OBJ_IS_NATIVE(obj)) { \ + ok = call; \ + } else { \ + JS_LOCK_OBJ(cx, obj); \ + PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ + if (sprop) { \ + JSScope *scope_ = OBJ_SCOPE(obj); \ + slot = (uintN)sprop->slot; \ + rval = (slot != SPROP_INVALID_SLOT) \ + ? LOCKED_OBJ_GET_SLOT(obj, slot) \ + : JSVAL_VOID; \ + JS_UNLOCK_SCOPE(cx, scope_); \ + ok = SPROP_GET(cx, sprop, obj, obj, &rval); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ + LOCKED_OBJ_SET_SLOT(obj, slot, rval); \ + JS_UNLOCK_SCOPE(cx, scope_); \ + } else { \ + JS_UNLOCK_OBJ(cx, obj); \ + ok = call; \ + /* No fill here: js_GetProperty fills the cache. */ \ + } \ + } \ + JS_END_MACRO + +#define CACHED_SET(call) \ + JS_BEGIN_MACRO \ + if (!OBJ_IS_NATIVE(obj)) { \ + ok = call; \ + } else { \ + JSScope *scope_; \ + JS_LOCK_OBJ(cx, obj); \ + PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ + if (sprop && \ + !(sprop->attrs & JSPROP_READONLY) && \ + (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ + JS_UNLOCK_SCOPE(cx, scope_); \ + ok = SPROP_SET(cx, sprop, obj, obj, &rval); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) { \ + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval); \ + GC_POKE(cx, JSVAL_NULL); /* XXX second arg ignored */ \ + } \ + JS_UNLOCK_SCOPE(cx, scope_); \ + } else { \ + JS_UNLOCK_OBJ(cx, obj); \ + ok = call; \ + /* No fill here: js_SetProperty writes through the cache. */ \ + } \ + } \ + JS_END_MACRO + + case JSOP_SETCONST: + obj = fp->varobj; + atom = GET_ATOM(cx, script, pc); + rval = FETCH_OPND(-1); + ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, rval, NULL, NULL, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY, + NULL); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_BINDNAME: + atom = GET_ATOM(cx, script, pc); + SAVE_SP(fp); + obj = js_FindIdentifierBase(cx, (jsid)atom); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_SETNAME: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); + obj = JSVAL_TO_OBJECT(lval); + SAVE_SP(fp); + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, rval); + break; + +#define INTEGER_OP(OP, EXTRA_CODE) \ + JS_BEGIN_MACRO \ + FETCH_INT(cx, -1, j); \ + FETCH_INT(cx, -2, i); \ + if (!ok) \ + goto out; \ + EXTRA_CODE \ + d = i OP j; \ + sp--; \ + STORE_NUMBER(cx, -1, d); \ + JS_END_MACRO + +#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) +#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) + + case JSOP_BITOR: + BITWISE_OP(|); + break; + + case JSOP_BITXOR: + BITWISE_OP(^); + break; + + case JSOP_BITAND: + BITWISE_OP(&); + break; + +#if defined(XP_WIN) +#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN) \ + ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ + ? (IFNAN) \ + : (LVAL) OP (RVAL)) +#else +#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) +#endif + +#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) { \ + ltmp = lval ^ JSVAL_VOID; \ + rtmp = rval ^ JSVAL_VOID; \ + if (ltmp && rtmp) { \ + cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ + } else { \ + d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ + d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ + cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE); \ + } \ + } else { \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ + VALUE_TO_PRIMITIVE(cx, rval, 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, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ + cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE); \ + } \ + } \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + +#define EQUALITY_OP(OP, IFNAN) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + ltmp = JSVAL_TAG(lval); \ + rtmp = JSVAL_TAG(rval); \ + if (ltmp == rtmp) { \ + if (ltmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else if (ltmp == JSVAL_DOUBLE) { \ + d = *JSVAL_TO_DOUBLE(lval); \ + d2 = *JSVAL_TO_DOUBLE(rval); \ + cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + } else { \ + /* 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) { \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval); \ + ltmp = JSVAL_TAG(lval); \ + } else if (rtmp == JSVAL_OBJECT) { \ + VALUE_TO_PRIMITIVE(cx, rval, 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_CompareStrings(str, str2) OP 0; \ + } else { \ + VALUE_TO_NUMBER(cx, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ + cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + } \ + } \ + } \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + + case JSOP_EQ: + EQUALITY_OP(==, JS_FALSE); + break; + + case JSOP_NE: + EQUALITY_OP(!=, JS_TRUE); + break; + +#if !JS_BUG_FALLIBLE_EQOPS +#define NEW_EQUALITY_OP(OP, IFNAN) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + ltmp = JSVAL_TAG(lval); \ + rtmp = JSVAL_TAG(rval); \ + if (ltmp == rtmp) { \ + if (ltmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else if (ltmp == JSVAL_DOUBLE) { \ + d = *JSVAL_TO_DOUBLE(lval); \ + d2 = *JSVAL_TO_DOUBLE(rval); \ + cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + } else { \ + cond = lval OP rval; \ + } \ + } else { \ + if (ltmp == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { \ + d = *JSVAL_TO_DOUBLE(lval); \ + d2 = JSVAL_TO_INT(rval); \ + cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + } else if (JSVAL_IS_INT(lval) && rtmp == JSVAL_DOUBLE) { \ + d = JSVAL_TO_INT(lval); \ + d2 = *JSVAL_TO_DOUBLE(rval); \ + cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + } else { \ + cond = lval OP rval; \ + } \ + } \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + + case JSOP_NEW_EQ: + NEW_EQUALITY_OP(==, JS_FALSE); + break; + + case JSOP_NEW_NE: + NEW_EQUALITY_OP(!=, JS_TRUE); + break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_CASE: + NEW_EQUALITY_OP(==, JS_FALSE); + (void) POP(); + if (cond) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } else { + PUSH(lval); + } + break; + + case JSOP_CASEX: + NEW_EQUALITY_OP(==, JS_FALSE); + (void) POP(); + if (cond) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } else { + PUSH(lval); + } + break; +#endif + +#endif /* !JS_BUG_FALLIBLE_EQOPS */ + + case JSOP_LT: + RELATIONAL_OP(<); + break; + + case JSOP_LE: + RELATIONAL_OP(<=); + break; + + case JSOP_GT: + RELATIONAL_OP(>); + break; + + case JSOP_GE: + RELATIONAL_OP(>=); + break; + +#undef EQUALITY_OP +#undef RELATIONAL_OP + + case JSOP_LSH: + SIGNED_SHIFT_OP(<<); + break; + + case JSOP_RSH: + SIGNED_SHIFT_OP(>>); + break; + + case JSOP_URSH: + { + uint32 u; + + FETCH_INT(cx, -1, j); + FETCH_UINT(cx, -2, u); + j &= 31; + d = u >> j; + sp--; + STORE_NUMBER(cx, -1, d); + break; + } + +#undef INTEGER_OP +#undef BITWISE_OP +#undef SIGNED_SHIFT_OP + + case JSOP_ADD: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, <mp); + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp); + if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) { + if (cond) { + str = JSVAL_TO_STRING(ltmp); + SAVE_SP(fp); + ok = (str2 = js_ValueToString(cx, rtmp)) != NULL; + } else { + str2 = JSVAL_TO_STRING(rtmp); + SAVE_SP(fp); + ok = (str = js_ValueToString(cx, ltmp)) != NULL; + } + if (!ok) + goto out; + str = js_ConcatStrings(cx, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + } else { + VALUE_TO_NUMBER(cx, lval, d); + VALUE_TO_NUMBER(cx, rval, d2); + d += d2; + sp--; + STORE_NUMBER(cx, -1, d); + } + break; + +#define BINARY_OP(OP) \ + JS_BEGIN_MACRO \ + FETCH_NUMBER(cx, -1, d2); \ + FETCH_NUMBER(cx, -2, d); \ + d = d OP d2; \ + sp--; \ + STORE_NUMBER(cx, -1, d); \ + JS_END_MACRO + + case JSOP_SUB: + BINARY_OP(-); + break; + + case JSOP_MUL: + BINARY_OP(*); + break; + + case JSOP_DIV: + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + sp--; + if (d2 == 0) { +#if defined(XP_WIN) + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + rval = DOUBLE_TO_JSVAL(rt->jsNaN); + else +#endif + if (d == 0 || JSDOUBLE_IS_NaN(d)) + rval = DOUBLE_TO_JSVAL(rt->jsNaN); + else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) + rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); + else + rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); + STORE_OPND(-1, rval); + } else { + d /= d2; + STORE_NUMBER(cx, -1, d); + } + break; + + case JSOP_MOD: + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + sp--; + if (d2 == 0) { + STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); + } else { +#if defined(XP_WIN) + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) +#endif + d = fmod(d, d2); + STORE_NUMBER(cx, -1, d); + } + break; + + case JSOP_NOT: + POP_BOOLEAN(cx, rval, cond); + PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); + break; + + case JSOP_BITNOT: + FETCH_INT(cx, -1, i); + d = (jsdouble) ~i; + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_NEG: + FETCH_NUMBER(cx, -1, d); +#ifdef HPUX + /* + * Negation of a zero doesn't produce a negative + * zero on HPUX. Perform the operation by bit + * twiddling. + */ + JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; +#else + d = -d; +#endif + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_POS: + FETCH_NUMBER(cx, -1, d); + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_NEW: + /* Get immediate argc and find the constructor function. */ + argc = GET_ARGC(pc); + +#if JS_HAS_INITIALIZERS + do_new: +#endif + vp = sp - (2 + argc); + JS_ASSERT(vp >= fp->spbase); + + 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) + { + SAVE_SP(fp); + fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); + if (!fun) { + ok = JS_FALSE; + goto out; + } + } + + clasp = &js_ObjectClass; + if (!obj2) { + proto = parent = NULL; + fun = NULL; + } else { + /* Get the constructor prototype object for this function. */ + ok = OBJ_GET_PROPERTY(cx, obj2, + (jsid)rt->atomState.classPrototypeAtom, + &rval); + if (!ok) + goto out; + proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; + parent = OBJ_GET_PARENT(cx, obj2); + + if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { + funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; + if (funclasp) + clasp = funclasp; + } + } + obj = js_NewObject(cx, clasp, proto, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + /* Now we have an object with a constructor method; call it. */ + vp[1] = OBJECT_TO_JSVAL(obj); + SAVE_SP(fp); + ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT); + RESTORE_SP(fp); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + + /* Check the return value and update obj from it. */ + rval = *vp; + if (JSVAL_IS_PRIMITIVE(rval)) { + if (fun || !JSVERSION_IS_ECMA(cx->version)) { + *vp = OBJECT_TO_JSVAL(obj); + break; + } + /* native [[Construct]] returning primitive is error */ + str = js_ValueToString(cx, rval); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + JS_RUNTIME_METER(rt, constructs); + break; + + case JSOP_DELNAME: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + + /* ECMA says to return true if name is undefined or inherited. */ + rval = JSVAL_TRUE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + } + PUSH_OPND(rval); + break; + + case JSOP_DELPROP: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); + STORE_OPND(-1, rval); + break; + + case JSOP_DELELEM: + ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_TYPEOF: + rval = POP_OPND(); + type = JS_TypeOfValue(cx, rval); + atom = rt->atomState.typeAtoms[type]; + str = ATOM_TO_STRING(atom); + PUSH_OPND(STRING_TO_JSVAL(str)); + break; + + case JSOP_VOID: + (void) POP_OPND(); + PUSH_OPND(JSVAL_VOID); + break; + + case JSOP_INCNAME: + case JSOP_DECNAME: + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (!prop) + goto atom_not_defined; + + OBJ_DROP_PROPERTY(cx, obj2, prop); + lval = OBJECT_TO_JSVAL(obj); + goto do_incop; + + case JSOP_INCPROP: + case JSOP_DECPROP: + case JSOP_PROPINC: + case JSOP_PROPDEC: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + lval = POP_OPND(); + goto do_incop; + + case JSOP_INCELEM: + case JSOP_DECELEM: + case JSOP_ELEMINC: + case JSOP_ELEMDEC: + POP_ELEMENT_ID(id); + lval = POP_OPND(); + + do_incop: + VALUE_TO_OBJECT(cx, lval, obj); + + /* The operand must contain a number. */ + SAVE_SP(fp); + CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + + /* The expression result goes in rtmp, the updated value in rval. */ + if (JSVAL_IS_INT(rval) && + rval != INT_TO_JSVAL(JSVAL_INT_MIN) && + rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { + if (cs->format & JOF_POST) { + rtmp = rval; + (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); + } else { + (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); + rtmp = rval; + } + } else { + +/* + * Initially, rval contains the value to increment or decrement, which is not + * yet converted. As above, the expression result goes in rtmp, the updated + * value goes in rval. + */ +#define NONINT_INCREMENT_OP() \ + JS_BEGIN_MACRO \ + VALUE_TO_NUMBER(cx, rval, d); \ + if (cs->format & JOF_POST) { \ + rtmp = rval; \ + if (!JSVAL_IS_NUMBER(rtmp)) { \ + ok = js_NewNumberValue(cx, d, &rtmp); \ + if (!ok) \ + goto out; \ + } \ + (cs->format & JOF_INC) ? d++ : d--; \ + ok = js_NewNumberValue(cx, d, &rval); \ + } else { \ + (cs->format & JOF_INC) ? ++d : --d; \ + ok = js_NewNumberValue(cx, d, &rval); \ + rtmp = rval; \ + } \ + if (!ok) \ + goto out; \ + JS_END_MACRO + + NONINT_INCREMENT_OP(); + } + + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + PUSH_OPND(rtmp); + break; + +/* + * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because + * it must break from the switch case that calls it, not from the do...while(0) + * loop created by the JS_BEGIN/END_MACRO brackets. + */ +#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \ + slot = (uintN)SLOT; \ + JS_ASSERT(slot < fp->fun->COUNT); \ + vp = fp->BASE + slot; \ + rval = *vp; \ + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + *vp = rval; \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_incop; + + case JSOP_INCARG: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); + case JSOP_DECARG: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); + case JSOP_ARGINC: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); + case JSOP_ARGDEC: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); + + case JSOP_INCVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX); + case JSOP_DECVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN); + case JSOP_VARINC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX); + case JSOP_VARDEC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN); + +#undef FAST_INCREMENT_OP + + do_nonint_fast_incop: + NONINT_INCREMENT_OP(); + *vp = rval; + PUSH_OPND(rtmp); + break; + + case JSOP_GETPROP: + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); + STORE_OPND(-1, rval); + break; + + case JSOP_SETPROP: + /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ + rval = FETCH_OPND(-1); + + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_GETELEM: + ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_SETELEM: + rval = FETCH_OPND(-1); + ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); + sp -= 2; + STORE_OPND(-1, rval); + break; + + case JSOP_ENUMELEM: + /* Funky: the value to set is under the [obj, id] pair. */ + FETCH_ELEMENT_ID(-1, id); + lval = FETCH_OPND(-2); + VALUE_TO_OBJECT(cx, lval, obj); + rval = FETCH_OPND(-3); + SAVE_SP(fp); + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + sp -= 3; + break; + +/* + * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the + * arguments object until it is truly needed. JSOP_ARGSUB optimizes away + * arguments objects when the only uses of the 'arguments' parameter are to + * fetch individual actual parameters. But if such a use were then invoked, + * e.g., arguments[i](), the 'this' parameter would and must bind to the + * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. + */ +#define LAZY_ARGS_THISP ((JSObject *) 1) + + case JSOP_PUSHOBJ: + if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_CALL: + case JSOP_EVAL: + argc = GET_ARGC(pc); + vp = sp - (argc + 2); + lval = *vp; + SAVE_SP(fp); + + if (JSVAL_IS_FUNCTION(cx, lval) && + (obj = JSVAL_TO_OBJECT(lval), + fun = (JSFunction *) JS_GetPrivate(cx, obj), + !fun->native && + !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) && + argc >= (uintN)(fun->nargs + fun->extra))) + /* inline_call: */ + { + uintN nframeslots, nvars; + void *newmark; + JSInlineFrame *newifp; + JSInterpreterHook hook; + + /* Restrict recursion of lightweight functions. */ + if (inlineCallCount == MAX_INLINE_CALL_COUNT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_OVER_RECURSED); + ok = JS_FALSE; + goto out; + } + +#if JS_HAS_JIT + /* ZZZbe should do this only if interpreted often enough. */ + ok = jsjit_Compile(cx, fun); + if (!ok) + goto out; +#endif + + /* Compute the number of stack slots needed for fun. */ + nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1) + / sizeof(jsval); + nvars = fun->nvars; + script = fun->script; + depth = (jsint) script->depth; + + /* Allocate the frame and space for vars and operands. */ + newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth, + &newmark); + if (!newsp) { + ok = JS_FALSE; + goto bad_inline_call; + } + newifp = (JSInlineFrame *) newsp; + newsp += nframeslots; + + /* Initialize the stack frame. */ + memset(newifp, 0, sizeof(JSInlineFrame)); + newifp->frame.script = script; + newifp->frame.fun = fun; + newifp->frame.argc = argc; + newifp->frame.argv = vp + 2; + newifp->frame.rval = JSVAL_VOID; + newifp->frame.nvars = nvars; + newifp->frame.vars = newsp; + newifp->frame.down = fp; + newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj); + newifp->mark = newmark; + + /* Compute the 'this' parameter now that argv is set. */ + ok = ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); + if (!ok) { + js_FreeRawStack(cx, newmark); + goto bad_inline_call; + } + + /* Push void to initialize local variables. */ + sp = newsp; + while (nvars--) + PUSH(JSVAL_VOID); + sp += depth; + newifp->frame.spbase = sp; + SAVE_SP(&newifp->frame); + + /* Call the debugger hook if present. */ + hook = cx->runtime->callHook; + if (hook) { + newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, + cx->runtime->callHookData); + LOAD_INTERRUPT_HANDLER(rt); + } + + /* Switch to new version if currentVersion wasn't overridden. */ + newifp->callerVersion = cx->version; + if (cx->version == currentVersion) { + currentVersion = script->version; + if (currentVersion != cx->version) + JS_SetVersion(cx, currentVersion); + } + + /* Push the frame and set interpreter registers. */ + cx->fp = fp = &newifp->frame; + pc = script->code; + endpc = pc + script->length; + inlineCallCount++; + JS_RUNTIME_METER(rt, inlineCalls); + continue; + + bad_inline_call: + script = fp->script; + depth = (jsint) script->depth; + goto out; + } + + ok = js_Invoke(cx, argc, 0); + RESTORE_SP(fp); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) + goto out; + JS_RUNTIME_METER(rt, nonInlineCalls); +#if JS_HAS_LVALUE_RETURN + if (cx->rval2set) { + /* + * Sneaky: use the stack depth we didn't claim in our budget, + * but that we know is there on account of [fun, this] already + * having been pushed, at a minimum (if no args). Those two + * slots have been popped and [rval] has been pushed, which + * leaves one more slot for rval2 before we might overflow. + * + * NB: rval2 must be the property identifier, and rval the + * object from which to get the property. The pair form an + * ECMA "reference type", which can be used on the right- or + * left-hand side of assignment ops. Only native methods can + * return reference types. See JSOP_SETCALL just below for + * the left-hand-side case. + */ + PUSH_OPND(cx->rval2); + cx->rval2set = JS_FALSE; + ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); + sp--; + STORE_OPND(-1, rval); + } +#endif + obj = NULL; + break; + +#if JS_HAS_LVALUE_RETURN + case JSOP_SETCALL: + argc = GET_ARGC(pc); + SAVE_SP(fp); + ok = js_Invoke(cx, argc, 0); + RESTORE_SP(fp); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) + goto out; + if (!cx->rval2set) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_LEFTSIDE_OF_ASS); + ok = JS_FALSE; + goto out; + } + PUSH_OPND(cx->rval2); + cx->rval2set = JS_FALSE; + obj = NULL; + break; +#endif + + case JSOP_NAME: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (!prop) { + /* Kludge to allow (typeof foo == "undefined") tests. */ + for (pc2 = pc + len; pc2 < endpc; pc2++) { + op2 = (JSOp)*pc2; + if (op2 == JSOP_TYPEOF) { + PUSH_OPND(JSVAL_VOID); + goto advance_pc; + } + if (op2 != JSOP_GROUP) + break; + } + 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)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + } + + /* Get and push the obj[id] property's value. */ + sprop = (JSScopeProperty *)prop; + slot = (uintN)sprop->slot; + rval = (slot != SPROP_INVALID_SLOT) + ? LOCKED_OBJ_GET_SLOT(obj2, slot) + : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj2); + ok = SPROP_GET(cx, sprop, obj, obj2, &rval); + JS_LOCK_OBJ(cx, obj2); + if (!ok) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + goto out; + } + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) + LOCKED_OBJ_SET_SLOT(obj2, slot, rval); + OBJ_DROP_PROPERTY(cx, obj2, prop); + PUSH_OPND(rval); + break; + + case JSOP_UINT16: + i = (jsint) GET_ATOM_INDEX(pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); + obj = NULL; + break; + + case JSOP_NUMBER: + case JSOP_STRING: + atom = GET_ATOM(cx, script, pc); + PUSH_OPND(ATOM_KEY(atom)); + obj = NULL; + break; + + case JSOP_OBJECT: + { +#if 0 + jsatomid atomIndex; + JSAtomMap *atomMap; + + /* + * Get a suitable object from an atom mapped by the bytecode at pc. + * + * 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, in order to rewrap the JSRegExp + * struct with a new object having the right prototype and parent. + * + * Unlike JSOP_DEFFUN and other prolog bytecodes, 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 do all fp->objAtomMap + * initialization lazily, here under JSOP_OBJECT. + * + * XXX This code is specific to regular expression objects. If we + * need JSOP_OBJECT for other kinds of object literals, we should + * push cloning down under JSObjectOps. Also, fp->objAtomMap is + * used only for object atoms, so it's sparse (wasting some stack + * space) and as its name implies, you can't get non-object atoms + * from it. + */ + atomIndex = GET_ATOM_INDEX(pc); + atomMap = fp->objAtomMap; + atom = atomMap ? atomMap->vector[atomIndex] : NULL; + if (!atom) { + /* Let atom and obj denote the regexp (object) mapped by pc. */ + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + JS_ASSERT(ATOM_IS_OBJECT(atom)); + obj = ATOM_TO_OBJECT(atom); + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); + + /* 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 elsewhere (see jsobj.c's FindConstructor, + * js_ConstructObject, and js_NewObject). It's fundamental. + */ + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneRegExpObject(cx, obj, obj2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) { + ok = JS_FALSE; + goto out; + } + } + + /* + * If fp->objAtomMap is null, initialize it now so we can map + * atom (whether atom is newly created for a cloned object, or + * the original atom mapped by script) for faster performance + * next time through JSOP_OBJECT. + */ + if (!atomMap) { + jsatomid mapLength = script->atomMap.length; + size_t vectorBytes = mapLength * sizeof(JSAtom *); + + /* Allocate an override atom map from cx->stackPool. */ + JS_ARENA_ALLOCATE_CAST(atomMap, JSAtomMap *, + &cx->stackPool, + sizeof(JSAtomMap) + vectorBytes); + if (!atomMap) { + JS_ReportOutOfMemory(cx); + ok = JS_FALSE; + goto out; + } + + atomMap->length = mapLength; + atomMap->vector = (JSAtom **)(atomMap + 1); + memset(atomMap->vector, 0, vectorBytes); + fp->objAtomMap = atomMap; + } + atomMap->vector[atomIndex] = atom; + } +#else + atom = GET_ATOM(cx, script, pc); + JS_ASSERT(ATOM_IS_OBJECT(atom)); +#endif + rval = ATOM_KEY(atom); + PUSH_OPND(rval); + obj = NULL; + break; + } + + case JSOP_ZERO: + PUSH_OPND(JSVAL_ZERO); + obj = NULL; + break; + + case JSOP_ONE: + PUSH_OPND(JSVAL_ONE); + obj = NULL; + break; + + case JSOP_NULL: + PUSH_OPND(JSVAL_NULL); + obj = NULL; + break; + + case JSOP_THIS: + PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp)); + obj = NULL; + break; + + case JSOP_FALSE: + PUSH_OPND(JSVAL_FALSE); + obj = NULL; + break; + + case JSOP_TRUE: + PUSH_OPND(JSVAL_TRUE); + obj = NULL; + break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_TABLESWITCH: + pc2 = 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.) + */ + if (cx->version == JSVERSION_DEFAULT || + cx->version >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } + + 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; + } + break; + + case JSOP_LOOKUPSWITCH: + lval = POP_OPND(); + pc2 = pc; + len = GET_JUMP_OFFSET(pc2); + + if (!JSVAL_IS_NUMBER(lval) && + !JSVAL_IS_STRING(lval) && + !JSVAL_IS_BOOLEAN(lval)) { + goto advance_pc; + } + + pc2 += JUMP_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + +#define SEARCH_PAIRS(MATCH_CODE) \ + while (npairs) { \ + atom = GET_ATOM(cx, script, pc2); \ + rval = ATOM_KEY(atom); \ + MATCH_CODE \ + if (match) { \ + pc2 += ATOM_INDEX_LEN; \ + len = GET_JUMP_OFFSET(pc2); \ + goto advance_pc; \ + } \ + pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ + npairs--; \ + } + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + SEARCH_PAIRS( + match = (JSVAL_IS_STRING(rval) && + ((str2 = JSVAL_TO_STRING(rval)) == str || + !js_CompareStrings(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 + break; + + case JSOP_TABLESWITCHX: + pc2 = 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.) + */ + if (cx->version == JSVERSION_DEFAULT || + cx->version >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } + + 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; + } + break; + + case JSOP_LOOKUPSWITCHX: + lval = POP_OPND(); + pc2 = pc; + len = GET_JUMPX_OFFSET(pc2); + + if (!JSVAL_IS_NUMBER(lval) && + !JSVAL_IS_STRING(lval) && + !JSVAL_IS_BOOLEAN(lval)) { + goto advance_pc; + } + + pc2 += JUMPX_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + +#define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ + while (npairs) { \ + atom = GET_ATOM(cx, script, pc2); \ + rval = ATOM_KEY(atom); \ + MATCH_CODE \ + if (match) { \ + pc2 += ATOM_INDEX_LEN; \ + len = GET_JUMPX_OFFSET(pc2); \ + goto advance_pc; \ + } \ + pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ + npairs--; \ + } + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + SEARCH_EXTENDED_PAIRS( + match = (JSVAL_IS_STRING(rval) && + ((str2 = JSVAL_TO_STRING(rval)) == str || + !js_CompareStrings(str2, str))); + ) + } else if (JSVAL_IS_DOUBLE(lval)) { + d = *JSVAL_TO_DOUBLE(lval); + SEARCH_EXTENDED_PAIRS( + match = (JSVAL_IS_DOUBLE(rval) && + *JSVAL_TO_DOUBLE(rval) == d); + ) + } else { + SEARCH_EXTENDED_PAIRS( + match = (lval == rval); + ) + } +#undef SEARCH_EXTENDED_PAIRS + break; + + case JSOP_CONDSWITCH: + break; + +#endif /* JS_HAS_SWITCH_STATEMENT */ + +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTALL: + obj = fp->varobj; + ida = JS_Enumerate(cx, obj); + if (!ida) { + ok = JS_FALSE; + } else { + for (i = 0, j = ida->length; i < j; i++) { + id = ida->vector[i]; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + break; + if (!prop) + continue; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + break; + } + JS_DestroyIdArray(cx, ida); + } + break; + + case JSOP_EXPORTNAME: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + obj = fp->varobj; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto out; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + if (!ok) + goto out; + break; + + case JSOP_IMPORTALL: + id = (jsid)JSVAL_VOID; + PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); + sp--; + break; + + case JSOP_IMPORTPROP: + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); + sp--; + break; + + case JSOP_IMPORTELEM: + ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); + sp -= 2; + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + case JSOP_TRAP: + switch (JS_HandleTrap(cx, script, pc, &rval)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + JS_ASSERT(JSVAL_IS_INT(rval)); + op = (JSOp) JSVAL_TO_INT(rval); + JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); + LOAD_INTERRUPT_HANDLER(rt); + goto do_op; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + break; + + case JSOP_ARGUMENTS: + SAVE_SP(fp); + ok = js_GetArgsValue(cx, fp, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_ARGSUB: + id = (jsid) INT_TO_JSVAL(GET_ARGNO(pc)); + SAVE_SP(fp); + ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); + if (!ok) + goto out; + if (!obj) { + /* + * If arguments was not overridden by eval('arguments = ...'), + * set obj to the magic cookie respected by JSOP_PUSHOBJ, just + * in case this bytecode is part of an 'arguments[i](j, k)' or + * similar such invocation sequence, where the function that + * is invoked expects its 'this' parameter to be the caller's + * arguments object. + */ + obj = LAZY_ARGS_THISP; + } + PUSH_OPND(rval); + break; + +#undef LAZY_ARGS_THISP + + case JSOP_ARGCNT: + id = (jsid) rt->atomState.lengthAtom; + SAVE_SP(fp); + ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_GETARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + PUSH_OPND(fp->argv[slot]); + obj = NULL; + break; + + case JSOP_SETARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + vp = &fp->argv[slot]; + GC_POKE(cx, *vp); + *vp = FETCH_OPND(-1); + obj = NULL; + break; + + case JSOP_GETVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + PUSH_OPND(fp->vars[slot]); + obj = NULL; + break; + + case JSOP_SETVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + vp = &fp->vars[slot]; + GC_POKE(cx, *vp); + *vp = FETCH_OPND(-1); + obj = NULL; + break; + + case JSOP_DEFCONST: + case JSOP_DEFVAR: + { + JSBool defined; + + atom = GET_ATOM(cx, script, pc); + obj = fp->varobj; + 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 = (jsid)atom; + ok = js_CheckRedeclaration(cx, obj, id, attrs, &defined); + if (!ok) + goto out; + + /* Bind a variable only if it's not yet defined. */ + if (!defined) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + attrs, NULL); + if (!ok) + goto out; + } + break; + } + + case JSOP_DEFFUN: + { + uintN flags; + + atom = GET_ATOM(cx, script, pc); + obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); + id = (jsid) fun->atom; + + /* + * We must be at top-level (either outermost block that forms a + * function's body, or a global) scope, not inside an expression + * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) + * in the same compilation unit (ECMA Program). + * + * However, we could be in a Program being eval'd from inside a + * with statement, so we need to distinguish variables object from + * scope chain head. Hence the two assignments to parent below. + * First we make sure the function object we're defining has the + * right scope chain. Then we define its name in fp->varobj. + * + * If static link is not current scope, clone fun's object to link + * to the current scope via parent. This clause exists to enable + * sharing of compiled functions among multiple equivalent scopes, + * splitting the cost of compilation evenly among the scopes and + * amortizing it over a number of executions. Examples include XUL + * scripts and event handlers shared among Mozilla chrome windows, + * and server-side JS user-defined functions shared among requests. + * + * NB: The Script object exposes compile and exec in the language, + * such that this clause introduces an incompatible change from old + * JS versions that supported Script. Such a JS version supported + * executing a script that defined and called functions scoped by + * the compile-time static link, not by the exec-time scope chain. + * + * We sacrifice compatibility, breaking such scripts, in order to + * promote compile-cost sharing and amortizing, and because Script + * is not and will not be standardized. + */ + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + + /* + * ECMA requires functions defined when entering Global code to be + * permanent, and functions defined when entering Eval code to be + * impermanent. + */ + attrs = JSPROP_ENUMERATE; + if (!(fp->flags & JSFRAME_EVAL)) + attrs |= 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. + */ + flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (flags) + attrs |= flags | JSPROP_SHARED; + + /* + * 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. + */ + parent = fp->varobj; + ok = js_CheckRedeclaration(cx, parent, id, attrs, &cond); + if (!ok) + goto out; + + ok = OBJ_DEFINE_PROPERTY(cx, parent, id, + flags ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), + (flags & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (flags & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs, + NULL); + if (!ok) + goto out; + break; + } + +#if JS_HAS_LEXICAL_CLOSURE + case JSOP_DEFLOCALFUN: + /* + * Define a local function (i.e., one nested at the top level of + * another function), parented by the current scope chain, and + * 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. + */ + pc2 = pc; + slot = GET_VARNO(pc2); + pc2 += VARNO_LEN; + atom = GET_ATOM(cx, script, pc2); + obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); + + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + fp->vars[slot] = OBJECT_TO_JSVAL(obj); + break; + + case JSOP_ANONFUNOBJ: + /* Push the specified function object literal. */ + atom = GET_ATOM(cx, script, pc); + obj = ATOM_TO_OBJECT(atom); + + /* If re-parenting, push a clone of the function object. */ + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_NAMEDFUNOBJ: + /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ + atom = GET_ATOM(cx, script, pc); + rval = ATOM_KEY(atom); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); + + /* + * 1. Create a new object as if by the expression new Object(). + * 2. Add Result(1) to the front of the scope chain. + * + * Step 2 is achieved by making the new object's parent be the + * current scope chain, and then making the new object the parent + * of the Function object clone. + */ + SAVE_SP(fp); + parent = js_ConstructObject(cx, &js_ObjectClass, NULL, + fp->scopeChain, 0, NULL); + if (!parent) { + ok = JS_FALSE; + goto out; + } + + /* + * 3. Create a new Function object as specified in section 13.2 + * with [parameters and body specified by the function expression + * that was parsed by the compiler into a Function object, and + * saved in the script's atom map]. + */ + obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + /* + * 4. Create a property in the object Result(1). The property's + * name is [fun->atom, the identifier parsed by the compiler], + * value is Result(3), and attributes are { DontDelete, ReadOnly }. + */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (attrs) + attrs |= JSPROP_SHARED; + ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, + attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs | + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY, + NULL); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + + /* + * 5. Remove Result(1) from the front of the scope chain [no-op]. + * 6. Return Result(3). + */ + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_CLOSURE: + /* + * ECMA ed. 3 extension: a named function expression in a compound + * statement (not at the top statement level of global code, or at + * the top level of a function body). + * + * Get immediate operand atom, which is a function object literal. + * From it, get the function to close. + */ + atom = GET_ATOM(cx, script, pc); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom))); + obj = ATOM_TO_OBJECT(atom); + + /* + * Clone the function object with the current scope chain as the + * clone's parent. The original function object is the prototype + * of the clone. Do this only if re-parenting; the compiler may + * have seen the right parent already and created a sufficiently + * well-scoped function object. + */ + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + + /* + * Make a property in fp->varobj with id fun->atom and value obj, + * unless fun is a getter or setter (in which case, obj is cast to + * a JSPropertyOp and passed accordingly). + */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (attrs) + attrs |= JSPROP_SHARED; + ok = OBJ_DEFINE_PROPERTY(cx, fp->varobj, (jsid)fun->atom, + attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs | JSPROP_ENUMERATE, + NULL); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + break; +#endif /* JS_HAS_LEXICAL_CLOSURE */ + +#if JS_HAS_GETTER_SETTER + case JSOP_GETTER: + case JSOP_SETTER: + JS_ASSERT(len == 1); + op2 = (JSOp) *++pc; + cs = &js_CodeSpec[op2]; + len = cs->length; + switch (op2) { + case JSOP_SETNAME: + case JSOP_SETPROP: + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + i = -1; + rval = FETCH_OPND(i); + goto gs_pop_lval; + + case JSOP_SETELEM: + rval = FETCH_OPND(-1); + i = -2; + FETCH_ELEMENT_ID(i, id); + gs_pop_lval: + lval = FETCH_OPND(i-1); + VALUE_TO_OBJECT(cx, lval, obj); + break; + +#if JS_HAS_INITIALIZERS + case JSOP_INITPROP: + JS_ASSERT(sp - fp->spbase >= 2); + i = -1; + rval = FETCH_OPND(i); + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + goto gs_get_lval; + + case JSOP_INITELEM: + JS_ASSERT(sp - fp->spbase >= 3); + rval = FETCH_OPND(-1); + i = -2; + FETCH_ELEMENT_ID(i, id); + gs_get_lval: + lval = FETCH_OPND(i-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + break; +#endif /* JS_HAS_INITIALIZERS */ + + default: + JS_ASSERT(0); + } + + 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); + ok = JS_FALSE; + goto out; + } + + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); + if (!ok) + goto out; + + if (op == JSOP_GETTER) { + getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); + setter = NULL; + attrs = JSPROP_GETTER; + } else { + getter = NULL; + setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); + attrs = JSPROP_SETTER; + } + attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; + + /* Check for a readonly or permanent property of the same name. */ + ok = js_CheckRedeclaration(cx, obj, id, attrs, &cond); + if (!ok) + goto out; + + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, + attrs, NULL); + if (!ok) + goto out; + + sp += i; + if (cs->ndefs) + STORE_OPND(-1, rval); + break; +#endif /* JS_HAS_GETTER_SETTER */ + +#if JS_HAS_INITIALIZERS + case JSOP_NEWINIT: + argc = 0; + fp->sharpDepth++; + goto do_new; + + case JSOP_ENDINIT: + if (--fp->sharpDepth == 0) + fp->sharpArray = NULL; + + /* Re-set the newborn root to the top of this object tree. */ + JS_ASSERT(sp - fp->spbase >= 1); + lval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); + break; + + case JSOP_INITPROP: + /* Pop the property's value into rval. */ + JS_ASSERT(sp - fp->spbase >= 2); + rval = FETCH_OPND(-1); + + /* Get the immediate property name into id. */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + i = -1; + goto do_init; + + case JSOP_INITELEM: + /* Pop the element's value into rval. */ + JS_ASSERT(sp - fp->spbase >= 3); + rval = FETCH_OPND(-1); + + /* Pop and conditionally atomize the element id. */ + FETCH_ELEMENT_ID(-2, id); + i = -2; + + do_init: + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(i-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + + /* Set the property named by obj[id] to rval. */ + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + sp += i; + break; + +#if JS_HAS_SHARP_VARS + case JSOP_DEFSHARP: + obj = fp->sharpArray; + if (!obj) { + obj = js_NewArrayObject(cx, 0, NULL); + if (!obj) { + ok = JS_FALSE; + goto out; + } + fp->sharpArray = obj; + } + i = (jsint) GET_ATOM_INDEX(pc); + id = (jsid) INT_TO_JSVAL(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); + ok = JS_FALSE; + goto out; + } + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + break; + + case JSOP_USESHARP: + i = (jsint) GET_ATOM_INDEX(pc); + id = (jsid) INT_TO_JSVAL(i); + obj = fp->sharpArray; + if (!obj) { + rval = JSVAL_VOID; + } else { + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + } + 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); + ok = JS_FALSE; + goto out; + } + PUSH_OPND(rval); + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + +#if JS_HAS_EXCEPTIONS + /* No-ops for ease of decompilation and jit'ing. */ + case JSOP_TRY: + case JSOP_FINALLY: + break; + + /* Reset the stack to the given depth. */ + case JSOP_SETSP: + i = (jsint) GET_ATOM_INDEX(pc); + JS_ASSERT(i >= 0); + sp = fp->spbase + i; + break; + + case JSOP_GOSUB: + i = PTRDIFF(pc, script->main, jsbytecode) + len; + len = GET_JUMP_OFFSET(pc); + PUSH(INT_TO_JSVAL(i)); + break; + + case JSOP_GOSUBX: + i = PTRDIFF(pc, script->main, jsbytecode) + len; + len = GET_JUMPX_OFFSET(pc); + PUSH(INT_TO_JSVAL(i)); + break; + + case JSOP_RETSUB: + rval = POP(); + JS_ASSERT(JSVAL_IS_INT(rval)); + i = JSVAL_TO_INT(rval); + pc = script->main + i; + len = 0; + break; + + case JSOP_EXCEPTION: + PUSH(cx->exception); + break; + + case JSOP_THROW: + cx->throwing = JS_TRUE; + cx->exception = POP_OPND(); + ok = JS_FALSE; + /* let the code at out try to catch the exception. */ + goto out; + + case JSOP_INITCATCHVAR: + /* Pop the property's value into rval. */ + JS_ASSERT(sp - fp->spbase >= 2); + rval = POP_OPND(); + + /* Get the immediate catch variable name into id. */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + + /* Define obj[id] to contain rval and to be permanent. */ + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, + JSPROP_PERMANENT, NULL); + if (!ok) + goto out; + break; +#endif /* JS_HAS_EXCEPTIONS */ + +#if JS_HAS_INSTANCEOF + case JSOP_INSTANCEOF: + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + SAVE_SP(fp); + str = js_DecompileValueGenerator(cx, -1, rval, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INSTANCEOF_RHS, + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + lval = FETCH_OPND(-2); + cond = JS_FALSE; + if (obj->map->ops->hasInstance) { + SAVE_SP(fp); + ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); + if (!ok) + goto out; + } + sp--; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); + break; +#endif /* JS_HAS_INSTANCEOF */ + +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + { + JSTrapHandler handler = rt->debuggerHandler; + if (handler) { + SAVE_SP(fp); + switch (handler(cx, script, pc, &rval, + rt->debuggerHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + break; + } +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", op); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_BYTECODE, numBuf); + ok = JS_FALSE; + goto out; + } + } + + advance_pc: + pc += len; + +#ifdef DEBUG + if (tracefp) { + intN ndefs, n; + jsval *siter; + + ndefs = cs->ndefs; + if (ndefs) { + SAVE_SP(fp); + for (n = -ndefs; n < 0; n++) { + str = js_DecompileValueGenerator(cx, n, sp[n], NULL); + if (str) { + fprintf(tracefp, "%s %s", + (n == -ndefs) ? " output:" : ",", + JS_GetStringBytes(str)); + } + } + fprintf(tracefp, " @ %d\n", sp - fp->spbase); + } + fprintf(tracefp, " stack: "); + for (siter = fp->spbase; siter < sp; siter++) { + str = js_ValueToSource(cx, *siter); + fprintf(tracefp, "%s ", + str ? JS_GetStringBytes(str) : ""); + } + fputc('\n', tracefp); + } +#endif + } +out: + +#if JS_HAS_EXCEPTIONS + /* + * Has an exception been raised? + */ + if (!ok && cx->throwing) { + /* + * Call debugger throw hook if set (XXX thread safety?). + */ + JSTrapHandler handler = rt->throwHook; + if (handler) { + SAVE_SP(fp); + switch (handler(cx, script, pc, &rval, rt->throwHookData)) { + case JSTRAP_ERROR: + cx->throwing = JS_FALSE; + goto no_catch; + case JSTRAP_RETURN: + ok = JS_TRUE; + cx->throwing = JS_FALSE; + fp->rval = rval; + goto no_catch; + case JSTRAP_THROW: + cx->exception = rval; + case JSTRAP_CONTINUE: + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + + /* + * Look for a try block within this frame that can catch the exception. + */ + SCRIPT_FIND_CATCH_START(script, pc, pc); + if (pc) { + len = 0; + cx->throwing = JS_FALSE; /* caught */ + ok = JS_TRUE; + goto advance_pc; + } + } +no_catch: +#endif + + /* + * Check whether control fell off the end of a lightweight function, or an + * exception thrown under such a function was not caught by it. If so, go + * to the inline code under JSOP_RETURN. + */ + if (inlineCallCount) + goto inline_return; + + /* + * Reset sp before freeing stack slots, because our caller may GC soon. + * Clear spbase to indicate that we've popped the 2 * depth operand slots. + * Restore the previous frame's execution state. + */ + fp->sp = fp->spbase; + fp->spbase = NULL; + js_FreeRawStack(cx, mark); + if (cx->version == currentVersion && currentVersion != originalVersion) + JS_SetVersion(cx, originalVersion); + cx->interpLevel--; + return ok; + +atom_not_defined: + { + const char *printable = js_AtomToPrintableString(cx, atom); + if (printable) + js_ReportIsNotDefined(cx, printable); + ok = JS_FALSE; + goto out; + } +} diff --git a/src/extension/script/js/jsinterp.h b/src/extension/script/js/jsinterp.h new file mode 100644 index 000000000..3e0634e4d --- /dev/null +++ b/src/extension/script/js/jsinterp.h @@ -0,0 +1,292 @@ +/* -*- 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 jsinterp_h___ +#define jsinterp_h___ +/* + * JS interpreter interface. + */ +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +/* + * JS stack frame, allocated on the C stack. + */ +struct JSStackFrame { + JSObject *callobj; /* lazily created Call object */ + JSObject *argsobj; /* lazily created arguments object */ + JSObject *varobj; /* variables object, where vars go */ + JSScript *script; /* script being interpreted */ + JSFunction *fun; /* function being called or null */ + JSObject *thisp; /* "this" pointer if in method */ + uintN argc; /* actual argument count */ + jsval *argv; /* base of argument stack slots */ + jsval rval; /* function return value */ + uintN nvars; /* local variable count */ + jsval *vars; /* base of variable stack slots */ + JSStackFrame *down; /* previous frame */ + void *annotation; /* used by Java security */ + JSObject *scopeChain; /* scope chain */ + jsbytecode *pc; /* program counter */ + jsval *sp; /* stack pointer */ + jsval *spbase; /* operand stack base */ + uintN sharpDepth; /* array/object initializer depth */ + JSObject *sharpArray; /* scope for #n= initializer vars */ + uint32 flags; /* frame flags -- see below */ + JSStackFrame *dormantNext; /* next dormant frame chain */ + JSAtomMap *objAtomMap; /* object atom map, non-null only if we + hit a regexp object literal */ +}; + +typedef struct JSInlineFrame { + JSStackFrame frame; /* base struct */ + 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_INTERNAL 0x02 /* internal call, not invoked by a script */ +#define JSFRAME_SKIP_CALLER 0x04 /* skip one link when evaluating f.caller + for this invocation of f */ +#define JSFRAME_ASSIGNING 0x08 /* a complex (not simplex JOF_ASSIGNING) op + is currently assigning to a property */ +#define JSFRAME_DEBUGGER 0x10 /* frame for JS_EvaluateInStackFrame */ +#define JSFRAME_EVAL 0x20 /* frame for obj_eval */ +#define JSFRAME_SPECIAL 0x30 /* special evaluation frame flags */ + +#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ +#define JSFRAME_OVERRIDE_BITS 8 + +/* + * Property cache for quickened get/set property opcodes. + */ +#define PROPERTY_CACHE_LOG2 10 +#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) +#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) + +#define PROPERTY_CACHE_HASH(obj, id) \ + ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK) + +#ifdef JS_THREADSAFE + +#if HAVE_ATOMIC_DWORD_ACCESS + +#define PCE_LOAD(cache, pce, entry) JS_ATOMIC_DWORD_LOAD(pce, entry) +#define PCE_STORE(cache, pce, entry) JS_ATOMIC_DWORD_STORE(pce, entry) + +#else /* !HAVE_ATOMIC_DWORD_ACCESS */ + +#define JS_PROPERTY_CACHE_METERING 1 + +#define PCE_LOAD(cache, pce, entry) \ + JS_BEGIN_MACRO \ + uint32 prefills_; \ + uint32 fills_ = (cache)->fills; \ + do { \ + /* Load until cache->fills is stable (see FILL macro below). */ \ + prefills_ = fills_; \ + (entry) = *(pce); \ + } while ((fills_ = (cache)->fills) != prefills_); \ + JS_END_MACRO + +#define PCE_STORE(cache, pce, entry) \ + JS_BEGIN_MACRO \ + do { \ + /* Store until no racing collider stores half or all of pce. */ \ + *(pce) = (entry); \ + } while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) || \ + PCE_PROPERTY(*pce) != PCE_PROPERTY(entry)); \ + JS_END_MACRO + +#endif /* !HAVE_ATOMIC_DWORD_ACCESS */ + +#else /* !JS_THREADSAFE */ + +#define PCE_LOAD(cache, pce, entry) ((entry) = *(pce)) +#define PCE_STORE(cache, pce, entry) (*(pce) = (entry)) + +#endif /* !JS_THREADSAFE */ + +typedef union JSPropertyCacheEntry { + struct { + JSObject *object; /* weak link to object */ + JSScopeProperty *property; /* weak link to property */ + } s; +#ifdef HAVE_ATOMIC_DWORD_ACCESS + prdword align; +#endif +} JSPropertyCacheEntry; + +/* These may be called in lvalue or rvalue position. */ +#define PCE_OBJECT(entry) ((entry).s.object) +#define PCE_PROPERTY(entry) ((entry).s.property) + +typedef struct JSPropertyCache { + JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE]; + JSBool empty; + JSBool disabled; +#ifdef JS_PROPERTY_CACHE_METERING + uint32 fills; + uint32 recycles; + uint32 tests; + uint32 misses; + uint32 flushes; +# define PCMETER(x) x +#else +# define PCMETER(x) /* nothing */ +#endif +} JSPropertyCache; + +#define PROPERTY_CACHE_FILL(cache, obj, id, sprop) \ + JS_BEGIN_MACRO \ + JSPropertyCache *cache_ = (cache); \ + if (!cache_->disabled) { \ + uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ + JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ + JSPropertyCacheEntry entry_; \ + JSScopeProperty *pce_sprop_; \ + PCE_LOAD(cache_, pce_, entry_); \ + pce_sprop_ = PCE_PROPERTY(entry_); \ + PCMETER(if (pce_sprop_ && pce_sprop_ != sprop) \ + cache_->recycles++); \ + PCE_OBJECT(entry_) = obj; \ + PCE_PROPERTY(entry_) = sprop; \ + cache_->empty = JS_FALSE; \ + PCMETER(cache_->fills++); \ + PCE_STORE(cache_, pce_, entry_); \ + } \ + JS_END_MACRO + +#define PROPERTY_CACHE_TEST(cache, obj, id, sprop) \ + JS_BEGIN_MACRO \ + uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ + JSPropertyCache *cache_ = (cache); \ + JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ + JSPropertyCacheEntry entry_; \ + JSScopeProperty *pce_sprop_; \ + PCE_LOAD(cache_, pce_, entry_); \ + pce_sprop_ = PCE_PROPERTY(entry_); \ + PCMETER(cache_->tests++); \ + if (pce_sprop_ && \ + PCE_OBJECT(entry_) == obj && \ + pce_sprop_->id == id) { \ + sprop = pce_sprop_; \ + } else { \ + PCMETER(cache_->misses++); \ + sprop = NULL; \ + } \ + JS_END_MACRO + +extern void +js_FlushPropertyCache(JSContext *cx); + +extern void +js_DisablePropertyCache(JSContext *cx); + +extern void +js_EnablePropertyCache(JSContext *cx); + +extern JS_FRIEND_API(jsval *) +js_AllocStack(JSContext *cx, uintN nslots, void **markp); + +extern JS_FRIEND_API(void) +js_FreeStack(JSContext *cx, void *mark); + +extern JSBool +js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +/* + * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp + * is non-null). + */ +extern JS_FRIEND_API(JSBool) +js_Invoke(JSContext *cx, uintN argc, uintN flags); + +/* + * Consolidated js_Invoke flags simply rename the low JSFRAME_* flags. + */ +#define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING +#define JSINVOKE_INTERNAL JSFRAME_INTERNAL +#define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER + +/* + * "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 JSBool +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, + JSStackFrame *down, uintN flags, jsval *result); + +extern JSBool +js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, + JSBool *foundp); + +extern JSBool +js_Interpret(JSContext *cx, jsval *result); + +JS_END_EXTERN_C + +#endif /* jsinterp_h___ */ diff --git a/src/extension/script/js/jslibmath.h b/src/extension/script/js/jslibmath.h new file mode 100644 index 000000000..7cbcf767a --- /dev/null +++ b/src/extension/script/js/jslibmath.h @@ -0,0 +1,290 @@ +/* -*- 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 ***** */ + +/* + * By default all math calls go to fdlibm. The defines for each platform + * remap the math calls to native routines. + */ + +#ifndef _LIBMATH_H +#define _LIBMATH_H + +#include +#include "jsconfig.h" + +/* + * Define which platforms on which to use fdlibm. Not used + * by default since there can be problems with endian-ness and such. + */ + +#if defined(_WIN32) && !defined(__MWERKS__) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(SUNOS4) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(IRIX) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(SOLARIS) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(HPUX) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(linux) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(OSF1) +/* Want to use some fdlibm functions but fdlibm broken on OSF1/alpha. */ +#define JS_USE_FDLIBM_MATH 0 + +#elif defined(AIX) +#define JS_USE_FDLIBM_MATH 1 + +#else +#define JS_USE_FDLIBM_MATH 0 +#endif + +#if !JS_USE_FDLIBM_MATH + +/* + * Use system provided math routines. + */ + +#define fd_acos acos +#define fd_asin asin +#define fd_atan atan +#define fd_atan2 atan2 +#define fd_ceil ceil +#define fd_copysign copysign +#define fd_cos cos +#define fd_exp exp +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod +#define fd_log log +#define fd_pow pow +#define fd_sin sin +#define fd_sqrt sqrt +#define fd_tan tan + +#else + +/* + * Use math routines in fdlibm. + */ + +#undef __P +#ifdef __STDC__ +#define __P(p) p +#else +#define __P(p) () +#endif + +#if defined _WIN32 || defined SUNOS4 + +#define fd_acos acos +#define fd_asin asin +#define fd_atan atan +#define fd_cos cos +#define fd_sin sin +#define fd_tan tan +#define fd_exp exp +#define fd_log log +#define fd_sqrt sqrt +#define fd_ceil ceil +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod + +extern double fd_atan2 __P((double, double)); +extern double fd_copysign __P((double, double)); +extern double fd_pow __P((double, double)); + +#elif defined IRIX + +#define fd_acos acos +#define fd_asin asin +#define fd_atan atan +#define fd_exp exp +#define fd_log log +#define fd_log10 log10 +#define fd_sqrt sqrt +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod + +extern double fd_cos __P((double)); +extern double fd_sin __P((double)); +extern double fd_tan __P((double)); +extern double fd_atan2 __P((double, double)); +extern double fd_pow __P((double, double)); +extern double fd_ceil __P((double)); +extern double fd_copysign __P((double, double)); + +#elif defined SOLARIS + +#define fd_atan atan +#define fd_cos cos +#define fd_sin sin +#define fd_tan tan +#define fd_exp exp +#define fd_sqrt sqrt +#define fd_ceil ceil +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod + +extern double fd_acos __P((double)); +extern double fd_asin __P((double)); +extern double fd_log __P((double)); +extern double fd_atan2 __P((double, double)); +extern double fd_pow __P((double, double)); +extern double fd_copysign __P((double, double)); + +#elif defined HPUX + +#define fd_cos cos +#define fd_sin sin +#define fd_exp exp +#define fd_sqrt sqrt +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod + +extern double fd_ceil __P((double)); +extern double fd_acos __P((double)); +extern double fd_log __P((double)); +extern double fd_atan2 __P((double, double)); +extern double fd_tan __P((double)); +extern double fd_pow __P((double, double)); +extern double fd_asin __P((double)); +extern double fd_atan __P((double)); +extern double fd_copysign __P((double, double)); + +#elif defined(linux) + +#define fd_atan atan +#define fd_atan2 atan2 +#define fd_ceil ceil +#define fd_cos cos +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod +#define fd_sin sin +#define fd_sqrt sqrt +#define fd_tan tan +#define fd_copysign copysign + +extern double fd_asin __P((double)); +extern double fd_acos __P((double)); +extern double fd_exp __P((double)); +extern double fd_log __P((double)); +extern double fd_pow __P((double, double)); + +#elif defined(OSF1) + +#define fd_acos acos +#define fd_asin asin +#define fd_atan atan +#define fd_copysign copysign +#define fd_cos cos +#define fd_exp exp +#define fd_fabs fabs +#define fd_fmod fmod +#define fd_sin sin +#define fd_sqrt sqrt +#define fd_tan tan + +extern double fd_atan2 __P((double, double)); +extern double fd_ceil __P((double)); +extern double fd_floor __P((double)); +extern double fd_log __P((double)); +extern double fd_pow __P((double, double)); + +#elif defined(AIX) + +#define fd_acos acos +#define fd_asin asin +#define fd_atan2 atan2 +#define fd_copysign copysign +#define fd_cos cos +#define fd_exp exp +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod +#define fd_log log +#define fd_sin sin +#define fd_sqrt sqrt + +extern double fd_atan __P((double)); +extern double fd_ceil __P((double)); +extern double fd_pow __P((double,double)); +extern double fd_tan __P((double)); + +#else /* other platform.. generic paranoid slow fdlibm */ + +extern double fd_acos __P((double)); +extern double fd_asin __P((double)); +extern double fd_atan __P((double)); +extern double fd_cos __P((double)); +extern double fd_sin __P((double)); +extern double fd_tan __P((double)); + +extern double fd_exp __P((double)); +extern double fd_log __P((double)); +extern double fd_sqrt __P((double)); + +extern double fd_ceil __P((double)); +extern double fd_fabs __P((double)); +extern double fd_floor __P((double)); +extern double fd_fmod __P((double, double)); + +extern double fd_atan2 __P((double, double)); +extern double fd_pow __P((double, double)); +extern double fd_copysign __P((double, double)); + +#endif + +#endif /* JS_USE_FDLIBM_MATH */ + +#endif /* _LIBMATH_H */ + diff --git a/src/extension/script/js/jslock.c b/src/extension/script/js/jslock.c new file mode 100644 index 000000000..9709c8cc0 --- /dev/null +++ b/src/extension/script/js/jslock.c @@ -0,0 +1,1241 @@ +/* -*- 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 "jsstddef.h" +#include +#include "jspubtd.h" +#include "prthread.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jstypes.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) + +#ifndef NSPR_LOCK + +#include + +static PRLock **global_locks; +static uint32 global_lock_count = 1; +static uint32 global_locks_log2 = 0; +static uint32 global_locks_mask = 0; + +#define GLOBAL_LOCK_INDEX(id) (((uint32)(id) >> 2) & global_locks_mask) + +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]); +} + +/* Exclude Alpha NT. */ +#if defined(_WIN32) && defined(_M_IX86) +#pragma warning( disable : 4035 ) + +static JS_INLINE int +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + __asm { + mov eax, ov + mov ecx, nv + mov ebx, w + lock cmpxchg [ebx], ecx + sete al + and eax, 1h + } +} + +#elif defined(__GNUC__) && defined(__i386__) + +/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ +static JS_INLINE int +js_CompareAndSwap(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(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC) + +static JS_INLINE int +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ +#if defined(__GNUC__) + unsigned int res; + JS_ASSERT(ov != nv); + 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; +#else /* !__GNUC__ */ + extern int compare_and_swap(jsword*, jsword, jsword); + JS_ASSERT(ov != nv); + return compare_and_swap(w, ov, nv); +#endif +} + +#elif defined(AIX) + +#include + +static JS_INLINE int +js_CompareAndSwap(jsword *w, jsword ov, jsword nv) +{ + return !_check_lock((atomic_p)w, ov, nv); +} + +#else + +#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction." + +#endif /* arch-tests */ + +#endif /* !NSPR_LOCK */ + +jsword +js_CurrentThreadId() +{ + return CurrentThreadId(); +} + +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 +} + +static void js_Dequeue(JSThinLock *); + +#ifdef DEBUG_SCOPE_COUNT + +#include +#include "jsdhash.h" + +static FILE *logfp; +static JSDHashTable logtbl; + +typedef struct logentry { + JSDHashEntryStub stub; + char op; + const char *file; + int line; +} logentry; + +static void +logit(JSScope *scope, 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 %c %s %d\n", scope, op, file, line); + + if (!logtbl.entryStore && + !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, + sizeof(logentry), 100)) { + return; + } + entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); + if (!entry) + return; + entry->stub.key = scope; + entry->op = op; + entry->file = file; + entry->line = line; +} + +void +js_unlog_scope(JSScope *scope) +{ + if (!logtbl.entryStore) + return; + (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); +} + +# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) + +#else + +# define LOGIT(scope,op) /* nothing */ + +#endif /* DEBUG_SCOPE_COUNT */ + +/* + * Return true if scope's ownercx, or the ownercx of a single-threaded scope + * for which ownercx is waiting to become multi-threaded and shared, is cx. + * That condition implies deadlock in ClaimScope if cx's thread were to wait + * to share scope. + * + * (i) rt->gcLock held + */ +static JSBool +WillDeadlock(JSScope *scope, JSContext *cx) +{ + JSContext *ownercx; + + do { + ownercx = scope->ownercx; + if (ownercx == cx) { + JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); + return JS_TRUE; + } + } while (ownercx && (scope = ownercx->scopeToShare) != NULL); + return JS_FALSE; +} + +/* + * Make scope 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 ClaimScope, immediately below, when we detect deadlock were we to wait + * for scope's lock, because its ownercx is waiting on a scope owned by the + * calling cx. + * + * (i) rt->gcLock held + */ +static void +ShareScope(JSRuntime *rt, JSScope *scope) +{ + JSScope **todop; + + if (scope->u.link) { + for (todop = &rt->scopeSharingTodo; *todop != scope; + todop = &(*todop)->u.link) { + JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO); + } + *todop = scope->u.link; + scope->u.link = NULL; /* null u.link for sanity ASAP */ + JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); + } + js_InitLock(&scope->lock); + if (scope == rt->setSlotScope) { + /* + * Nesting locks on another thread that's using scope->ownercx: give + * the held lock a reentrancy count of 1 and set its lock.owner field + * directly (no compare-and-swap needed while scope->ownercx is still + * non-null). See below in ClaimScope, before the ShareScope call, + * for more on why this is necessary. + * + * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and + * acquiring scope->lock.fat here, against another thread holding that + * fat lock and trying to grab rt->gcLock. This is because no other + * thread can attempt to acquire scope->lock.fat until scope->ownercx + * is null *and* our thread has released rt->gcLock, which interlocks + * scope->ownercx's transition to null against tests of that member + * in ClaimScope. + */ + scope->lock.owner = scope->ownercx->thread; +#ifdef NSPR_LOCK + JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat); +#endif + scope->u.count = 1; + } else { + scope->u.count = 0; + } + js_FinishSharingScope(rt, scope); +} + +/* + * js_FinishSharingScope is the tail part of ShareScope, split out to become a + * subroutine of JS_EndRequest too. The bulk of the work here involves making + * mutable strings in the scope'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_MinimizeDependentStrings, + * or js_UndependString. + * + * The last bit of work done by js_FinishSharingScope nulls scope->ownercx and + * updates rt->sharedScopes. + */ +#define MAKE_STRING_IMMUTABLE(rt, v, vp) \ + JS_BEGIN_MACRO \ + JSString *str_ = JSVAL_TO_STRING(v); \ + uint8 *flagp_ = js_GetGCThingFlags(str_); \ + if (*flagp_ & GCF_MUTABLE) { \ + if (JSSTRING_IS_DEPENDENT(str_) && \ + !js_UndependString(NULL, str_)) { \ + JS_RUNTIME_METER(rt, badUndependStrings); \ + *vp = JSVAL_VOID; \ + } else { \ + *flagp_ &= ~GCF_MUTABLE; \ + } \ + } \ + JS_END_MACRO + +void +js_FinishSharingScope(JSRuntime *rt, JSScope *scope) +{ + JSObject *obj; + uint32 nslots; + jsval v, *vp, *end; + + obj = scope->object; + nslots = JS_MIN(obj->map->freeslot, obj->map->nslots); + for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { + v = *vp; + if (JSVAL_IS_STRING(v)) + MAKE_STRING_IMMUTABLE(rt, v, vp); + } + + scope->ownercx = NULL; /* NB: set last, after lock init */ + JS_RUNTIME_METER(rt, sharedScopes); +} + +/* + * Given a scope with apparently non-null ownercx different from cx, try to + * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope. + * If we claim ownership, return true. Otherwise, we wait for ownercx to be + * set to null (indicating that scope is multi-threaded); or if waiting would + * deadlock, we set ownercx to null ourselves via ShareScope. In any case, + * once ownercx is null we return false. + */ +static JSBool +ClaimScope(JSScope *scope, JSContext *cx) +{ + JSRuntime *rt; + JSContext *ownercx; + jsrefcount saveDepth; + PRStatus stat; + + 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 = scope->ownercx) != NULL) { + /* + * Avoid selflock if ownercx is dead, or is not running a request, or + * has the same thread as cx. Set scope->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_UnlockScope or js_UnlockObj + * function call. + * + * If scope->u.link is non-null, scope has already been inserted on + * the rt->scopeSharingTodo list, because another thread's context + * already wanted to lock scope while ownercx was running a request. + * We can't claim any scope whose u.link is non-null at this point, + * even if ownercx->requestDepth is 0 (see below where we suspend our + * request before waiting on rt->scopeSharingDone). + */ + if (!scope->u.link && + (!js_ValidContextPointer(rt, ownercx) || + !ownercx->requestDepth || + ownercx->thread == cx->thread)) { + JS_ASSERT(scope->u.count == 0); + scope->ownercx = cx; + JS_UNLOCK_GC(rt); + JS_RUNTIME_METER(rt, claimedScopes); + return JS_TRUE; + } + + /* + * Avoid deadlock if scope's owner context is waiting on a scope that + * we own, by revoking scope's ownership. This approach to deadlock + * avoidance works because the engine never nests scope locks, except + * for the notable case of js_SetProtoOrParent (see jsobj.c). + * + * If cx could hold locks on ownercx->scopeToShare, or if ownercx + * could hold locks on scope, 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". + * Apart from the js_SetProtoOrParent exception, the engine promotes + * a scope from exclusive to shared access only when locking, never + * when holding or unlocking. + * + * If ownercx's thread is calling js_SetProtoOrParent, trying to lock + * the inner scope (the scope of the object being set as the prototype + * of the outer object), ShareScope will find the outer object's scope + * at rt->setSlotScope. If it's the same as scope, we give it a lock + * held by ownercx's thread with reentrancy count of 1, then we return + * here and break. After that we unwind to js_[GS]etSlotThreadSafe or + * js_LockScope (our caller), where we wait on the newly-fattened lock + * until ownercx's thread unwinds from js_SetProtoOrParent. + * + * Avoid deadlock before any of this scope/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 scope wanted by the GC (from + * a finalizer) that can't be claimed must be slated for sharing. + */ + if (rt->gcThread == cx->thread || + (ownercx->scopeToShare && + WillDeadlock(ownercx->scopeToShare, cx))) { + ShareScope(rt, scope); + break; + } + + /* + * Thanks to the non-zero NO_SCOPE_SHARING_TODO link terminator, we + * can decide whether scope is on rt->scopeSharingTodo with a single + * non-null test, and avoid double-insertion bugs. + */ + if (!scope->u.link) { + scope->u.link = rt->scopeSharingTodo; + rt->scopeSharingTodo = scope; + js_HoldObjectMap(cx, &scope->map); + } + + /* + * Inline JS_SuspendRequest before we wait on rt->scopeSharingDone, + * saving and clearing cx->requestDepth so we don't deadlock if the + * GC needs to run on ownercx. + * + * Unlike JS_SuspendRequest and JS_EndRequest, we must take care not + * to decrement rt->requestCount if cx is active on the GC's thread, + * because the GC has already reduced rt->requestCount to exclude all + * such such contexts. + */ + saveDepth = cx->requestDepth; + if (saveDepth) { + cx->requestDepth = 0; + if (rt->gcThread != cx->thread) { + JS_ASSERT(rt->requestCount > 0); + rt->requestCount--; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + } + } + + /* + * We know that some other thread's context owns scope, which is now + * linked onto rt->scopeSharingTodo, awaiting the end of that other + * thread's request. So it is safe to wait on rt->scopeSharingDone. + */ + cx->scopeToShare = scope; + stat = PR_WaitCondVar(rt->scopeSharingDone, PR_INTERVAL_NO_TIMEOUT); + JS_ASSERT(stat != PR_FAILURE); + + /* + * Inline JS_ResumeRequest after waiting on rt->scopeSharingDone, + * restoring cx->requestDepth. Same note as above for the inlined, + * specialized JS_SuspendRequest code: beware rt->gcThread. + */ + if (saveDepth) { + if (rt->gcThread != cx->thread) { + while (rt->gcLevel > 0) + JS_AWAIT_GC_DONE(rt); + rt->requestCount++; + } + cx->requestDepth = saveDepth; + } + + /* + * Don't clear cx->scopeToShare until after we're through waiting on + * all condition variables protected by rt->gcLock -- that includes + * rt->scopeSharingDone *and* rt->gcDone (hidden in JS_AWAIT_GC_DONE, + * in the inlined JS_ResumeRequest code immediately above). + * + * Otherwise, the GC could easily deadlock with another thread that + * owns a scope wanted by a finalizer. By keeping cx->scopeToShare + * set till here, we ensure that such deadlocks are detected, which + * results in the finalized object's scope being shared (it must, of + * course, have other, live objects sharing it). + */ + cx->scopeToShare = NULL; + } + + JS_UNLOCK_GC(rt); + return JS_FALSE; +} + +/* 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; +#ifndef NSPR_LOCK + JSThinLock *tl; + jsword me; +#endif + + /* + * We handle non-native objects via JSObjectOps.getRequiredSlot, treating + * all slots starting from 0 as required slots. A property definition or + * some prior arrangement must have allocated slot. + * + * Note once again (see jspubtd.h, before JSGetRequiredSlotOp's typedef) + * the crucial distinction between a |required slot number| that's passed + * to the get/setRequiredSlot JSObjectOps, and a |reserved slot index| + * passed to the JS_Get/SetReservedSlot APIs. + */ + if (!OBJ_IS_NATIVE(obj)) + return OBJ_GET_REQUIRED_SLOT(cx, obj, slot); + + /* + * Native object locking is inlined here to optimize the single-threaded + * and contention-free multi-threaded cases. + */ + scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->ownercx != cx); + JS_ASSERT(obj->slots && slot < obj->map->freeslot); + + /* + * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). + * 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_IS_SEALED(scope) && scope->object == obj) || + (scope->ownercx && ClaimScope(scope, cx))) { + return obj->slots[slot]; + } + +#ifndef NSPR_LOCK + tl = &scope->lock; + me = cx->thread; + JS_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&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 = obj->slots[slot]; + if (!js_CompareAndSwap(&tl->owner, me, 0)) { + /* Assert that scope locks never revert to flyweight. */ + JS_ASSERT(scope->ownercx != cx); + LOGIT(scope, '1'); + scope->u.count = 1; + js_UnlockObj(cx, obj); + } + return v; + } + if (!js_CompareAndSwap(&tl->owner, me, 0)) + js_Dequeue(tl); + } + else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { + return obj->slots[slot]; + } +#endif + + js_LockObj(cx, obj); + v = obj->slots[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!). + */ + scope = OBJ_SCOPE(obj); + if (scope->ownercx != cx) + js_UnlockScope(cx, scope); + return v; +} + +void +js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +{ + JSScope *scope; +#ifndef NSPR_LOCK + JSThinLock *tl; + jsword me; +#endif + + /* Any string stored in a thread-safe object must be immutable. */ + if (JSVAL_IS_STRING(v)) + MAKE_STRING_IMMUTABLE(cx->runtime, v, &v); + + /* + * We handle non-native objects via JSObjectOps.setRequiredSlot, as above + * for the Get case. + */ + if (!OBJ_IS_NATIVE(obj)) { + OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); + return; + } + + /* + * Native object locking is inlined here to optimize the single-threaded + * and contention-free multi-threaded cases. + */ + scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->ownercx != cx); + JS_ASSERT(obj->slots && slot < obj->map->freeslot); + + /* + * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). + * 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_IS_SEALED(scope) && scope->object == obj) || + (scope->ownercx && ClaimScope(scope, cx))) { + obj->slots[slot] = v; + return; + } + +#ifndef NSPR_LOCK + tl = &scope->lock; + me = cx->thread; + JS_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&tl->owner, 0, me)) { + if (scope == OBJ_SCOPE(obj)) { + obj->slots[slot] = v; + if (!js_CompareAndSwap(&tl->owner, me, 0)) { + /* Assert that scope locks never revert to flyweight. */ + JS_ASSERT(scope->ownercx != cx); + LOGIT(scope, '1'); + scope->u.count = 1; + js_UnlockObj(cx, obj); + } + return; + } + if (!js_CompareAndSwap(&tl->owner, me, 0)) + js_Dequeue(tl); + } + else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { + obj->slots[slot] = v; + return; + } +#endif + + js_LockObj(cx, obj); + obj->slots[slot] = v; + + /* + * Same drill as above, in js_GetSlotThreadSafe. Note that we cannot + * assume obj has its own mutable scope (where scope->object == obj) yet, + * because OBJ_SET_SLOT is called for the "universal", common slots such + * as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope. + * See also the JSPROP_SHARED attribute and its usage. + */ + scope = OBJ_SCOPE(obj); + if (scope->ownercx != cx) + js_UnlockScope(cx, scope); +} + +#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); + 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 **) 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 *) 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]); + 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; + } + free(fl_list_table); + fl_list_table = NULL; + fl_list_table_len = 0; + } +#endif /* !NSPR_LOCK */ +} + +void +js_InitContextForLocking(JSContext *cx) +{ + cx->thread = CurrentThreadId(); + JS_ASSERT(Thin_GetWait(cx->thread) == 0); +} + +#ifndef NSPR_LOCK + +/* + * 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 && js_CompareAndSwap(&tl->owner, o, n)) { + if (js_SuspendThread(tl)) + me = Thin_RemoveWait(me); + else + me = Thin_SetWait(me); + } + else if (js_CompareAndSwap(&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 (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */ + JS_ASSERT(0); + js_ResumeThread(tl); +} + +JS_INLINE void +js_Lock(JSThinLock *tl, jsword me) +{ + JS_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&tl->owner, 0, me)) + return; + if (Thin_RemoveWait(ReadWord(tl->owner)) != me) + js_Enqueue(tl, me); +#ifdef DEBUG + else + JS_ASSERT(0); +#endif +} + +JS_INLINE void +js_Unlock(JSThinLock *tl, jsword me) +{ + JS_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&tl->owner, me, 0)) + return; + if (Thin_RemoveWait(ReadWord(tl->owner)) == me) + js_Dequeue(tl); +#ifdef DEBUG + else + JS_ASSERT(0); +#endif +} + +#endif /* !NSPR_LOCK */ + +void +js_LockRuntime(JSRuntime *rt) +{ + PR_Lock(rt->rtLock); +#ifdef DEBUG + rt->rtLockOwner = CurrentThreadId(); +#endif +} + +void +js_UnlockRuntime(JSRuntime *rt) +{ +#ifdef DEBUG + rt->rtLockOwner = 0; +#endif + PR_Unlock(rt->rtLock); +} + +void +js_LockScope(JSContext *cx, JSScope *scope) +{ + jsword me = cx->thread; + + JS_ASSERT(me == CurrentThreadId()); + JS_ASSERT(scope->ownercx != cx); + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + if (scope->ownercx && ClaimScope(scope, cx)) + return; + + if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) { + JS_ASSERT(scope->u.count > 0); + LOGIT(scope, '+'); + scope->u.count++; + } else { + JSThinLock *tl = &scope->lock; + JS_LOCK0(tl, me); + JS_ASSERT(scope->u.count == 0); + LOGIT(scope, '1'); + scope->u.count = 1; + } +} + +void +js_UnlockScope(JSContext *cx, JSScope *scope) +{ + jsword me = cx->thread; + + /* We hope compilers use me instead of reloading cx->thread in the macro. */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return; + if (cx->lockedSealedScope == scope) { + cx->lockedSealedScope = NULL; + return; + } + + JS_ASSERT(scope->ownercx == NULL); + JS_ASSERT(scope->u.count > 0); + if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) { + JS_ASSERT(0); /* unbalanced unlock */ + return; + } + LOGIT(scope, '-'); + if (--scope->u.count == 0) { + JSThinLock *tl = &scope->lock; + JS_UNLOCK0(tl, me); + } +} + +/* + * NB: oldscope may be null if our caller is js_GetMutableScope and it just + * dropped the last reference to oldscope. + */ +void +js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) +{ + jsword me; + JSThinLock *tl; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope)); + + /* + * If the last reference to oldscope went away, newscope needs no lock + * state update. + */ + if (!oldscope) + return; + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, oldscope)); + + /* + * Special case in js_LockScope and js_UnlockScope for the GC calling + * code that locks, unlocks, or mutates. Nothing to do in these cases, + * because scope and newscope 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_UnlockScope 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->lockedSealedScope != newscope); + if (cx->lockedSealedScope == oldscope) { + JS_ASSERT(newscope->ownercx == cx || + (!newscope->ownercx && newscope->u.count == 1)); + cx->lockedSealedScope = NULL; + return; + } + + /* + * If oldscope is single-threaded, there's nothing to do. + */ + if (oldscope->ownercx) { + JS_ASSERT(oldscope->ownercx == cx); + JS_ASSERT(newscope->ownercx == cx || + (!newscope->ownercx && newscope->u.count == 1)); + return; + } + + /* + * We transfer oldscope->u.count only if newscope is not single-threaded. + * Flow unwinds from here through some number of JS_UNLOCK_SCOPE and/or + * JS_UNLOCK_OBJ macro calls, which will decrement newscope->u.count only + * if they find newscope->ownercx != cx. + */ + if (newscope->ownercx != cx) { + JS_ASSERT(!newscope->ownercx); + newscope->u.count = oldscope->u.count; + } + + /* + * Reset oldscope's lock state so that it is completely unlocked. + */ + LOGIT(oldscope, '0'); + oldscope->u.count = 0; + tl = &oldscope->lock; + me = cx->thread; + JS_UNLOCK0(tl, me); +} + +void +js_LockObj(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + for (;;) { + scope = OBJ_SCOPE(obj); + if (SCOPE_IS_SEALED(scope) && scope->object == obj && + !cx->lockedSealedScope) { + cx->lockedSealedScope = scope; + return; + } + + js_LockScope(cx, scope); + + /* 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_UnlockScope(cx, scope); + } +} + +void +js_UnlockObj(JSContext *cx, JSObject *obj) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + js_UnlockScope(cx, OBJ_SCOPE(obj)); +} + +#ifdef DEBUG + +JSBool +js_IsRuntimeLocked(JSRuntime *rt) +{ + return CurrentThreadId() == rt->rtLockOwner; +} + +JSBool +js_IsObjLocked(JSContext *cx, JSObject *obj) +{ + JSScope *scope = OBJ_SCOPE(obj); + + return MAP_IS_NATIVE(&scope->map) && js_IsScopeLocked(cx, scope); +} + +JSBool +js_IsScopeLocked(JSContext *cx, JSScope *scope) +{ + /* Special case: the GC locking any object's scope, see js_LockScope. */ + if (CX_THREAD_IS_RUNNING_GC(cx)) + return JS_TRUE; + + /* Special case: locked object owning a sealed scope, see js_LockObj. */ + if (cx->lockedSealedScope == scope) + return JS_TRUE; + + /* + * General case: the scope is either exclusively owned (by cx), or it has + * a thin or fat lock to cope with shared (concurrent) ownership. + */ + if (scope->ownercx) { + JS_ASSERT(scope->ownercx == cx); + return JS_TRUE; + } + return CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner)); +} + +#endif /* DEBUG */ +#endif /* JS_THREADSAFE */ diff --git a/src/extension/script/js/jslock.h b/src/extension/script/js/jslock.h new file mode 100644 index 000000000..9ece59c9b --- /dev/null +++ b/src/extension/script/js/jslock.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 jslock_h__ +#define jslock_h__ + +#ifdef JS_THREADSAFE + +#include "jstypes.h" +#include "pratom.h" +#include "prlock.h" +#include "prcvar.h" + +#include "jsprvtd.h" /* for JSScope, etc. */ +#include "jspubtd.h" /* for JSRuntime, etc. */ + +#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; + +struct JSFatLock { + int susp; + PRLock *slock; + PRCondVar *svar; + JSFatLock *next; + JSFatLock **prevp; +}; + +typedef struct JSThinLock { + jsword owner; + JSFatLock *fat; +} JSThinLock; + +typedef PRLock JSLock; + +typedef struct JSFatLockTable { + JSFatLock *free; + JSFatLock *taken; +} JSFatLockTable; + +/* + * 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 CurrentThreadId() (jsword)PR_GetCurrentThread() +#define JS_CurrentThreadId() js_CurrentThreadId() +#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_LOCK0(P,M) js_Lock(P,M) +#define JS_UNLOCK0(P,M) js_Unlock(P,M) + +#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) + +/* + * Include jsscope.h so JS_LOCK_OBJ macro callers don't have to include it. + * Since there is a JSThinLock member in JSScope, we can't nest this include + * much earlier (see JSThinLock's typedef, above). Yes, that means there is + * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here, + * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h, + * and so on. + * + * We also need jsscope.h #ifdef DEBUG for SET_OBJ_INFO and SET_SCOPE_INFO, + * but we do not want any nested includes that depend on DEBUG. Those lead + * to build bustage when someone makes a change that depends in a subtle way + * on jsscope.h being included directly or indirectly, but does not test by + * building optimized as well as DEBUG. + */ +#include "jsscope.h" + +#ifdef DEBUG + +#define SET_OBJ_INFO(obj_,file_,line_) \ + SET_SCOPE_INFO(OBJ_SCOPE(obj_),file_,line_) + +#define SET_SCOPE_INFO(scope_,file_,line_) \ + ((scope_)->ownercx ? (void)0 : \ + (JS_ASSERT((0 < (scope_)->u.count && (scope_)->u.count <= 4) || \ + SCOPE_IS_SEALED(scope_)), \ + (void)((scope_)->file[(scope_)->u.count-1] = (file_), \ + (scope_)->line[(scope_)->u.count-1] = (line_)))) +#endif /* DEBUG */ + +#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)->ownercx == (cx)) \ + ? (void)0 \ + : (js_LockObj(cx, obj), \ + SET_OBJ_INFO(obj,__FILE__,__LINE__))) +#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ + ? (void)0 : js_UnlockObj(cx, obj)) + +#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 : \ + (js_LockScope(cx, scope), \ + SET_SCOPE_INFO(scope,__FILE__,__LINE__))) +#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 : \ + js_UnlockScope(cx, scope)) +#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ + js_TransferScopeLock(cx, scope, newscope) + +extern jsword js_CurrentThreadId(); +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 void js_LockScope(JSContext *cx, JSScope *scope); +extern void js_UnlockScope(JSContext *cx, JSScope *scope); +extern int js_SetupLocks(int,int); +extern void js_CleanupLocks(); +extern void js_InitContextForLocking(JSContext *); +extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); +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 *); +extern void js_FinishSharingScope(JSRuntime *rt, JSScope *scope); + +#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_SCOPE_LOCKED(cx,scope) js_IsScopeLocked(cx,scope) + +extern JSBool js_IsRuntimeLocked(JSRuntime *rt); +extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj); +extern JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope); + +#else + +#define JS_IS_RUNTIME_LOCKED(rt) 0 +#define JS_IS_OBJ_LOCKED(cx,obj) 1 +#define JS_IS_SCOPE_LOCKED(cx,scope) 1 + +#endif /* DEBUG */ + +#define JS_LOCK_OBJ_VOID(cx, obj, e) \ + JS_BEGIN_MACRO \ + JS_LOCK_OBJ(cx, obj); \ + e; \ + JS_UNLOCK_OBJ(cx, obj); \ + JS_END_MACRO + +#define JS_LOCK_VOID(cx, e) \ + JS_BEGIN_MACRO \ + JSRuntime *_rt = (cx)->runtime; \ + JS_LOCK_RUNTIME_VOID(_rt, e); \ + JS_END_MACRO + +#if defined(JS_USE_ONLY_NSPR_LOCKS) || \ + !( (defined(_WIN32) && defined(_M_IX86)) || \ + (defined(__GNUC__) && defined(__i386__)) || \ + (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) || \ + defined(AIX) ) + +#define NSPR_LOCK 1 + +#undef JS_LOCK0 +#undef JS_UNLOCK0 +#define JS_LOCK0(P,M) (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M)) +#define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat))) + +#else /* arch-tests */ + +#undef NSPR_LOCK + +extern JS_INLINE void js_Lock(JSThinLock *tl, jsword me); +extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); + +#endif /* arch-tests */ + +#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_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_LOCK0(P,M) ((void)0) +#define JS_UNLOCK0(P,M) ((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_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_SCOPE_LOCKED(cx,scope) 1 +#define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) + +#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_LOCK_GC_VOID(rt,e) (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt)) +#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) + +#define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread) +#define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread) + +#ifndef SET_OBJ_INFO +#define SET_OBJ_INFO(obj,f,l) ((void)0) +#endif +#ifndef SET_SCOPE_INFO +#define SET_SCOPE_INFO(scope,f,l) ((void)0) +#endif + +#endif /* jslock_h___ */ diff --git a/src/extension/script/js/jslocko.asm b/src/extension/script/js/jslocko.asm new file mode 100644 index 000000000..2589bb7f9 --- /dev/null +++ b/src/extension/script/js/jslocko.asm @@ -0,0 +1,59 @@ +COMMENT | -*- 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/src/extension/script/js/jslog2.c b/src/extension/script/js/jslog2.c new file mode 100644 index 000000000..113c276d0 --- /dev/null +++ b/src/extension/script/js/jslog2.c @@ -0,0 +1,83 @@ +/* -*- 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 ***** */ + +#include "jsstddef.h" +#include "jsbit.h" + +/* +** 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 = 0; + + if (n & (n-1)) + log2++; + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; + 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 = 0; + + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; + return log2; +} diff --git a/src/extension/script/js/jslong.c b/src/extension/script/js/jslong.c new file mode 100644 index 000000000..76259e502 --- /dev/null +++ b/src/extension/script/js/jslong.c @@ -0,0 +1,281 @@ +/* -*- 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 ***** */ + +#include "jsstddef.h" +#include "jstypes.h" +#include "jslong.h" + +static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 ); +static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff ); +static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 ); + +#ifdef HAVE_WATCOM_BUG_2 +JSInt64 __pascal __loadds __export + JSLL_Zero(void) { return ll_zero; } +JSInt64 __pascal __loadds __export + JSLL_MaxInt(void) { return ll_maxint; } +JSInt64 __pascal __loadds __export + JSLL_MinInt(void) { return ll_minint; } +#else +JS_PUBLIC_API(JSInt64) JSLL_Zero(void) { return ll_zero; } +JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; } +JS_PUBLIC_API(JSInt64) JSLL_MinInt(void) { return ll_minint; } +#endif + +#ifndef JS_HAVE_LONG_LONG +/* +** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. +*/ +static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) +{ + JSUint32 d1, d0, q1, q0; + JSUint32 r1, r0, m; + + d1 = jshi16(b); + d0 = jslo16(b); + r1 = a.hi % d1; + q1 = a.hi / d1; + m = q1 * d0; + r1 = (r1 << 16) | jshi16(a.lo); + if (r1 < m) { + q1--, r1 += b; + if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ + && r1 < m) { + q1--, r1 += b; + } + } + r1 -= m; + r0 = r1 % d1; + q0 = r1 / d1; + m = q0 * d0; + r0 = (r0 << 16) | jslo16(a.lo); + if (r0 < m) { + q0--, r0 += b; + if (r0 >= b + && r0 < m) { + q0--, r0 += b; + } + } + *qp = (q1 << 16) | q0; + *rp = r0 - m; +} + +static JSUint32 CountLeadingZeros(JSUint32 a) +{ + JSUint32 t; + JSUint32 r = 32; + + if ((t = a >> 16) != 0) + r -= 16, a = t; + if ((t = a >> 8) != 0) + r -= 8, a = t; + if ((t = a >> 4) != 0) + r -= 4, a = t; + if ((t = a >> 2) != 0) + r -= 2, a = t; + if ((t = a >> 1) != 0) + r -= 1, a = t; + if (a & 1) + r--; + return r; +} + +JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b) +{ + JSUint32 n0, n1, n2; + JSUint32 q0, q1; + JSUint32 rsh, lsh; + + n0 = a.lo; + n1 = a.hi; + + if (b.hi == 0) { + if (b.lo > n1) { + /* (0 q0) = (n1 n0) / (0 D0) */ + + lsh = CountLeadingZeros(b.lo); + + if (lsh) { + /* + * Normalize, i.e. make the most significant bit of the + * denominator be set. + */ + b.lo = b.lo << lsh; + n1 = (n1 << lsh) | (n0 >> (32 - lsh)); + n0 = n0 << lsh; + } + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + q1 = 0; + + /* remainder is in n0 >> lsh */ + } else { + /* (q1 q0) = (n1 n0) / (0 d0) */ + + if (b.lo == 0) /* user wants to divide by zero! */ + b.lo = 1 / b.lo; /* so go ahead and crash */ + + lsh = CountLeadingZeros(b.lo); + + if (lsh == 0) { + /* + * From (n1 >= b.lo) + * && (the most significant bit of b.lo is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the leading quotient digit q1 = 1). + * + * This special case is necessary, not an optimization + * (Shifts counts of 32 are undefined). + */ + n1 -= b.lo; + q1 = 1; + } else { + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q1, &n1, a, b.lo); + } + + /* n1 != b.lo... */ + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + + /* remainder in n0 >> lsh */ + } + + if (rp) { + rp->lo = n0 >> lsh; + rp->hi = 0; + } + } else { + if (b.hi > n1) { + /* (0 0) = (n1 n0) / (D1 d0) */ + + q0 = 0; + q1 = 0; + + /* remainder in (n1 n0) */ + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + /* (0 q0) = (n1 n0) / (d1 d0) */ + + lsh = CountLeadingZeros(b.hi); + if (lsh == 0) { + /* + * From (n1 >= b.hi) + * && (the most significant bit of b.hi is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the quotient digit q0 = 0 or 1). + * + * This special case is necessary, not an optimization. + */ + + /* + * The condition on the next line takes advantage of that + * n1 >= b.hi (true due to control flow). + */ + if (n1 > b.hi || n0 >= b.lo) { + q0 = 1; + a.lo = n0, a.hi = n1; + JSLL_SUB(a, a, b); + } else { + q0 = 0; + } + q1 = 0; + + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + JSInt64 m; + + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.hi = (b.hi << lsh) | (b.lo >> rsh); + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q0, &n1, a, b.hi); + JSLL_MUL32(m, q0, b.lo); + + if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { + q0--; + JSLL_SUB(m, m, b); + } + + q1 = 0; + + /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ + if (rp) { + a.lo = n0, a.hi = n1; + JSLL_SUB(a, a, m); + rp->lo = (a.hi << rsh) | (a.lo >> lsh); + rp->hi = a.hi >> lsh; + } + } + } + } + + if (qp) { + qp->lo = q0; + qp->hi = q1; + } +} +#endif /* !JS_HAVE_LONG_LONG */ diff --git a/src/extension/script/js/jslong.h b/src/extension/script/js/jslong.h new file mode 100644 index 000000000..bde8bfbbf --- /dev/null +++ b/src/extension/script/js/jslong.h @@ -0,0 +1,437 @@ +/* -*- 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 + +/*********************************************************************** +** DEFINES: JSLL_MaxInt +** JSLL_MinInt +** JSLL_Zero +** DESCRIPTION: +** Various interesting constants and static variable +** initializer +***********************************************************************/ +#ifdef HAVE_WATCOM_BUG_2 +JSInt64 __pascal __loadds __export + JSLL_MaxInt(void); +JSInt64 __pascal __loadds __export + JSLL_MinInt(void); +JSInt64 __pascal __loadds __export + JSLL_Zero(void); +#else +extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void); +extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void); +extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); +#endif + +#define JSLL_MAXINT JSLL_MaxInt() +#define JSLL_MININT JSLL_MinInt() +#define JSLL_ZERO JSLL_Zero() + +#ifdef JS_HAVE_LONG_LONG + +#if JS_BYTES_PER_LONG == 8 +#define JSLL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) +#elif (defined(WIN32) || defined(WIN16)) && !defined(__GNUC__) +#define JSLL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) +#else +#define JSLL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) +#endif + +/*********************************************************************** +** 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))) + +#else /* !JS_HAVE_LONG_LONG */ + +#ifdef IS_LITTLE_ENDIAN +#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)} +#else +#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)} +#endif + +#define JSLL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) +#define JSLL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) +#define JSLL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) +#define JSLL_GE_ZERO(a) (((a).hi >> 31) == 0) + +#ifdef DEBUG +#define JSLL_CMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b)) +#define JSLL_UCMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_UCMP(a, op, b)) +#else +#define JSLL_CMP(a, op, b) JSLL_REAL_CMP(a, op, b) +#define JSLL_UCMP(a, op, b) JSLL_REAL_UCMP(a, op, b) +#endif + +#define JSLL_REAL_CMP(a,op,b) (((JSInt32)(a).hi op (JSInt32)(b).hi) || \ + (((a).hi == (b).hi) && ((a).lo op (b).lo))) +#define JSLL_REAL_UCMP(a,op,b) (((a).hi op (b).hi) || \ + (((a).hi == (b).hi) && ((a).lo op (b).lo))) + +#define JSLL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ + (r).hi = (a).hi & (b).hi) +#define JSLL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ + (r).hi = (a).hi | (b).hi) +#define JSLL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ + (r).hi = (a).hi ^ (b).hi) +#define JSLL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ + (r).hi = (r).hi | (a).hi) +#define JSLL_NOT(r, a) ((r).lo = ~(a).lo, \ + (r).hi = ~(a).hi) + +#define JSLL_NEG(r, a) ((r).lo = -(JSInt32)(a).lo, \ + (r).hi = -(JSInt32)(a).hi - ((r).lo != 0)) +#define JSLL_ADD(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo + _b.lo; \ + (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ +} + +#define JSLL_SUB(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo - _b.lo; \ + (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ +} + +#define JSLL_MUL(r, a, b) { \ + JSInt64 _a, _b; \ + _a = a; _b = b; \ + JSLL_MUL32(r, _a.lo, _b.lo); \ + (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ +} + +#define jslo16(a) ((a) & JS_BITMASK(16)) +#define jshi16(a) ((a) >> 16) + +#define JSLL_MUL32(r, a, b) { \ + JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ + _a1 = jshi16(a), _a0 = jslo16(a); \ + _b1 = jshi16(b), _b0 = jslo16(b); \ + _y0 = _a0 * _b0; \ + _y1 = _a0 * _b1; \ + _y2 = _a1 * _b0; \ + _y3 = _a1 * _b1; \ + _y1 += jshi16(_y0); /* can't carry */ \ + _y1 += _y2; /* might carry */ \ + if (_y1 < _y2) \ + _y3 += (JSUint32)(JS_BIT(16)); /* propagate */ \ + (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \ + (r).hi = _y3 + jshi16(_y1); \ +} + +#define JSLL_UDIVMOD(qp, rp, a, b) jsll_udivmod(qp, rp, a, b) + +extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b); + +#define JSLL_DIV(r, a, b) { \ + JSInt64 _a, _b; \ + JSUint32 _negative = (JSInt32)(a).hi < 0; \ + if (_negative) { \ + JSLL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((JSInt32)(b).hi < 0) { \ + _negative ^= 1; \ + JSLL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + JSLL_UDIVMOD(&(r), 0, _a, _b); \ + if (_negative) \ + JSLL_NEG(r, r); \ +} + +#define JSLL_MOD(r, a, b) { \ + JSInt64 _a, _b; \ + JSUint32 _negative = (JSInt32)(a).hi < 0; \ + if (_negative) { \ + JSLL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((JSInt32)(b).hi < 0) { \ + JSLL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + JSLL_UDIVMOD(0, &(r), _a, _b); \ + if (_negative) \ + JSLL_NEG(r, r); \ +} + +#define JSLL_SHL(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = _a.lo << ((b) & 31); \ + (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = _a.lo << ((b) & 31); \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +/* a is an JSInt32, b is JSInt32, r is JSInt64 */ +#define JSLL_ISHL(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a.lo = (a); \ + _a.hi = 0; \ + if ((b) < 32) { \ + (r).lo = (a) << ((b) & 31); \ + (r).hi = ((a) >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = (a) << ((b) & 31); \ + } \ + } else { \ + (r).lo = (a); \ + (r).hi = 0; \ + } \ +} + +#define JSLL_SHR(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = (JSInt32)_a.hi >> ((b) & 31); \ + } else { \ + (r).lo = (JSInt32)_a.hi >> ((b) & 31); \ + (r).hi = (JSInt32)_a.hi >> 31; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define JSLL_USHR(r, a, b) { \ + if (b) { \ + JSInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = _a.hi >> ((b) & 31); \ + } else { \ + (r).lo = _a.hi >> ((b) & 31); \ + (r).hi = 0; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define JSLL_L2I(i, l) ((i) = (l).lo) +#define JSLL_L2UI(ui, l) ((ui) = (l).lo) +#define JSLL_L2F(f, l) { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; } + +#define JSLL_L2D(d, l) { \ + int _negative; \ + JSInt64 _absval; \ + \ + _negative = (l).hi >> 31; \ + if (_negative) { \ + JSLL_NEG(_absval, l); \ + } else { \ + _absval = l; \ + } \ + (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ + if (_negative) \ + (d) = -(d); \ +} + +#define JSLL_I2L(l, i) { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; } +#define JSLL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) +#define JSLL_F2L(l, f) { double _d = (double)f; JSLL_D2L(l, _d); } + +#define JSLL_D2L(l, d) { \ + int _negative; \ + double _absval, _d_hi; \ + JSInt64 _lo_d; \ + \ + _negative = ((d) < 0); \ + _absval = _negative ? -(d) : (d); \ + \ + (l).hi = _absval / 4.294967296e9; \ + (l).lo = 0; \ + JSLL_L2D(_d_hi, l); \ + _absval -= _d_hi; \ + _lo_d.hi = 0; \ + if (_absval < 0) { \ + _lo_d.lo = -_absval; \ + JSLL_SUB(l, l, _lo_d); \ + } else { \ + _lo_d.lo = _absval; \ + JSLL_ADD(l, l, _lo_d); \ + } \ + \ + if (_negative) \ + JSLL_NEG(l, l); \ +} + +#endif /* !JS_HAVE_LONG_LONG */ + +JS_END_EXTERN_C + +#endif /* jslong_h___ */ diff --git a/src/extension/script/js/jsmath.c b/src/extension/script/js/jsmath.c new file mode 100644 index 000000000..9c6fcec0e --- /dev/null +++ b/src/extension/script/js/jsmath.c @@ -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 ***** */ + +/* + * JS math package. + */ +#include "jsstddef.h" +#include "jslibmath.h" +#include +#include "jstypes.h" +#include "jslong.h" +#include "prmjtime.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.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}} +}; + +static JSClass math_class = { + "Math", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_fabs(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_acos(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; +#ifdef XP_MAC + if (x == 0) + return js_NewNumberValue(cx, x, rval); +#endif + z = fd_asin(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; +#ifdef XP_MAC + if (x == 0) + return js_NewNumberValue(cx, x, rval); +#endif + z = fd_atan(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, y, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + if (!js_ValueToNumber(cx, argv[1], &y)) + return JS_FALSE; + z = fd_atan2(x, y); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_ceil(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_cos(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; +#ifdef _WIN32 + if (!JSDOUBLE_IS_NaN(x)) { + if (x == *cx->runtime->jsPositiveInfinity) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); + return JS_TRUE; + } + if (x == *cx->runtime->jsNegativeInfinity) { + *rval = JSVAL_ZERO; + return JS_TRUE; + } + } +#endif + z = fd_exp(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_floor(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_log(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z = *cx->runtime->jsNegativeInfinity; + uintN i; + + if (argc == 0) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); + return JS_TRUE; + } + for (i = 0; i < argc; i++) { + if (!js_ValueToNumber(cx, argv[i], &x)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(x)) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1)) + z = x; + else + z = (x > z) ? x : z; + } + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z = *cx->runtime->jsPositiveInfinity; + uintN i; + + if (argc == 0) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); + return JS_TRUE; + } + for (i = 0; i < argc; i++) { + if (!js_ValueToNumber(cx, argv[i], &x)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(x)) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1)) + z = x; + else + z = (x < z) ? x : z; + } + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, y, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + if (!js_ValueToNumber(cx, argv[1], &y)) + return JS_FALSE; + z = fd_pow(x, y); + return js_NewNumberValue(cx, z, rval); +} + +/* + * Math.random() support, lifted from java.util.Random.java. + */ +static void +random_setSeed(JSRuntime *rt, int64 seed) +{ + int64 tmp; + + JSLL_I2L(tmp, 1000); + JSLL_DIV(seed, seed, tmp); + JSLL_XOR(tmp, seed, rt->rngMultiplier); + JSLL_AND(rt->rngSeed, tmp, rt->rngMask); +} + +static void +random_init(JSRuntime *rt) +{ + int64 tmp, tmp2; + + /* Do at most once. */ + if (rt->rngInitialized) + return; + rt->rngInitialized = JS_TRUE; + + /* rt->rngMultiplier = 0x5DEECE66DL */ + JSLL_ISHL(tmp, 0x5, 32); + JSLL_UI2L(tmp2, 0xDEECE66DL); + JSLL_OR(rt->rngMultiplier, tmp, tmp2); + + /* rt->rngAddend = 0xBL */ + JSLL_I2L(rt->rngAddend, 0xBL); + + /* rt->rngMask = (1L << 48) - 1 */ + JSLL_I2L(tmp, 1); + JSLL_SHL(tmp2, tmp, 48); + JSLL_SUB(rt->rngMask, tmp2, tmp); + + /* rt->rngDscale = (jsdouble)(1L << 53) */ + JSLL_SHL(tmp2, tmp, 53); + JSLL_L2D(rt->rngDscale, tmp2); + + /* Finally, set the seed from current time. */ + random_setSeed(rt, PRMJ_Now()); +} + +static uint32 +random_next(JSRuntime *rt, int bits) +{ + int64 nextseed, tmp; + uint32 retval; + + JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); + JSLL_ADD(nextseed, nextseed, rt->rngAddend); + JSLL_AND(nextseed, nextseed, rt->rngMask); + rt->rngSeed = nextseed; + JSLL_USHR(tmp, nextseed, 48 - bits); + JSLL_L2I(retval, tmp); + return retval; +} + +static jsdouble +random_nextDouble(JSRuntime *rt) +{ + int64 tmp, tmp2; + jsdouble d; + + JSLL_ISHL(tmp, random_next(rt, 26), 27); + JSLL_UI2L(tmp2, random_next(rt, 27)); + JSLL_ADD(tmp, tmp, tmp2); + JSLL_L2D(d, tmp); + return d / rt->rngDscale; +} + +static JSBool +math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSRuntime *rt; + jsdouble z; + + rt = cx->runtime; + JS_LOCK_RUNTIME(rt); + random_init(rt); + z = random_nextDouble(rt); + JS_UNLOCK_RUNTIME(rt); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_copysign(fd_floor(x + 0.5), x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_sin(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_sqrt(x); + return js_NewNumberValue(cx, z, rval); +} + +static JSBool +math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x, z; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + z = fd_tan(x); + return js_NewNumberValue(cx, z, rval); +} + +#if JS_HAS_TOSOURCE +static JSBool +math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = ATOM_KEY(cx->runtime->atomState.MathAtom); + return JS_TRUE; +} +#endif + +static JSFunctionSpec math_static_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, math_toSource, 0, 0, 0}, +#endif + {"abs", math_abs, 1, 0, 0}, + {"acos", math_acos, 1, 0, 0}, + {"asin", math_asin, 1, 0, 0}, + {"atan", math_atan, 1, 0, 0}, + {"atan2", math_atan2, 2, 0, 0}, + {"ceil", math_ceil, 1, 0, 0}, + {"cos", math_cos, 1, 0, 0}, + {"exp", math_exp, 1, 0, 0}, + {"floor", math_floor, 1, 0, 0}, + {"log", math_log, 1, 0, 0}, + {"max", math_max, 2, 0, 0}, + {"min", math_min, 2, 0, 0}, + {"pow", math_pow, 2, 0, 0}, + {"random", math_random, 0, 0, 0}, + {"round", math_round, 1, 0, 0}, + {"sin", math_sin, 1, 0, 0}, + {"sqrt", math_sqrt, 1, 0, 0}, + {"tan", math_tan, 1, 0, 0}, + {0,0,0,0,0} +}; + +JSObject * +js_InitMathClass(JSContext *cx, JSObject *obj) +{ + JSObject *Math; + + Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0); + if (!Math) + 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/src/extension/script/js/jsmath.h b/src/extension/script/js/jsmath.h new file mode 100644 index 000000000..7a6b21657 --- /dev/null +++ b/src/extension/script/js/jsmath.h @@ -0,0 +1,55 @@ +/* ***** 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 ***** */ + +/* -*- Mode: C; tab-width: 8 -*- + * Copyright (C) 1998-1999 Netscape Communications Corporation, All Rights Reserved. + */ + +#ifndef jsmath_h___ +#define jsmath_h___ +/* + * JS math functions. + */ + +JS_BEGIN_EXTERN_C + +extern JSObject * +js_InitMathClass(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jsmath_h___ */ diff --git a/src/extension/script/js/jsnum.c b/src/extension/script/js/jsnum.c new file mode 100644 index 000000000..6f6963b69 --- /dev/null +++ b/src/extension/script/js/jsnum.c @@ -0,0 +1,1058 @@ +/* -*- 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 ***** */ + +/* + * JS number type and wrapper class. + */ +#include "jsstddef.h" +#if defined(XP_WIN) || defined(XP_OS2) +#include +#endif +#include +#include +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdtoa.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsprf.h" +#include "jsstr.h" + +static JSBool +num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); + return JS_TRUE; +} + +static JSBool +num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble x; + + if (!js_ValueToNumber(cx, argv[0], &x)) + return JS_FALSE; + *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); + return JS_TRUE; +} + +static JSBool +num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + const jschar *bp, *ep; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + /* XXXbe js_strtod shouldn't require NUL termination */ + bp = js_UndependString(cx, str); + if (!bp) + return JS_FALSE; + if (!js_strtod(cx, bp, &ep, &d)) + return JS_FALSE; + if (ep == bp) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + return js_NewNumberValue(cx, d, rval); +} + +/* See ECMA 15.1.2.2. */ +static JSBool +num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsint radix; + jsdouble d; + const jschar *bp, *ep; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + if (argc > 1) { + if (!js_ValueToECMAInt32(cx, argv[1], &radix)) + return JS_FALSE; + } else + radix = 0; + + if (radix != 0 && (radix < 2 || radix > 36)) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + /* XXXbe js_strtointeger shouldn't require NUL termination */ + bp = js_UndependString(cx, str); + if (!bp) + return JS_FALSE; + if (!js_strtointeger(cx, bp, &ep, radix, &d)) + return JS_FALSE; + if (ep == bp) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + return js_NewNumberValue(cx, d, rval); +} + +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"; + +static JSFunctionSpec number_functions[] = { + {"isNaN", num_isNaN, 1,0,0}, + {"isFinite", num_isFinite, 1,0,0}, + {"parseFloat", num_parseFloat, 1,0,0}, + {"parseInt", num_parseInt, 2,0,0}, + {0,0,0,0,0} +}; + +static JSClass number_class = { + "Number", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static JSBool +Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsdouble d; + jsval v; + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + } else { + d = 0.0; + } + if (!js_NewNumberValue(cx, d, &v)) + return JS_FALSE; + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + *rval = v; + return JS_TRUE; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); + return JS_TRUE; +} + +#if JS_HAS_TOSOURCE +static JSBool +num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + jsdouble d; + char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; + char buf[64]; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &number_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + 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))", number_class.name, numStr); + str = JS_NewStringCopyZ(cx, buf); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ +static char * +IntToString(jsint i, 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. + */ + do { + jsuint newu = u / 10; + *--cp = (char)(u - newu * 10) + '0'; + u = newu; + } while (u != 0); + + if (i < 0) + *--cp = '-'; + + return cp; +} + +static JSBool +num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + jsdouble d; + jsint base; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &number_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + 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) { + if (!js_ValueToECMAInt32(cx, argv[0], &base)) + return JS_FALSE; + if (base < 2 || base > 36) { + char numBuf[12]; + char *numStr = IntToString(base, numBuf, sizeof numBuf); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, + numStr); + return JS_FALSE; + } + } + if (base == 10) + str = js_NumberToString(cx, d); + else { + char *dStr = JS_dtobasestr(base, d); + if (!dStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + str = JS_NewStringCopyZ(cx, dStr); + free(dStr); + } + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ +/* + * For now, forcibly ignore the first (or any) argument and return toString(). + * ECMA allows this, although it doesn't 'encourage it'. + * [The first argument is being reserved by ECMA and we don't want it confused + * with a radix] + */ + return num_toString(cx, obj, 0, argv, rval); +} + +static JSBool +num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_InstanceOf(cx, obj, &number_class, argv)) + return JS_FALSE; + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + return JS_TRUE; +} + + +#if JS_HAS_NUMBER_FORMATS +#define MAX_PRECISION 100 + +static JSBool +num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode, + JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset) +{ + jsval v; + jsdouble d, precision; + JSString *str; + char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */ + + if (!JS_InstanceOf(cx, obj, &number_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_NUMBER(v)); + d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); + + if (JSVAL_IS_VOID(argv[0])) { + precision = 0.0; + oneArgMode = zeroArgMode; + } else { + if (!js_ValueToNumber(cx, argv[0], &precision)) + 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; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ + return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0); +} + +static JSBool +num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ + return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1); +} + +static JSBool +num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ + return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0); +} +#endif /* JS_HAS_NUMBER_FORMATS */ + + +static JSFunctionSpec number_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, num_toSource, 0,0,0}, +#endif + {js_toString_str, num_toString, 0,0,0}, + {js_toLocaleString_str, num_toLocaleString, 0,0,0}, + {js_valueOf_str, num_valueOf, 0,0,0}, +#if JS_HAS_NUMBER_FORMATS + {"toFixed", num_toFixed, 1,0,0}, + {"toExponential", num_toExponential, 1,0,0}, + {"toPrecision", num_toPrecision, 1,0,0}, +#endif + {0,0,0,0,0} +}; + +/* 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}} +}; + +static jsdouble NaN; + + +#if defined XP_WIN && \ + !defined __MWERKS__ && \ + (defined _M_IX86 || \ + (defined __GNUC__ && !defined __MINGW32__)) + +/* + * Set the exception mask to mask all exceptions and set the FPU precision + * to 53 bit mantissa. + * On Alpha platform this is handled via Compiler option. + */ +#define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC) + +#else + +#define FIX_FPU() ((void)0) + +#endif + +JSBool +js_InitRuntimeNumberState(JSContext *cx) +{ + JSRuntime *rt; + jsdpun u; + + rt = cx->runtime; + JS_ASSERT(!rt->jsNaN); + + FIX_FPU(); + + u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; + u.s.lo = 0xffffffff; + number_constants[NC_NaN].dval = NaN = u.d; + rt->jsNaN = js_NewDouble(cx, NaN); + if (!rt->jsNaN || !js_LockGCThing(cx, rt->jsNaN)) + return JS_FALSE; + + u.s.hi = JSDOUBLE_HI32_EXPMASK; + u.s.lo = 0x00000000; + number_constants[NC_POSITIVE_INFINITY].dval = u.d; + rt->jsPositiveInfinity = js_NewDouble(cx, u.d); + if (!rt->jsPositiveInfinity || + !js_LockGCThing(cx, rt->jsPositiveInfinity)) { + return JS_FALSE; + } + + u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; + u.s.lo = 0x00000000; + number_constants[NC_NEGATIVE_INFINITY].dval = u.d; + rt->jsNegativeInfinity = js_NewDouble(cx, u.d); + if (!rt->jsNegativeInfinity || + !js_LockGCThing(cx, rt->jsNegativeInfinity)) { + return JS_FALSE; + } + + u.s.hi = 0; + u.s.lo = 1; + number_constants[NC_MIN_VALUE].dval = u.d; + + return JS_TRUE; +} + +void +js_FinishRuntimeNumberState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + js_UnlockGCThingRT(rt, rt->jsNaN); + js_UnlockGCThingRT(rt, rt->jsNegativeInfinity); + js_UnlockGCThingRT(rt, rt->jsPositiveInfinity); + + rt->jsNaN = NULL; + rt->jsNegativeInfinity = NULL; + rt->jsPositiveInfinity = 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, &number_class, Number, 1, + NULL, number_methods, NULL, NULL); + if (!proto || !(ctor = JS_GetConstructor(cx, proto))) + return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, 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, DOUBLE_TO_JSVAL(rt->jsNaN), + NULL, NULL, JSPROP_PERMANENT)) { + return NULL; + } + + /* ECMA 15.1.1.2 */ + if (!JS_DefineProperty(cx, obj, js_Infinity_str, + DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), + NULL, NULL, JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +jsdouble * +js_NewDouble(JSContext *cx, jsdouble d) +{ + jsdouble *dp; + + dp = (jsdouble *) js_AllocGCThing(cx, GCX_DOUBLE); + if (!dp) + return NULL; + *dp = d; + return dp; +} + +void +js_FinalizeDouble(JSContext *cx, jsdouble *dp) +{ + *dp = NaN; +} + +JSBool +js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) +{ + jsdouble *dp; + + dp = js_NewDouble(cx, d); + if (!dp) + return JS_FALSE; + *rval = DOUBLE_TO_JSVAL(dp); + return JS_TRUE; +} + +JSBool +js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) +{ + jsint i; + JSBool ok; + + SET_FPU(); + + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { + *rval = INT_TO_JSVAL(i); + ok = JS_TRUE; + } else { + ok = js_NewDoubleValue(cx, d, rval); + } + + RESTORE_FPU(); + return ok; +} + +JSObject * +js_NumberToObject(JSContext *cx, jsdouble d) +{ + JSObject *obj; + jsval v; + + obj = js_NewObject(cx, &number_class, NULL, NULL); + if (!obj) + return NULL; + if (!js_NewNumberValue(cx, d, &v)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); + return obj; +} + +JSString * +js_NumberToString(JSContext *cx, jsdouble d) +{ + jsint i; + char buf[DTOSTR_STANDARD_BUFFER_SIZE]; + char *numStr; + + if (JSDOUBLE_IS_INT(d, i)) + numStr = IntToString(i, buf, sizeof buf); + else { + numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + return JS_NewStringCopyZ(cx, numStr); +} + +JSBool +js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) +{ + JSObject *obj; + JSString *str; + const jschar *bp, *ep; + + if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj) { + *dp = 0; + return JS_TRUE; + } + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v)) + return JS_FALSE; + } + if (JSVAL_IS_INT(v)) { + *dp = (jsdouble)JSVAL_TO_INT(v); + } else if (JSVAL_IS_DOUBLE(v)) { + *dp = *JSVAL_TO_DOUBLE(v); + } else 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). + */ + /* XXXbe js_strtod shouldn't require NUL termination */ + bp = js_UndependString(cx, str); + if (!bp) + return JS_FALSE; + if ((!js_strtod(cx, bp, &ep, dp) || + js_SkipWhiteSpace(ep) != bp + str->length) && + (!js_strtointeger(cx, bp, &ep, 0, dp) || + js_SkipWhiteSpace(ep) != bp + str->length)) { + goto badstr; + } + } else if (JSVAL_IS_BOOLEAN(v)) { + *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; + } else { +#if JS_BUG_FALLIBLE_TONUM + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); +badstr: + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN, + JS_GetStringBytes(str)); + + } + return JS_FALSE; +#else +badstr: + *dp = *cx->runtime->jsNaN; +#endif + } + return JS_TRUE; +} + +JSBool +js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + jsdouble d; + + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + return js_DoubleToECMAInt32(cx, d, ip); +} + +JSBool +js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip) +{ + jsdouble two32 = 4294967296.0; + jsdouble two31 = 2147483648.0; + + if (!JSDOUBLE_IS_FINITE(d) || d == 0) { + *ip = 0; + return JS_TRUE; + } + d = fmod(d, two32); + d = (d >= 0) ? floor(d) : ceil(d) + two32; + if (d >= two31) + *ip = (int32)(d - two32); + else + *ip = (int32)d; + return JS_TRUE; +} + +JSBool +js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + jsdouble d; + + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + return js_DoubleToECMAUint32(cx, d, ip); +} + +JSBool +js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip) +{ + JSBool neg; + jsdouble two32 = 4294967296.0; + + if (!JSDOUBLE_IS_FINITE(d) || d == 0) { + *ip = 0; + return JS_TRUE; + } + + neg = (d < 0); + d = floor(neg ? -d : d); + d = neg ? -d : d; + + d = fmod(d, two32); + + d = (d >= 0) ? d : d + two32; + *ip = (uint32)d; + return JS_TRUE; +} + +JSBool +js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) +{ + jsdouble d; + JSString *str; + + if (JSVAL_IS_INT(v)) { + *ip = JSVAL_TO_INT(v); + return JS_TRUE; + } + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_CONVERT, JS_GetStringBytes(str)); + + } + return JS_FALSE; + } + *ip = (int32)floor(d + 0.5); /* Round to nearest */ + return JS_TRUE; +} + +JSBool +js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) +{ + jsdouble d; + jsuint i, m; + JSBool neg; + + if (!js_ValueToNumber(cx, v, &d)) + return JS_FALSE; + if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { + *ip = 0; + return JS_TRUE; + } + i = (jsuint)d; + if ((jsdouble)i == d) { + *ip = (uint16)i; + return JS_TRUE; + } + 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; + *ip = (uint16) d; + return JS_TRUE; +} + +jsdouble +js_DoubleToInteger(jsdouble d) +{ + JSBool neg; + + if (d == 0) + return d; + if (!JSDOUBLE_IS_FINITE(d)) { + if (JSDOUBLE_IS_NaN(d)) + return 0; + return d; + } + neg = (d < 0); + d = floor(neg ? -d : d); + return neg ? -d : d; +} + + +JSBool +js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp) +{ + char cbuf[32]; + size_t i; + char *cstr, *istr, *estr; + JSBool negative; + jsdouble d; + const jschar *s1 = js_SkipWhiteSpace(s); + size_t length = js_strlen(s1); + + /* Use cbuf to avoid malloc */ + if (length >= sizeof cbuf) { + cstr = (char *) JS_malloc(cx, length + 1); + if (!cstr) + return JS_FALSE; + } else { + cstr = cbuf; + } + + for (i = 0; i <= length; i++) { + if (s1[i] >> 8) { + cstr[i] = 0; + break; + } + cstr[i] = (char)s1[i]; + } + + istr = cstr; + if ((negative = (*istr == '-')) != 0 || *istr == '+') + istr++; + if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { + d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); + estr = istr + 8; + } else { + int err; + d = JS_strtod(cstr, &estr, &err); + if (err == JS_DTOA_ENOMEM) { + JS_ReportOutOfMemory(cx); + if (cstr != cbuf) + JS_free(cx, cstr); + return JS_FALSE; + } + if (err == JS_DTOA_ERANGE) { + if (d == HUGE_VAL) + d = *cx->runtime->jsPositiveInfinity; + else if (d == -HUGE_VAL) + d = *cx->runtime->jsNegativeInfinity; + } +#ifdef HPUX + if (d == 0.0 && negative) { + /* + * "-0", "-1e-2000" come out as positive zero + * here on HPUX. Force a negative zero instead. + */ + JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT; + JSDOUBLE_LO32(d) = 0; + } +#endif + } + + i = estr - cstr; + if (cstr != cbuf) + JS_free(cx, 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 **ep, jsint base, jsdouble *dp) +{ + JSBool negative; + jsdouble value; + const jschar *start; + const jschar *s1 = js_SkipWhiteSpace(s); + + if ((negative = (*s1 == '-')) != 0 || *s1 == '+') + s1++; + + 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] == 'X' || s1[1] == 'x') { /* Hex */ + s1 += 2; + base = 16; + } else { /* Octal */ + base = 8; + } + } else { + base = 10; /* Default to decimal. */ + } + } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) { + /* If base is 16, ignore hex prefix. */ + s1 += 2; + } + + /* + * Done with the preliminaries; find some prefix of the string that's + * a number in the given base. + */ + start = s1; /* Mark - if string is empty, we return NaN. */ + value = 0.0; + for (;;) { + 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; + s1++; + } + + 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 *) JS_malloc(cx, 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); + JS_free(cx, cstr); + return JS_FALSE; + } + if (err == JS_DTOA_ERANGE && value == HUGE_VAL) + value = *cx->runtime->jsPositiveInfinity; + JS_free(cx, 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.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) { + *dp = 0.0; + *ep = s; + } else { + *dp = negative ? -value : value; + *ep = s1; + } + return JS_TRUE; +} diff --git a/src/extension/script/js/jsnum.h b/src/extension/script/js/jsnum.h new file mode 100644 index 000000000..46992e2b6 --- /dev/null +++ b/src/extension/script/js/jsnum.h @@ -0,0 +1,280 @@ +/* -*- 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___ +/* + * 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. + * + * When a JS number is treated as an object (followed by . or []), the runtime + * wraps it with a JSObject whose valueOf method returns the unwrapped number. + */ + +JS_BEGIN_EXTERN_C + +/* + * Stefan Hanske reports: + * ARM is a little endian architecture but 64 bit double words are stored + * differently: the 32 bit words are in little endian byte order, the two words + * are stored in big endian`s way. + */ + +#if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) +#define CPU_IS_ARM +#endif + +typedef union jsdpun { + struct { +#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) + uint32 lo, hi; +#else + uint32 hi, lo; +#endif + } s; + jsdouble d; +} jsdpun; + +#if (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || __GNUC__ > 2 +/* + * This version of the macros is safe for the alias optimizations that gcc + * does, but uses gcc-specific extensions. + */ + +#define JSDOUBLE_HI32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.hi; })) +#define JSDOUBLE_LO32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.lo; })) +#define JSDOUBLE_SET_HI32(x, y) \ + (__extension__ ({ jsdpun u; u.d = (x); u.s.hi = (y); (x) = u.d; })) +#define JSDOUBLE_SET_LO32(x, y) \ + (__extension__ ({ jsdpun u; u.d = (x); u.s.lo = (y); (x) = u.d; })) + +#else /* not or old GNUC */ + +/* + * We don't know of any non-gcc compilers that perform alias optimization, + * so this code should work. + */ + +#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) +#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) +#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) +#else +#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0]) +#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1]) +#endif + +#define JSDOUBLE_SET_HI32(x, y) (JSDOUBLE_HI32(x)=(y)) +#define JSDOUBLE_SET_LO32(x, y) (JSDOUBLE_LO32(x)=(y)) + +#endif /* not or old GNUC */ + +#define JSDOUBLE_HI32_SIGNBIT 0x80000000 +#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 +#define JSDOUBLE_HI32_MANTMASK 0x000fffff + +#define JSDOUBLE_IS_NaN(x) \ + ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \ + (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK))) + +#define JSDOUBLE_IS_INFINITE(x) \ + ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \ + !JSDOUBLE_LO32(x)) + +#define JSDOUBLE_IS_FINITE(x) \ + ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK) + +#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \ + JSDOUBLE_LO32(d) == 0) + +/* + * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid + * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is + * safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point + * comparisons under MSVC. + */ +#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) \ + && !JSDOUBLE_IS_NEGZERO(d) \ + && ((d) == (i = (jsint)(d)))) + +/* Initialize number constants and runtime state for the first context. */ +extern JSBool +js_InitRuntimeNumberState(JSContext *cx); + +extern void +js_FinishRuntimeNumberState(JSContext *cx); + +/* Initialize the Number class, returning its prototype object. */ +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[]; + +/* GC-allocate a new JS number. */ +extern jsdouble * +js_NewDouble(JSContext *cx, jsdouble d); + +extern void +js_FinalizeDouble(JSContext *cx, jsdouble *dp); + +extern JSBool +js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); + +extern JSBool +js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); + +/* Construct a Number instance that wraps around d. */ +extern JSObject * +js_NumberToObject(JSContext *cx, jsdouble d); + +/* Convert a number to a GC'ed string. */ +extern JSString * +js_NumberToString(JSContext *cx, jsdouble d); + +/* + * Convert a value to a number, returning false after reporting any error, + * otherwise returning true with *dp set. + */ +extern JSBool +js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); + +/* + * Convert a value or a double to an int32, according to the ECMA rules + * for ToInt32. + */ +extern JSBool +js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); + +extern JSBool +js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip); + +/* + * Convert a value or a double to a uint32, according to the ECMA rules + * for ToUint32. + */ +extern JSBool +js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); + +extern JSBool +js_DoubleToECMAUint32(JSContext *cx, jsdouble d, 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 JSBool +js_ValueToInt32(JSContext *cx, jsval v, int32 *ip); + +/* + * Convert a value to a number, then to a uint16 according to the ECMA rules + * for ToUint16. + */ +extern JSBool +js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); + +/* + * 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. + */ +extern jsdouble +js_DoubleToInteger(jsdouble 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 **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 **ep, jsint radix, jsdouble *dp); + + +#ifdef XP_OS2 +/* XXX see bug 224487 + * On OS/2, some system function calls seem to change the FPU control word, + * such that we crash with a floating underflow exception. The FIX_FPU() call + * in jsnum.c does not always work, as sometimes FIX_FPU() is called BEFORE the + * OS/2 system call that horks the FPU control word. So, on OS/2, we need to do + * what the comment at the top of this file (around line 80) suggests: setting + * the FPU precision (& exceptions mask) before calling strtod or dtoa. Also, + * we play nice by reverting to the previous FPU control word at the end of + * those functions. + * + * Set the exception mask to mask all exceptions and set the FPU precision + * to 53 bit mantissa. + */ + #define SET_FPU() unsigned cw = _control87(0,0); \ + _control87((CW_DEFAULT & ~MCW_PC) | PC_53, 0xffff) + #define RESTORE_FPU() _control87(cw, MCW_EM | MCW_IC | MCW_RC | MCW_PC) +#else + #define SET_FPU() ((void)0) + #define RESTORE_FPU() ((void)0) +#endif + +JS_END_EXTERN_C + +#endif /* jsnum_h___ */ diff --git a/src/extension/script/js/jsobj.c b/src/extension/script/js/jsobj.c new file mode 100644 index 000000000..1da1f138b --- /dev/null +++ b/src/extension/script/js/jsobj.c @@ -0,0 +1,3900 @@ +/* -*- 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 object implementation. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ +#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 "jscntxt.h" +#include "jsconfig.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsopcode.h" + +#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ + +#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 + +#ifdef XP_MAC +#pragma export on +#endif + +JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { + js_NewObjectMap, js_DestroyObjectMap, +#if defined JS_THREADSAFE && defined DEBUG + _js_LookupProperty, js_DefineProperty, +#else + js_LookupProperty, js_DefineProperty, +#endif + js_GetProperty, js_SetProperty, + js_GetAttributes, js_SetAttributes, + js_DeleteProperty, js_DefaultValue, + js_Enumerate, js_CheckAccess, + NULL, NATIVE_DROP_PROPERTY, + js_Call, js_Construct, + NULL, js_HasInstance, + js_SetProtoOrParent, js_SetProtoOrParent, + js_Mark, js_Clear, + js_GetRequiredSlot, js_SetRequiredSlot +}; + +#ifdef XP_MAC +#pragma export off +#endif + +JSClass js_ObjectClass = { + js_Object_str, + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + 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_PERMANENT,obj_getCount, obj_getCount}, + {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; + JSAccessMode mode; + uintN attrs; + + slot = (uint32) JSVAL_TO_INT(id); + if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) + return JS_FALSE; + if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { + id = (jsid)cx->runtime->atomState.protoAtom; + mode = JSACC_PROTO; + } else { + id = (jsid)cx->runtime->atomState.parentAtom; + mode = JSACC_PARENT; + } + if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs)) + return JS_FALSE; + *vp = OBJ_GET_SLOT(cx, obj, slot); + return JS_TRUE; +} + +static JSBool +obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSObject *pobj; + uint32 slot; + uintN attrs; + + if (!JSVAL_IS_OBJECT(*vp)) + return JS_TRUE; + pobj = JSVAL_TO_OBJECT(*vp); + 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. */ + if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs)) + return JS_FALSE; + + return js_SetProtoOrParent(cx, obj, slot, pobj); +} + +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; + + /* Get the number of properties to enumerate. */ + iter_state = JSVAL_NULL; + ok = OBJ_ENUMERATE(cx, obj, 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 (iter_state != JSVAL_NULL) + ok = OBJ_ENUMERATE(cx, obj, 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) +{ + JSRuntime *rt; + JSObject *obj2, *oldproto; + JSScope *scope, *newscope; + + /* + * Serialize all proto and parent setting in order to detect cycles. + * We nest locks in this function, and only here, in the following orders: + * + * (1) rt->setSlotLock < pobj's scope lock; + * rt->setSlotLock < pobj's proto-or-parent's scope lock; + * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock; + * etc... + * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock. + * + * We avoid AB-BA deadlock by restricting obj from being on pobj's parent + * or proto chain (pobj may already be on obj's parent or proto chain; it + * could be moving up or down). We finally order obj with respect to pobj + * at the bottom of this routine (just before releasing rt->setSlotLock), + * by making pobj be obj's prototype or parent. + * + * After we have set the slot and released rt->setSlotLock, another call + * to js_SetProtoOrParent could nest locks according to the first order + * list above, but it cannot deadlock with any other thread. For there + * to be a deadlock, other parts of the engine would have to nest scope + * locks in the opposite order. XXXbe ensure they don't! + */ + rt = cx->runtime; +#ifdef JS_THREADSAFE + + JS_ACQUIRE_LOCK(rt->setSlotLock); + while (rt->setSlotBusy) { + jsrefcount saveDepth; + + /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */ + JS_RELEASE_LOCK(rt->setSlotLock); + saveDepth = JS_SuspendRequest(cx); + JS_ACQUIRE_LOCK(rt->setSlotLock); + if (rt->setSlotBusy) + JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT); + JS_RELEASE_LOCK(rt->setSlotLock); + JS_ResumeRequest(cx, saveDepth); + JS_ACQUIRE_LOCK(rt->setSlotLock); + } + rt->setSlotBusy = JS_TRUE; + JS_RELEASE_LOCK(rt->setSlotLock); + +#define SET_SLOT_DONE(rt) \ + JS_BEGIN_MACRO \ + JS_ACQUIRE_LOCK((rt)->setSlotLock); \ + (rt)->setSlotBusy = JS_FALSE; \ + JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \ + JS_RELEASE_LOCK((rt)->setSlotLock); \ + JS_END_MACRO + +#else + +#define SET_SLOT_DONE(rt) /* nothing */ + +#endif + + obj2 = pobj; + while (obj2) { + if (obj2 == obj) { + SET_SLOT_DONE(rt); + 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; + } + obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); + } + + if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { + /* Check to see whether obj shares its prototype's scope. */ + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)); + if (oldproto && OBJ_SCOPE(oldproto) == scope) { + /* Either obj needs a new empty scope, or it should share pobj's. */ + if (!pobj || + !OBJ_IS_NATIVE(pobj) || + OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) { + /* + * With no proto and no scope of its own, obj is truly empty. + * + * If pobj is not native, obj needs its own empty scope -- it + * should not continue to share oldproto's scope once oldproto + * is not on obj's prototype chain. That would put properties + * from oldproto's scope ahead of properties defined by pobj, + * in lookup order. + * + * If pobj's class differs from oldproto's, we may need a new + * scope to handle differences in private and reserved slots, + * so we suboptimally but safely make one. + */ + scope = js_GetMutableScope(cx, obj); + if (!scope) { + JS_UNLOCK_OBJ(cx, obj); + SET_SLOT_DONE(rt); + return JS_FALSE; + } + } else if (OBJ_SCOPE(pobj) != scope) { +#ifdef JS_THREADSAFE + /* + * We are about to nest scope locks. Help jslock.c:ShareScope + * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while + * avoiding deadlock, by recording scope in rt->setSlotScope. + */ + if (scope->ownercx) { + JS_ASSERT(scope->ownercx == cx); + rt->setSlotScope = scope; + } +#endif + + /* We can't deadlock because we checked for cycles above (2). */ + JS_LOCK_OBJ(cx, pobj); + newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); + obj->map = &newscope->map; + js_DropObjectMap(cx, &scope->map, obj); + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + scope = newscope; +#ifdef JS_THREADSAFE + rt->setSlotScope = NULL; +#endif + } + } + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj)); + JS_UNLOCK_SCOPE(cx, scope); + } else { + OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj)); + } + + SET_SLOT_DONE(rt); + return JS_TRUE; + +#undef SET_SLOT_DONE +} + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_object(const void *key) +{ + return (JSHashNumber)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; + + map = &cx->sharpObjectMap; + 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, (void *)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_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + break; + if (prop) { + ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); + if (ok) { + if (OBJ_IS_NATIVE(obj2) && + (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + val = JSVAL_NULL; + if (attrs & JSPROP_GETTER) + val = (jsval) ((JSScopeProperty*)prop)->getter; + 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 = (jsval) ((JSScopeProperty*)prop)->setter; + } + } else { + ok = OBJ_GET_PROPERTY(cx, obj, id, &val); + } + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } +#else + ok = OBJ_GET_PROPERTY(cx, obj, 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 = (jsatomid) he->value; + if (sharpid == 0) { + sharpid = ++map->sharpgen << SHARP_ID_SHIFT; + he->value = (void *) 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; + + /* 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; + } + + ida = NULL; + if (map->depth == 0) { + he = MarkSharpObjects(cx, obj, &ida); + if (!he) + goto bad; + JS_ASSERT((((jsatomid) 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 OBJ_GET_PROPERTY() call is not + * idempotent. + */ + if (!he) { + he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); + if (!he) { + JS_ReportOutOfMemory(cx); + goto bad; + } + *sp = NULL; + sharpid = 0; + goto out; + } + } + + sharpid = (jsatomid) he->value; + if (sharpid == 0) { + *sp = NULL; + } else { + 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) { + JS_free(cx, *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) { + 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) { + map->sharpgen = 0; + JS_HashTableDestroy(map->table); + map->table = NULL; + } + if (idap) { + ida = *idap; + if (ida) { + JS_DestroyIdArray(cx, ida); + *idap = NULL; + } + } +} + +#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */ + +#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE +JSBool +js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSBool ok, outermost; + JSHashEntry *he; + JSIdArray *ida; + jschar *chars, *ochars, *vsharp; + const jschar *idstrchars, *vchars; + size_t nchars, idstrlength, gsoplength, vlength, vsharplength; + char *comma; + jsint i, j, length, valcnt; + jsid id; +#if JS_HAS_GETTER_SETTER + JSObject *obj2; + JSProperty *prop; + uintN attrs; +#endif + jsval val[2]; + JSString *gsop[2]; + JSAtom *atom; + JSString *idstr, *valstr, *str; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return JS_FALSE; + } + + /* + * obj_toString for 1.2 calls toSource, and doesn't want the extra parens + * on the outside. + */ + outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0); + he = js_EnterSharpObject(cx, obj, &ida, &chars); + if (!he) + return JS_FALSE; + 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 *) 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 *) + realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); + if (!chars) { + 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; + + for (i = 0, length = ida->length; i < length; i++) { + /* Get strings for id and value and GC-root them via argv. */ + id = ida->vector[i]; + +#if JS_HAS_GETTER_SETTER + + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto error; + valcnt = 0; + if (prop) { + ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); + if (!ok) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + goto error; + } + if (OBJ_IS_NATIVE(obj2) && + (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + if (attrs & JSPROP_GETTER) { + val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; +#ifdef OLD_GETTER_SETTER + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.getterAtom); +#else + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.getAtom); +#endif + valcnt++; + } + if (attrs & JSPROP_SETTER) { + val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; +#ifdef OLD_GETTER_SETTER + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.setterAtom); +#else + gsop[valcnt] = + ATOM_TO_STRING(cx->runtime->atomState.setAtom); +#endif + valcnt++; + } + } else { + valcnt = 1; + gsop[0] = NULL; + ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + +#else /* !JS_HAS_GETTER_SETTER */ + + valcnt = 1; + gsop[0] = NULL; + ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); + +#endif /* !JS_HAS_GETTER_SETTER */ + + if (!ok) + goto error; + + /* Convert id to a jsval and then to a string. */ + atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id; + id = ID_TO_VALUE(id); + idstr = js_ValueToString(cx, id); + if (!idstr) { + ok = JS_FALSE; + goto error; + } + argv[0] = STRING_TO_JSVAL(idstr); + + /* + * If id is a string that's a reserved identifier, or else id is not + * an identifier at all, then it needs to be quoted. + */ + if (atom && (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))) { + idstr = js_QuoteString(cx, idstr, (jschar)'\''); + if (!idstr) { + ok = JS_FALSE; + goto error; + } + argv[0] = STRING_TO_JSVAL(idstr); + } + idstrchars = JSSTRING_CHARS(idstr); + idstrlength = JSSTRING_LENGTH(idstr); + + 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; + } + argv[1+j] = STRING_TO_JSVAL(valstr); + vchars = JSSTRING_CHARS(valstr); + vlength = JSSTRING_LENGTH(valstr); + +#ifndef OLD_GETTER_SETTER + /* Remove 'function ' from beginning of valstr. */ + if (gsop[j]) { + int n = strlen(js_function_str) + 1; + vchars += n; + vlength -= n; + } +#endif + + /* 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); + } else { + if (vsharp) { + vsharplength = js_strlen(vsharp); + MAKE_SHARP(he); + } + js_LeaveSharpObject(cx, NULL); + } + } +#endif + + /* Allocate 1 + 1 at end for closing brace and terminating 0. */ + chars = (jschar *) + realloc((ochars = chars), + (nchars + (comma ? 2 : 0) + + idstrlength + 1 + + (gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) + + vsharplength + vlength + + (outermost ? 2 : 1) + 1) * sizeof(jschar)); + if (!chars) { + /* Save code space on error: let JS_free ignore null vsharp. */ + JS_free(cx, vsharp); + free(ochars); + goto error; + } + + if (comma) { + chars[nchars++] = comma[0]; + chars[nchars++] = comma[1]; + } + comma = ", "; + +#ifdef OLD_GETTER_SETTER + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + if (gsop[j]) { + chars[nchars++] = ' '; + gsoplength = JSSTRING_LENGTH(gsop[j]); + js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); + nchars += gsoplength; + } + chars[nchars++] = ':'; +#else + if (gsop[j]) { + gsoplength = JSSTRING_LENGTH(gsop[j]); + js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); + nchars += gsoplength; + chars[nchars++] = ' '; + } + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + if (!gsop[j]) + chars[nchars++] = ':'; +#endif + if (vsharplength) { + js_strncpy(&chars[nchars], vsharp, vsharplength); + nchars += vsharplength; + } + js_strncpy(&chars[nchars], vchars, vlength); + nchars += vlength; + + if (vsharp) + JS_free(cx, vsharp); + } + } + + chars[nchars++] = '}'; + if (outermost) + chars[nchars++] = ')'; + chars[nchars] = 0; + + error: + js_LeaveSharpObject(cx, &ida); + + if (!ok) { + if (chars) + free(chars); + return ok; + } + + if (!chars) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + make_string: + str = js_NewString(cx, chars, nchars, 0); + if (!str) { + free(chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */ + +JSBool +js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jschar *chars; + size_t nchars; + const char *clazz, *prefix; + JSString *str; + +#if JS_HAS_INITIALIZERS + if (cx->version == JSVERSION_1_2) + return js_obj_toSource(cx, obj, argc, argv, rval); +#endif + + clazz = OBJ_GET_CLASS(cx, obj)->name; + nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ + chars = (jschar *) JS_malloc(cx, (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, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp, *caller; + JSBool indirectCall; + JSObject *scopeobj; + JSString *str; + const char *file; + uintN line; + JSPrincipals *principals; + JSScript *script; + JSBool ok; +#if JS_HAS_EVAL_THIS_SCOPE + JSObject *callerScopeChain = NULL, *callerVarObj = NULL; + JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE; +#endif + + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL); + + if (JSVERSION_IS_ECMA(cx->version) && + indirectCall && + !JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + 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 JS_HAS_SCRIPT_OBJECT + /* + * Script.prototype.compile/exec and Object.prototype.eval all take an + * optional trailing argument that overrides the scope object. + */ + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + if (!scopeobj) +#endif + { +#if JS_HAS_EVAL_THIS_SCOPE + /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ + if (indirectCall) { + callerScopeChain = caller->scopeChain; + if (obj != callerScopeChain) { + scopeobj = js_NewObject(cx, &js_WithClass, obj, + callerScopeChain); + if (!scopeobj) + return JS_FALSE; + + /* Set fp->scopeChain too, for the compiler. */ + caller->scopeChain = fp->scopeChain = scopeobj; + setCallerScopeChain = JS_TRUE; + } + + callerVarObj = caller->varobj; + if (obj != callerVarObj) { + /* Set fp->varobj too, for the compiler. */ + caller->varobj = fp->varobj = obj; + setCallerVarObj = JS_TRUE; + } + } + /* From here on, control must exit through label out with ok set. */ +#endif + +#if JS_BUG_EVAL_THIS_SCOPE + /* An old version used the object in which eval was found for scope. */ + scopeobj = obj; +#else + /* Compile using caller's current scope object. */ + if (caller) + scopeobj = caller->scopeChain; +#endif + } + + str = JSVAL_TO_STRING(argv[0]); + if (caller) { + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); + principals = JS_EvalFramePrincipals(cx, fp, caller); + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* XXXbe set only for the compiler, which does not currently test it */ + fp->flags |= JSFRAME_EVAL; + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, + JSSTRING_CHARS(str), + JSSTRING_LENGTH(str), + file, line); + if (!script) { + ok = JS_FALSE; + goto out; + } + +#if !JS_BUG_EVAL_THIS_SCOPE +#if JS_HAS_SCRIPT_OBJECT + if (argc < 2) +#endif + { + /* Execute using caller's new scope object (might be a Call object). */ + if (caller) + scopeobj = caller->scopeChain; + } +#endif + ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + JS_DestroyScript(cx, script); + +out: +#if JS_HAS_EVAL_THIS_SCOPE + /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ + if (setCallerScopeChain) + caller->scopeChain = callerScopeChain; + if (setCallerVarObj) + caller->varobj = callerVarObj; +#endif + return ok; +} + +JS_STATIC_DLL_CALLBACK(const void *) +resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr) +{ + JSResolvingEntry *entry = (JSResolvingEntry *)hdr; + + return &entry->key; +} + +JS_STATIC_DLL_CALLBACK(JSDHashNumber) +resolving_HashKey(JSDHashTable *table, const void *ptr) +{ + const JSResolvingKey *key = (const JSResolvingKey *)ptr; + + return ((JSDHashNumber)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_GetKey, + resolving_HashKey, + resolving_MatchEntry, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +static JSBool +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; +} + +static void +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 StartResolving. + */ + table = cx->resolvingTable; + if (table->generation != generation) { + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); + } + 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); +} + +#if JS_HAS_OBJ_WATCHPOINT + +static JSBool +obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, + void *closure) +{ + JSResolvingKey key; + JSResolvingEntry *entry; + uint32 generation; + JSObject *funobj; + jsval argv[3]; + JSBool ok; + + /* Avoid recursion on (obj, id) already being watched on cx. */ + key.obj = obj; + key.id = id; + if (!StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) + return JS_FALSE; + if (!entry) + return JS_TRUE; + generation = cx->resolvingTable->generation; + + funobj = (JSObject *) closure; + argv[0] = id; + argv[1] = old; + argv[2] = *nvp; + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp); + StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); + return ok; +} + +static JSBool +obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSFunction *fun; + jsval userid, value; + jsid propid; + uintN attrs; + + fun = js_ValueToFunction(cx, &argv[1], 0); + if (!fun) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(fun->object); + + /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ + userid = argv[0]; + if (!JS_ValueToId(cx, userid, &propid)) + return JS_FALSE; + + if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) + return JS_FALSE; + if (attrs & JSPROP_READONLY) + return JS_TRUE; + return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object); +} + +static JSBool +obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL); +} + +#endif /* JS_HAS_OBJ_WATCHPOINT */ + +#if JS_HAS_NEW_OBJ_METHODS +/* + * Prototype and property query methods, to complement the 'in' and + * 'instanceof' operators. + */ + +/* Proposed ECMA 15.2.4.5. */ +static JSBool +obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + *rval = JSVAL_FALSE; + } else if (obj2 == obj) { + *rval = JSVAL_TRUE; + } else if (OBJ_IS_NATIVE(obj2)) { + sprop = (JSScopeProperty *)prop; + *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); + } else { + *rval = JSVAL_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; +} + +/* Proposed ECMA 15.2.4.6. */ +static JSBool +obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSBool b; + + if (!js_IsDelegate(cx, obj, *argv, &b)) + return JS_FALSE; + *rval = BOOLEAN_TO_JSVAL(b); + return JS_TRUE; +} + +/* Proposed ECMA 15.2.4.7. */ +static JSBool +obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + uintN attrs; + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + + if (!prop) { + *rval = 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 (obj2 != obj && + !(OBJ_IS_NATIVE(obj2) && + SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + *rval = JSVAL_FALSE; + return JS_TRUE; + } + + ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (ok) + *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); + return ok; +} +#endif /* JS_HAS_NEW_OBJ_METHODS */ + +#if JS_HAS_GETTER_SETTER +static JSBool +obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval fval, junk; + jsid id; + JSBool found; + uintN attrs; + + fval = argv[1]; + if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + js_getter_str); + return JS_FALSE; + } + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found)) + return JS_FALSE; + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) + return JS_FALSE; + return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, + (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL, + JSPROP_GETTER | JSPROP_SHARED, NULL); +} + +static JSBool +obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval fval, junk; + jsid id; + JSBool found; + uintN attrs; + + fval = argv[1]; + if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + js_setter_str); + return JS_FALSE; + } + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found)) + return JS_FALSE; + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) + return JS_FALSE; + return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, + NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval), + JSPROP_SETTER | JSPROP_SHARED, NULL); +} + +static JSBool +obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + JSObject *pobj; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop)) + return JS_FALSE; + if (sprop) { + if (sprop->attrs & JSPROP_GETTER) + *rval = OBJECT_TO_JSVAL(sprop->getter); + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + } + return JS_TRUE; +} + +static JSBool +obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + JSObject *pobj; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop)) + return JS_FALSE; + if (sprop) { + if (sprop->attrs & JSPROP_SETTER) + *rval = OBJECT_TO_JSVAL(sprop->setter); + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + } + return JS_TRUE; +} +#endif /* JS_HAS_GETTER_SETTER */ + +#if JS_HAS_OBJ_WATCHPOINT +const char js_watch_str[] = "watch"; +const char js_unwatch_str[] = "unwatch"; +#endif +#if JS_HAS_NEW_OBJ_METHODS +const char js_hasOwnProperty_str[] = "hasOwnProperty"; +const char js_isPrototypeOf_str[] = "isPrototypeOf"; +const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; +#endif +#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 + +static JSFunctionSpec object_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA}, +#endif + {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, + {js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, + {js_valueOf_str, obj_valueOf, 0,0,0}, + {js_eval_str, obj_eval, 1,0,0}, +#if JS_HAS_OBJ_WATCHPOINT + {js_watch_str, obj_watch, 2,0,0}, + {js_unwatch_str, obj_unwatch, 1,0,0}, +#endif +#if JS_HAS_NEW_OBJ_METHODS + {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, + {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, + {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, +#endif +#if JS_HAS_GETTER_SETTER + {js_defineGetter_str, obj_defineGetter, 2,0,0}, + {js_defineSetter_str, obj_defineSetter, 2,0,0}, + {js_lookupGetter_str, obj_lookupGetter, 1,0,0}, + {js_lookupSetter_str, obj_lookupSetter, 1,0,0}, +#endif + {0,0,0,0,0} +}; + +static JSBool +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 (cx->fp->flags & JSFRAME_CONSTRUCTING) + return JS_TRUE; + obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * ObjectOps and Class for with-statement stack objects. + */ +static JSBool +with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#endif + ) +{ + JSObject *proto; + JSScopeProperty *sprop; + JSStackFrame *fp; + + proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return js_LookupProperty(cx, obj, id, objp, propp); + if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp)) + return JS_FALSE; + + /* + * Check whether id names an argument or local variable in an active + * function. If so, pretend we didn't find it, so that the real arg or + * var property can be found in the function's call object, later on in + * the scope chain. But skip unshared arg and var properties -- those + * result when a script explicitly sets a function "static" property of + * the same name. See jsinterp.c:SetFunctionSlot. + * + * XXX blame pre-ECMA reflection of function args and vars as properties + */ + if ((sprop = (JSScopeProperty *) *propp) && + (proto = *objp, OBJ_IS_NATIVE(proto)) && + (sprop->getter == js_GetArgument || + sprop->getter == js_GetLocalVariable) && + (sprop->attrs & JSPROP_SHARED)) { + JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass); + for (fp = cx->fp; fp && (!fp->fun || fp->fun->native); fp = fp->down) + continue; + if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) { + OBJ_DROP_PROPERTY(cx, proto, *propp); + *objp = NULL; + *propp = NULL; + } + } + return JS_TRUE; +} + +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 OBJ_GET_PROPERTY(cx, proto, 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 OBJ_SET_PROPERTY(cx, proto, 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 OBJ_GET_ATTRIBUTES(cx, proto, 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 OBJ_SET_ATTRIBUTES(cx, proto, 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 OBJ_DELETE_PROPERTY(cx, proto, 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 OBJ_DEFAULT_VALUE(cx, proto, 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 OBJ_ENUMERATE(cx, proto, 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 OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); +} + +static JSObject * +with_ThisObject(JSContext *cx, JSObject *obj) +{ + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if (!proto) + return obj; + return OBJ_THIS_OBJECT(cx, proto); +} + +JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { + js_NewObjectMap, js_DestroyObjectMap, + 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, NULL, + js_SetProtoOrParent, js_SetProtoOrParent, + js_Mark, js_Clear, + NULL, NULL +}; + +static JSObjectOps * +with_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_WithObjectOps; +} + +JSClass js_WithClass = { + "With", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, + with_getObjectOps, + 0,0,0,0,0,0,0 +}; + +#if JS_HAS_OBJ_PROTO_PROP +static JSBool +With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *parent, *proto; + jsval v; + + if (!JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_DEPRECATED_USAGE, + js_WithClass.name)) { + return JS_FALSE; + } + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewObject(cx, &js_WithClass, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + parent = cx->fp->scopeChain; + if (argc > 0) { + if (!js_ValueToObject(cx, argv[0], &proto)) + return JS_FALSE; + v = OBJECT_TO_JSVAL(proto); + if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v)) + return JS_FALSE; + if (argc > 1) { + if (!js_ValueToObject(cx, argv[1], &parent)) + return JS_FALSE; + } + } + v = OBJECT_TO_JSVAL(parent); + return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v); +} +#endif + +JSObject * +js_InitObjectClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + jsval eval; + +#if JS_HAS_SHARP_VARS + JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1); +#endif + + proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, + object_props, object_methods, NULL, NULL); + if (!proto) + return NULL; + +#if JS_HAS_OBJ_PROTO_PROP + if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0, + NULL, NULL, NULL, NULL)) { + return NULL; + } +#endif + + /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ + if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom, + &eval)) { + return NULL; + } + if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom, + eval, NULL, NULL, 0, NULL)) { + return NULL; + } + + return proto; +} + +void +js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp) +{ + map->nrefs = nrefs; + map->ops = ops; + map->nslots = JS_INITIAL_NSLOTS; + map->freeslot = JSSLOT_FREE(clasp); +} + +JSObjectMap * +js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp, JSObject *obj) +{ + return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); +} + +void +js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) +{ + js_DestroyScope(cx, (JSScope *)map); +} + +JSObjectMap * +js_HoldObjectMap(JSContext *cx, JSObjectMap *map) +{ + JS_ASSERT(map->nrefs >= 0); + JS_ATOMIC_INCREMENT(&map->nrefs); + return map; +} + +JSObjectMap * +js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) +{ + JS_ASSERT(map->nrefs > 0); + JS_ATOMIC_DECREMENT(&map->nrefs); + if (map->nrefs == 0) { + map->ops->destroyObjectMap(cx, map); + return NULL; + } + if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) + ((JSScope *)map)->object = NULL; + return map; +} + +static JSBool +GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, + JSObject **protop); + +JSObject * +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) +{ + JSObject *obj, *ctor; + JSObjectOps *ops; + JSObjectMap *map; + jsval cval; + uint32 nslots, i; + jsval *newslots; + + /* Allocate an object from the GC heap and zero it. */ + obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT); + if (!obj) + return NULL; + + /* Bootstrap the ur-object, and make it the default prototype object. */ + if (!proto) { + if (!GetClassPrototype(cx, parent, clasp->name, &proto)) + goto bad; + if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto)) + goto bad; + } + + /* Always call the class's getObjectOps hook if it has one. */ + ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + + /* + * Share proto's map only if it has the same JSObjectOps, and only if + * proto's class has the same private and reserved slots, as obj's map + * and class have. + */ + if (proto && + (map = proto->map)->ops == ops && + ((clasp->flags ^ OBJ_GET_CLASS(cx, proto)->flags) & + (JSCLASS_HAS_PRIVATE | + (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) + == 0) { + /* Default parent to the parent of the prototype's constructor. */ + if (!parent) { + if (!OBJ_GET_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, + &cval)) { + goto bad; + } + if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL) + parent = OBJ_GET_PARENT(cx, ctor); + } + + /* Share the given prototype's map. */ + obj->map = js_HoldObjectMap(cx, map); + + /* Ensure that obj starts with the minimum slots for clasp. */ + nslots = JS_INITIAL_NSLOTS; + } else { + /* Leave parent alone. Allocate a new map for obj. */ + map = ops->newObjectMap(cx, 1, ops, clasp, obj); + if (!map) + goto bad; + obj->map = map; + + /* Let ops->newObjectMap set nslots so as to reserve slots. */ + nslots = map->nslots; + } + + /* Allocate a slots vector, with a -1'st element telling its length. */ + newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval)); + if (!newslots) { + js_DropObjectMap(cx, obj->map, obj); + obj->map = NULL; + goto bad; + } + newslots[0] = nslots; + newslots++; + + /* Set the proto, parent, and class properties. */ + newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); + newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); + newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp); + + /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */ + for (i = JSSLOT_CLASS + 1; i < nslots; i++) + newslots[i] = JSVAL_VOID; + + /* Store newslots after initializing all of 'em, just in case. */ + obj->slots = newslots; + + if (cx->runtime->objectHook) + cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData); + + return obj; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +static JSBool +FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp) +{ + JSAtom *atom; + JSObject *obj; + JSObject *pobj; + JSScopeProperty *sprop; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + + if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) { + /* Find the topmost object in the scope chain. */ + do { + obj = scope; + scope = OBJ_GET_PARENT(cx, obj); + } while (scope); + } else { + obj = cx->globalObject; + if (!obj) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + } + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop)) + return JS_FALSE; + if (!sprop) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JS_ASSERT(OBJ_IS_NATIVE(pobj)); + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + return JS_TRUE; +} + +JSObject * +js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv) +{ + jsval cval, rval; + JSObject *obj, *ctor; + + if (!FindConstructor(cx, parent, clasp->name, &cval)) + return NULL; + if (JSVAL_IS_PRIMITIVE(cval)) { + js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); + return NULL; + } + + /* + * 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 (!OBJ_GET_PROPERTY(cx, ctor, + (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)) + goto bad; + return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj; +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +void +js_FinalizeObject(JSContext *cx, JSObject *obj) +{ + JSObjectMap *map; + + /* Cope with stillborn objects that have no map. */ + map = obj->map; + if (!map) + return; + JS_ASSERT(obj->slots); + + if (cx->runtime->objectHook) + cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData); + + /* Remove all watchpoints with weak links to obj. */ + JS_ClearWatchPointsForObject(cx, obj); + + /* + * Finalize obj first, in case it needs map and slots. Optimized to use + * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting" + * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when + * we're called from the GC. Only the GC should call js_FinalizeObject, + * and no other threads run JS (and possibly racing to update obj->slots) + * while the GC is running. + */ + LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj); + + /* Drop map and free slots. */ + js_DropObjectMap(cx, map, obj); + obj->map = NULL; + JS_free(cx, obj->slots - 1); + obj->slots = NULL; +} + +/* 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) +{ + JSObjectMap *map; + uint32 nslots, i; + size_t nbytes; + jsval *newslots; + + map = obj->map; + JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); + nslots = map->nslots; + if (map->freeslot >= nslots) { + nslots = map->freeslot; + JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); + nslots += (nslots + 1) / 2; + + nbytes = (nslots + 1) * sizeof(jsval); +#if defined _MSC_VER && _MSC_VER <= 800 + if (nbytes > 60000U) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } +#endif + + newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes); + if (!newslots) + return JS_FALSE; + for (i = 1 + newslots[0]; i <= nslots; i++) + newslots[i] = JSVAL_VOID; + newslots[0] = map->nslots = nslots; + obj->slots = newslots + 1; + } + +#ifdef TOO_MUCH_GC + obj->slots[map->freeslot] = JSVAL_VOID; +#endif + *slotp = map->freeslot++; + return JS_TRUE; +} + +void +js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) +{ + JSObjectMap *map; + uint32 nslots; + size_t nbytes; + jsval *newslots; + + OBJ_CHECK_SLOT(obj, slot); + obj->slots[slot] = JSVAL_VOID; + map = obj->map; + JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); + if (map->freeslot == slot + 1) + map->freeslot = slot; + nslots = map->nslots; + if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) { + nslots = map->freeslot; + nslots += nslots / 2; + if (nslots < JS_INITIAL_NSLOTS) + nslots = JS_INITIAL_NSLOTS; + nbytes = (nslots + 1) * sizeof(jsval); + newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes); + if (!newslots) + return; + newslots[0] = map->nslots = nslots; + obj->slots = newslots + 1; + } +} + +#if JS_BUG_EMPTY_INDEX_ZERO +#define CHECK_FOR_EMPTY_INDEX(id) \ + JS_BEGIN_MACRO \ + if (JSSTRING_LENGTH(_str) == 0) \ + id = JSVAL_ZERO; \ + JS_END_MACRO +#else +#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */ +#endif + +/* JSVAL_INT_MAX as a string */ +#define JSVAL_INT_MAX_STRING "1073741823" + +#define CHECK_FOR_FUNNY_INDEX(id) \ + JS_BEGIN_MACRO \ + if (!JSVAL_IS_INT(id)) { \ + JSAtom *atom_ = (JSAtom *)id; \ + JSString *str_ = ATOM_TO_STRING(atom_); \ + const jschar *cp_ = str_->chars; \ + JSBool negative_ = (*cp_ == '-'); \ + if (negative_) cp_++; \ + if (JS7_ISDEC(*cp_) && \ + str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \ + id = CheckForFunnyIndex(id, cp_, negative_); \ + } else { \ + CHECK_FOR_EMPTY_INDEX(id); \ + } \ + } \ + JS_END_MACRO + +static jsid +CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative) +{ + 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++; + } + } + if (*cp == 0 && + (oldIndex < (JSVAL_INT_MAX / 10) || + (oldIndex == (JSVAL_INT_MAX / 10) && + c <= (JSVAL_INT_MAX % 10)))) { + if (negative) + index = 0 - index; + id = INT_TO_JSVAL((jsint)index); + } + return id; +} + +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_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (!scope) { + sprop = NULL; + } else { + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(id); + sprop = js_AddScopeProperty(cx, scope, 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 = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, + getter, setter); + if (sprop) { + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id, + sprop); + } + } + JS_UNLOCK_OBJ(cx, obj); + return sprop; +} + +JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, + 0, 0, propp); +} + +JSBool +js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN shortid, JSProperty **propp) +{ + JSClass *clasp; + JSScope *scope; + JSScopeProperty *sprop; + + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(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. + */ + if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + JSObject *pobj; + + /* + * 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, (JSProperty **)&sprop)) + return JS_FALSE; + if (sprop && + pobj == obj && + (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { + sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, + attrs, sprop->attrs, + (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 bad; + goto out; + } + + if (sprop) { + /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + sprop = NULL; + } + } +#endif /* JS_HAS_GETTER_SETTER */ + + /* 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 = LOCKED_OBJ_GET_CLASS(obj); + 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 bad; + + /* Add the property to scope, or replace an existing one of the same id. */ + if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) + attrs |= JSPROP_SHARED; + sprop = js_AddScopeProperty(cx, scope, id, getter, setter, + SPROP_INVALID_SLOT, attrs, flags, shortid); + if (!sprop) + goto bad; + + /* XXXbe called with lock held */ + if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) { + (void) js_RemoveScopeProperty(cx, scope, id); + goto bad; + } + + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); + +#if JS_HAS_GETTER_SETTER +out: +#endif + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); + if (propp) + *propp = (JSProperty *) sprop; + else + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; + +bad: + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; +} + +#if defined JS_THREADSAFE && defined DEBUG +JS_FRIEND_API(JSBool) +_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp, const char *file, uintN line) +#else +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +#endif +{ + JSObject *start, *obj2, *proto; + JSScope *scope; + JSScopeProperty *sprop; + JSClass *clasp; + JSResolveOp resolve; + JSResolvingKey key; + JSResolvingEntry *entry; + uint32 generation; + JSNewResolveOp newresolve; + uintN flags; + uint32 format; + JSBool ok; + + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(id); + + /* Search scopes starting with obj and following the prototype link. */ + start = obj; + for (;;) { + JS_LOCK_OBJ(cx, obj); + SET_OBJ_INFO(obj, file, line); + scope = OBJ_SCOPE(obj); + if (scope->object == obj) { + sprop = SCOPE_GET_PROPERTY(scope, id); + } else { + /* Shared prototype scope: try resolve before lookup. */ + sprop = NULL; + } + + /* Try obj's class resolve hook if id was not found in obj's scope. */ + if (!sprop) { + clasp = LOCKED_OBJ_GET_CLASS(obj); + 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 (!StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } + if (!entry) { + /* Already resolving id in obj -- dampen 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; + flags = 0; + if (cx->fp && cx->fp->pc) { + format = js_CodeSpec[*cx->fp->pc].format; + if ((format & JOF_MODEMASK) != JOF_NAME) + flags |= JSRESOLVE_QUALIFIED; + if ((format & JOF_ASSIGNING) || + (cx->fp->flags & JSFRAME_ASSIGNING)) { + flags |= JSRESOLVE_ASSIGNING; + } + } + 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); + SET_OBJ_INFO(obj, file, line); + if (obj2) { + /* Resolved: juggle locks and lookup id again. */ + if (obj2 != obj) { + JS_UNLOCK_OBJ(cx, obj); + JS_LOCK_OBJ(cx, obj2); + } + scope = OBJ_SCOPE(obj2); + if (!MAP_IS_NATIVE(&scope->map)) { + /* Whoops, newresolve handed back a foreign obj2. */ + JS_ASSERT(obj2 != obj); + JS_UNLOCK_OBJ(cx, obj2); + ok = OBJ_LOOKUP_PROPERTY(cx, obj2, 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. + */ + if (scope->object == obj2) + sprop = SCOPE_GET_PROPERTY(scope, id); + } + if (obj2 != obj && !sprop) { + 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); + SET_OBJ_INFO(obj, file, line); + scope = OBJ_SCOPE(obj); + JS_ASSERT(MAP_IS_NATIVE(&scope->map)); + if (scope->object == obj) + sprop = SCOPE_GET_PROPERTY(scope, id); + } + + cleanup: + StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation); + if (!ok || *propp) + return ok; + } + } + + if (sprop) { + JS_ASSERT(OBJ_SCOPE(obj) == scope); + *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ + + *propp = (JSProperty *) sprop; + return JS_TRUE; + } + + proto = LOCKED_OBJ_GET_PROTO(obj); + JS_UNLOCK_OBJ(cx, obj); + if (!proto) + break; + if (!OBJ_IS_NATIVE(proto)) + return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); + obj = proto; + } + +out: + *objp = NULL; + *propp = NULL; + return JS_TRUE; +} + +JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp) +{ + JSRuntime *rt; + JSObject *obj, *pobj, *lastobj; + JSScopeProperty *sprop; + JSProperty *prop; + + rt = cx->runtime; + obj = cx->fp->scopeChain; + do { + /* Try the property cache and return immediately on cache hit. */ + if (OBJ_IS_NATIVE(obj)) { + JS_LOCK_OBJ(cx, obj); + PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); + if (sprop) { + JS_ASSERT(OBJ_IS_NATIVE(obj)); + *objp = obj; + *pobjp = obj; + *propp = (JSProperty *) sprop; + return JS_TRUE; + } + JS_UNLOCK_OBJ(cx, obj); + } + + /* If cache miss, take the slow path. */ + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop); + } + *objp = obj; + *pobjp = pobj; + *propp = prop; + return JS_TRUE; + } + lastobj = obj; + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + *objp = lastobj; + *pobjp = NULL; + *propp = NULL; + return JS_TRUE; +} + +JSObject * +js_FindIdentifierBase(JSContext *cx, jsid id) +{ + JSObject *obj, *pobj; + JSProperty *prop; + + /* + * Look for id's property along the "with" statement chain and the + * statically-linked scope chain. + */ + if (!js_FindProperty(cx, id, &obj, &pobj, &prop)) + return NULL; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + return obj; + } + + /* + * Use the top-level scope from the scope chain, which won't end in the + * same scope as cx->globalObject for cross-context function calls. + */ + JS_ASSERT(obj); + + /* + * Property not found. Give a strict warning if binding an undeclared + * top-level variable. + */ + if (JS_HAS_STRICT_OPTION(cx)) { + JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id)); + if (!JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_UNDECLARED_VAR, + JS_GetStringBytes(str))) { + return NULL; + } + } + return obj; +} + +JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *obj2; + JSScopeProperty *sprop; + JSScope *scope; + uint32 slot; + + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(id); + + if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop)) + return JS_FALSE; + if (!sprop) { + jsval default_val; + +#if JS_BUG_NULL_INDEX_PROPS + /* Indexed properties defaulted to null in old versions. */ + default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0) + ? JSVAL_NULL + : JSVAL_VOID; +#else + default_val = JSVAL_VOID; +#endif + *vp = default_val; + + if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) + return JS_FALSE; + + /* + * Give a strict warning if foo.bar is evaluated by a script for an + * object foo with no property named 'bar'. + */ + if (JS_HAS_STRICT_OPTION(cx) && + *vp == default_val && + cx->fp && cx->fp->pc && + (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM)) + { + jsbytecode *pc, *endpc; + JSString *str; + + /* Kludge to allow (typeof foo == "undefined") tests. */ + JS_ASSERT(cx->fp->script); + pc = cx->fp->pc; + pc += js_CodeSpec[*pc].length; + endpc = cx->fp->script->code + cx->fp->script->length; + while (pc < endpc) { + if (*pc == JSOP_TYPEOF) + return JS_TRUE; + if (*pc != JSOP_GROUP) + break; + pc++; + } + + /* Ok, bad undefined property reference: whine about it. */ + str = js_DecompileValueGenerator(cx, JS_FALSE, ID_TO_VALUE(id), + NULL); + if (!str || + !JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING|JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_UNDEFINED_PROP, + JS_GetStringBytes(str))) { + return JS_FALSE; + } + } + return JS_TRUE; + } + + if (!OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop); + return OBJ_GET_PROPERTY(cx, obj2, id, vp); + } + + /* Unlock obj2 before calling getter, relock after to avoid deadlock. */ + scope = OBJ_SCOPE(obj2); + slot = sprop->slot; + if (slot != SPROP_INVALID_SLOT) { + JS_ASSERT(slot < obj2->map->freeslot); + *vp = LOCKED_OBJ_GET_SLOT(obj2, slot); + + /* If sprop has a stub getter, we're done. */ + if (!sprop->getter) + goto out; + } else { + *vp = JSVAL_VOID; + } + + JS_UNLOCK_SCOPE(cx, scope); + if (!SPROP_GET(cx, sprop, obj, obj2, vp)) + return JS_FALSE; + JS_LOCK_SCOPE(cx, scope); + + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + LOCKED_OBJ_SET_SLOT(obj2, slot, *vp); + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); + } + +out: + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; +} + +JSBool +js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *pobj; + JSScopeProperty *sprop; + JSScope *scope; + uintN attrs, flags; + intN shortid; + JSClass *clasp; + JSPropertyOp getter, setter; + jsval pval; + uint32 slot; + + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(id); + + if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop)) + return JS_FALSE; + + if (sprop && !OBJ_IS_NATIVE(pobj)) { + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + sprop = NULL; + } + + /* + * 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 OBJ_DROP_PROPERTY 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_IS_SEALED(scope) && pobj == obj)) { + JS_UNLOCK_SCOPE(cx, scope); + if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version)) + return JS_TRUE; + 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) + return SPROP_SET(cx, sprop, obj, pobj, 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 + } + + if (!sprop) { + if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) + goto read_only_error; + + /* 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; + sprop = js_AddScopeProperty(cx, scope, id, getter, setter, + SPROP_INVALID_SLOT, attrs, flags, shortid); + if (!sprop) { + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; + } + + /* XXXbe called with obj locked */ + if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { + (void) js_RemoveScopeProperty(cx, scope, id); + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; + } + + /* Initialize new property value (passed to setter) to undefined. */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); + + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); + } + + /* Get the current property value from its slot. */ + slot = sprop->slot; + if (slot != SPROP_INVALID_SLOT) { + JS_ASSERT(slot < obj->map->freeslot); + pval = LOCKED_OBJ_GET_SLOT(obj, slot); + + /* If sprop has a stub setter, keep scope locked and just store *vp. */ + if (!sprop->setter) + goto set_slot; + } + + /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */ + JS_UNLOCK_SCOPE(cx, scope); + + /* Let the setter modify vp before copying from it to obj->slots[slot]. */ + if (!SPROP_SET(cx, sprop, obj, obj, vp)) + return JS_FALSE; + + /* Relock obj's scope until we are done with sprop. */ + JS_LOCK_SCOPE(cx, scope); + + /* + * Check whether sprop is still around (was not deleted), and whether it + * has a slot (it may never have had one, or we may have lost a race with + * someone who cleared scope). + */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + set_slot: + GC_POKE(cx, pval); + LOCKED_OBJ_SET_SLOT(obj, slot, *vp); + } + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; + + read_only_error: { + JSString *str = js_DecompileValueGenerator(cx, + JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), + NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_READ_ONLY, + JS_GetStringBytes(str)); + } + return JS_FALSE; + } +} + +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_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + *attrsp = sprop->attrs; + if (noprop) + OBJ_DROP_PROPERTY(cx, obj, 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_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; + } + } + sprop = (JSScopeProperty *)prop; + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, + *attrsp & + ~(JSPROP_GETTER | JSPROP_SETTER), 0, + sprop->getter, sprop->setter); + if (noprop) + OBJ_DROP_PROPERTY(cx, obj, prop); + return (sprop != NULL); +} + +JSBool +js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ +#if JS_HAS_PROP_DELETE + + JSObject *proto; + JSProperty *prop; + JSScopeProperty *sprop; + JSString *str; + JSScope *scope; + JSBool ok; + + *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID; + + /* + * Handle old bug that took empty string as zero index. Also convert + * string indices to integers if appropriate. + */ + CHECK_FOR_FUNNY_INDEX(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; + } + OBJ_DROP_PROPERTY(cx, proto, 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_DROP_PROPERTY(cx, obj, prop); + if (JSVERSION_IS_ECMA(cx->version)) { + *rval = JSVAL_FALSE; + return JS_TRUE; + } + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_PERMANENT, JS_GetStringBytes(str)); + } + return JS_FALSE; + } + + /* XXXbe called with obj locked */ + if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop), + rval)) { + OBJ_DROP_PROPERTY(cx, obj, 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)); + + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL); + ok = js_RemoveScopeProperty(cx, scope, id); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; + +#else /* !JS_HAS_PROP_DELETE */ + + jsval null = JSVAL_NULL; + + *rval = JSVAL_VOID; + return js_SetProperty(cx, obj, id, &null); + +#endif /* !JS_HAS_PROP_DELETE */ +} + +JSBool +js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + jsval v; + JSString *str; + + v = OBJECT_TO_JSVAL(obj); + switch (hint) { + case JSTYPE_STRING: + /* + * 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; + + /* + * JS1.2 never failed (except for malloc failure) to convert an + * object to a string. ECMA requires an error if both toString + * and valueOf fail to produce a primitive value. + */ + if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) { + char *bytes = JS_smprintf("[object %s]", + OBJ_GET_CLASS(cx, obj)->name); + if (!bytes) + return JS_FALSE; + str = JS_NewString(cx, bytes, strlen(bytes)); + if (!str) { + free(bytes); + return JS_FALSE; + } + v = STRING_TO_JSVAL(str); + goto out; + } + } + 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; + } + /* Don't convert to string (source object literal) for JS1.2. */ + if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN) + 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 through js_DecompileValueGenerator. */ + 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); + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_CONVERT_TO, + JS_GetStringBytes(str), + (hint == JSTYPE_VOID) + ? "primitive type" + : js_type_str[hint]); + } + return JS_FALSE; + } +out: + *vp = v; + return JS_TRUE; +} + +JSIdArray * +js_NewIdArray(JSContext *cx, jsint length) +{ + JSIdArray *ida; + + ida = (JSIdArray *) + JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval)); + if (ida) + ida->length = length; + return ida; +} + +JSIdArray * +js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length) +{ + ida = (JSIdArray *) + JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval)); + if (ida) + ida->length = length; + return ida; +} + +/* Private type used to iterate over all properties of a native JS object */ +typedef struct JSNativeIteratorState { + jsint next_index; /* index into jsid array */ + JSIdArray *ida; /* All property ids in enumeration */ +} JSNativeIteratorState; + +/* + * This function is used to enumerate the properties of native JSObjects + * and those host objects that do not define a JSNewEnumerateOp-style iterator + * function. + */ +JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSObject *proto_obj; + JSClass *clasp; + JSEnumerateOp enumerate; + JSScopeProperty *sprop, *lastProp; + jsint i, length; + JSScope *scope; + JSIdArray *ida; + JSNativeIteratorState *state; + + clasp = OBJ_GET_CLASS(cx, obj); + enumerate = clasp->enumerate; + if (clasp->flags & JSCLASS_NEW_ENUMERATE) + return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); + + switch (enum_op) { + + case JSENUMERATE_INIT: + if (!enumerate(cx, obj)) + goto init_error; + length = 0; + + /* + * The set of all property ids is pre-computed when the iterator + * is initialized so as to avoid problems with properties being + * deleted during the iteration. + */ + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + + /* + * If this object shares a scope with its prototype, don't enumerate + * its properties. Otherwise they will be enumerated a second time + * when the prototype object is enumerated. + */ + proto_obj = OBJ_GET_PROTO(cx, obj); + if (proto_obj && scope == OBJ_SCOPE(proto_obj)) { + ida = js_NewIdArray(cx, 0); + if (!ida) { + JS_UNLOCK_OBJ(cx, obj); + goto init_error; + } + } else { + /* Object has a private scope; Enumerate all props in scope. */ + for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop; + sprop = sprop->parent) { + if ((sprop->attrs & JSPROP_ENUMERATE) && + !(sprop->flags & SPROP_IS_ALIAS) && + (!SCOPE_HAD_MIDDLE_DELETE(scope) || + SCOPE_HAS_PROPERTY(scope, sprop))) { + length++; + } + } + ida = js_NewIdArray(cx, length); + if (!ida) { + JS_UNLOCK_OBJ(cx, obj); + goto init_error; + } + i = length; + for (sprop = lastProp; sprop; sprop = sprop->parent) { + if ((sprop->attrs & JSPROP_ENUMERATE) && + !(sprop->flags & SPROP_IS_ALIAS) && + (!SCOPE_HAD_MIDDLE_DELETE(scope) || + SCOPE_HAS_PROPERTY(scope, sprop))) { + JS_ASSERT(i > 0); + ida->vector[--i] = sprop->id; + } + } + } + JS_UNLOCK_OBJ(cx, obj); + + state = (JSNativeIteratorState *) + JS_malloc(cx, sizeof(JSNativeIteratorState)); + if (!state) { + JS_DestroyIdArray(cx, ida); + goto init_error; + } + state->ida = ida; + state->next_index = 0; + *statep = PRIVATE_TO_JSVAL(state); + if (idp) + *idp = INT_TO_JSVAL(length); + return JS_TRUE; + + case JSENUMERATE_NEXT: + state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); + ida = state->ida; + length = ida->length; + if (state->next_index != length) { + *idp = ida->vector[state->next_index++]; + return JS_TRUE; + } + + /* Fall through ... */ + + case JSENUMERATE_DESTROY: + state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); + JS_DestroyIdArray(cx, state->ida); + JS_free(cx, state); + *statep = JSVAL_NULL; + return JS_TRUE; + + default: + JS_ASSERT(0); + return JS_FALSE; + } + +init_error: + *statep = JSVAL_NULL; + return JS_FALSE; +} + +JSBool +js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + JSClass *clasp; + JSBool ok; + + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + *vp = JSVAL_VOID; + *attrsp = 0; + clasp = OBJ_GET_CLASS(cx, obj); + return !clasp->checkAccess || + clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp); + } + if (!OBJ_IS_NATIVE(pobj)) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); + } + sprop = (JSScopeProperty *)prop; + *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) + ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) + : JSVAL_VOID; + *attrsp = sprop->attrs; + clasp = LOCKED_OBJ_GET_CLASS(obj); + if (clasp->checkAccess) { + JS_UNLOCK_OBJ(cx, pobj); + ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp); + JS_LOCK_OBJ(cx, pobj); + } else { + ok = JS_TRUE; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + return ok; +} + +#ifdef JS_THREADSAFE +void +js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) +{ + JS_UNLOCK_OBJ(cx, obj); +} +#endif + +static void +ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) +{ + /* + * The decompiler may need to access the args of the function in + * progress rather than the one we had hoped to call. + * So we switch the cx->fp to the frame below us. We stick the + * current frame in the dormantFrameChain to protect it from gc. + */ + + JSStackFrame *fp = cx->fp; + if (fp->down) { + JS_ASSERT(!fp->dormantNext); + fp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = fp; + cx->fp = fp->down; + } + + js_ReportIsNotFunction(cx, vp, flags); + + if (fp->down) { + JS_ASSERT(cx->dormantFrameChain == fp); + cx->dormantFrameChain = fp->dormantNext; + fp->dormantNext = NULL; + cx->fp = fp; + } +} + +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) { + ReportIsNotFunction(cx, &argv[-2], 0); + 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) { + 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); + *bp = JS_FALSE; + return JS_TRUE; +} + +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, const char *name, JSObject **protop) +{ + return GetClassPrototype(cx, NULL, name, protop); +} + +static JSBool +GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, + JSObject **protop) +{ + jsval v; + JSObject *ctor; + + if (!FindConstructor(cx, scope, name, &v)) + return JS_FALSE; + if (JSVAL_IS_FUNCTION(cx, v)) { + ctor = JSVAL_TO_OBJECT(v); + if (!OBJ_GET_PROPERTY(cx, ctor, + (jsid)cx->runtime->atomState.classPrototypeAtom, + &v)) { + return JS_FALSE; + } + } + *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; + return JS_TRUE; +} + +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 DontEnum | DontDelete prototype (it + * may be reset), while native or "system" constructors require DontEnum | + * ReadOnly | DontDelete. + */ + if (!OBJ_DEFINE_PROPERTY(cx, ctor, + (jsid)cx->runtime->atomState.classPrototypeAtom, + OBJECT_TO_JSVAL(proto), NULL, NULL, + attrs, NULL)) { + return JS_FALSE; + } + + /* + * ECMA says that Object.prototype.constructor, or f.prototype.constructor + * for a user-defined function f, is DontEnum. + */ + return OBJ_DEFINE_PROPERTY(cx, proto, + (jsid)cx->runtime->atomState.constructorAtom, + OBJECT_TO_JSVAL(ctor), NULL, NULL, + 0, NULL); +} + +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_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) + return JS_FALSE; + if (JSVAL_IS_OBJECT(v)) + obj = JSVAL_TO_OBJECT(v); + } else { + if (JSVAL_IS_STRING(v)) { + obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); + } else if (JSVAL_IS_INT(v)) { + obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); + } else if (JSVAL_IS_DOUBLE(v)) { + obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); + } else { + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); + } + if (!obj) + return JS_FALSE; + } + *objp = obj; + return JS_TRUE; +} + +JSObject * +js_ValueToNonNullObject(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + + if (!js_ValueToObject(cx, v, &obj)) + return NULL; + if (!obj) { + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NO_PROPERTIES, JS_GetStringBytes(str)); + } + } + return obj; +} + +JSBool +js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) +{ +#if JS_HAS_VALUEOF_HINT + jsval argv[1]; + + argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); + return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, + rval); +#else + return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL, + rval); +#endif +} + +JSBool +js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN argc, jsval *argv, jsval *rval) +{ + JSErrorReporter older; + jsval fval; + JSBool ok; + + /* + * 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); + if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) && + !JSVAL_IS_PRIMITIVE(fval)) { + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + } else { + ok = JS_TRUE; + } + JS_SetErrorReporter(cx, older); + return ok; +} + +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +JSBool +js_XDRObject(JSXDRState *xdr, JSObject **objp) +{ + JSContext *cx; + JSClass *clasp; + const char *className; + uint32 classId, classDef; + JSBool ok; + JSObject *proto; + + cx = xdr->cx; + if (xdr->mode == JSXDR_ENCODE) { + clasp = OBJ_GET_CLASS(cx, *objp); + className = clasp->name; + classId = JS_XDRFindClassIdByName(xdr, className); + classDef = !classId; + if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId)) + return JS_FALSE; + } else { + classDef = 0; + className = NULL; + clasp = NULL; /* quell GCC overwarning */ + } + + /* XDR a flag word followed (if true) by the class name. */ + if (!JS_XDRUint32(xdr, &classDef)) + return JS_FALSE; + if (classDef && !JS_XDRCString(xdr, (char **) &className)) + return JS_FALSE; + + /* From here on, return through out: to free className if it was set. */ + ok = JS_XDRUint32(xdr, &classId); + if (!ok) + goto out; + + if (xdr->mode != JSXDR_ENCODE) { + if (classDef) { + ok = js_GetClassPrototype(cx, className, &proto); + if (!ok) + goto out; + clasp = OBJ_GET_CLASS(cx, proto); + ok = JS_XDRRegisterClass(xdr, clasp, &classId); + if (!ok) + goto out; + } 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); + ok = JS_FALSE; + goto out; + } + } + } + + if (!clasp->xdrObject) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_XDR_CLASS, clasp->name); + ok = JS_FALSE; + } else { + ok = clasp->xdrObject(xdr, objp); + } +out: + if (xdr->mode != JSXDR_ENCODE && className) + JS_free(cx, (void *)className); + return ok; +} + +#endif /* JS_HAS_XDR */ + +#ifdef DEBUG_brendan + +#include +#include + +uint32 js_entry_count_max; +uint32 js_entry_count_sum; +double js_entry_count_sqsum; +uint32 js_entry_count_hist[11]; + +static void +MeterEntryCount(uintN count) +{ + if (count) { + js_entry_count_sum += count; + js_entry_count_sqsum += (double)count * count; + if (count > js_entry_count_max) + js_entry_count_max = count; + } + js_entry_count_hist[JS_MIN(count, 10)]++; +} + +void +js_DumpScopeMeters(JSRuntime *rt) +{ + static FILE *logfp; + if (!logfp) + logfp = fopen("/tmp/scope.stats", "a"); + + { + double mean = 0., var = 0., sigma = 0.; + double nscopes = rt->liveScopes; + double nentrys = js_entry_count_sum; + if (nscopes > 0 && nentrys >= 0) { + mean = nentrys / nscopes; + var = nscopes * js_entry_count_sqsum - nentrys * nentrys; + if (var < 0.0 || nscopes <= 1) + var = 0.0; + else + var /= nscopes * (nscopes - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + + fprintf(logfp, + "scopes %g entries %g mean %g sigma %g max %u", + nscopes, nentrys, mean, sigma, js_entry_count_max); + } + + fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n", + js_entry_count_hist[0], js_entry_count_hist[1], + js_entry_count_hist[2], js_entry_count_hist[3], + js_entry_count_hist[4], js_entry_count_hist[5], + js_entry_count_hist[6], js_entry_count_hist[7], + js_entry_count_hist[8], js_entry_count_hist[9], + js_entry_count_hist[10]); + js_entry_count_sum = js_entry_count_max = 0; + js_entry_count_sqsum = 0; + memset(js_entry_count_hist, 0, sizeof js_entry_count_hist); + fflush(logfp); +} + +#endif /* DEBUG_brendan */ + +uint32 +js_Mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSScope *scope; + JSScopeProperty *sprop; + JSClass *clasp; + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + scope = OBJ_SCOPE(obj); +#ifdef DEBUG_brendan + if (scope->object == obj) + MeterEntryCount(scope->entryCount); +#endif + + JS_ASSERT(!SCOPE_LAST_PROP(scope) || + SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope))); + + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) + continue; + MARK_SCOPE_PROPERTY(sprop); + if (!JSVAL_IS_INT(sprop->id)) + GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg); + +#if JS_HAS_GETTER_SETTER + if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { +#ifdef GC_MARK_DEBUG + char buf[64]; + JSAtom *atom = (JSAtom *)sprop->id; + const char *id = (atom && ATOM_IS_STRING(atom)) + ? JS_GetStringBytes(ATOM_TO_STRING(atom)) + : "unknown"; +#endif + + if (sprop->attrs & JSPROP_GETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_getter_str); +#endif + GC_MARK(cx, + JSVAL_TO_GCTHING((jsval) sprop->getter), + buf, + arg); + } + if (sprop->attrs & JSPROP_SETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_setter_str); +#endif + GC_MARK(cx, + JSVAL_TO_GCTHING((jsval) sprop->setter), + buf, + arg); + } + } +#endif /* JS_HAS_GETTER_SETTER */ + } + + /* No one runs while the GC is running, so we can use LOCKED_... here. */ + clasp = LOCKED_OBJ_GET_CLASS(obj); + if (clasp->mark) + (void) clasp->mark(cx, obj, arg); + + if (scope->object != obj) { + /* + * An unmutated object that shares a prototype's scope. We can't tell + * how many slots are allocated and in use at obj->slots by looking at + * scope, so we get obj->slots' length from its -1'st element. + */ + return (uint32) obj->slots[-1]; + } + return JS_MIN(obj->map->freeslot, obj->map->nslots); +} + +void +js_Clear(JSContext *cx, JSObject *obj) +{ + JSScope *scope; + JSRuntime *rt; + JSScopeProperty *sprop; + 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 unmutated and therefore sharing + * its prototype'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->object == obj) { + /* Clear the property cache before we clear the scope. */ + rt = cx->runtime; + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (!SCOPE_HAD_MIDDLE_DELETE(scope) || + SCOPE_HAS_PROPERTY(scope, sprop)) { + PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL); + } + } + + /* Now that we're done using scope->lastProp/table, clear scope. */ + js_ClearScope(cx, scope); + + /* Clear slot values and reset freeslot so we're consistent. */ + i = scope->map.nslots; + n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); + while (--i >= n) + obj->slots[i] = JSVAL_VOID; + scope->map.freeslot = n; + } + JS_UNLOCK_OBJ(cx, obj); +} + +jsval +js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) +{ + jsval v; + + JS_LOCK_OBJ(cx, obj); + v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj); + return v; +} + +void +js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +{ + uint32 nslots, rlimit, i; + JSClass *clasp; + jsval *newslots; + + JS_LOCK_OBJ(cx, obj); + nslots = (uint32) obj->slots[-1]; + if (slot >= nslots) { + clasp = LOCKED_OBJ_GET_CLASS(obj); + rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp); + JS_ASSERT(slot < rlimit); + if (rlimit > nslots) + nslots = rlimit; + + newslots = (jsval *) + JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + if (!newslots) { + JS_UNLOCK_OBJ(cx, obj); + return; + } + for (i = 1 + newslots[0]; i <= rlimit; i++) + newslots[i] = JSVAL_VOID; + newslots[0] = nslots; + if (OBJ_SCOPE(obj)->object == obj) + obj->map->nslots = nslots; + obj->slots = newslots + 1; + } + + obj->slots[slot] = v; + JS_UNLOCK_OBJ(cx, obj); +} + +#ifdef DEBUG + +/* Routines to print out values during debugging. */ + +void printChar(jschar *cp) { + fprintf(stderr, "jschar* (0x%p) \"", (void *)cp); + while (*cp) + fputc(*cp++, stderr); + fputc('"', stderr); + fputc('\n', stderr); +} + +void printString(JSString *str) { + size_t i, n; + jschar *s; + fprintf(stderr, "string (0x%p) \"", (void *)str); + s = JSSTRING_CHARS(str); + for (i=0, n=JSSTRING_LENGTH(str); i < n; i++) + fputc(s[i], stderr); + fputc('"', stderr); + fputc('\n', stderr); +} + +void printVal(JSContext *cx, jsval val); + +void printObj(JSContext *cx, JSObject *jsobj) { + jsuint i; + jsval val; + JSClass *clasp; + + fprintf(stderr, "object 0x%p\n", (void *)jsobj); + clasp = OBJ_GET_CLASS(cx, jsobj); + fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name); + for (i=0; i < jsobj->map->nslots; i++) { + fprintf(stderr, "slot %3d ", i); + val = jsobj->slots[i]; + if (JSVAL_IS_OBJECT(val)) + fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val)); + else + printVal(cx, val); + } +} + +void printVal(JSContext *cx, jsval val) { + fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val); + if (JSVAL_IS_NULL(val)) { + fprintf(stderr, "null\n"); + } else if (JSVAL_IS_VOID(val)) { + fprintf(stderr, "undefined\n"); + } else if (JSVAL_IS_OBJECT(val)) { + printObj(cx, JSVAL_TO_OBJECT(val)); + } else if (JSVAL_IS_INT(val)) { + fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val)); + } else if (JSVAL_IS_STRING(val)) { + printString(JSVAL_TO_STRING(val)); + } else if (JSVAL_IS_DOUBLE(val)) { + fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val)); + } else { + JS_ASSERT(JSVAL_IS_BOOLEAN(val)); + fprintf(stderr, "(boolean) %s\n", + JSVAL_TO_BOOLEAN(val) ? "true" : "false"); + } + fflush(stderr); +} + +void printId(JSContext *cx, jsid id) { + fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id); + printVal(cx, ID_TO_VALUE(id)); +} + +void printAtom(JSAtom *atom) { + printString(ATOM_TO_STRING(atom)); +} + +#endif diff --git a/src/extension/script/js/jsobj.h b/src/extension/script/js/jsobj.h new file mode 100644 index 000000000..a523194db --- /dev/null +++ b/src/extension/script/js/jsobj.h @@ -0,0 +1,464 @@ +/* -*- 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 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 + +struct JSObjectMap { + jsrefcount nrefs; /* count of all referencing objects */ + JSObjectOps *ops; /* high level object operation vtable */ + uint32 nslots; /* length of obj->slots vector */ + uint32 freeslot; /* index of next free obj->slots element */ +}; + +/* Shorthand macros for frequently-made calls. */ +#if defined JS_THREADSAFE && defined DEBUG +#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ + (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) +#else +#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ + (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) +#endif +#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ + (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) +#define OBJ_GET_PROPERTY(cx,obj,id,vp) \ + (obj)->map->ops->getProperty(cx,obj,id,vp) +#define OBJ_SET_PROPERTY(cx,obj,id,vp) \ + (obj)->map->ops->setProperty(cx,obj,id,vp) +#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ + (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp) +#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ + (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp) +#define OBJ_DELETE_PROPERTY(cx,obj,id,rval) \ + (obj)->map->ops->deleteProperty(cx,obj,id,rval) +#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp) \ + (obj)->map->ops->defaultValue(cx,obj,hint,vp) +#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp) \ + (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp) +#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp) \ + (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp) + +/* These four are time-optimized to avoid stub calls. */ +#define OBJ_THIS_OBJECT(cx,obj) \ + ((obj)->map->ops->thisObject \ + ? (obj)->map->ops->thisObject(cx,obj) \ + : (obj)) +#define OBJ_DROP_PROPERTY(cx,obj,prop) \ + ((obj)->map->ops->dropProperty \ + ? (obj)->map->ops->dropProperty(cx,obj,prop) \ + : (void)0) +#define OBJ_GET_REQUIRED_SLOT(cx,obj,slot) \ + ((obj)->map->ops->getRequiredSlot \ + ? (obj)->map->ops->getRequiredSlot(cx, obj, slot) \ + : JSVAL_VOID) +#define OBJ_SET_REQUIRED_SLOT(cx,obj,slot,v) \ + ((obj)->map->ops->setRequiredSlot \ + ? (obj)->map->ops->setRequiredSlot(cx, obj, slot, v) \ + : (void)0) + +/* + * In the original JS engine design, obj->slots pointed to a vector of length + * JS_INITIAL_NSLOTS words if obj->map was shared with a prototype object, + * else of length obj->map->nslots. With the advent of JS_GetReservedSlot, + * JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS (see jsapi.h), the size + * of the minimum length slots vector in the case where map is shared cannot + * be constant. This length starts at JS_INITIAL_NSLOTS, but may advance to + * include all the reserved slots. + * + * Therefore slots must be self-describing. Rather than tag its low order bit + * (a bit is all we need) to distinguish initial length from reserved length, + * we do "the BSTR thing": over-allocate slots by one jsval, and store the + * *net* length (counting usable slots, which have non-negative obj->slots[] + * indices) in obj->slots[-1]. All code that sets obj->slots must be aware of + * this hack -- you have been warned, and jsobj.c has been updated! + */ +struct JSObject { + JSObjectMap *map; + jsval *slots; +}; + +#define JSSLOT_PROTO 0 +#define JSSLOT_PARENT 1 +#define JSSLOT_CLASS 2 +#define JSSLOT_PRIVATE 3 +#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ + ? JSSLOT_PRIVATE + 1 \ + : JSSLOT_CLASS + 1) + +#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ + + JSCLASS_RESERVED_SLOTS(clasp)) + +#define JS_INITIAL_NSLOTS 5 + +#ifdef DEBUG +#define MAP_CHECK_SLOT(map,slot) \ + JS_ASSERT((uint32)slot < JS_MIN((map)->freeslot, (map)->nslots)) +#define OBJ_CHECK_SLOT(obj,slot) \ + MAP_CHECK_SLOT((obj)->map, slot) +#else +#define OBJ_CHECK_SLOT(obj,slot) ((void)0) +#endif + +/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */ +#define LOCKED_OBJ_GET_SLOT(obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot]) +#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ + (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value)) +#define LOCKED_OBJ_GET_PROTO(obj) \ + JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)) +#define LOCKED_OBJ_GET_CLASS(obj) \ + ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS))) + +#ifdef JS_THREADSAFE + +/* Thread-safe functions and wrapper macros for accessing obj->slots. */ +#define OBJ_GET_SLOT(cx,obj,slot) \ + (OBJ_CHECK_SLOT(obj, slot), \ + (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ + ? LOCKED_OBJ_GET_SLOT(obj, slot) \ + : js_GetSlotThreadSafe(cx, obj, slot)) + +#define OBJ_SET_SLOT(cx,obj,slot,value) \ + (OBJ_CHECK_SLOT(obj, slot), \ + (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ + ? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value) \ + : js_SetSlotThreadSafe(cx, obj, slot, value)) + +/* + * 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 there is no hazard in directly accessing + * obj->slots[slot] from the GC's thread, once rt->gcRunning has been 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) + +#define GC_AWARE_GET_SLOT(cx, obj, slot) \ + ((OBJ_IS_NATIVE(obj) && CX_THREAD_IS_RUNNING_GC(cx)) \ + ? (obj)->slots[slot] \ + : OBJ_GET_SLOT(cx, obj, slot)) + +#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) +#define GC_AWARE_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) + +#endif /* !JS_THREADSAFE */ + +/* Thread-safe proto, parent, and class access macros. */ +#define OBJ_GET_PROTO(cx,obj) \ + JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)) +#define OBJ_SET_PROTO(cx,obj,proto) \ + OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) + +#define OBJ_GET_PARENT(cx,obj) \ + JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)) +#define OBJ_SET_PARENT(cx,obj,parent) \ + OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)) + +#define OBJ_GET_CLASS(cx,obj) \ + ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS))) + +/* Test whether a map or object is native. */ +#define MAP_IS_NATIVE(map) \ + ((map)->ops == &js_ObjectOps || \ + ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap)) + +#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) + +extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; +extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; +extern JSClass js_ObjectClass; +extern JSClass js_WithClass; + +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) ((jsatomid)(he)->value & SHARP_BIT) +#define MAKE_SHARP(he) ((he)->value = (void*)((jsatomid)(he)->value|SHARP_BIT)) +#define IS_BUSY(he) ((jsatomid)(he)->value & BUSY_BIT) +#define MAKE_BUSY(he) ((he)->value = (void*)((jsatomid)(he)->value|BUSY_BIT)) +#define CLEAR_BUSY(he) ((he)->value = (void*)((jsatomid)(he)->value&~BUSY_BIT)) + +extern JSHashEntry * +js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, + jschar **sp); + +extern void +js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); + +extern JSBool +js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSBool +js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +extern JSObject * +js_InitObjectClass(JSContext *cx, JSObject *obj); + +/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */ +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 void +js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp); + +extern JSObjectMap * +js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, + JSClass *clasp, JSObject *obj); + +extern void +js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); + +extern JSObjectMap * +js_HoldObjectMap(JSContext *cx, JSObjectMap *map); + +extern JSObjectMap * +js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); + +extern JSObject * +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); + +extern JSObject * +js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv); + +extern void +js_FinalizeObject(JSContext *cx, JSObject *obj); + +extern JSBool +js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); + +extern void +js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); + +/* + * 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); + +/* + * 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_DROP_PROPERTY(cx, obj, *propp) both to + * drop the held property, and to release the lock on obj. + */ +extern JSBool +js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp); + +extern JSBool +js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN shortid, JSProperty **propp); + +/* + * Unlike js_DefineProperty, 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 OBJ_DROP_PROPERTY(cx, *objp, *propp). + */ +#if defined JS_THREADSAFE && defined DEBUG +extern JS_FRIEND_API(JSBool) +_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp, const char *file, uintN line); + +#define js_LookupProperty(cx,obj,id,objp,propp) \ + _js_LookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) +#else +extern JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); +#endif + +extern JS_FRIEND_API(JSBool) +js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, + JSProperty **propp); + +extern JSObject * +js_FindIdentifierBase(JSContext *cx, jsid id); + +extern JSObject * +js_FindVariableScope(JSContext *cx, JSFunction **funp); + +extern JSBool +js_GetProperty(JSContext *cx, JSObject *obj, jsid id, 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 JSIdArray * +js_NewIdArray(JSContext *cx, jsint length); + +extern JSIdArray * +js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length); + +extern JSBool +js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp); + +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); + +extern JSBool +js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +extern JSBool +js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop); + +extern JSBool +js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, + uintN attrs); + +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 uint32 +js_Mark(JSContext *cx, JSObject *obj, void *arg); + +extern void +js_Clear(JSContext *cx, JSObject *obj); + +extern jsval +js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); + +extern void +js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); + +JS_END_EXTERN_C + +#endif /* jsobj_h___ */ diff --git a/src/extension/script/js/jsopcode.c b/src/extension/script/js/jsopcode.c new file mode 100644 index 000000000..166f602d5 --- /dev/null +++ b/src/extension/script/js/jsopcode.c @@ -0,0 +1,2660 @@ +/* -*- 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 bytecode descriptors, disassemblers, and decompilers. + */ +#include "jsstddef.h" +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include +#include "jstypes.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 "jsconfig.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" + +const char js_const_str[] = "const"; +const char js_var_str[] = "var"; +const char js_function_str[] = "function"; +const char js_in_str[] = "in"; +const char js_instanceof_str[] = "instanceof"; +const char js_new_str[] = "new"; +const char js_delete_str[] = "delete"; +const char js_typeof_str[] = "typeof"; +const char js_void_str[] = "void"; +const char js_null_str[] = "null"; +const char js_this_str[] = "this"; +const char js_false_str[] = "false"; +const char js_true_str[] = "true"; + +const char *js_incop_str[] = {"++", "--"}; + +/* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ +#ifdef __WINDOWS_386__ + #ifdef FAR + #undef FAR + #endif +#else /* !__WINDOWS_386__ */ +#ifndef FAR +#define FAR +#endif +#endif /* !__WINDOWS_386__ */ + +const JSCodeSpec FAR js_CodeSpec[] = { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + {name,token,length,nuses,ndefs,prec,format}, +#include "jsopcode.tbl" +#undef OPDEF +}; + +uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0]; + +/************************************************************************/ + +static ptrdiff_t +GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) +{ + uint32 type; + + type = (js_CodeSpec[*pc].format & JOF_TYPEMASK); + if (JOF_TYPE_IS_EXTENDED_JUMP(type)) + return GET_JUMPX_OFFSET(pc2); + return GET_JUMP_OFFSET(pc2); +} + +#ifdef DEBUG + +JS_FRIEND_API(void) +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, + PTRDIFF(pc, script->code, jsbytecode), + lines, fp); + if (!len) + return; + pc += len; + } +} + +JS_FRIEND_API(uintN) +js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, + JSBool lines, FILE *fp) +{ + JSOp op; + const JSCodeSpec *cs; + intN len, off; + JSAtom *atom; + JSString *str; + char *cstr; + + 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 = (intN)cs->length; + fprintf(fp, "%05u:", loc); + if (lines) + fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); + fprintf(fp, " %s", cs->name); + switch (cs->format & JOF_TYPEMASK) { + case JOF_BYTE: + if (op == JSOP_TRAP) { + op = JS_GetTrapOpcode(cx, script, pc); + if (op == JSOP_LIMIT) + return 0; + len = (intN)js_CodeSpec[op].length; + } + break; + + case JOF_JUMP: + case JOF_JUMPX: + off = GetJumpOffset(pc, pc); + fprintf(fp, " %u (%d)", loc + off, off); + break; + + case JOF_CONST: + atom = GET_ATOM(cx, script, pc); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); + if (!cstr) + return 0; + fprintf(fp, " %s", cstr); + JS_free(cx, cstr); + break; + + case JOF_UINT16: + fprintf(fp, " %u", GET_ARGC(pc)); + break; + +#if JS_HAS_SWITCH_STATEMENT + case JOF_TABLESWITCH: + { + jsbytecode *pc2; + jsint i, low, high; + + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + low = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + high = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); + for (i = low; i <= high; i++) { + off = GetJumpOffset(pc, pc2); + fprintf(fp, "\n\t%d: %d", i, off); + pc2 += JUMP_OFFSET_LEN; + } + len = 1 + pc2 - pc; + break; + } + + case JOF_LOOKUPSWITCH: + { + jsbytecode *pc2; + jsint npairs; + + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + fprintf(fp, " offset %d npairs %u", off, (uintN) npairs); + while (npairs) { + atom = GET_ATOM(cx, script, pc2); + pc2 += ATOM_INDEX_LEN; + off = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + cstr = js_DeflateString(cx, JSSTRING_CHARS(str), + JSSTRING_LENGTH(str)); + if (!cstr) + return 0; + fprintf(fp, "\n\t%s: %d", cstr, off); + JS_free(cx, cstr); + npairs--; + } + len = 1 + pc2 - pc; + break; + } +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case JOF_QARG: + fprintf(fp, " %u", GET_ARGNO(pc)); + break; + + case JOF_QVAR: + fprintf(fp, " %u", GET_VARNO(pc)); + break; + +#if JS_HAS_LEXICAL_CLOSURE + case JOF_DEFLOCALVAR: + fprintf(fp, " %u", GET_VARNO(pc)); + pc += VARNO_LEN; + atom = GET_ATOM(cx, script, pc); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); + if (!cstr) + return 0; + fprintf(fp, " %s", cstr); + JS_free(cx, cstr); + break; +#endif + + 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 +SprintAlloc(Sprinter *sp, size_t nb) +{ + if (!sp->base) { + JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb); + } else { + JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb); + } + if (!sp->base) { + JS_ReportOutOfMemory(sp->context); + return JS_FALSE; + } + sp->size += nb; + return JS_TRUE; +} + +static ptrdiff_t +SprintPut(Sprinter *sp, const char *s, size_t len) +{ + ptrdiff_t nb, offset; + char *bp; + + /* Allocate space for s, including the '\0' at the end. */ + nb = (sp->offset + len + 1) - sp->size; + if (nb > 0 && !SprintAlloc(sp, nb)) + 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 +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 = SprintPut(sp, bp, strlen(bp)); + free(bp); + return offset; +} + +const jschar js_EscapeMap[] = { + '\b', 'b', + '\f', 'f', + '\n', 'n', + '\r', 'r', + '\t', 't', + '\v', 'v', + '"', '"', + '\'', '\'', + '\\', '\\', + 0 +}; + +static char * +QuoteString(Sprinter *sp, JSString *str, jschar quote) +{ + ptrdiff_t off, len, nb; + const jschar *s, *t, *u, *z; + char *bp; + jschar c; + JSBool ok; + + /* Sample off first for later return value pointer computation. */ + off = sp->offset; + if (quote && Sprint(sp, "%c", (char)quote) < 0) + return NULL; + + /* Loop control variables: z points at end of string sentinel. */ + s = JSSTRING_CHARS(str); + z = s + JSSTRING_LENGTH(str); + for (t = s; t < z; s = ++t) { + /* Move t forward from s past un-quote-worthy characters. */ + c = *t; + while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) { + c = *++t; + if (t == z) + break; + } + len = PTRDIFF(t, s, jschar); + + /* Allocate space for s, including the '\0' at the end. */ + nb = (sp->offset + len + 1) - sp->size; + if (nb > 0 && !SprintAlloc(sp, nb)) + 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 ((u = js_strchr(js_EscapeMap, c)) != NULL) + ok = Sprint(sp, "\\%c", (char)u[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 (quote && Sprint(sp, "%c", (char)quote) < 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 */ + JSBool pretty; /* pretty-print: indent, use newlines */ + JSScript *script; /* script being printed */ + JSScope *scope; /* script function scope */ +}; + +JSPrinter * +js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) +{ + JSPrinter *jp; + JSStackFrame *fp; + JSObjectMap *map; + + jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter)); + if (!jp) + return NULL; + INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); + JS_InitArenaPool(&jp->pool, name, 256, 1); + jp->indent = indent; + jp->pretty = pretty; + jp->script = NULL; + jp->scope = NULL; + fp = cx->fp; + if (fp && fp->fun && fp->fun->object) { + map = fp->fun->object->map; + if (MAP_IS_NATIVE(map)) + jp->scope = (JSScope *)map; + } + return jp; +} + +void +js_DestroyPrinter(JSPrinter *jp) +{ + JS_FinishArenaPool(&jp->pool); + JS_free(jp->sprinter.context, 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; +} + +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') { + if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) + return -1; + format++; + } + + /* 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) + return -1; + fp[cc] = '\0'; + format = fp; + } + + /* Allocate temp space, convert format, and put. */ + bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ + if (fp) { + JS_free(jp->sprinter.context, fp); + format = NULL; + } + if (!bp) { + JS_ReportOutOfMemory(jp->sprinter.context); + return -1; + } + + cc = strlen(bp); + if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) + cc = -1; + free(bp); + + va_end(ap); + return cc; +} + +JSBool +js_puts(JSPrinter *jp, const char *s) +{ + return SprintPut(&jp->sprinter, s, strlen(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 */ + JSPrinter *printer; /* permanent output goes here */ +} SprintStack; + +/* Gap between stacked strings to allow for insertion of parens and commas. */ +#define PAREN_SLOP (2 + 1) + +/* + * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, + * JSOP_SETPROP, and JSOP_SETELEM, respectively. See the first assertion in + * PushOff. + */ +#define JSOP_GETPROP2 254 +#define JSOP_GETELEM2 255 + +static JSBool +PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) +{ + uintN top; + +#if JSOP_LIMIT > JSOP_GETPROP2 +#error JSOP_LIMIT must be <= JSOP_GETPROP2 +#endif + if (!SprintAlloc(&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 < ss->printer->script->depth); + if (top >= ss->printer->script->depth) { + 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] = (op == JSOP_GETPROP2) ? JSOP_GETPROP + : (op == JSOP_GETELEM2) ? JSOP_GETELEM + : (jsbytecode) op; + ss->top = ++top; + ss->sprinter.offset += PAREN_SLOP; + return JS_TRUE; +} + +static ptrdiff_t +PopOff(SprintStack *ss, JSOp op) +{ + uintN top; + const JSCodeSpec *cs, *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; + topcs = &js_CodeSpec[ss->opcodes[top]]; + cs = &js_CodeSpec[op]; + if (topcs->prec != 0 && topcs->prec < cs->prec) { + ss->offsets[top] -= 2; + ss->sprinter.offset = ss->offsets[top]; + off = Sprint(&ss->sprinter, "(%s)", + OFF2STR(&ss->sprinter, ss->sprinter.offset + 2)); + } else { + off = ss->sprinter.offset = ss->offsets[top]; + } + return off; +} + +#if JS_HAS_SWITCH_STATEMENT +typedef struct TableEntry { + jsval key; + ptrdiff_t offset; +} TableEntry; + +static int +CompareOffsets(const void *v1, const void *v2, void *arg) +{ + const TableEntry *te1 = (const TableEntry *) v1, + *te2 = (const TableEntry *) v2; + + return te1->offset - te2->offset; +} + +static JSBool +Decompile(SprintStack *ss, jsbytecode *pc, intN nb); + +static JSBool +DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, + jsbytecode *pc, ptrdiff_t switchLength, + ptrdiff_t defaultOffset, JSBool isCondSwitch) +{ + JSContext *cx; + JSPrinter *jp; + char *lval, *rval; + uintN i; + ptrdiff_t diff, off, off2, caseExprOff; + jsval key; + JSString *str; + + cx = ss->sprinter.context; + jp = ss->printer; + + lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP)); + js_printf(jp, "\tswitch (%s) {\n", lval); + + if (tableLength) { + diff = table[0].offset - defaultOffset; + if (diff > 0) { + jp->indent += 2; + js_printf(jp, "\tdefault:\n"); + jp->indent += 2; + if (!Decompile(ss, pc + defaultOffset, diff)) + return JS_FALSE; + jp->indent -= 4; + } + + caseExprOff = isCondSwitch + ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length + : 0; + + for (i = 0; i < tableLength; i++) { + off = table[i].offset; + if (i + 1 < tableLength) + off2 = table[i + 1].offset; + else + off2 = 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)) { + return JS_FALSE; + } + caseExprOff = nextCaseExprOff; + } else { + /* + * key comes from an atom, not the decompiler, so we need to + * quote it if it's a string literal. + */ + str = js_ValueToString(cx, key); + if (!str) + return JS_FALSE; + jp->indent += 2; + if (JSVAL_IS_STRING(key)) { + rval = QuoteString(&ss->sprinter, str, (jschar)'"'); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + } else { + rval = JS_GetStringBytes(str); + } + 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)) + return JS_FALSE; + off = defaultOffset; + } + jp->indent -= 2; + js_printf(jp, "\tdefault:\n"); + jp->indent += 2; + } + if (!Decompile(ss, pc + off, off2 - off)) + return JS_FALSE; + jp->indent -= 4; + } + } + + if (defaultOffset == switchLength) { + jp->indent += 2; + js_printf(jp, "\tdefault:\n"); + jp->indent -= 2; + } + js_printf(jp, "\t}\n"); + return JS_TRUE; +} +#endif + +static JSAtom * +GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) +{ + JSScope *scope; + JSScopeProperty *sprop; + JSObject *obj, *proto; + + scope = jp->scope; + while (scope) { + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (sprop->getter != getter) + continue; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + JS_ASSERT(!JSVAL_IS_INT(sprop->id)); + if ((uintN) sprop->shortid == slot) + return (JSAtom *) sprop->id; + } + obj = scope->object; + if (!obj) + break; + proto = OBJ_GET_PROTO(jp->sprinter.context, obj); + if (!proto) + break; + scope = OBJ_SCOPE(proto); + } + return NULL; +} + +static const char * +VarPrefix(jssrcnote *sn) +{ + const char *kw; + static char buf[8]; + + kw = NULL; + if (sn) { + if (SN_TYPE(sn) == SRC_VAR) + kw = js_var_str; + else if (SN_TYPE(sn) == SRC_CONST) + kw = js_const_str; + } + if (!kw) + return ""; + JS_snprintf(buf, sizeof buf, "%s ", kw); + return buf; +} + +static JSBool +Decompile(SprintStack *ss, jsbytecode *pc, intN nb) +{ + JSContext *cx; + JSPrinter *jp, *jp2; + jsbytecode *endpc, *done, *forelem_tail, *forelem_done; + ptrdiff_t len, todo, oplen, cond, next, tail; + JSOp op, lastop, saveop; + const JSCodeSpec *cs, *topcs; + jssrcnote *sn, *sn2; + const char *lval, *rval, *xval, *fmt; + jsint i, argc; + char **argv; + JSAtom *atom; + JSObject *obj; + JSFunction *fun; + JSString *str; + JSBool ok; + jsval val; + +/* + * Local macros + */ +#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return JS_FALSE +#define POP_STR() OFF2STR(&ss->sprinter, PopOff(ss, op)) +#define LOCAL_ASSERT(expr) JS_ASSERT(expr); if (!(expr)) return JS_FALSE + +/* + * Get atom from 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 \ + jschar quote_; \ + atom = GET_ATOM(cx, jp->script, pc); \ + if (ATOM_KEYWORD(atom)) { \ + quote_ = '\''; \ + fmt = qfmt; \ + } else { \ + quote_ = 0; \ + fmt = ufmt; \ + } \ + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ + if (!rval) \ + return JS_FALSE; \ + JS_END_MACRO + + cx = ss->sprinter.context; + jp = ss->printer; + endpc = pc + nb; + forelem_tail = forelem_done = NULL; + todo = -2; /* NB: different from Sprint() error return. */ + tail = -1; + op = JSOP_NOP; + sn = NULL; + rval = NULL; + + while (pc < endpc) { + lastop = op; + op = saveop = (JSOp) *pc; + if (op >= JSOP_LIMIT) { + switch (op) { + case JSOP_GETPROP2: + saveop = JSOP_GETPROP; + break; + case JSOP_GETELEM2: + saveop = JSOP_GETELEM; + break; + default:; + } + } + cs = &js_CodeSpec[saveop]; + len = oplen = cs->length; + + if (cs->token) { + switch (cs->nuses) { + case 2: + rval = POP_STR(); + lval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { + /* Print only the right operand of the assignment-op. */ + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); + } else { + todo = Sprint(&ss->sprinter, "%s %s %s", + lval, cs->token, rval); + } + break; + + case 1: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval); + break; + + case 0: +#if JS_HAS_GETTER_SETTER + if (op == JSOP_GETTER || op == JSOP_SETTER) { + todo = -2; + break; + } +#endif + todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->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) { +#if JS_HAS_DO_WHILE_LOOP + case SRC_WHILE: + js_printf(jp, "\tdo {\n"); + jp->indent += 4; + break; +#endif /* JS_HAS_DO_WHILE_LOOP */ + + case SRC_FOR: + rval = ""; + + do_forloop: + /* Skip the JSOP_NOP or JSOP_POP bytecode. */ + pc++; + + /* Get the cond, next, and loop-closing tail offsets. */ + cond = js_GetSrcNoteOffset(sn, 0); + next = js_GetSrcNoteOffset(sn, 1); + tail = js_GetSrcNoteOffset(sn, 2); + LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0); + + /* Print the keyword and the possibly empty init-part. */ + js_printf(jp, "\tfor (%s;", rval); + + if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) { + /* Decompile the loop condition. */ + DECOMPILE_CODE(pc, cond); + js_printf(jp, " %s", POP_STR()); + } + + /* Need a semicolon whether or not there was a cond. */ + js_puts(jp, ";"); + + if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) { + /* Decompile the loop updater. */ + DECOMPILE_CODE(pc + next, tail - next - 1); + js_printf(jp, " %s", POP_STR()); + } + + /* Do the loop body. */ + js_puts(jp, ") {\n"); + jp->indent += 4; + oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0; + DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); + 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: + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + jp->indent -= 4; + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\t%s:\n", rval); + jp->indent += 4; + break; + + case SRC_LABELBRACE: + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + 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_CATCH: + jp->indent -= 4; + sn = js_GetSrcNote(jp->script, pc); + pc += oplen; + js_printf(jp, "\t} catch ("); + + LOCAL_ASSERT(*pc == JSOP_NAME); + pc += js_CodeSpec[JSOP_NAME].length; + LOCAL_ASSERT(*pc == JSOP_PUSHOBJ); + pc += js_CodeSpec[JSOP_PUSHOBJ].length; + LOCAL_ASSERT(*pc == JSOP_NEWINIT); + pc += js_CodeSpec[JSOP_NEWINIT].length; + LOCAL_ASSERT(*pc == JSOP_EXCEPTION); + pc += js_CodeSpec[JSOP_EXCEPTION].length; + LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR); + atom = GET_ATOM(cx, jp->script, pc); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "%s", rval); + pc += js_CodeSpec[JSOP_INITCATCHVAR].length; + LOCAL_ASSERT(*pc == JSOP_ENTERWITH); + pc += js_CodeSpec[JSOP_ENTERWITH].length; + + len = js_GetSrcNoteOffset(sn, 0); + if (len) { + js_printf(jp, " if "); + DECOMPILE_CODE(pc, len); + js_printf(jp, "%s", POP_STR()); + pc += len; + LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + pc += js_CodeSpec[*pc].length; + } + + js_printf(jp, ") {\n"); + jp->indent += 4; + len = 0; + break; + + case SRC_FUNCDEF: + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + JS_ASSERT(ATOM_IS_OBJECT(atom)); + do_function: + obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); + jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), + jp->indent, jp->pretty); + if (!jp2) + return JS_FALSE; + jp2->scope = jp->scope; + if (js_DecompileFunction(jp2, fun)) { + str = js_GetPrinterOutput(jp2); + if (str) + js_printf(jp, "%s\n", JS_GetStringBytes(str)); + } + js_DestroyPrinter(jp2); + break; + + default:; + } + case JSOP_RETRVAL: + break; + + case JSOP_GROUP: + /* Use last real op so PopOff adds parens if needed. */ + todo = PopOff(ss, lastop); + + /* Now add user-supplied parens only if PopOff did not. */ + cs = &js_CodeSpec[lastop]; + topcs = &js_CodeSpec[ss->opcodes[ss->top]]; + if (topcs->prec >= cs->prec) { + todo = Sprint(&ss->sprinter, "(%s)", + OFF2STR(&ss->sprinter, todo)); + } + break; + + case JSOP_PUSH: + case JSOP_PUSHOBJ: + case JSOP_BINDNAME: + todo = Sprint(&ss->sprinter, ""); + break; + +#if JS_HAS_EXCEPTIONS + case JSOP_TRY: + js_printf(jp, "\ttry {\n"); + jp->indent += 4; + todo = -2; + break; + + { + static const char finally_cookie[] = "finally-cookie"; + + case JSOP_FINALLY: + jp->indent -= 4; + js_printf(jp, "\t} finally {\n"); + jp->indent += 4; + + /* + * We must push an empty string placeholder for gosub's return + * address, popped by JSOP_RETSUB and counted by script->depth + * but not by ss->top (see JSOP_SETSP, below). + */ + todo = Sprint(&ss->sprinter, finally_cookie); + break; + + case JSOP_RETSUB: + rval = POP_STR(); + LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0); + todo = -2; + break; + } + + case JSOP_SWAP: + /* + * We don't generate this opcode currently, and previously we + * did not need to decompile it. If old, serialized bytecode + * uses it still, we should fall through and set todo = -2. + */ + /* FALL THROUGH */ + + 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_SETSP: + /* + * 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. + */ + ss->top = (uintN) GET_ATOM_INDEX(pc); + break; + + case JSOP_EXCEPTION: + /* + * The only other JSOP_EXCEPTION case occurs as part of a code + * sequence that follows a SRC_CATCH-annotated JSOP_NOP. + */ + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN); + todo = -2; + break; +#endif /* JS_HAS_EXCEPTIONS */ + + case JSOP_POP: + case JSOP_POPV: + sn = js_GetSrcNote(jp->script, pc); + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_FOR: + rval = POP_STR(); + todo = -2; + goto do_forloop; + + case SRC_PCDELTA: + /* Pop and save to avoid blowing stack depth budget. */ + lval = JS_strdup(cx, POP_STR()); + if (!lval) + return JS_FALSE; + + /* + * 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)) { + JS_free(cx, (char *)lval); + return JS_FALSE; + } + + /* Pop Decompile result and print comma expression. */ + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); + JS_free(cx, (char *)lval); + break; + + case SRC_HIDDEN: + /* Hide this pop, it's from a goto in a with or for/in. */ + todo = -2; + break; + + default: + rval = POP_STR(); + if (*rval != '\0') + js_printf(jp, "\t%s;\n", rval); + todo = -2; + break; + } + break; + + case JSOP_POP2: + (void) PopOff(ss, op); + (void) PopOff(ss, op); + todo = -2; + break; + + { + static const char with_cookie[] = "with-cookie"; + + case JSOP_ENTERWITH: + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_HIDDEN) { + todo = -2; + break; + } + 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_SETRVAL: + case JSOP_RETURN: + rval = POP_STR(); + if (*rval != '\0') + js_printf(jp, "\t%s %s;\n", cs->name, rval); + else + js_printf(jp, "\t%s;\n", cs->name); + todo = -2; + break; + +#if JS_HAS_EXCEPTIONS + 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", cs->name, rval); + break; +#endif /* JS_HAS_EXCEPTIONS */ + + case JSOP_GOTO: + case JSOP_GOTOX: + sn = js_GetSrcNote(jp->script, pc); + switch (sn ? SN_TYPE(sn) : SRC_NULL) { + case SRC_CONT2LABEL: + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\tcontinue %s;\n", rval); + break; + case SRC_CONTINUE: + js_printf(jp, "\tcontinue;\n"); + break; + case SRC_BREAK2LABEL: + atom = js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) js_GetSrcNoteOffset(sn, 0)); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + 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: + 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_STR(); + js_printf(jp, "\tif (%s) {\n", rval); + jp->indent += 4; + if (SN_TYPE(sn) == SRC_IF) { + DECOMPILE_CODE(pc + oplen, len - oplen); + } else { + len = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, len - oplen); + jp->indent -= 4; + pc += len; + LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + oplen = js_CodeSpec[*pc].length; + len = GetJumpOffset(pc, pc); + js_printf(jp, "\t} else {\n"); + jp->indent += 4; + DECOMPILE_CODE(pc + oplen, len - oplen); + } + jp->indent -= 4; + js_printf(jp, "\t}\n"); + todo = -2; + break; + + case SRC_WHILE: + rval = POP_STR(); + js_printf(jp, "\twhile (%s) {\n", rval); + jp->indent += 4; + tail = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, tail - oplen); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + todo = -2; + break; + + case SRC_COND: + xval = JS_strdup(cx, POP_STR()); + if (!xval) + return JS_FALSE; + len = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, len - oplen); + lval = JS_strdup(cx, POP_STR()); + if (!lval) { + JS_free(cx, (void *)xval); + return JS_FALSE; + } + 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); + JS_free(cx, (void *)xval); + JS_free(cx, (void *)lval); + break; + + default: + break; + } + break; + + case JSOP_IFNE: + case JSOP_IFNEX: +#if JS_HAS_DO_WHILE_LOOP + /* Currently, this must be a do-while loop's upward branch. */ + jp->indent -= 4; + js_printf(jp, "\t} while (%s);\n", POP_STR()); + todo = -2; +#else + JS_ASSERT(0); +#endif /* JS_HAS_DO_WHILE_LOOP */ + 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 JS_FALSE; + done = pc + GetJumpOffset(pc, pc); + pc += len; + len = PTRDIFF(done, pc, jsbytecode); + DECOMPILE_CODE(pc, len); + 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); + JS_free(cx, (char *)rval); + } + if (tail < 0) + todo = -1; + } else { + todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); + } + JS_free(cx, (char *)lval); + break; + + case JSOP_AND: + case JSOP_ANDX: + xval = "&&"; + goto do_logical_connective; + + case JSOP_FORARG: + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_fornameinloop; + + case JSOP_FORVAR: + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); + LOCAL_ASSERT(atom); + goto do_fornameinloop; + + case JSOP_FORNAME: + atom = GET_ATOM(cx, jp->script, pc); + + do_fornameinloop: + sn = js_GetSrcNote(jp->script, pc); + xval = NULL; + lval = ""; + goto do_forinloop; + + case JSOP_FORPROP: + xval = NULL; + atom = GET_ATOM(cx, jp->script, pc); + if (ATOM_KEYWORD(atom)) { + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + (jschar)'\''); + if (!xval) + return JS_FALSE; + atom = NULL; + } + lval = POP_STR(); + sn = NULL; + + do_forinloop: + pc += oplen; + LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + oplen = js_CodeSpec[*pc].length; + len = GetJumpOffset(pc, pc); + sn2 = js_GetSrcNote(jp->script, pc); + tail = js_GetSrcNoteOffset(sn2, 0); + + do_forinbody: + js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval); + if (atom) { + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!xval) + return JS_FALSE; + RETRACT(&ss->sprinter, xval); + js_printf(jp, *lval ? ".%s" : "%s", xval); + } else if (xval) { + js_printf(jp, "[%s]", xval); + } + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); + js_printf(jp, " in %s) {\n", rval); + jp->indent += 4; + DECOMPILE_CODE(pc + oplen, tail - oplen); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + todo = -2; + break; + + case JSOP_FORELEM: + pc++; + LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + len = js_CodeSpec[*pc].length; + + /* + * Arrange for the JSOP_ENUMELEM case to set tail for use by + * do_forinbody: code that uses on it to find the loop-closing + * jump (whatever its format, normal or extended), in order to + * bound the recursively decompiled loop body. + */ + sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(!forelem_tail); + forelem_tail = pc + js_GetSrcNoteOffset(sn, 0); + + /* + * This gets a little wacky. Only the length of the for loop + * body PLUS the element-indexing expression is known here, so + * we pass the after-loop pc to the JSOP_ENUMELEM case, which + * is immediately below, to decompile that helper bytecode via + * the 'forelem_done' local. + * + * Since a for..in loop can't nest in the head of another for + * loop, we can use forelem_{tail,done} singletons to remember + * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto) + * to label do_forinbody. + */ + JS_ASSERT(!forelem_done); + forelem_done = pc + GetJumpOffset(pc, pc); + break; + + case JSOP_ENUMELEM: + /* + * 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; + xval = POP_STR(); + lval = POP_STR(); + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); + JS_ASSERT(forelem_tail > pc); + tail = forelem_tail - pc; + forelem_tail = NULL; + JS_ASSERT(forelem_done > pc); + len = forelem_done - pc; + forelem_done = NULL; + goto do_forinbody; + + case JSOP_DUP2: + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]); + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); + if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2])) + return JS_FALSE; + /* FALL THROUGH */ + + case JSOP_DUP: + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); + op = ss->opcodes[ss->top-1]; + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); + break; + + case JSOP_SETARG: + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_setname; + + case JSOP_SETVAR: + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); + LOCAL_ASSERT(atom); + goto do_setname; + + case JSOP_SETCONST: + case JSOP_SETNAME: + atom = GET_ATOM(cx, jp->script, pc); + do_setname: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return JS_FALSE; + 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, js_CodeSpec[lastop].token, rval); + } else { + sn = js_GetSrcNote(jp->script, pc); + todo = Sprint(&ss->sprinter, "%s%s = %s", + VarPrefix(sn), lval, rval); + } + break; + + case JSOP_NEW: + case JSOP_CALL: + case JSOP_EVAL: +#if JS_HAS_LVALUE_RETURN + case JSOP_SETCALL: +#endif + saveop = op; + op = JSOP_NOP; /* turn off parens */ + argc = GET_ARGC(pc); + argv = (char **) + JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv); + if (!argv) + return JS_FALSE; + + ok = JS_TRUE; + for (i = argc; i > 0; i--) { + argv[i] = JS_strdup(cx, POP_STR()); + if (!argv[i]) { + ok = JS_FALSE; + break; + } + } + + /* Skip the JSOP_PUSHOBJ-created empty string. */ + LOCAL_ASSERT(ss->top >= 2); + (void) PopOff(ss, op); + + /* Get the callee's decompiled image in argv[0]. */ + argv[0] = JS_strdup(cx, POP_STR()); + if (!argv[i]) + ok = JS_FALSE; + + lval = "(", rval = ")"; + if (saveop == JSOP_NEW) { + todo = Sprint(&ss->sprinter, "%s %s%s", + js_new_str, argv[0], lval); + } else { + todo = Sprint(&ss->sprinter, "%s%s", + argv[0], lval); + } + if (todo < 0) + ok = JS_FALSE; + + for (i = 1; i <= argc; i++) { + if (!argv[i] || + Sprint(&ss->sprinter, "%s%s", + argv[i], (i < argc) ? ", " : "") < 0) { + ok = JS_FALSE; + break; + } + } + if (Sprint(&ss->sprinter, rval) < 0) + ok = JS_FALSE; + + for (i = 0; i <= argc; i++) { + if (argv[i]) + JS_free(cx, argv[i]); + } + JS_free(cx, argv); + if (!ok) + return JS_FALSE; + op = saveop; +#if JS_HAS_LVALUE_RETURN + if (op == JSOP_SETCALL) { + if (!PushOff(ss, todo, op)) + return JS_FALSE; + todo = Sprint(&ss->sprinter, ""); + } +#endif + break; + + case JSOP_DELNAME: + atom = GET_ATOM(cx, jp->script, pc); + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return JS_FALSE; + RETRACT(&ss->sprinter, 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); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval); + break; + + case JSOP_DELELEM: + xval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s[%s]", + js_delete_str, lval, xval); + break; + + case JSOP_TYPEOF: + case JSOP_VOID: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval); + break; + + case JSOP_INCARG: + case JSOP_DECARG: + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_incatom; + + case JSOP_INCVAR: + case JSOP_DECVAR: + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); + LOCAL_ASSERT(atom); + goto do_incatom; + + case JSOP_INCNAME: + case JSOP_DECNAME: + atom = GET_ATOM(cx, jp->script, pc); + do_incatom: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return JS_FALSE; + RETRACT(&ss->sprinter, lval); + todo = Sprint(&ss->sprinter, "%s%s", + js_incop_str[!(cs->format & JOF_INC)], lval); + break; + + case JSOP_INCPROP: + case JSOP_DECPROP: + GET_ATOM_QUOTE_AND_FMT("%s%s[%s]", "%s%s.%s", rval); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, + js_incop_str[!(cs->format & JOF_INC)], + lval, rval); + break; + + case JSOP_INCELEM: + case JSOP_DECELEM: + xval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s%s[%s]", + js_incop_str[!(cs->format & JOF_INC)], + lval, xval); + break; + + case JSOP_ARGINC: + case JSOP_ARGDEC: + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_atominc; + + case JSOP_VARINC: + case JSOP_VARDEC: + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); + LOCAL_ASSERT(atom); + goto do_atominc; + + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + atom = GET_ATOM(cx, jp->script, pc); + do_atominc: + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return JS_FALSE; + todo = STR2OFF(&ss->sprinter, lval); + SprintPut(&ss->sprinter, + js_incop_str[!(cs->format & JOF_INC)], + 2); + break; + + case JSOP_PROPINC: + case JSOP_PROPDEC: + GET_ATOM_QUOTE_AND_FMT("%s[%s]%s", "%s.%s%s", rval); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, lval, rval, + js_incop_str[!(cs->format & JOF_INC)]); + break; + + case JSOP_ELEMINC: + case JSOP_ELEMDEC: + xval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s[%s]%s", + lval, xval, + js_incop_str[!(cs->format & JOF_INC)]); + break; + + case JSOP_GETPROP2: + op = JSOP_GETPROP; + (void) PopOff(ss, lastop); + /* FALL THROUGH */ + + case JSOP_GETPROP: + GET_ATOM_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, fmt, lval, rval); + break; + + case JSOP_SETPROP: + GET_ATOM_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); + rval = POP_STR(); + lval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc - 1); + todo = Sprint(&ss->sprinter, fmt, lval, xval, + (sn && SN_TYPE(sn) == SRC_ASSIGNOP) + ? js_CodeSpec[lastop].token + : "", + rval); + break; + + case JSOP_GETELEM2: + op = JSOP_GETELEM; + (void) PopOff(ss, lastop); + /* FALL THROUGH */ + + case JSOP_GETELEM: + op = JSOP_NOP; /* turn off parens */ + xval = POP_STR(); + op = JSOP_GETELEM; + lval = POP_STR(); + if (*xval == '\0') + todo = Sprint(&ss->sprinter, "%s", lval); + else + todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval); + break; + + case JSOP_SETELEM: + op = JSOP_NOP; /* turn off parens */ + rval = POP_STR(); + xval = POP_STR(); + op = JSOP_SETELEM; + lval = POP_STR(); + if (*xval == '\0') + goto do_setlval; + sn = js_GetSrcNote(jp->script, pc - 1); + todo = Sprint(&ss->sprinter, "%s[%s] %s= %s", + lval, xval, + (sn && SN_TYPE(sn) == SRC_ASSIGNOP) + ? js_CodeSpec[lastop].token + : "", + rval); + break; + + case JSOP_ARGSUB: + i = (jsint) GET_ATOM_INDEX(pc); + todo = Sprint(&ss->sprinter, "%s[%d]", + js_arguments_str, (int) i); + break; + + case JSOP_ARGCNT: + todo = Sprint(&ss->sprinter, "%s.%s", + js_arguments_str, js_length_str); + break; + + case JSOP_GETARG: + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); + LOCAL_ASSERT(atom); + goto do_name; + + case JSOP_GETVAR: + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); + LOCAL_ASSERT(atom); + goto do_name; + + case JSOP_NAME: + atom = GET_ATOM(cx, jp->script, pc); + do_name: + sn = js_GetSrcNote(jp->script, pc); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + todo = Sprint(&ss->sprinter, "%s%s", VarPrefix(sn), rval); + break; + + case JSOP_UINT16: + i = (jsint) GET_ATOM_INDEX(pc); + todo = Sprint(&ss->sprinter, "%u", (unsigned) i); + break; + + case JSOP_NUMBER: + atom = GET_ATOM(cx, jp->script, pc); + val = ATOM_KEY(atom); + if (JSVAL_IS_INT(val)) { + long ival = (long)JSVAL_TO_INT(val); + todo = Sprint(&ss->sprinter, "%ld", ival); + } else { + char buf[DTOSTR_STANDARD_BUFFER_SIZE]; + char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, + 0, *JSVAL_TO_DOUBLE(val)); + if (!numStr) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + todo = Sprint(&ss->sprinter, numStr); + } + break; + + case JSOP_STRING: + atom = GET_ATOM(cx, jp->script, pc); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + (jschar)'"'); + if (!rval) + return JS_FALSE; + todo = STR2OFF(&ss->sprinter, rval); + break; + + case JSOP_OBJECT: + case JSOP_ANONFUNOBJ: + case JSOP_NAMEDFUNOBJ: + atom = GET_ATOM(cx, jp->script, pc); + if (op == JSOP_OBJECT) { + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return JS_FALSE; + } else { + if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), + JS_DONT_PRETTY_PRINT, 0, NULL, + &val)) { + return JS_FALSE; + } + str = JSVAL_TO_STRING(val); + } + todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str), + JSSTRING_LENGTH(str)); + break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_TABLESWITCH: + case JSOP_TABLESWITCHX: + { + jsbytecode *pc2; + ptrdiff_t off, off2; + jsint j, n, low, high; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + low = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + high = GetJumpOffset(pc, pc2); + + n = high - low + 1; + if (n == 0) { + table = NULL; + j = 0; + } else { + table = (TableEntry *) + JS_malloc(cx, (size_t)n * sizeof *table); + if (!table) + return JS_FALSE; + for (i = j = 0; i < n; i++) { + pc2 += JUMP_OFFSET_LEN; + off2 = GetJumpOffset(pc, pc2); + if (off2) { + table[j].key = INT_TO_JSVAL(low + i); + table[j++].offset = off2; + } + } + js_HeapSort(table, (size_t) j, sizeof *table, + CompareOffsets, NULL); + } + + ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, + JS_FALSE); + JS_free(cx, table); + if (!ok) + return ok; + todo = -2; + break; + } + + case JSOP_LOOKUPSWITCH: + case JSOP_LOOKUPSWITCHX: + { + jsbytecode *pc2; + ptrdiff_t off, off2; + jsint npairs; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + + table = (TableEntry *) + JS_malloc(cx, (size_t)npairs * sizeof *table); + if (!table) + return JS_FALSE; + for (i = 0; i < npairs; i++) { + atom = GET_ATOM(cx, jp->script, pc2); + pc2 += ATOM_INDEX_LEN; + off2 = GetJumpOffset(pc, pc2); + pc2 += JUMP_OFFSET_LEN; + table[i].key = ATOM_KEY(atom); + table[i].offset = off2; + } + + ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, + JS_FALSE); + JS_free(cx, table); + if (!ok) + return ok; + todo = -2; + break; + } + + case JSOP_CONDSWITCH: + { + jsbytecode *pc2; + ptrdiff_t off, off2, caseOff; + jsint ncases; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + JS_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; + JS_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); + JS_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 *) + JS_malloc(cx, (size_t)ncases * sizeof *table); + if (!table) + return JS_FALSE; + pc2 = pc; + off2 = off; + for (i = 0; i < ncases; i++) { + pc2 += off2; + JS_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); + JS_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); + JS_free(cx, table); + if (!ok) + return ok; + todo = -2; + break; + } + + case JSOP_CASE: + case JSOP_CASEX: + { + lval = POP_STR(); + if (!lval) + return JS_FALSE; + js_printf(jp, "\tcase %s:\n", lval); + todo = -2; + break; + } + +#endif /* JS_HAS_SWITCH_STATEMENT */ + +#if !JS_BUG_FALLIBLE_EQOPS + case JSOP_NEW_EQ: + case JSOP_NEW_NE: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %c%s %s", + lval, + (op == JSOP_NEW_EQ) ? '=' : '!', +#if JS_HAS_TRIPLE_EQOPS + JSVERSION_IS_ECMA(cx->version) ? "==" : +#endif + "=", + rval); + break; +#endif /* !JS_BUG_FALLIBLE_EQOPS */ + +#if JS_HAS_LEXICAL_CLOSURE + case JSOP_CLOSURE: + atom = GET_ATOM(cx, jp->script, pc); + JS_ASSERT(ATOM_IS_OBJECT(atom)); + goto do_function; +#endif /* JS_HAS_LEXICAL_CLOSURE */ + +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTALL: + js_printf(jp, "\texport *\n"); + todo = -2; + break; + + case JSOP_EXPORTNAME: + atom = GET_ATOM(cx, jp->script, pc); + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + js_printf(jp, "\texport %s\n", rval); + todo = -2; + break; + + case JSOP_IMPORTALL: + lval = POP_STR(); + js_printf(jp, "\timport %s.*\n", lval); + todo = -2; + break; + + case JSOP_IMPORTPROP: + GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n", + rval); + lval = POP_STR(); + js_printf(jp, fmt, lval, rval); + todo = -2; + break; + + case JSOP_IMPORTELEM: + xval = POP_STR(); + op = JSOP_GETELEM; + lval = POP_STR(); + js_printf(jp, "\timport %s[%s]\n", lval, xval); + todo = -2; + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + case JSOP_TRAP: + op = JS_GetTrapOpcode(cx, jp->script, pc); + if (op == JSOP_LIMIT) + return JS_FALSE; + *pc = op; + cs = &js_CodeSpec[op]; + len = cs->length; + DECOMPILE_CODE(pc, len); + *pc = JSOP_TRAP; + todo = -2; + break; + +#if JS_HAS_INITIALIZERS + case JSOP_NEWINIT: + LOCAL_ASSERT(ss->top >= 2); + (void) PopOff(ss, op); + lval = POP_STR(); +#if JS_HAS_SHARP_VARS + op = (JSOp)pc[len]; + if (op == JSOP_DEFSHARP) { + pc += len; + cs = &js_CodeSpec[op]; + len = cs->length; + i = (jsint) GET_ATOM_INDEX(pc); + todo = Sprint(&ss->sprinter, "#%u=%c", + (unsigned) i, + (*lval == 'O') ? '{' : '['); + } else +#endif /* JS_HAS_SHARP_VARS */ + { + todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "["); + } + break; + + case JSOP_ENDINIT: + rval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc); + todo = Sprint(&ss->sprinter, "%s%s%c", + rval, + (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", + (*rval == '{') ? '}' : ']'); + break; + + case JSOP_INITPROP: + case JSOP_INITCATCHVAR: + atom = GET_ATOM(cx, jp->script, pc); + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), + ATOM_KEYWORD(atom) ? '\'' : 0); + if (!xval) + return JS_FALSE; + rval = POP_STR(); + lval = POP_STR(); + do_initprop: +#ifdef OLD_GETTER_SETTER + todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", + lval, + (lval[1] != '\0') ? ", " : "", + 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) { + todo = Sprint(&ss->sprinter, "%s%s%s %s%s", + lval, + (lval[1] != '\0') ? ", " : "", + (lastop == JSOP_GETTER) + ? js_get_str : js_set_str, + xval, + rval + strlen(js_function_str) + 1); + } else { + todo = Sprint(&ss->sprinter, "%s%s%s:%s", + lval, + (lval[1] != '\0') ? ", " : "", + xval, + rval); + } +#endif + break; + + case JSOP_INITELEM: + rval = POP_STR(); + xval = POP_STR(); + lval = POP_STR(); + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_LABEL) + goto do_initprop; + todo = Sprint(&ss->sprinter, "%s%s%s", + lval, + (lval[1] != '\0' || *xval != '0') ? ", " : "", + rval); + break; + +#if JS_HAS_SHARP_VARS + case JSOP_DEFSHARP: + i = (jsint) GET_ATOM_INDEX(pc); + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); + break; + + case JSOP_USESHARP: + i = (jsint) GET_ATOM_INDEX(pc); + todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + js_printf(jp, "\tdebugger;\n"); + todo = -2; + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + + default: + todo = -2; + break; + } + } + + if (todo < 0) { + /* -2 means "don't push", -1 means reported error. */ + if (todo == -1) + return JS_FALSE; + } else { + if (!PushOff(ss, todo, op)) + return JS_FALSE; + } + pc += len; + } + +/* + * Undefine local macros. + */ +#undef DECOMPILE_CODE +#undef POP_STR +#undef LOCAL_ASSERT +#undef GET_ATOM_QUOTE_AND_FMT + + return JS_TRUE; +} + + +JSBool +js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len) +{ + SprintStack ss; + JSContext *cx; + void *mark, *space; + size_t offsetsz, opcodesz; + JSBool ok; + JSScript *oldscript; + char *last; + + /* Initialize a sprinter for use with the offset stack. */ + ss.printer = jp; + cx = jp->sprinter.context; + mark = JS_ARENA_MARK(&cx->tempPool); + INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP); + + /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ + offsetsz = script->depth * sizeof(ptrdiff_t); + opcodesz = script->depth * sizeof(jsbytecode); + JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); + if (!space) { + ok = JS_FALSE; + goto out; + } + ss.offsets = (ptrdiff_t *) space; + ss.opcodes = (jsbytecode *) ((char *)space + offsetsz); + ss.top = 0; + + /* Call recursive subroutine to do the hard work. */ + oldscript = jp->script; + jp->script = script; + ok = Decompile(&ss, pc, len); + jp->script = oldscript; + + /* If the given code didn't empty the stack, do it now. */ + if (ss.top) { + do { + last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP)); + } while (ss.top); + 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 js_DecompileCode(jp, script, script->code, (uintN)script->length); +} + +static const char native_code_str[] = "\t[native code]\n"; + +JSBool +js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) +{ + JSScript *script; + JSScope *scope, *save; + JSBool ok; + + script = fun->script; + if (!script) { + js_printf(jp, native_code_str); + return JS_TRUE; + } + scope = fun->object ? OBJ_SCOPE(fun->object) : NULL; + save = jp->scope; + jp->scope = scope; + ok = js_DecompileCode(jp, script, script->code, (uintN)script->length); + jp->scope = save; + return ok; +} + +JSBool +js_DecompileFunction(JSPrinter *jp, JSFunction *fun) +{ + JSContext *cx; + uintN i, nargs, indent; + void *mark; + JSAtom **params; + JSScope *scope, *oldscope; + JSScopeProperty *sprop; + JSBool ok; + + /* + * 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_puts(jp, "\n"); + js_printf(jp, "\t"); + } else { + if (fun->flags & JSFUN_LAMBDA) + js_puts(jp, "("); + } + if (fun->flags & JSFUN_GETTER) + js_printf(jp, "%s ", js_getter_str); + else if (fun->flags & JSFUN_SETTER) + 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->script && fun->object) { + /* + * Print the parameters. + * + * This code is complicated by the need to handle duplicate parameter + * names, as required by ECMA (bah!). A duplicate parameter is stored + * as another node with the same id (the parameter name) but different + * shortid (the argument index) along the property tree ancestor line + * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param + * is mapped by the scope's hash table. + */ + cx = jp->sprinter.context; + nargs = fun->nargs; + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, + nargs * sizeof(JSAtom *)); + if (!params) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + scope = OBJ_SCOPE(fun->object); + for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { + if (sprop->getter != js_GetArgument) + continue; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + JS_ASSERT((uintN) sprop->shortid < nargs); + JS_ASSERT(!JSVAL_IS_INT(sprop->id)); + params[(uintN) sprop->shortid] = (JSAtom *) sprop->id; + } + for (i = 0; i < nargs; i++) { + if (i > 0) + js_puts(jp, ", "); + if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) + return JS_FALSE; + } + JS_ARENA_RELEASE(&cx->tempPool, mark); +#ifdef __GNUC__ + } else { + scope = NULL; +#endif + } + + js_printf(jp, ") {\n"); + indent = jp->indent; + jp->indent += 4; + if (fun->script && fun->object) { + oldscope = jp->scope; + jp->scope = scope; + ok = js_DecompileScript(jp, fun->script); + jp->scope = oldscope; + if (!ok) { + jp->indent = indent; + return JS_FALSE; + } + } else { + js_printf(jp, native_code_str); + } + jp->indent -= 4; + js_printf(jp, "\t}"); + + if (jp->pretty) { + js_puts(jp, "\n"); + } else { + if (fun->flags & JSFUN_LAMBDA) + js_puts(jp, ")"); + } + return JS_TRUE; +} + +JSString * +js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, + JSString *fallback) +{ + JSStackFrame *fp, *down; + jsbytecode *pc, *begin, *end, *tmp; + jsval *sp, *base, *limit; + JSScript *script; + JSOp op; + const JSCodeSpec *cs; + uint32 format, mode; + intN depth; + jssrcnote *sn; + uintN len, off; + JSPrinter *jp; + JSString *name; + + fp = cx->fp; + if (!fp) + goto do_fallback; + + /* Try to find sp's generating pc depth slots under it on the stack. */ + pc = fp->pc; + if (spindex == JSDVG_SEARCH_STACK) { + if (!pc) { + /* + * Current frame is native: look under it for a scripted call + * in which a decompilable bytecode string that generated the + * value as an actual argument might exist. + */ + JS_ASSERT(!fp->script && fp->fun && fp->fun->native); + down = fp->down; + if (!down) + goto do_fallback; + script = down->script; + base = fp->argv; + limit = base + fp->argc; + } else { + /* + * This should be a script activation, either a top-level + * script or a scripted function. But be paranoid about calls + * to js_DecompileValueGenerator from code that hasn't fully + * initialized a (default-all-zeroes) frame. + */ + script = fp->script; + base = fp->spbase; + limit = fp->sp; + } + + /* + * Pure paranoia about default-zeroed frames being active while + * js_DecompileValueGenerator is called. It can't hurt much now; + * error reporting performance is not an issue. + */ + if (!script || !base || !limit) + goto do_fallback; + + /* + * Try to find operand-generating pc depth slots below sp. + * + * In the native case, we know the arguments have generating pc's + * under them, on account of fp->down->script being non-null: all + * compiled scripts get depth slots for generating pc's allocated + * upon activation, at the top of js_Interpret. + * + * In the script or scripted function case, the same reasoning + * applies to fp rather than to fp->down. + */ + for (sp = base; sp < limit; sp++) { + if (*sp == v) { + depth = (intN)script->depth; + pc = (jsbytecode *) sp[-depth]; + break; + } + } + } else { + /* + * At this point, pc may or may not be null, i.e., we could be in + * a script activation, or we could be in a native frame that was + * called by another native function. Check pc and script. + */ + if (!pc) + goto do_fallback; + script = fp->script; + if (!script) + goto do_fallback; + + if (spindex != JSDVG_IGNORE_STACK) { + JS_ASSERT(spindex < 0); + depth = (intN)script->depth; +#if !JS_HAS_NO_SUCH_METHOD + JS_ASSERT(-depth <= spindex); +#endif + spindex -= depth; + + base = (jsval *) cx->stackPool.current->base; + limit = (jsval *) cx->stackPool.current->avail; + sp = fp->sp + spindex; + if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base)) + pc = (jsbytecode *) *sp; + } + } + + /* + * Again, be paranoid, this time about possibly loading an invalid pc + * from sp[-(1+depth)]. + */ + if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) { + pc = fp->pc; + if (!pc) + goto do_fallback; + } + op = (JSOp) *pc; + if (op == JSOP_TRAP) + op = JS_GetTrapOpcode(cx, script, pc); + + /* XXX handle null as a special case, to avoid calling null "object" */ + if (op == JSOP_NULL) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + + cs = &js_CodeSpec[op]; + format = cs->format; + mode = (format & JOF_MODEMASK); + + /* NAME ops are self-contained, but others require left context. */ + if (mode == JOF_NAME) { + begin = pc; + } else { + sn = js_GetSrcNote(script, pc); + if (!sn || SN_TYPE(sn) != SRC_PCBASE) + goto do_fallback; + begin = pc - js_GetSrcNoteOffset(sn, 0); + } + end = pc + cs->length; + len = PTRDIFF(end, begin, jsbytecode); + + if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT | JOF_FOR)) { + tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode)); + if (!tmp) + return NULL; + memcpy(tmp, begin, len * sizeof(jsbytecode)); + if (mode == JOF_NAME) { + tmp[0] = JSOP_NAME; + } 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. + */ + off = len - cs->length; + JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode)); + if (mode == JOF_PROP) { + tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; + } else if (mode == JOF_ELEM) { + tmp[off] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; + } else { + /* + * A zero mode means precisely that op is uncategorized for our + * purposes, so we must write per-op special case code here. + */ + switch (op) { + case JSOP_ENUMELEM: + tmp[off] = JSOP_GETELEM; + break; +#if JS_HAS_LVALUE_RETURN + case JSOP_SETCALL: + tmp[off] = JSOP_CALL; + break; +#endif + default: + JS_ASSERT(0); + } + } + } + begin = tmp; + } else { + /* No need to revise script bytecode. */ + tmp = NULL; + } + + jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); + if (jp && js_DecompileCode(jp, script, begin, len)) + name = js_GetPrinterOutput(jp); + else + name = NULL; + js_DestroyPrinter(jp); + if (tmp) + JS_free(cx, tmp); + return name; + + do_fallback: + return fallback ? fallback : js_ValueToString(cx, v); +} diff --git a/src/extension/script/js/jsopcode.h b/src/extension/script/js/jsopcode.h new file mode 100644 index 000000000..e24fb2bf8 --- /dev/null +++ b/src/extension/script/js/jsopcode.h @@ -0,0 +1,273 @@ +/* -*- 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" + +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_CONST 2 /* unsigned 16-bit constant pool 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_QVAR 7 /* quickened get/set local variable ops */ +#define JOF_DEFLOCALVAR 8 /* define local var with initial value */ +#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_TYPEMASK 0x000f /* mask for above immediate types */ +#define JOF_NAME 0x0010 /* name operation */ +#define JOF_PROP 0x0020 /* obj.prop operation */ +#define JOF_ELEM 0x0030 /* obj[index] operation */ +#define JOF_MODEMASK 0x0030 /* mask for above addressing modes */ +#define JOF_SET 0x0040 /* set (i.e., assignment) operation */ +#define JOF_DEL 0x0080 /* delete operation */ +#define JOF_DEC 0x0100 /* decrement (--, not ++) opcode */ +#define JOF_INC 0x0200 /* increment (++, not --) opcode */ +#define JOF_INCDEC 0x0300 /* increment or decrement opcode */ +#define JOF_POST 0x0400 /* postorder increment or decrement */ +#define JOF_IMPORT 0x0800 /* import property op */ +#define JOF_FOR 0x1000 /* for-in property op */ +#define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops + that do simplex assignment */ +#define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ +#define JOF_LEFTASSOC 0x8000 /* left-associative operator */ + +#define JOF_TYPE_IS_EXTENDED_JUMP(t) \ + ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) + +/* + * Immediate operand getters, setters, and bounds. + */ + +/* 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)(((pc)[1] << 8) | (pc)[2])) +#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)(((pc)[1] << 8) | (pc)[2])) +#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 map. */ +#define ATOM_INDEX_LEN 2 +#define ATOM_INDEX_HI(index) ((jsbytecode)((index) >> 8)) +#define ATOM_INDEX_LO(index) ((jsbytecode)(index)) +#define GET_ATOM_INDEX(pc) ((jsatomid)(((pc)[1] << 8) | (pc)[2])) +#define SET_ATOM_INDEX(pc,index)((pc)[1] = ATOM_INDEX_HI(index), \ + (pc)[2] = ATOM_INDEX_LO(index)) +#define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ + GET_ATOM_INDEX(pc)) +#define ATOM_INDEX_LIMIT_LOG2 16 +#define ATOM_INDEX_LIMIT ((uint32)1 << ATOM_INDEX_LIMIT_LOG2) + +/* Actual argument count operand format helpers. */ +#define ARGC_HI(argc) ((jsbytecode)((argc) >> 8)) +#define ARGC_LO(argc) ((jsbytecode)(argc)) +#define GET_ARGC(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) +#define ARGC_LIMIT ((uint32)1 << 16) + +/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */ +#define GET_ARGNO(pc) GET_ARGC(pc) +#define SET_ARGNO(pc,argno) SET_JUMP_OFFSET(pc,argno) +#define ARGNO_LEN JUMP_OFFSET_LEN +#define GET_VARNO(pc) GET_ARGC(pc) +#define SET_VARNO(pc,varno) SET_JUMP_OFFSET(pc,varno) +#define VARNO_LEN JUMP_OFFSET_LEN + +struct JSCodeSpec { + const char *name; /* JS bytecode name */ + const char *token; /* JS source literal or null */ + 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 char js_const_str[]; +extern const char js_var_str[]; +extern const char js_function_str[]; +extern const char js_in_str[]; +extern const char js_instanceof_str[]; +extern const char js_new_str[]; +extern const char js_delete_str[]; +extern const char js_typeof_str[]; +extern const char js_void_str[]; +extern const char js_null_str[]; +extern const char js_this_str[]; +extern const char js_false_str[]; +extern const char js_true_str[]; +extern const JSCodeSpec js_CodeSpec[]; +extern uintN js_NumCodeSpecs; +extern const jschar 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. + */ +extern JSPrinter * +js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty); + +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); + +#ifdef DEBUG +/* + * Disassemblers, for debugging only. + */ +#include + +extern JS_FRIEND_API(void) +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_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len); + +extern JSBool +js_DecompileScript(JSPrinter *jp, JSScript *script); + +extern JSBool +js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun); + +extern JSBool +js_DecompileFunction(JSPrinter *jp, JSFunction *fun); + +/* + * Find the source expression that resulted in v, and return a new 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. + */ +extern JSString * +js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, + JSString *fallback); + +#define JSDVG_IGNORE_STACK 0 +#define JSDVG_SEARCH_STACK 1 + +JS_END_EXTERN_C + +#endif /* jsopcode_h___ */ diff --git a/src/extension/script/js/jsopcode.tbl b/src/extension/script/js/jsopcode.tbl new file mode 100644 index 000000000..d4bad0fd9 --- /dev/null +++ b/src/extension/script/js/jsopcode.tbl @@ -0,0 +1,333 @@ +/* -*- 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 ***** */ + +/* + * 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 + * prec Operator precedence, zero if not an operator + * format Bytecode plus immediate operand encoding format + * + * This file is best viewed with 116 columns: +01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345 + */ + +/* legend: op val name image len use def prec format */ + +/* Longstanding JavaScript bytecodes. */ +OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) +OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 0, JOF_JUMP) +OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP) + +/* Get the arguments object for the current, lightweight function activation. */ +OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 12, JOF_BYTE) + +/* ECMA-compliant for-in loop with argument or local variable loop control. */ +OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 0, JOF_QARG|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 0, JOF_QVAR|JOF_NAME|JOF_FOR) + +/* More longstanding 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, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_NEG, 34, "neg", "-", 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 10, JOF_UINT16) +OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEL) +OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEL) +OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEL) +OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) +OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_INC) +OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_INC) +OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC) +OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEC) +OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEC) +OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) +OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) +OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) +OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) +OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 11, JOF_CONST|JOF_PROP) +OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 1, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 11, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) +OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_PUSHOBJ, 57, "pushobj", NULL, 1, 0, 1, 0, JOF_BYTE) +OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 11, JOF_UINT16) +OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME) +OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 0, JOF_JUMP) +OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 0, JOF_JUMP) + +/* The switch bytecodes have variable length. */ +OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH) +OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH) + +/* New, infallible/transitive identity ops. */ +OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 5, JOF_BYTE) +OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 5, JOF_BYTE) + +/* Lexical closure constructor. */ +OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST) + +/* Export and import ops. */ +OPDEF(JSOP_EXPORTALL, 75, "exportall", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_EXPORTNAME,76, "exportname", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) +OPDEF(JSOP_IMPORTALL, 77, "importall", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP|JOF_IMPORT) +OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_IMPORT) + +/* Push object literal. */ +OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 12, JOF_CONST) + +/* Pop value and discard it. */ +OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 0, JOF_BYTE) + +/* Convert value to number, for unary +. */ +OPDEF(JSOP_POS, 82, "pos", "+", 1, 1, 1, 10, JOF_BYTE) + +/* 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, 12, JOF_QARG |JOF_NAME) +OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 1, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 12, JOF_QVAR |JOF_NAME) +OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 1, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING) + +/* Push unsigned 16-bit int constant. */ +OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 12, JOF_UINT16) + +/* Object and array literal support. */ +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 1, 2, 1, 10, JOF_BYTE) +OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP) +OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM) +OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) +OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) + +/* Fast inc/dec ops for args and local vars. */ +OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC) +OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC) +OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC) +OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC) +OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) + +/* ECMA-compliant for/in ops. */ +OPDEF(JSOP_TOOBJECT, 103,"toobject", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 0, JOF_CONST|JOF_PROP|JOF_FOR) +OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 4, 0, JOF_BYTE |JOF_ELEM|JOF_FOR) +OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) + +/* ECMA-compliant assignment ops. */ +OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) + +/* Exception handling ops. */ +OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) + +/* 'in' and 'instanceof' ops. */ +OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,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, 1, 0, JOF_JUMP) +OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 1, 0, 0, JOF_BYTE) + +/* More exception handling ops. */ +OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) +OPDEF(JSOP_SETSP, 117,"setsp", 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) +OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 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, 11, JOF_UINT16) + +/* + * ECMA-compliant helper for 'for (x[i] in o)' loops. + */ +OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 1, JOF_BYTE |JOF_SET|JOF_ASSIGNING) + +/* + * 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,js_getter_str,1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETTER, 124,js_setter_str,js_setter_str,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_CONST) +OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) +OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) + +/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */ +OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 12, JOF_CONST) + +/* ECMA ed. 3 named function expression. */ +OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 12, JOF_CONST) + +/* Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA ed. 3 catch variables. */ +OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP) + +/* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */ +OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */ +OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 11, JOF_UINT16|JOF_SET|JOF_ASSIGNING) + +/* + * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN + * srcnote-annotated JSOP_NOPs. + */ +OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Swap the top two stack elements. + * N.B. JSOP_SWAP doesn't swap the corresponding pc stack generating pcs, as + * they're not needed for the current use of preserving the top-of-stack return + * value when popping scopes while returning from catch blocks. + */ +OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) + +/* + * 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, 12, JOF_QARG |JOF_NAME) +OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 12, 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_DEFLOCALVAR) + +/* Extended jumps. */ +OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) +OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 1, 0, JOF_JUMPX) +OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 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) +OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX) + +/* 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) +OPDEF(JSOP_BACKPATCH_PUSH,151,"backpatch_push",NULL, 3, 0, 1, 0, JOF_JUMP|JOF_BACKPATCH) + +/* Set and get return value pseudo-register in stack frame. */ +OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) diff --git a/src/extension/script/js/jsosdep.h b/src/extension/script/js/jsosdep.h new file mode 100644 index 000000000..c93eee2f1 --- /dev/null +++ b/src/extension/script/js/jsosdep.h @@ -0,0 +1,127 @@ +/* -*- 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 jsosdep_h___ +#define jsosdep_h___ +/* + * OS (and machine, and compiler XXX) dependent information. + */ + +#if defined(XP_WIN) || defined(XP_OS2) + +#if defined(_WIN32) || defined (XP_OS2) +#define JS_HAVE_LONG_LONG +#else +#undef JS_HAVE_LONG_LONG +#endif +#endif /* XP_WIN || XP_OS2 */ + +#ifdef XP_MAC +#define JS_HAVE_LONG_LONG + +JS_BEGIN_EXTERN_C + +#include + +extern void* reallocSmaller(void* block, size_t newSize); + +extern char* strdup(const char* str); + +JS_END_EXTERN_C + +#endif /* XP_MAC */ + +#ifdef XP_BEOS +#define JS_HAVE_LONG_LONG +#endif + + +#ifdef XP_UNIX + +/* + * Get OS specific header information. + */ +#if defined(AIXV3) || defined(AIX) +#define JS_HAVE_LONG_LONG + +#elif defined(BSDI) +#define JS_HAVE_LONG_LONG + +#elif defined(HPUX) +#define JS_HAVE_LONG_LONG + +#elif defined(IRIX) +#define JS_HAVE_LONG_LONG + +#elif defined(linux) +#define JS_HAVE_LONG_LONG + +#elif defined(OSF1) +#define JS_HAVE_LONG_LONG + +#elif defined(_SCO_DS) +#undef JS_HAVE_LONG_LONG + +#elif defined(SOLARIS) +#define JS_HAVE_LONG_LONG + +#elif defined(FREEBSD) +#define JS_HAVE_LONG_LONG + +#elif defined(SUNOS4) +#undef JS_HAVE_LONG_LONG + +/* +** Missing function prototypes +*/ + +extern void *sbrk(int); + +#elif defined(UNIXWARE) +#undef JS_HAVE_LONG_LONG + +#elif defined(VMS) && defined(__ALPHA) +#define JS_HAVE_LONG_LONG + +#endif + +#endif /* XP_UNIX */ + +#endif /* jsosdep_h___ */ + diff --git a/src/extension/script/js/jsotypes.h b/src/extension/script/js/jsotypes.h new file mode 100644 index 000000000..ad8b5203e --- /dev/null +++ b/src/extension/script/js/jsotypes.h @@ -0,0 +1,211 @@ +/* -*- 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(XP_MAC) && !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 */ +#ifdef HPUX +#include +#else +#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2) +typedef JSInt32 int32; +#else +typedef long int32; +#endif +typedef JSInt16 int16; +typedef JSInt8 int8; +#endif /* HPUX */ +#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 + +#ifdef XP_MAC +#ifndef TRUE /* Mac standard is lower case true */ + #define TRUE 1 +#endif +#ifndef FALSE /* Mac standard is lower case false */ + #define FALSE 0 +#endif +#endif + +#endif /* !defined(PROTYPES_H) */ diff --git a/src/extension/script/js/jsparse.c b/src/extension/script/js/jsparse.c new file mode 100644 index 000000000..7cf18d454 --- /dev/null +++ b/src/extension/script/js/jsparse.c @@ -0,0 +1,3547 @@ +/* -*- 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 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. The dense JSTokenType enumeration + * was designed with error recovery built on 64-bit first and follow bitsets + * in mind, however. + */ +#include "jsstddef.h" +#include +#include +#include +#include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsemit.h" +#include "jsfun.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" + +/* + * JS parsers, from lowest to highest precedence. + * + * Each parser takes a context and a token stream, and emits bytecode using + * a code generator. + */ + +typedef JSParseNode * +JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); + +typedef JSParseNode * +JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowCallSyntax); + +static JSParser FunctionStmt; +#if JS_HAS_LEXICAL_CLOSURE +static JSParser FunctionExpr; +#endif +static JSParser Statements; +static JSParser Statement; +static JSParser 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 JSParser PrimaryExpr; + +/* + * 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 + +#define CHECK_RECURSION() \ + JS_BEGIN_MACRO \ + int stackDummy; \ + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \ + JSMSG_OVER_RECURSED); \ + return NULL; \ + } \ + JS_END_MACRO + +#ifdef METER_PARSENODES +static uint32 parsenodes = 0; +static uint32 maxparsenodes = 0; +static uint32 recyclednodes = 0; +#endif + +static void +RecycleTree(JSParseNode *pn, JSTreeContext *tc) +{ + if (!pn) + return; + JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ + pn->pn_next = tc->nodeList; + tc->nodeList = pn; +#ifdef METER_PARSENODES + recyclednodes++; +#endif +} + +static JSParseNode * +NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = tc->nodeList; + if (!pn) { + JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); + if (!pn) + JS_ReportOutOfMemory(cx); + } else { + tc->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: + if (pn->pn_head) { + /* XXX check for dup recycles in the list */ + *pn->pn_tail = tc->nodeList; + tc->nodeList = pn->pn_head; +#ifdef METER_PARSENODES + recyclednodes += pn->pn_count; +#endif + } + break; + case PN_TERNARY: + RecycleTree(pn->pn_kid1, tc); + RecycleTree(pn->pn_kid2, tc); + RecycleTree(pn->pn_kid3, tc); + break; + case PN_BINARY: + RecycleTree(pn->pn_left, tc); + RecycleTree(pn->pn_right, tc); + break; + case PN_UNARY: + RecycleTree(pn->pn_kid, tc); + break; + case PN_NAME: + RecycleTree(pn->pn_expr, tc); + break; + case PN_NULLARY: + break; + } + } + return pn; +} + +/* + * Allocate a JSParseNode from cx's temporary arena. + */ +static JSParseNode * +NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity, + JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = NewOrRecycledNode(cx, tc); + if (!pn) + return NULL; + pn->pn_type = tok->type; + pn->pn_pos = tok->pos; + pn->pn_op = JSOP_NOP; + pn->pn_arity = arity; + pn->pn_next = NULL; +#ifdef METER_PARSENODES + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; +#endif + return pn; +} + +static JSParseNode * +NewBinary(JSContext *cx, 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 (left->pn_type == tt && + left->pn_op == 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; + PN_INIT_LIST_1(left, pn1); + PN_APPEND(left, pn2); + left->pn_extra = 0; + if (tt == TOK_PLUS) { + if (pn1->pn_type == TOK_STRING) + left->pn_extra |= PNX_STRCAT; + else if (pn1->pn_type != TOK_NUMBER) + left->pn_extra |= PNX_CANTFOLD; + if (pn2->pn_type == TOK_STRING) + left->pn_extra |= PNX_STRCAT; + else if (pn2->pn_type != TOK_NUMBER) + left->pn_extra |= PNX_CANTFOLD; + } + } + PN_APPEND(left, right); + left->pn_pos.end = right->pn_pos.end; + if (tt == TOK_PLUS) { + if (right->pn_type == TOK_STRING) + left->pn_extra |= PNX_STRCAT; + else if (right->pn_type != TOK_NUMBER) + left->pn_extra |= 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; + RecycleTree(right, tc); + return left; + } + + pn = NewOrRecycledNode(cx, tc); + if (!pn) + return NULL; + pn->pn_type = tt; + pn->pn_pos.begin = left->pn_pos.begin; + pn->pn_pos.end = right->pn_pos.end; + pn->pn_op = op; + pn->pn_arity = PN_BINARY; + pn->pn_left = left; + pn->pn_right = right; + pn->pn_next = NULL; +#ifdef METER_PARSENODES + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; +#endif + 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; + 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 + +/* + * Parse a top-level JS script. + */ +JS_FRIEND_API(JSParseNode *) +js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) +{ + JSStackFrame *fp, frame; + JSTreeContext tc; + JSParseNode *pn; + + /* + * Push a compiler frame if we have no frames, or if the top frame is a + * lightweight function activation, or if its scope chain doesn't match + * the one passed to us. + */ + fp = cx->fp; + if (!fp || !fp->varobj || fp->scopeChain != chain) { + memset(&frame, 0, sizeof frame); + frame.varobj = frame.scopeChain = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((chain = JS_GetParent(cx, chain)) != NULL) + frame.varobj = chain; + } + frame.down = fp; + cx->fp = &frame; + } + + /* + * 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_AllocGCThing), 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. + */ + JS_KEEP_ATOMS(cx->runtime); + TREE_CONTEXT_INIT(&tc); + pn = Statements(cx, ts, &tc); + if (pn) { + if (!js_MatchToken(cx, ts, TOK_EOF)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + pn = NULL; + } else { + pn->pn_type = TOK_LC; + if (!js_FoldConstants(cx, pn, &tc)) + pn = NULL; + } + } + + TREE_CONTEXT_FINISH(&tc); + JS_UNKEEP_ATOMS(cx->runtime); + cx->fp = fp; + return pn; +} + +/* + * Compile a top-level script. + */ +JS_FRIEND_API(JSBool) +js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSCodeGenerator *cg) +{ + JSStackFrame *fp, frame; + JSParseNode *pn; + JSBool ok; +#ifdef METER_PARSENODES + void *sbrk(ptrdiff_t), *before = sbrk(0); +#endif + + /* + * Push a compiler frame if we have no frames, or if the top frame is a + * lightweight function activation, or if its scope chain doesn't match + * the one passed to us. + */ + fp = cx->fp; + if (!fp || !fp->varobj || fp->scopeChain != chain) { + memset(&frame, 0, sizeof frame); + frame.varobj = frame.scopeChain = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((chain = JS_GetParent(cx, chain)) != NULL) + frame.varobj = chain; + } + frame.down = fp; + cx->fp = &frame; + } + + /* Prevent GC activation while compiling. */ + JS_KEEP_ATOMS(cx->runtime); + + pn = Statements(cx, ts, &cg->treeContext); + if (!pn) { + ok = JS_FALSE; + } else if (!js_MatchToken(cx, ts, TOK_EOF)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + ok = JS_FALSE; + } else { +#ifdef METER_PARSENODES + printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", + (char *)sbrk(0) - (char *)before, + parsenodes, + maxparsenodes, + parsenodes - recyclednodes); + before = sbrk(0); +#endif + + /* + * No need to emit code here -- Statements already has, for each + * statement in turn. Search for TCF_COMPILING in Statements, below. + * That flag is set for every tc == &cg->treeContext, and it implies + * that the tc can be downcast to a cg and used to emit code during + * parsing, rather than at the end of the parse phase. + */ + JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); + ok = JS_TRUE; + } + +#ifdef METER_PARSENODES + printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", + (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); +#endif +#ifdef JS_ARENAMETER + JS_DumpArenaStats(stdout); +#endif + JS_UNKEEP_ATOMS(cx->runtime); + cx->fp = fp; + return ok; +} + +/* + * Insist on a final return before control flows out of pn, but don't be too + * smart about loops (do {...; return e2;} while(0) at the end of a function + * that contains an early return e1 will get a strict-option-only warning). + */ +static JSBool +HasFinalReturn(JSParseNode *pn) +{ + JSBool ok, hasDefault; + JSParseNode *pn2, *pn3; + + switch (pn->pn_type) { + case TOK_LC: + if (!pn->pn_head) + return JS_FALSE; + return HasFinalReturn(PN_LAST(pn)); + + case TOK_IF: + ok = HasFinalReturn(pn->pn_kid2); + ok &= pn->pn_kid3 && HasFinalReturn(pn->pn_kid3); + return ok; + +#if JS_HAS_SWITCH_STATEMENT + case TOK_SWITCH: + ok = JS_TRUE; + hasDefault = JS_FALSE; + for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_DEFAULT) + hasDefault = JS_TRUE; + pn3 = pn2->pn_right; + JS_ASSERT(pn3->pn_type == TOK_LC); + if (pn3->pn_head) + ok &= HasFinalReturn(PN_LAST(pn3)); + } + /* If a final switch has no default case, we judge it harshly. */ + ok &= hasDefault; + return ok; +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case TOK_WITH: + return HasFinalReturn(pn->pn_right); + + case TOK_RETURN: + return JS_TRUE; + +#if JS_HAS_EXCEPTIONS + case TOK_THROW: + return JS_TRUE; + + case TOK_TRY: + /* If we have a finally block that returns, we are done. */ + if (pn->pn_kid3 && HasFinalReturn(pn->pn_kid3)) + return JS_TRUE; + + /* Else check the try block and any and all catch statements. */ + ok = HasFinalReturn(pn->pn_kid1); + if (pn->pn_kid2) + ok &= HasFinalReturn(pn->pn_kid2); + return ok; + + case TOK_CATCH: + /* Check this block's code and iterate over further catch blocks. */ + ok = HasFinalReturn(pn->pn_kid3); + for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2) + ok &= HasFinalReturn(pn2->pn_kid3); + return ok; +#endif + + default: + return JS_FALSE; + } +} + +static JSBool +ReportNoReturnValue(JSContext *cx, JSTokenStream *ts) +{ + JSFunction *fun; + JSBool ok; + + fun = cx->fp->fun; + if (fun->atom) { + char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom)); + ok = js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_NO_RETURN_VALUE, name); + } else { + ok = js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_ANON_NO_RETURN_VALUE); + } + return ok; +} + +static JSBool +CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) +{ + return HasFinalReturn(pn) || ReportNoReturnValue(cx, ts); +} + +static JSParseNode * +FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, + JSTreeContext *tc) +{ + JSStackFrame *fp, frame; + JSObject *funobj; + uintN oldflags; + JSParseNode *pn; + + fp = cx->fp; + funobj = fun->object; + if (!fp || fp->fun != fun || fp->varobj != funobj || + fp->scopeChain != funobj) { + memset(&frame, 0, sizeof frame); + frame.fun = fun; + frame.varobj = frame.scopeChain = funobj; + frame.down = fp; + cx->fp = &frame; + } + + oldflags = tc->flags; + tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); + tc->flags |= TCF_IN_FUNCTION; + pn = Statements(cx, ts, tc); + + /* Check for falling off the end of a function that returns a value. */ + if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { + if (!CheckFinalReturn(cx, ts, pn)) + pn = NULL; + } + + cx->fp = fp; + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + return pn; +} + +/* + * Compile a JS function body, which might appear as the value of an event + * handler attribute in an HTML tag. + */ +JSBool +js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) +{ + JSArenaPool codePool, notePool; + JSCodeGenerator funcg; + JSStackFrame *fp, frame; + JSObject *funobj; + JSParseNode *pn; + JSBool ok; + + JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); + JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); + if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool, + ts->filename, ts->lineno, + ts->principals)) { + return JS_FALSE; + } + + /* Prevent GC activation while compiling. */ + JS_KEEP_ATOMS(cx->runtime); + + /* Push a JSStackFrame for use by FunctionBody and js_EmitFunctionBody. */ + fp = cx->fp; + funobj = fun->object; + JS_ASSERT(!fp || fp->fun != fun || fp->varobj != funobj || + fp->scopeChain != funobj); + memset(&frame, 0, sizeof frame); + frame.fun = fun; + frame.varobj = frame.scopeChain = funobj; + frame.down = fp; + cx->fp = &frame; + + /* Ensure that the body looks like a block statement to js_EmitTree. */ + CURRENT_TOKEN(ts).type = TOK_LC; + pn = FunctionBody(cx, ts, fun, &funcg.treeContext); + if (!pn) { + ok = JS_FALSE; + } else { + /* + * No need to emit code here -- Statements (via FunctionBody) already + * has. See similar comment in js_CompileTokenStream, and bug 108257. + */ + fun->script = js_NewScriptFromCG(cx, &funcg, fun); + if (!fun->script) { + ok = JS_FALSE; + } else { + if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + ok = JS_TRUE; + } + } + + /* Restore saved state and release code generation arenas. */ + cx->fp = fp; + JS_UNKEEP_ATOMS(cx->runtime); + js_FinishCodeGenerator(cx, &funcg); + JS_FinishArenaPool(&codePool); + JS_FinishArenaPool(¬ePool); + return ok; +} + +static JSParseNode * +FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool lambda) +{ + JSParseNode *pn, *body; + JSOp op, prevop; + JSAtom *funAtom, *argAtom; + JSFunction *fun; + JSObject *parent; + JSObject *pobj; + JSScopeProperty *sprop; + uintN dupflag; + JSBool ok; + JSTreeContext funtc; + JSAtomListElement *ale; + + /* Make a TOK_FUNCTION node. */ + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc); + if (!pn) + return NULL; +#if JS_HAS_GETTER_SETTER + op = CURRENT_TOKEN(ts).t_op; +#endif + + /* Scan the optional function name into funAtom. */ + if (js_MatchToken(cx, ts, TOK_NAME)) + funAtom = CURRENT_TOKEN(ts).t_atom; + else + funAtom = NULL; + + /* Find the nearest variable-declaring scope and use it as our parent. */ + parent = cx->fp->varobj; + fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, parent, + funAtom); + if (!fun) + return NULL; + +#if JS_HAS_GETTER_SETTER + if (op != JSOP_NOP) + fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; +#endif + + /* Now parse formal argument list and compute fun->nargs. */ + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); + if (!js_MatchToken(cx, ts, TOK_RP)) { + do { + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL); + argAtom = CURRENT_TOKEN(ts).t_atom; + pobj = NULL; + if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, + (JSProperty **)&sprop)) { + return NULL; + } + dupflag = 0; + if (sprop) { + ok = JS_TRUE; + if (pobj == fun->object && + sprop->getter == js_GetArgument) { + const char *name = js_AtomToPrintableString(cx, argAtom); + + /* + * A duplicate parameter name. We force a duplicate node + * on the SCOPE_LAST_PROP(scope) list with the same id, + * distinguished by the SPROP_IS_DUPLICATE flag, and not + * mapped by an entry in scope. + */ + ok = name && + js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DUPLICATE_FORMAL, + name); + + dupflag = SPROP_IS_DUPLICATE; + } + OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); + if (!ok) + return NULL; + sprop = NULL; + } + if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_SHARED, + SPROP_HAS_SHORTID | dupflag, + fun->nargs)) { + return NULL; + } + fun->nargs++; + } while (js_MatchToken(cx, ts, TOK_COMMA)); + + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); + } + + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); + pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; + + TREE_CONTEXT_INIT(&funtc); + body = FunctionBody(cx, ts, fun, &funtc); + if (!body) + return NULL; + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + +#if JS_HAS_LEXICAL_CLOSURE + /* + * If we collected flags that indicate nested heavyweight functions, or + * this function contains heavyweight-making statements (references to + * __parent__ or __proto__; use of with, eval, import, or export; and + * assignment to arguments), flag the function as heavyweight (requiring + * a call object per invocation). + */ + if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { + fun->flags |= JSFUN_HEAVYWEIGHT; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } else { + /* + * If this function is a named statement function not at top-level + * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that + * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our + * enclosing function, if any, must be heavyweight. + */ + if ((!lambda && funAtom && tc->topStmt) || + (funtc.flags & TCF_FUN_USES_NONLOCALS)) { + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + } +#endif + + /* + * Record names for function statements in tc->decls so we know when to + * avoid optimizing variable references that might name a function. + */ + if (!lambda && funAtom) { + ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); + if (ale) { + prevop = ALE_JSOP(ale); + if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { + const char *name = js_AtomToPrintableString(cx, funAtom); + if (!name || + !js_ReportCompileErrorNumber(cx, ts, NULL, + (prevop != JSOP_DEFCONST) + ? JSREPORT_WARNING | + JSREPORT_STRICT + : JSREPORT_ERROR, + JSMSG_REDECLARED_VAR, + (prevop == JSOP_DEFFUN || + prevop == JSOP_CLOSURE) + ? js_function_str + : (prevop == JSOP_DEFCONST) + ? js_const_str + : js_var_str, + name)) { + return NULL; + } + } + if (tc->topStmt && prevop == JSOP_DEFVAR) + tc->flags |= TCF_FUN_CLOSURE_VS_VAR; + } else { + ale = js_IndexAtom(cx, funAtom, &tc->decls); + if (!ale) + return NULL; + } + ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN); + +#if JS_HAS_LEXICAL_CLOSURE + /* + * A function nested at top level inside another's body needs only a + * local variable to bind its name to its value, and not an activation + * object property (it might also need the activation property, if the + * outer function contains with statements, e.g., but the stack slot + * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a + * JSOP_GETVAR bytecode). + */ + if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) { + JSStackFrame *fp; + JSObject *varobj; + + /* + * Define a property on the outer function so that LookupArgOrVar + * can properly optimize accesses. + * + * XXX Here and in Variables, we use the function object's scope, + * XXX arguably polluting it, when we could use a compiler-private + * XXX scope structure. Tradition! + */ + fp = cx->fp; + varobj = fp->varobj; + JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); + JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); + if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom, + OBJECT_TO_JSVAL(fun->object), + js_GetLocalVariable, + js_SetLocalVariable, + JSPROP_ENUMERATE, + SPROP_HAS_SHORTID, fp->fun->nvars, + NULL)) { + return NULL; + } + fp->fun->nvars++; + } +#endif + } + +#if JS_HAS_LEXICAL_CLOSURE + if (lambda || !funAtom) { + /* + * ECMA ed. 3 standard: function expression, possibly anonymous (even + * if at top-level, an unnamed function is an expression statement, not + * a function declaration). + */ + op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; + } else if (tc->topStmt) { + /* + * ECMA ed. 3 extension: a function expression statement not at the + * top level, e.g., in a compound statement such as the "then" part + * of an "if" statement, binds a closure only if control reaches that + * sub-statement. + */ + op = JSOP_CLOSURE; + } else +#endif + op = JSOP_NOP; + + /* + * Pending a better automatic GC root management scheme (see Mozilla bug + * 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to + * atomize here to protect against a GC activation. + */ + pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0); + if (!pn->pn_funAtom) + return NULL; + + pn->pn_op = op; + pn->pn_body = body; + pn->pn_flags = funtc.flags & TCF_FUN_FLAGS; + pn->pn_tryCount = funtc.tryCount; + TREE_CONTEXT_FINISH(&funtc); + return pn; +} + +static JSParseNode * +FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + return FunctionDef(cx, ts, tc, JS_FALSE); +} + +#if JS_HAS_LEXICAL_CLOSURE +static JSParseNode * +FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + return FunctionDef(cx, ts, tc, JS_TRUE); +} +#endif + +/* + * Parse the statements in a block, creating a TOK_LC node that lists the + * statements' trees. If called from block-parsing code, the caller must + * match { before and } after. + */ +static JSParseNode * +Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSTokenType tt; + + CHECK_RECURSION(); + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + PN_INIT_LIST(pn); + + ts->flags |= TSF_REGEXP; + while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { + ts->flags &= ~TSF_REGEXP; + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + ts->flags |= TSF_REGEXP; + + /* If compiling top-level statements, emit as we go to save space. */ + if (!tc->topStmt && (tc->flags & TCF_COMPILING)) { + if (cx->fp->fun && + JS_HAS_STRICT_OPTION(cx) && + (tc->flags & TCF_RETURN_EXPR)) { + /* + * Check pn2 for lack of a final return statement if it is the + * last statement in the block. + */ + tt = js_PeekToken(cx, ts); + if ((tt == TOK_EOF || tt == TOK_RC) && + !CheckFinalReturn(cx, ts, pn2)) { + tt = TOK_ERROR; + break; + } + + /* + * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to + * CheckFinalReturn again. + */ + tc->flags &= ~TCF_RETURN_EXPR; + } + if (!js_FoldConstants(cx, pn2, tc) || + !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) || + !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) { + tt = TOK_ERROR; + break; + } + RecycleTree(pn2, tc); + } else { + PN_APPEND(pn, pn2); + } + } + ts->flags &= ~TSF_REGEXP; + if (tt == TOK_ERROR) + return NULL; + + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + return pn; +} + +static JSParseNode * +Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); + pn = Expr(cx, ts, tc); + if (!pn) + return NULL; + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); + + /* + * Check for (a = b) and "correct" it to (a == b) iff b's operator has + * greater precedence than ==. + * XXX not ECMA, but documented in several books -- now a strict warning. + */ + if (pn->pn_type == TOK_ASSIGN && + pn->pn_op == JSOP_NOP && + pn->pn_right->pn_type > TOK_EQOP) + { + JSBool rewrite = !JSVERSION_IS_ECMA(cx->version); + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | JSREPORT_STRICT, + JSMSG_EQUAL_AS_ASSIGN, + rewrite + ? "\nAssuming equality test" + : "")) { + return NULL; + } + if (rewrite) { + pn->pn_type = TOK_EQOP; + pn->pn_op = (JSOp)cx->jsop_eq; + pn2 = pn->pn_left; + switch (pn2->pn_op) { + case JSOP_SETNAME: + pn2->pn_op = JSOP_NAME; + break; + case JSOP_SETPROP: + pn2->pn_op = JSOP_GETPROP; + break; + case JSOP_SETELEM: + pn2->pn_op = JSOP_GETELEM; + break; + default: + JS_ASSERT(0); + } + } + } + return pn; +} + +static JSBool +MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) +{ + JSAtom *label; +#if JS_HAS_LABEL_STATEMENT + JSTokenType tt; + + tt = js_PeekTokenSameLine(cx, ts); + if (tt == TOK_ERROR) + return JS_FALSE; + if (tt == TOK_NAME) { + (void) js_GetToken(cx, ts); + label = CURRENT_TOKEN(ts).t_atom; + } else { + label = NULL; + } +#else + label = NULL; +#endif + pn->pn_atom = label; + return JS_TRUE; +} + +#if JS_HAS_EXPORT_IMPORT +static JSParseNode * +ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2, *pn3; + JSTokenType tt; + + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn) + return NULL; + pn->pn_op = JSOP_NAME; + pn->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn->pn_expr = NULL; + pn->pn_slot = -1; + pn->pn_attrs = 0; + + ts->flags |= TSF_REGEXP; + while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { + ts->flags &= ~TSF_REGEXP; + if (pn->pn_op == JSOP_IMPORTALL) + goto bad_import; + + if (tt == TOK_DOT) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn2) + return NULL; + if (js_MatchToken(cx, ts, TOK_STAR)) { + pn2->pn_op = JSOP_IMPORTALL; + pn2->pn_atom = NULL; + } else { + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); + pn2->pn_op = JSOP_GETPROP; + pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn2->pn_slot = -1; + pn2->pn_attrs = 0; + } + pn2->pn_expr = pn; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + } else { + /* Make a TOK_LB node. */ + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn2) + return NULL; + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + + pn2->pn_op = JSOP_GETELEM; + pn2->pn_left = pn; + pn2->pn_right = pn3; + } + + pn = pn2; + ts->flags |= TSF_REGEXP; + } + ts->flags &= ~TSF_REGEXP; + if (tt == TOK_ERROR) + return NULL; + js_UngetToken(ts); + + switch (pn->pn_op) { + case JSOP_GETPROP: + pn->pn_op = JSOP_IMPORTPROP; + break; + case JSOP_GETELEM: + pn->pn_op = JSOP_IMPORTELEM; + break; + case JSOP_IMPORTALL: + break; + default: + goto bad_import; + } + return pn; + + bad_import: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT); + return NULL; +} +#endif /* JS_HAS_EXPORT_IMPORT */ + +extern const char js_with_statement_str[]; + +static JSParseNode * +Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; + JSStmtInfo stmtInfo, *stmt, *stmt2; + JSAtom *label; + + ts->flags |= TSF_REGEXP; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + +#if JS_HAS_GETTER_SETTER + if (tt == TOK_NAME) { + tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); + if (tt == TOK_ERROR) + return NULL; + } +#endif + + switch (tt) { +#if JS_HAS_EXPORT_IMPORT + case TOK_EXPORT: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + PN_INIT_LIST(pn); + if (js_MatchToken(cx, ts, TOK_STAR)) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } else { + do { + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME); + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn2) + return NULL; + pn2->pn_op = JSOP_NAME; + pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn2->pn_expr = NULL; + pn2->pn_slot = -1; + pn2->pn_attrs = 0; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + } + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + break; + + case TOK_IMPORT: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + PN_INIT_LIST(pn); + do { + pn2 = ImportExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + case TOK_FUNCTION: + return FunctionStmt(cx, ts, tc); + + case TOK_IF: + /* An IF node has three kids: condition, then, and optional else. */ + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + if (!pn) + return NULL; + pn1 = Condition(cx, ts, tc); + if (!pn1) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_IF, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + if (js_MatchToken(cx, ts, TOK_ELSE)) { + stmtInfo.type = STMT_ELSE; + pn3 = Statement(cx, ts, tc); + if (!pn3) + return NULL; + pn->pn_pos.end = pn3->pn_pos.end; + } else { + pn3 = NULL; + pn->pn_pos.end = pn2->pn_pos.end; + } + js_PopStatement(tc); + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + pn->pn_kid3 = pn3; + return pn; + +#if JS_HAS_SWITCH_STATEMENT + case TOK_SWITCH: + { + JSParseNode *pn5; + JSBool seenDefault = JS_FALSE; + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn) + return NULL; + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); + + /* pn1 points to the switch's discriminant. */ + pn1 = Expr(cx, ts, tc); + if (!pn1) + return NULL; + + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); + + /* pn2 is a list of case nodes. The default case has pn_left == NULL */ + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn2) + return NULL; + PN_INIT_LIST(pn2); + + js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); + + while ((tt = js_GetToken(cx, ts)) != TOK_RC) { + switch (tt) { + case TOK_DEFAULT: + if (seenDefault) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_TOO_MANY_DEFAULTS); + return NULL; + } + seenDefault = JS_TRUE; + /* fall through */ + + case TOK_CASE: + pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn3) + return NULL; + if (tt == TOK_DEFAULT) { + pn3->pn_left = NULL; + } else { + pn3->pn_left = Expr(cx, ts, tc); + if (!pn3->pn_left) + return NULL; + } + PN_APPEND(pn2, pn3); + if (pn2->pn_count == JS_BIT(16)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_TOO_MANY_CASES); + return NULL; + } + break; + + case TOK_ERROR: + return NULL; + + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_SWITCH); + return NULL; + } + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); + + pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn4) + return NULL; + pn4->pn_type = TOK_LC; + PN_INIT_LIST(pn4); + while ((tt = js_PeekToken(cx, ts)) != TOK_RC && + tt != TOK_CASE && tt != TOK_DEFAULT) { + if (tt == TOK_ERROR) + return NULL; + pn5 = Statement(cx, ts, tc); + if (!pn5) + return NULL; + pn4->pn_pos.end = pn5->pn_pos.end; + PN_APPEND(pn4, pn5); + } + + /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ + if (pn4->pn_head) + pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; + pn3->pn_pos.end = pn4->pn_pos.end; + pn3->pn_right = pn4; + } + + js_PopStatement(tc); + + pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + return pn; + } +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case TOK_WHILE: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); + pn2 = Condition(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_left = pn2; + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + return pn; + +#if JS_HAS_DO_WHILE_LOOP + case TOK_DO: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_left = pn2; + MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); + pn2 = Condition(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + break; +#endif /* JS_HAS_DO_WHILE_LOOP */ + + case TOK_FOR: + /* A FOR node is binary, left is loop control and right is the body. */ + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn) + return NULL; + js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); + + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); + tt = js_PeekToken(cx, ts); + if (tt == TOK_SEMI) { + /* No initializer -- set first kid of left sub-node to null. */ + pn1 = NULL; + } else { + /* Set pn1 to a var list or an initializing expression. */ +#if JS_HAS_IN_OPERATOR + /* + * Set the TCF_IN_FOR_INIT flag during parsing of the first clause + * of the for statement. This flag will be used by the RelExpr + * production; if it is set, then the 'in' keyword will not be + * recognized as an operator, leaving it available to be parsed as + * part of a for/in loop. A side effect of this restriction is + * that (unparenthesized) expressions involving an 'in' operator + * are illegal in the init clause of an ordinary for loop. + */ + tc->flags |= TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ + if (tt == TOK_VAR) { + (void) js_GetToken(cx, ts); + pn1 = Variables(cx, ts, tc); + } else { + pn1 = Expr(cx, ts, tc); + } +#if JS_HAS_IN_OPERATOR + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ + if (!pn1) + return NULL; + } + + /* + * We can be sure that it's a for/in loop if there's still an 'in' + * keyword here, even if JavaScript recognizes 'in' as an operator, + * as we've excluded 'in' from being parsed in RelExpr by setting + * the TCF_IN_FOR_INIT flag in our JSTreeContext. + */ + if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { + stmtInfo.type = STMT_FOR_IN_LOOP; + + /* Check that the left side of the 'in' is valid. */ + if ((pn1->pn_type == TOK_VAR) + ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST) + : (pn1->pn_type != TOK_NAME && + pn1->pn_type != TOK_DOT && + pn1->pn_type != TOK_LB)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_FOR_LEFTSIDE); + return NULL; + } + + if (pn1->pn_type == TOK_VAR) { + /* Tell js_EmitTree(TOK_VAR) to generate a final POP. */ + pn1->pn_extra = JS_TRUE; + pn2 = pn1->pn_head; + } else { + pn2 = pn1; + } + + /* Beware 'for (arguments in ...)' with or without a 'var'. */ + if (pn2->pn_type == TOK_NAME && + pn2->pn_atom == cx->runtime->atomState.argumentsAtom) { + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + + /* Parse the object expression as the right operand of 'in'. */ + pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc); + if (!pn2) + return NULL; + pn->pn_left = pn2; + } else { + /* Parse the loop condition or null into pn2. */ + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); + if (js_PeekToken(cx, ts) == TOK_SEMI) { + pn2 = NULL; + } else { + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + } + + /* Parse the update expression or null into pn3. */ + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); + if (js_PeekToken(cx, ts) == TOK_RP) { + pn3 = NULL; + } else { + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; + } + + /* Build the RESERVED node to use as the left kid of pn. */ + pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + if (!pn4) + return NULL; + pn4->pn_type = TOK_RESERVED; + pn4->pn_op = JSOP_NOP; + pn4->pn_kid1 = pn1; + pn4->pn_kid2 = pn2; + pn4->pn_kid3 = pn3; + pn->pn_left = pn4; + } + + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); + + /* Parse the loop body into pn->pn_right. */ + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_right = pn2; + js_PopStatement(tc); + + /* Record the absolute line number for source note emission. */ + pn->pn_pos.end = pn2->pn_pos.end; + return pn; + +#if JS_HAS_EXCEPTIONS + case TOK_TRY: { + JSParseNode *catchtail = NULL; + /* + * try nodes are ternary. + * kid1 is the try Statement + * kid2 is the catch node + * kid3 is the finally Statement + * + * catch nodes are ternary. + * kid1 is the discriminant + * kid2 is the next catch node, or NULL + * kid3 is the catch block (on kid3 so that we can always append a + * new catch pn on catchtail->kid2) + * + * catch discriminant nodes are binary + * atom is the receptacle + * expr is the discriminant code + * + * finally nodes are unary (just the finally expression) + */ + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn->pn_op = JSOP_NOP; + + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); + js_PushStatement(tc, &stmtInfo, STMT_TRY, -1); + pn->pn_kid1 = Statements(cx, ts, tc); + if (!pn->pn_kid1) + return NULL; + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); + js_PopStatement(tc); + + catchtail = pn; + while (js_PeekToken(cx, ts) == TOK_CATCH) { + /* check for another catch after unconditional catch */ + if (catchtail != pn && !catchtail->pn_kid1->pn_expr) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_CATCH_AFTER_GENERAL); + return NULL; + } + + /* + * legal catch forms are: + * catch (v) + * catch (v if ) + * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) + */ + (void) js_GetToken(cx, ts); /* eat `catch' */ + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + if (!pn2) + return NULL; + + /* + * We use a PN_NAME for the discriminant (catchguard) node + * with the actual discriminant code in the initializer spot + */ + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER); + pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn3) + return NULL; + + pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn3->pn_expr = NULL; +#if JS_HAS_CATCH_GUARD + /* + * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to + * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax. + */ + if (js_PeekToken(cx, ts) == TOK_IF) { + (void)js_GetToken(cx, ts); /* eat `if' */ + pn3->pn_expr = Expr(cx, ts, tc); + if (!pn3->pn_expr) + return NULL; + } +#endif + pn2->pn_kid1 = pn3; + + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); + + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); + js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1); + stmtInfo.label = pn3->pn_atom; + pn2->pn_kid3 = Statements(cx, ts, tc); + if (!pn2->pn_kid3) + return NULL; + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); + js_PopStatement(tc); + + catchtail = catchtail->pn_kid2 = pn2; + } + catchtail->pn_kid2 = NULL; + + if (js_MatchToken(cx, ts, TOK_FINALLY)) { + tc->tryCount++; + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); + js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); + pn->pn_kid3 = Statements(cx, ts, tc); + if (!pn->pn_kid3) + return NULL; + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); + js_PopStatement(tc); + } else { + pn->pn_kid3 = NULL; + } + if (!pn->pn_kid2 && !pn->pn_kid3) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_CATCH_OR_FINALLY); + return NULL; + } + tc->tryCount++; + return pn; + } + + case TOK_THROW: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_op = JSOP_THROW; + pn->pn_kid = pn2; + break; + + /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ + case TOK_CATCH: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_CATCH_WITHOUT_TRY); + return NULL; + + case TOK_FINALLY: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_FINALLY_WITHOUT_TRY); + return NULL; + +#endif /* JS_HAS_EXCEPTIONS */ + + case TOK_BREAK: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + if (!MatchLabel(cx, ts, pn)) + return NULL; + stmt = tc->topStmt; + label = pn->pn_atom; + if (label) { + for (; ; stmt = stmt->down) { + if (!stmt) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_LABEL_NOT_FOUND); + return NULL; + } + if (stmt->type == STMT_LABEL && stmt->label == label) + break; + } + } else { + for (; ; stmt = stmt->down) { + if (!stmt) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_TOUGH_BREAK); + return NULL; + } + if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) + break; + } + } + if (label) + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + break; + + case TOK_CONTINUE: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + if (!MatchLabel(cx, ts, pn)) + return NULL; + stmt = tc->topStmt; + label = pn->pn_atom; + if (label) { + for (stmt2 = NULL; ; stmt = stmt->down) { + if (!stmt) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_LABEL_NOT_FOUND); + return NULL; + } + if (stmt->type == STMT_LABEL) { + if (stmt->label == label) { + if (!stmt2 || !STMT_IS_LOOP(stmt2)) { + js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_ERROR, + JSMSG_BAD_CONTINUE); + return NULL; + } + break; + } + } else { + stmt2 = stmt; + } + } + } else { + for (; ; stmt = stmt->down) { + if (!stmt) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_CONTINUE); + return NULL; + } + if (STMT_IS_LOOP(stmt)) + break; + } + } + if (label) + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + break; + + case TOK_WITH: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn) + return NULL; + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); + pn->pn_left = pn2; + + js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + js_PopStatement(tc); + + /* Deprecate after parsing, in case of WERROR option. */ + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | JSREPORT_STRICT, + JSMSG_DEPRECATED_USAGE, + js_with_statement_str)) { + return NULL; + } + + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_right = pn2; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + return pn; + + case TOK_VAR: + pn = Variables(cx, ts, tc); + if (!pn) + return NULL; + + /* Tell js_EmitTree to generate a final POP. */ + pn->pn_extra = JS_TRUE; + break; + + case TOK_RETURN: + if (!(tc->flags & TCF_IN_FUNCTION)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_RETURN); + return NULL; + } + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + + /* This is ugly, but we don't want to require a semicolon. */ + ts->flags |= TSF_REGEXP; + tt = js_PeekTokenSameLine(cx, ts); + ts->flags &= ~TSF_REGEXP; + if (tt == TOK_ERROR) + return NULL; + + if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + tc->flags |= TCF_RETURN_EXPR; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + } else { + tc->flags |= TCF_RETURN_VOID; + pn->pn_kid = NULL; + } + + if (JS_HAS_STRICT_OPTION(cx) && + (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) { + /* + * We must be in a frame with a non-native function, because + * we're compiling one. + */ + if (!ReportNoReturnValue(cx, ts)) + return NULL; + } + break; + + case TOK_LC: + js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); + pn = Statements(cx, ts, tc); + if (!pn) + return NULL; + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); + js_PopStatement(tc); + return pn; + + case TOK_EOL: + case TOK_SEMI: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_SEMI; + pn->pn_kid = NULL; + return pn; + +#if JS_HAS_DEBUGGER_KEYWORD + case TOK_DEBUGGER: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_DEBUGGER; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + break; +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + + case TOK_ERROR: + return NULL; + + default: + js_UngetToken(ts); + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + + if (js_PeekToken(cx, ts) == TOK_COLON) { + if (pn2->pn_type != TOK_NAME) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_LABEL); + return NULL; + } + label = pn2->pn_atom; + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (stmt->type == STMT_LABEL && stmt->label == label) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_DUPLICATE_LABEL); + return NULL; + } + } + (void) js_GetToken(cx, ts); + + /* Push a label struct and parse the statement. */ + js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); + stmtInfo.label = label; + pn = Statement(cx, ts, tc); + if (!pn) + return NULL; + + /* Pop the label, set pn_expr, and return early. */ + js_PopStatement(tc); + pn2->pn_type = TOK_COLON; + pn2->pn_pos.end = pn->pn_pos.end; + pn2->pn_expr = pn; + return pn2; + } + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_SEMI; + pn->pn_pos = pn2->pn_pos; + pn->pn_kid = pn2; + break; + } + + /* Check termination of this primitive statement. */ + if (ON_CURRENT_LINE(ts, pn->pn_pos)) { + tt = js_PeekTokenSameLine(cx, ts); + if (tt == TOK_ERROR) + return NULL; + if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SEMI_BEFORE_STMNT); + return NULL; + } + } + + (void) js_MatchToken(cx, ts, TOK_SEMI); + return pn; +} + +static JSParseNode * +Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSObject *obj, *pobj; + JSStackFrame *fp; + JSFunction *fun; + JSClass *clasp; + JSPropertyOp getter, setter, currentGetter, currentSetter; + JSAtom *atom; + JSAtomListElement *ale; + JSOp prevop; + JSProperty *prop; + JSScopeProperty *sprop; + JSBool ok; + + /* + * The tricky part of this code is to create special parsenode opcodes for + * getting and setting variables (which will be stored as special slots in + * the frame). The complex special case is an eval() inside a function. + * If the evaluated string references variables in the enclosing function, + * then we need to generate the special variable opcodes. We determine + * this by looking up the variable id in the current variable scope. + */ + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR); + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + pn->pn_op = CURRENT_TOKEN(ts).t_op; + pn->pn_extra = JS_FALSE; /* assume no JSOP_POP needed */ + PN_INIT_LIST(pn); + + /* + * Skip eval and debugger frames when looking for the function whose code + * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION + * will be set in tc->flags, and we can be sure fp->fun is the function to + * use. But if a function calls eval, the string argument is treated as a + * Program (per ECMA), so TCF_IN_FUNCTION won't be set. + * + * What's more, when the following code is reached from eval, cx->fp->fun + * is eval's JSFunction (a native function), so we need to skip its frame. + * We should find the scripted caller's function frame just below it, but + * we code a loop out of paranoia. + */ + for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down) + continue; + obj = fp->varobj; + fun = fp->fun; + clasp = OBJ_GET_CLASS(cx, obj); + if (fun && clasp == &js_FunctionClass) { + /* We are compiling code inside a function */ + getter = js_GetLocalVariable; + setter = js_SetLocalVariable; + } else if (fun && clasp == &js_CallClass) { + /* We are compiling code from an eval inside a function */ + getter = js_GetCallVariable; + setter = js_SetCallVariable; + } else { + getter = clasp->getProperty; + setter = clasp->setProperty; + } + + ok = JS_TRUE; + do { + currentGetter = getter; + currentSetter = setter; + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); + atom = CURRENT_TOKEN(ts).t_atom; + + ATOM_LIST_SEARCH(ale, &tc->decls, atom); + if (ale) { + prevop = ALE_JSOP(ale); + if (JS_HAS_STRICT_OPTION(cx) || + pn->pn_op == JSOP_DEFCONST || + prevop == JSOP_DEFCONST) { + const char *name = js_AtomToPrintableString(cx, atom); + if (!name || + !js_ReportCompileErrorNumber(cx, ts, NULL, + (pn->pn_op != JSOP_DEFCONST && + prevop != JSOP_DEFCONST) + ? JSREPORT_WARNING | + JSREPORT_STRICT + : JSREPORT_ERROR, + JSMSG_REDECLARED_VAR, + (prevop == JSOP_DEFFUN || + prevop == JSOP_CLOSURE) + ? js_function_str + : (prevop == JSOP_DEFCONST) + ? js_const_str + : js_var_str, + name)) { + return NULL; + } + } + if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) + tc->flags |= TCF_FUN_CLOSURE_VS_VAR; + } else { + ale = js_IndexAtom(cx, atom, &tc->decls); + if (!ale) + return NULL; + } + ALE_SET_JSOP(ale, pn->pn_op); + + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn2) + return NULL; + pn2->pn_op = JSOP_NAME; + pn2->pn_atom = atom; + pn2->pn_expr = NULL; + pn2->pn_slot = -1; + pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST) + ? JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY + : JSPROP_ENUMERATE | JSPROP_PERMANENT; + PN_APPEND(pn, pn2); + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + return NULL; + if (pobj == obj && + OBJ_IS_NATIVE(pobj) && + (sprop = (JSScopeProperty *)prop) != NULL) { + if (sprop->getter == js_GetArgument) { + const char *name = js_AtomToPrintableString(cx, atom); + if (!name) { + ok = JS_FALSE; + } else if (pn->pn_op == JSOP_DEFCONST) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_REDECLARED_PARAM, + name); + ok = JS_FALSE; + } else { + currentGetter = js_GetArgument; + currentSetter = js_SetArgument; + ok = js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_VAR_HIDES_ARG, + name); + } + } else { + if (fun) { + /* Not an argument, must be a redeclared local var. */ + if (clasp == &js_FunctionClass) { + JS_ASSERT(sprop->getter == js_GetLocalVariable); + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + sprop->shortid < fun->nvars); + } else if (clasp == &js_CallClass) { + if (sprop->getter == js_GetCallVariable) { + /* + * Referencing a variable introduced by a var + * statement in the enclosing function. Check + * that the slot number we have is in range. + */ + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + sprop->shortid < fun->nvars); + } else { + /* + * A variable introduced through another eval: + * don't use the special getters and setters + * since we can't allocate a slot in the frame. + */ + currentGetter = sprop->getter; + currentSetter = sprop->setter; + } + } + + /* Override the old getter and setter, to handle eval. */ + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, + 0, sprop->attrs, + currentGetter, + currentSetter); + if (!sprop) + ok = JS_FALSE; + } + } + } else { + /* + * Property not found in current variable scope: we have not seen + * this variable before. Define a new local variable by adding a + * property to the function's scope, allocating one slot in the + * function's frame. Global variables and any locals declared in + * with statement bodies are handled at runtime, by script prolog + * JSOP_DEFVAR bytecodes generated for slot-less vars. + */ + sprop = NULL; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = NULL; + } + if (currentGetter == js_GetCallVariable) { + /* Can't increase fun->nvars in an active frame! */ + currentGetter = clasp->getProperty; + currentSetter = clasp->setProperty; + } + if (currentGetter == js_GetLocalVariable && + atom != cx->runtime->atomState.argumentsAtom && + fp->scopeChain == obj && + !js_InWithStatement(tc)) { + if (!js_AddNativeProperty(cx, obj, (jsid)atom, + currentGetter, currentSetter, + SPROP_INVALID_SLOT, + pn2->pn_attrs | JSPROP_SHARED, + SPROP_HAS_SHORTID, fun->nvars)) { + ok = JS_FALSE; + } + fun->nvars++; + } + } + + if (js_MatchToken(cx, ts, TOK_ASSIGN)) { + if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_VAR_INIT); + ok = JS_FALSE; + } else { + pn2->pn_expr = AssignExpr(cx, ts, tc); + if (!pn2->pn_expr) { + ok = JS_FALSE; + } else { + pn2->pn_op = (pn->pn_op == JSOP_DEFCONST) + ? JSOP_SETCONST + : JSOP_SETNAME; + if (atom == cx->runtime->atomState.argumentsAtom) + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + } + } + + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) + return NULL; + } while (js_MatchToken(cx, ts, TOK_COMMA)); + + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + return pn; +} + +static JSParseNode * +Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + + pn = AssignExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn2) + return NULL; + pn2->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(pn2, pn); + pn = pn2; + do { + pn2 = AssignExpr(cx, ts, tc); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + } + return pn; +} + +static JSParseNode * +AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSTokenType tt; + JSOp op; + + CHECK_RECURSION(); + + pn = CondExpr(cx, ts, tc); + if (!pn) + return NULL; + + tt = js_GetToken(cx, ts); +#if JS_HAS_GETTER_SETTER + if (tt == TOK_NAME) { + tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN); + if (tt == TOK_ERROR) + return NULL; + } +#endif + if (tt != TOK_ASSIGN) { + js_UngetToken(ts); + return pn; + } + + op = CURRENT_TOKEN(ts).t_op; + for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid) + continue; + switch (pn2->pn_type) { + case TOK_NAME: + pn2->pn_op = JSOP_SETNAME; + if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) + tc->flags |= TCF_FUN_HEAVYWEIGHT; + break; + case TOK_DOT: + pn2->pn_op = JSOP_SETPROP; + break; + case TOK_LB: + pn2->pn_op = JSOP_SETELEM; + break; +#if JS_HAS_LVALUE_RETURN + case TOK_LP: + pn2->pn_op = JSOP_SETCALL; + break; +#endif + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_LEFTSIDE_OF_ASS); + return NULL; + } + pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); + return pn; +} + +static JSParseNode * +CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn1, *pn2, *pn3; +#if JS_HAS_IN_OPERATOR + uintN oldflags; +#endif /* JS_HAS_IN_OPERATOR */ + + pn = OrExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { + pn1 = pn; + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + if (!pn) + return NULL; +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in the middle clause of a ternary, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ + pn2 = AssignExpr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); +#endif /* JS_HAS_IN_OPERATOR */ + + if (!pn2) + return NULL; + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); + pn3 = AssignExpr(cx, ts, tc); + if (!pn3) + return NULL; + pn->pn_pos.begin = pn1->pn_pos.begin; + pn->pn_pos.end = pn3->pn_pos.end; + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; + pn->pn_kid3 = pn3; + } + return pn; +} + +static JSParseNode * +OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = AndExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_OR)) + pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc); + return pn; +} + +static JSParseNode * +AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = BitOrExpr(cx, ts, tc); + if (pn && js_MatchToken(cx, ts, TOK_AND)) + pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc); + return pn; +} + +static JSParseNode * +BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = BitXorExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITOR)) { + pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc), + tc); + } + return pn; +} + +static JSParseNode * +BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = BitAndExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) { + pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc), + tc); + } + return pn; +} + +static JSParseNode * +BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = EqExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_BITAND)) + pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc); + return pn; +} + +static JSParseNode * +EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSOp op; + + pn = RelExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_EQOP)) { + op = CURRENT_TOKEN(ts).t_op; + pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc); + } + return pn; +} + +static JSParseNode * +RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSTokenType tt; + JSOp op; +#if JS_HAS_IN_OPERATOR + uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT; + + /* + * Uses of the in operator in ShiftExprs are always unambiguous, + * so unset the flag that prohibits recognizing it. + */ + tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ + + pn = ShiftExpr(cx, ts, tc); + while (pn && + (js_MatchToken(cx, ts, TOK_RELOP) +#if JS_HAS_IN_OPERATOR + /* + * Recognize the 'in' token as an operator only if we're not + * currently in the init expr of a for loop. + */ + || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) +#endif /* JS_HAS_IN_OPERATOR */ +#if JS_HAS_INSTANCEOF + || js_MatchToken(cx, ts, TOK_INSTANCEOF) +#endif /* JS_HAS_INSTANCEOF */ + )) { + tt = CURRENT_TOKEN(ts).type; + op = CURRENT_TOKEN(ts).t_op; + pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc); + } +#if JS_HAS_IN_OPERATOR + /* Restore previous state of inForInit flag. */ + tc->flags |= inForInitFlag; +#endif /* JS_HAS_IN_OPERATOR */ + + return pn; +} + +static JSParseNode * +ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSOp op; + + pn = AddExpr(cx, ts, tc); + while (pn && js_MatchToken(cx, ts, TOK_SHOP)) { + op = CURRENT_TOKEN(ts).t_op; + pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc); + } + return pn; +} + +static JSParseNode * +AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSTokenType tt; + JSOp op; + + pn = MulExpr(cx, ts, tc); + while (pn && + (js_MatchToken(cx, ts, TOK_PLUS) || + js_MatchToken(cx, ts, TOK_MINUS))) { + tt = CURRENT_TOKEN(ts).type; + op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; + pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc); + } + return pn; +} + +static JSParseNode * +MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSTokenType tt; + JSOp op; + + pn = UnaryExpr(cx, ts, tc); + while (pn && + (js_MatchToken(cx, ts, TOK_STAR) || + js_MatchToken(cx, ts, TOK_DIVOP))) { + tt = CURRENT_TOKEN(ts).type; + op = CURRENT_TOKEN(ts).t_op; + pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc); + } + return pn; +} + +static JSParseNode * +SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, + const char *name) +{ + while (kid->pn_type == TOK_RP) + kid = kid->pn_kid; + if (kid->pn_type != TOK_NAME && + kid->pn_type != TOK_DOT && +#if JS_HAS_LVALUE_RETURN + (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && +#endif + kid->pn_type != TOK_LB) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_OPERAND, name); + return NULL; + } + pn->pn_kid = kid; + return kid; +} + +static const char *incop_name_str[] = {"increment", "decrement"}; + +static JSBool +SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *pn, JSParseNode *kid, + JSTokenType tt, JSBool preorder) +{ + JSOp op; + + kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]); + if (!kid) + return JS_FALSE; + switch (kid->pn_type) { + case TOK_NAME: + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC) + : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC); + if (kid->pn_atom == cx->runtime->atomState.argumentsAtom) + tc->flags |= TCF_FUN_HEAVYWEIGHT; + break; + + case TOK_DOT: + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCPROP : JSOP_PROPINC) + : (preorder ? JSOP_DECPROP : JSOP_PROPDEC); + break; + +#if JS_HAS_LVALUE_RETURN + case TOK_LP: + kid->pn_op = JSOP_SETCALL; +#endif + case TOK_LB: + op = (tt == TOK_INC) + ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC) + : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC); + break; + + default: + JS_ASSERT(0); + op = JSOP_NOP; + } + pn->pn_op = op; + return JS_TRUE; +} + +static JSParseNode * +UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode *pn, *pn2; + + ts->flags |= TSF_REGEXP; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + + switch (tt) { + case TOK_UNARYOP: + case TOK_PLUS: + case TOK_MINUS: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ + pn->pn_op = CURRENT_TOKEN(ts).t_op; + pn2 = UnaryExpr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + break; + + case TOK_INC: + case TOK_DEC: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn2 = MemberExpr(cx, ts, tc, JS_TRUE); + if (!pn2) + return NULL; + if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE)) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + break; + + case TOK_DELETE: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn2 = UnaryExpr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + + /* + * Under ECMA3, deleting any unary expression is valid -- it simply + * returns true. Here we strip off any parentheses. + */ + while (pn2->pn_type == TOK_RP) + pn2 = pn2->pn_kid; + pn->pn_kid = pn2; + break; + + case TOK_ERROR: + return NULL; + + default: + js_UngetToken(ts); + pn = MemberExpr(cx, ts, tc, JS_TRUE); + if (!pn) + return NULL; + + /* Don't look across a newline boundary for a postfix incop. */ + if (ON_CURRENT_LINE(ts, pn->pn_pos)) { + tt = js_PeekTokenSameLine(cx, ts); + if (tt == TOK_INC || tt == TOK_DEC) { + (void) js_GetToken(cx, ts); + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn2) + return NULL; + if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) + return NULL; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn = pn2; + } + } + break; + } + return pn; +} + +static JSBool +ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSParseNode *listNode) +{ + JSBool matched; + + ts->flags |= TSF_REGEXP; + matched = js_MatchToken(cx, ts, TOK_RP); + ts->flags &= ~TSF_REGEXP; + if (!matched) { + do { + JSParseNode *argNode = AssignExpr(cx, ts, tc); + if (!argNode) + return JS_FALSE; + PN_APPEND(listNode, argNode); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + + if (js_GetToken(cx, ts) != TOK_RP) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_PAREN_AFTER_ARGS); + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSParseNode * +MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowCallSyntax) +{ + JSParseNode *pn, *pn2, *pn3; + JSTokenType tt; + + CHECK_RECURSION(); + + /* Check for new expression first. */ + ts->flags |= TSF_REGEXP; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + if (tt == TOK_NEW) { + (void) js_GetToken(cx, ts); + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + pn2 = MemberExpr(cx, ts, tc, JS_FALSE); + if (!pn2) + return NULL; + pn->pn_op = JSOP_NEW; + PN_INIT_LIST_1(pn, pn2); + pn->pn_pos.begin = pn2->pn_pos.begin; + + if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn)) + return NULL; + if (pn->pn_count > ARGC_LIMIT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_CON_ARGS); + return NULL; + } + pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; + } else { + pn = PrimaryExpr(cx, ts, tc); + if (!pn) + return NULL; + } + + while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { + if (tt == TOK_DOT) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + if (!pn2) + return NULL; + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_expr = pn; + pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; + } else if (tt == TOK_LB) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn2) + return NULL; + pn3 = Expr(cx, ts, tc); + if (!pn3) + return NULL; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + + /* Optimize o['p'] to o.p by rewriting pn2. */ + if (pn3->pn_type == TOK_STRING) { + pn2->pn_type = TOK_DOT; + pn2->pn_op = JSOP_GETPROP; + pn2->pn_arity = PN_NAME; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + } else { + pn2->pn_op = JSOP_GETELEM; + pn2->pn_left = pn; + pn2->pn_right = pn3; + } + } else if (allowCallSyntax && tt == TOK_LP) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn2) + return NULL; + + /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */ + pn2->pn_op = JSOP_CALL; + if (pn->pn_op == JSOP_NAME && + pn->pn_atom == cx->runtime->atomState.evalAtom) { + pn2->pn_op = JSOP_EVAL; + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + + PN_INIT_LIST_1(pn2, pn); + pn2->pn_pos.begin = pn->pn_pos.begin; + + if (!ArgumentList(cx, ts, tc, pn2)) + return NULL; + if (pn2->pn_count > ARGC_LIMIT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + return NULL; + } + pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end; + } else { + js_UngetToken(ts); + return pn; + } + + pn = pn2; + } + if (tt == TOK_ERROR) + return NULL; + return pn; +} + +static JSParseNode * +PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode *pn, *pn2, *pn3; + char *badWord; +#if JS_HAS_GETTER_SETTER + JSAtom *atom; + JSRuntime *rt; +#endif + +#if JS_HAS_SHARP_VARS + JSParseNode *defsharp; + JSBool notsharp; + + defsharp = NULL; + notsharp = JS_FALSE; + again: + /* + * Control flows here after #n= is scanned. If the following primary is + * not valid after such a "sharp variable" definition, the token type case + * should set notsharp. + */ +#endif + + CHECK_RECURSION(); + + ts->flags |= TSF_REGEXP; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + +#if JS_HAS_GETTER_SETTER + if (tt == TOK_NAME) { + tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); + if (tt == TOK_ERROR) + return NULL; + } +#endif + + switch (tt) { +#if JS_HAS_LEXICAL_CLOSURE + case TOK_FUNCTION: + pn = FunctionExpr(cx, ts, tc); + if (!pn) + return NULL; + break; +#endif + +#if JS_HAS_INITIALIZERS + case TOK_LB: + { + JSBool matched; + jsuint atomIndex; + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_RB; + pn->pn_extra = JS_FALSE; + +#if JS_HAS_SHARP_VARS + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else +#endif + PN_INIT_LIST(pn); + + ts->flags |= TSF_REGEXP; + matched = js_MatchToken(cx, ts, TOK_RB); + ts->flags &= ~TSF_REGEXP; + if (!matched) { + for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) { + ts->flags |= TSF_REGEXP; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_REGEXP; + if (tt == TOK_RB) { + pn->pn_extra = JS_TRUE; + break; + } + + if (tt == TOK_COMMA) { + /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ + js_MatchToken(cx, ts, TOK_COMMA); + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + } else { + pn2 = AssignExpr(cx, ts, tc); + } + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + + if (tt != TOK_COMMA) { + /* If we didn't already match TOK_COMMA in above case. */ + if (!js_MatchToken(cx, ts, TOK_COMMA)) + break; + } + } + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); + } + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + return pn; + } + + case TOK_LC: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_RC; + +#if JS_HAS_SHARP_VARS + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else +#endif + PN_INIT_LIST(pn); + + if (!js_MatchToken(cx, ts, TOK_RC)) { + do { + JSOp op; + + tt = js_GetToken(cx, ts); + switch (tt) { + case TOK_NUMBER: + pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (pn3) + pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; + break; + case TOK_NAME: +#if JS_HAS_GETTER_SETTER + atom = CURRENT_TOKEN(ts).t_atom; + rt = cx->runtime; + if (atom == rt->atomState.getAtom || + atom == rt->atomState.setAtom) { + op = (atom == rt->atomState.getAtom) + ? JSOP_GETTER + : JSOP_SETTER; + if (js_MatchToken(cx, ts, TOK_NAME)) { + pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, + tc); + if (!pn3) + return NULL; + pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn3->pn_expr = NULL; + + /* We have to fake a 'function' token here. */ + CURRENT_TOKEN(ts).t_op = JSOP_NOP; + CURRENT_TOKEN(ts).type = TOK_FUNCTION; + pn2 = FunctionExpr(cx, ts, tc); + pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc); + goto skip; + } + } + /* else fall thru ... */ +#endif + case TOK_STRING: + pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (pn3) + pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; + break; + case TOK_RC: + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_TRAILING_COMMA)) { + return NULL; + } + goto end_obj_init; + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_PROP_ID); + return NULL; + } + + tt = js_GetToken(cx, ts); +#if JS_HAS_GETTER_SETTER + if (tt == TOK_NAME) { + tt = CheckGetterOrSetter(cx, ts, TOK_COLON); + if (tt == TOK_ERROR) + return NULL; + } +#endif + if (tt != TOK_COLON) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_COLON_AFTER_ID); + return NULL; + } + op = CURRENT_TOKEN(ts).t_op; + pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), + tc); +#if JS_HAS_GETTER_SETTER + skip: +#endif + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST); + } + end_obj_init: + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + return pn; + +#if JS_HAS_SHARP_VARS + case TOK_DEFSHARP: + if (defsharp) + goto badsharp; + defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!defsharp) + return NULL; + defsharp->pn_kid = NULL; + defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; + goto again; + + case TOK_USESHARP: + /* Check for forward/dangling references at runtime, to allow eval. */ + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; + notsharp = JS_TRUE; + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + + case TOK_LP: + { +#if JS_HAS_IN_OPERATOR + uintN oldflags; +#endif + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in a parenthesized expression, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif + pn2 = Expr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); +#endif + if (!pn2) + return NULL; + + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); + pn->pn_type = TOK_RP; + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + pn->pn_kid = pn2; + break; + } + + case TOK_STRING: +#if JS_HAS_SHARP_VARS + notsharp = JS_TRUE; +#endif + /* FALL THROUGH */ + case TOK_NAME: + case TOK_OBJECT: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + pn->pn_op = CURRENT_TOKEN(ts).t_op; + pn->pn_atom = CURRENT_TOKEN(ts).t_atom; + if (tt == TOK_NAME) { + pn->pn_arity = PN_NAME; + pn->pn_expr = NULL; + pn->pn_slot = -1; + pn->pn_attrs = 0; + + /* Unqualified __parent__ and __proto__ uses require activations. */ + if (pn->pn_atom == cx->runtime->atomState.parentAtom || + pn->pn_atom == cx->runtime->atomState.protoAtom) { + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + } + break; + + case TOK_NUMBER: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + pn->pn_dval = CURRENT_TOKEN(ts).t_dval; +#if JS_HAS_SHARP_VARS + notsharp = JS_TRUE; +#endif + break; + + case TOK_PRIMARY: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + pn->pn_op = CURRENT_TOKEN(ts).t_op; +#if JS_HAS_SHARP_VARS + notsharp = JS_TRUE; +#endif + break; + +#if !JS_HAS_EXPORT_IMPORT + case TOK_EXPORT: + case TOK_IMPORT: +#endif + case TOK_RESERVED: + badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr, + (size_t) CURRENT_TOKEN(ts).pos.end.index + - CURRENT_TOKEN(ts).pos.begin.index); + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_RESERVED_ID, badWord); + JS_free(cx, badWord); + return NULL; + + case TOK_ERROR: + /* The scanner or one of its subroutines reported the error. */ + return NULL; + + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + +#if JS_HAS_SHARP_VARS + if (defsharp) { + if (notsharp) { + badsharp: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_SHARP_VAR_DEF); + return NULL; + } + defsharp->pn_kid = pn; + return defsharp; + } +#endif + return pn; +} + +static JSBool +ContainsVarStmt(JSParseNode *pn) +{ + JSParseNode *pn2; + + if (!pn) + return JS_FALSE; + switch (pn->pn_arity) { + case PN_LIST: + if (pn->pn_type == TOK_VAR) + return JS_TRUE; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (ContainsVarStmt(pn2)) + return JS_TRUE; + } + break; + case PN_TERNARY: + return ContainsVarStmt(pn->pn_kid1) || + ContainsVarStmt(pn->pn_kid2) || + ContainsVarStmt(pn->pn_kid3); + case PN_BINARY: + /* + * Limit recursion if pn is a binary expression, which can't contain a + * var statement. + */ + if (pn->pn_op != JSOP_NOP) + return JS_FALSE; + return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right); + case PN_UNARY: + if (pn->pn_op != JSOP_NOP) + return JS_FALSE; + return ContainsVarStmt(pn->pn_kid); + default:; + } + return JS_FALSE; +} + +/* + * Fold from one constant type to another. + * XXX handles only strings and numbers for now + */ +static JSBool +FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type) +{ + if (pn->pn_type != type) { + switch (type) { + case TOK_NUMBER: + if (pn->pn_type == TOK_STRING) { + jsdouble d; + if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d)) + return JS_FALSE; + pn->pn_dval = d; + pn->pn_type = TOK_NUMBER; + pn->pn_op = JSOP_NUMBER; + } + break; + + case TOK_STRING: + if (pn->pn_type == TOK_NUMBER) { + JSString *str = js_NumberToString(cx, pn->pn_dval); + if (!str) + return JS_FALSE; + pn->pn_atom = js_AtomizeString(cx, str, 0); + if (!pn->pn_atom) + return JS_FALSE; + pn->pn_type = TOK_STRING; + pn->pn_op = JSOP_STRING; + } + break; + + default:; + } + } + return JS_TRUE; +} + +/* + * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless + * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after + * a successful call to this function. + */ +static JSBool +FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, + JSParseNode *pn, JSTreeContext *tc) +{ + jsdouble d, d2; + int32 i, j; + uint32 u; + + JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER); + d = pn1->pn_dval; + d2 = pn2->pn_dval; + switch (op) { + case JSOP_LSH: + case JSOP_RSH: + if (!js_DoubleToECMAInt32(cx, d, &i)) + return JS_FALSE; + if (!js_DoubleToECMAInt32(cx, d2, &j)) + return JS_FALSE; + j &= 31; + d = (op == JSOP_LSH) ? i << j : i >> j; + break; + + case JSOP_URSH: + if (!js_DoubleToECMAUint32(cx, d, &u)) + return JS_FALSE; + if (!js_DoubleToECMAInt32(cx, d2, &j)) + return JS_FALSE; + j &= 31; + d = u >> j; + break; + + case JSOP_ADD: + d += d2; + break; + + case JSOP_SUB: + d -= d2; + break; + + case JSOP_MUL: + d *= d2; + break; + + case JSOP_DIV: + if (d2 == 0) { +#if defined(XP_WIN) + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + d = *cx->runtime->jsNaN; + else +#endif + if (d == 0 || JSDOUBLE_IS_NaN(d)) + d = *cx->runtime->jsNaN; + else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) + d = *cx->runtime->jsNegativeInfinity; + else + d = *cx->runtime->jsPositiveInfinity; + } else { + d /= d2; + } + break; + + case JSOP_MOD: + if (d2 == 0) { + d = *cx->runtime->jsNaN; + } else { +#if defined(XP_WIN) + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) +#endif + d = fmod(d, d2); + } + break; + + default:; + } + + /* Take care to allow pn1 or pn2 to alias pn. */ + if (pn1 != pn) + RecycleTree(pn1, tc); + if (pn2 != pn) + RecycleTree(pn2, tc); + pn->pn_type = TOK_NUMBER; + pn->pn_op = JSOP_NUMBER; + pn->pn_arity = PN_NULLARY; + pn->pn_dval = d; + return JS_TRUE; +} + +JSBool +js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) +{ + JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return JS_FALSE; + } + + switch (pn->pn_arity) { + case PN_FUNC: + if (!js_FoldConstants(cx, pn->pn_body, tc)) + return JS_FALSE; + break; + + case PN_LIST: + /* Save the list head in pn1 for later use. */ + for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_FoldConstants(cx, pn2, tc)) + return JS_FALSE; + } + break; + + case PN_TERNARY: + /* Any kid may be null (e.g. for (;;)). */ + pn1 = pn->pn_kid1; + pn2 = pn->pn_kid2; + pn3 = pn->pn_kid3; + if (pn1 && !js_FoldConstants(cx, pn1, tc)) + return JS_FALSE; + if (pn2 && !js_FoldConstants(cx, pn2, tc)) + return JS_FALSE; + if (pn3 && !js_FoldConstants(cx, pn3, tc)) + return JS_FALSE; + break; + + case PN_BINARY: + /* First kid may be null (for default case in switch). */ + pn1 = pn->pn_left; + pn2 = pn->pn_right; + if (pn1 && !js_FoldConstants(cx, pn1, tc)) + return JS_FALSE; + if (!js_FoldConstants(cx, pn2, tc)) + return JS_FALSE; + break; + + case PN_UNARY: + /* Our kid may be null (e.g. return; vs. return e;). */ + pn1 = pn->pn_kid; + if (pn1 && !js_FoldConstants(cx, pn1, tc)) + return JS_FALSE; + break; + + case PN_NAME: + /* + * Skip pn1 down along a chain of dotted member expressions to avoid + * excessive recursion. Our only goal here is to fold constants (if + * any) in the primary expression operand to the left of the first + * dot in the chain. + */ + pn1 = pn->pn_expr; + while (pn1 && pn1->pn_arity == PN_NAME) + pn1 = pn1->pn_expr; + if (pn1 && !js_FoldConstants(cx, pn1, tc)) + return JS_FALSE; + break; + + case PN_NULLARY: + break; + } + + switch (pn->pn_type) { + case TOK_IF: + if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3)) + break; + /* FALL THROUGH */ + + case TOK_HOOK: + /* Reduce 'if (C) T; else E' into T for true C, E for false. */ + switch (pn1->pn_type) { + case TOK_NUMBER: + if (pn1->pn_dval == 0) + pn2 = pn3; + break; + case TOK_STRING: + if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0) + pn2 = pn3; + break; + case TOK_PRIMARY: + if (pn1->pn_op == JSOP_TRUE) + break; + if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) { + pn2 = pn3; + break; + } + /* FALL THROUGH */ + default: + /* Early return to dodge common code that copies pn2 to pn. */ + return JS_TRUE; + } + + if (pn2) { + /* pn2 is the then- or else-statement subtree to compile. */ + PN_MOVE_NODE(pn, pn2); + } else { + /* False condition and no else: make pn an empty statement. */ + pn->pn_type = TOK_SEMI; + pn->pn_arity = PN_UNARY; + pn->pn_kid = NULL; + } + RecycleTree(pn2, tc); + if (pn3 && pn3 != pn2) + RecycleTree(pn3, tc); + break; + + case TOK_PLUS: + if (pn->pn_arity == PN_LIST) { + size_t length, length2; + jschar *chars; + JSString *str, *str2; + + /* + * Any string literal term with all others number or string means + * this is a concatenation. If any term is not a string or number + * literal, we can't fold. + */ + JS_ASSERT(pn->pn_count > 2); + if (pn->pn_extra & PNX_CANTFOLD) + return JS_TRUE; + if (pn->pn_extra != PNX_STRCAT) + goto do_binary_op; + + /* Ok, we're concatenating: convert non-string constant operands. */ + length = 0; + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { + if (!FoldType(cx, pn2, TOK_STRING)) + return JS_FALSE; + /* XXX fold only if all operands convert to string */ + if (pn2->pn_type != TOK_STRING) + return JS_TRUE; + length += ATOM_TO_STRING(pn2->pn_atom)->length; + } + + /* Allocate a new buffer and string descriptor for the result. */ + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + + /* Fill the buffer, advancing chars and recycling kids as we go. */ + for (pn2 = pn1; pn2; pn2 = pn3) { + str2 = ATOM_TO_STRING(pn2->pn_atom); + length2 = str2->length; + js_strncpy(chars, str2->chars, length2); + chars += length2; + pn3 = pn2->pn_next; + RecycleTree(pn2, tc); + } + *chars = 0; + + /* Atomize the result string and mutate pn to refer to it. */ + pn->pn_atom = js_AtomizeString(cx, str, 0); + if (!pn->pn_atom) + return JS_FALSE; + pn->pn_type = TOK_STRING; + pn->pn_op = JSOP_STRING; + pn->pn_arity = PN_NULLARY; + break; + } + + /* Handle a binary string concatenation. */ + JS_ASSERT(pn->pn_arity == PN_BINARY); + if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) { + JSString *left, *right, *str; + + if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2, + TOK_STRING)) { + return JS_FALSE; + } + if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING) + return JS_TRUE; + left = ATOM_TO_STRING(pn1->pn_atom); + right = ATOM_TO_STRING(pn2->pn_atom); + str = js_ConcatStrings(cx, left, right); + if (!str) + return JS_FALSE; + pn->pn_atom = js_AtomizeString(cx, str, 0); + if (!pn->pn_atom) + return JS_FALSE; + pn->pn_type = TOK_STRING; + pn->pn_op = JSOP_STRING; + pn->pn_arity = PN_NULLARY; + RecycleTree(pn1, tc); + RecycleTree(pn2, tc); + break; + } + + /* Can't concatenate string literals, let's try numbers. */ + goto do_binary_op; + + case TOK_STAR: + /* The * in 'import *;' parses as a nullary star node. */ + if (pn->pn_arity == PN_NULLARY) + break; + /* FALL THROUGH */ + + case TOK_SHOP: + case TOK_MINUS: + case TOK_DIVOP: + do_binary_op: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_count > 2); + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { + if (!FoldType(cx, pn2, TOK_NUMBER)) + return JS_FALSE; + /* XXX fold only if all operands convert to number */ + if (pn2->pn_type != TOK_NUMBER) + break; + } + if (!pn2) { + JSOp op = pn->pn_op; + + pn2 = pn1->pn_next; + pn3 = pn2->pn_next; + if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc)) + return JS_FALSE; + while ((pn2 = pn3) != NULL) { + pn3 = pn2->pn_next; + if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc)) + return JS_FALSE; + } + } + } else { + JS_ASSERT(pn->pn_arity == PN_BINARY); + if (!FoldType(cx, pn1, TOK_NUMBER) || + !FoldType(cx, pn2, TOK_NUMBER)) { + return JS_FALSE; + } + if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) { + if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc)) + return JS_FALSE; + } + } + break; + + case TOK_UNARYOP: + if (pn1->pn_type == TOK_NUMBER) { + jsdouble d; + int32 i; + + /* Operate on one numeric constant. */ + d = pn1->pn_dval; + switch (pn->pn_op) { + case JSOP_BITNOT: + if (!js_DoubleToECMAInt32(cx, d, &i)) + return JS_FALSE; + d = ~i; + break; + + case JSOP_NEG: +#ifdef HPUX + /* + * Negation of a zero doesn't produce a negative + * zero on HPUX. Perform the operation by bit + * twiddling. + */ + JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; +#else + d = -d; +#endif + break; + + case JSOP_POS: + break; + + case JSOP_NOT: + pn->pn_type = TOK_PRIMARY; + pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE; + pn->pn_arity = PN_NULLARY; + /* FALL THROUGH */ + + default: + /* Return early to dodge the common TOK_NUMBER code. */ + return JS_TRUE; + } + pn->pn_type = TOK_NUMBER; + pn->pn_op = JSOP_NUMBER; + pn->pn_arity = PN_NULLARY; + pn->pn_dval = d; + RecycleTree(pn1, tc); + } + break; + + default:; + } + + return JS_TRUE; +} diff --git a/src/extension/script/js/jsparse.h b/src/extension/script/js/jsparse.h new file mode 100644 index 000000000..1c6ff893a --- /dev/null +++ b/src/extension/script/js/jsparse.h @@ -0,0 +1,337 @@ +/* -*- 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 jsparse_h___ +#define jsparse_h___ +/* + * JS parser definitions. + */ +#include "jsprvtd.h" +#include "jspubtd.h" +#include "jsscan.h" + +JS_BEGIN_EXTERN_C + +/* + * Parsing builds a tree of nodes that directs code generation. This tree is + * not a concrete syntax tree in all respects (for example, || and && are left + * associative, but (A && B && C) translates into the right-associated tree + * > so that code generation can emit a left-associative branch + * around when A is false). Nodes are labeled by token type, with a + * JSOp secondary label when needed: + * + * Label Variant Members + * ----- ------- ------- + * + * TOK_FUNCTION func pn_funAtom: atom holding function object containing + * arg and var properties. We create the function + * object at parse (not emit) time to specialize arg + * and var bytecodes early. + * pn_body: TOK_LC node for function body statements + * pn_flags: TCF_FUN_* flags (see jsemit.h) collected + * while parsing the function's body + * pn_tryCount: of try statements in function + * + * + * TOK_LC list pn_head: list of pn_count statements + * TOK_EXPORT list pn_head: list of pn_count TOK_NAMEs or one TOK_STAR + * (which is not a multiply node) + * TOK_IMPORT list pn_head: list of pn_count sub-trees of the form + * a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a. + * Each member is expressed with TOK_DOT or TOK_LB. + * Each sub-tree's root node has a pn_op in the set + * JSOP_IMPORT{ALL,PROP,ELEM} + * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null + * TOK_SWITCH binary pn_left: discriminant + * pn_right: list of TOK_CASE nodes, with at most one + * TOK_DEFAULT node + * TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT + * TOK_DEFAULT pn_right: TOK_LC node for this case's statements + * TOK_WHILE binary pn_left: cond, pn_right: body + * TOK_DO binary pn_left: body, pn_right: cond + * TOK_FOR binary pn_left: either + * for/in loop: a binary TOK_IN node with + * pn_left: TOK_VAR or TOK_NAME to left of 'in' + * pn_right: object expr to right of 'in' + * for(;;) loop: a ternary TOK_RESERVED node with + * pn_kid1: init expr before first ';' + * pn_kid2: cond expr before second ';' + * pn_kid3: update expr after second ';' + * any kid may be null + * pn_right: body + * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception + * TOK_TRY ternary pn_kid1: try block + * pn_kid2: catch blocks or null + * pn_kid3: finally block or null + * TOK_CATCH ternary pn_kid1: PN_NAME node for catch var (with pn_expr + * null or the catch guard expression) + * pn_kid2: more catch blocks or null + * pn_kid3: catch block statements + * TOK_BREAK name pn_atom: label or null + * TOK_CONTINUE name pn_atom: label or null + * TOK_WITH binary pn_left: head expr, pn_right: body + * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes + * each name node has + * pn_atom: variable name + * pn_expr: initializer or null + * TOK_RETURN unary pn_kid: return expr or null + * TOK_SEMI unary pn_kid: expr or null statement + * TOK_COLON name pn_atom: label, pn_expr: labeled statement + * + * + * All left-associated binary trees of the same type are optimized into lists + * to avoid recursion when processing expression chains. + * TOK_COMMA list pn_head: list of pn_count comma-separated exprs + * TOK_ASSIGN binary pn_left: lvalue, pn_right: rvalue + * pn_op: JSOP_ADD for +=, etc. + * TOK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else + * TOK_OR binary pn_left: first in || chain, pn_right: rest of chain + * TOK_AND binary pn_left: first in && chain, pn_right: rest of chain + * TOK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr + * TOK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr + * TOK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr + * TOK_EQOP binary pn_left: left-assoc EQ expr, pn_right: REL expr + * pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE + * TOK_RELOP binary pn_left: left-assoc REL expr, pn_right: SH expr + * pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE + * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr + * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH + * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr + * pn_extra: if a left-associated binary TOK_PLUS + * tree has been flattened into a list (see above + * under ), pn_extra will contain + * PNX_STRCAT if at least one list element is a + * string literal (TOK_STRING); if such a list has + * any non-string, non-number term, pn_extra will + * contain PNX_CANTFOLD. + * pn_ + * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB + * TOK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr + * TOK_DIVOP pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD + * TOK_UNARYOP unary pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS, + * JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID + * TOK_INC, unary pn_kid: MEMBER expr + * TOK_DEC + * TOK_NEW list pn_head: list of ctor, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * ctor is a MEMBER expr + * TOK_DELETE unary pn_kid: MEMBER expr + * TOK_DOT name pn_expr: MEMBER expr to left of . + * pn_atom: name to right of . + * TOK_LB binary pn_left: MEMBER expr to left of [ + * pn_right: expr between [ and ] + * TOK_LP list pn_head: list of call, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * call is a MEMBER expr naming a callable object + * TOK_RB list pn_head: list of pn_count array element exprs + * [,,] holes are represented by TOK_COMMA nodes + * #n=[...] produces TOK_DEFSHARP at head of list + * pn_extra: true if extra comma at end + * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where + * each has pn_left: property id, pn_right: value + * #n={...} produces TOK_DEFSHARP at head of list + * TOK_DEFSHARP unary pn_num: jsint value of n in #n= + * pn_kid: null for #n=[...] and #n={...}, primary + * if #n=primary for function, paren, name, object + * literal expressions + * TOK_USESHARP nullary pn_num: jsint value of n in #n# + * TOK_RP unary pn_kid: parenthesized expression + * TOK_NAME, name pn_atom: name, string, or object atom + * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT + * TOK_OBJECT If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR + * with pn_slot >= 0 and pn_attrs telling const-ness + * TOK_NUMBER dval pn_dval: double value of numeric literal + * TOK_PRIMARY nullary pn_op: JSOp bytecode + */ +typedef enum JSParseNodeArity { + PN_FUNC = -3, + PN_LIST = -2, + PN_TERNARY = 3, + PN_BINARY = 2, + PN_UNARY = 1, + PN_NAME = -1, + PN_NULLARY = 0 +} JSParseNodeArity; + +struct JSParseNode { + JSTokenType pn_type; + JSTokenPos pn_pos; + JSOp pn_op; + ptrdiff_t pn_offset; /* first generated bytecode offset */ + JSParseNodeArity pn_arity; + union { + struct { /* TOK_FUNCTION node */ + JSAtom *funAtom; /* atomized function object */ + JSParseNode *body; /* TOK_LC list of statements */ + uint32 flags; /* accumulated tree context flags */ + uint32 tryCount; /* count of try statements in body */ + } func; + struct { /* list of next-linked nodes */ + JSParseNode *head; /* first node in list */ + JSParseNode **tail; /* ptr to ptr to last node in list */ + uint32 count; /* number of nodes in list */ + uint32 extra; /* extra comma flag for [1,2,,] */ + } list; + struct { /* ternary: if, for(;;), ?: */ + JSParseNode *kid1; /* condition, discriminant, etc. */ + JSParseNode *kid2; /* then-part, case list, etc. */ + JSParseNode *kid3; /* else-part, default case, etc. */ + } ternary; + struct { /* two kids if binary */ + JSParseNode *left; + JSParseNode *right; + jsval val; /* switch case value */ + } binary; + struct { /* one kid if unary */ + JSParseNode *kid; + jsint num; /* -1 or sharp variable number */ + } unary; + struct { /* name, labeled statement, etc. */ + JSAtom *atom; /* name or label atom, null if slot */ + JSParseNode *expr; /* object or initializer */ + jsint slot; /* -1 or arg or local var slot */ + uintN attrs; /* attributes if local var or const */ + } name; + jsdouble dval; /* aligned numeric literal value */ + } pn_u; + JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ +}; + +#define pn_funAtom pn_u.func.funAtom +#define pn_body pn_u.func.body +#define pn_flags pn_u.func.flags +#define pn_tryCount pn_u.func.tryCount +#define pn_head pn_u.list.head +#define pn_tail pn_u.list.tail +#define pn_count pn_u.list.count +#define pn_extra pn_u.list.extra +#define pn_kid1 pn_u.ternary.kid1 +#define pn_kid2 pn_u.ternary.kid2 +#define pn_kid3 pn_u.ternary.kid3 +#define pn_left pn_u.binary.left +#define pn_right pn_u.binary.right +#define pn_val pn_u.binary.val +#define pn_kid pn_u.unary.kid +#define pn_num pn_u.unary.num +#define pn_atom pn_u.name.atom +#define pn_expr pn_u.name.expr +#define pn_slot pn_u.name.slot +#define pn_attrs pn_u.name.attrs +#define pn_dval pn_u.dval + +/* PN_LIST pn_extra flags. */ +#define PNX_STRCAT 0x1 /* TOK_PLUS list has string term */ +#define PNX_CANTFOLD 0x2 /* TOK_PLUS list has unfoldable term */ + +/* + * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off + * any kids in pn2->pn_u, by clearing pn2. + */ +#define PN_MOVE_NODE(pn, pn2) \ + JS_BEGIN_MACRO \ + (pn)->pn_type = (pn2)->pn_type; \ + (pn)->pn_op = (pn2)->pn_op; \ + (pn)->pn_arity = (pn2)->pn_arity; \ + (pn)->pn_u = (pn2)->pn_u; \ + PN_CLEAR_NODE(pn2); \ + JS_END_MACRO + +#define PN_CLEAR_NODE(pn) \ + JS_BEGIN_MACRO \ + (pn)->pn_type = TOK_EOF; \ + (pn)->pn_op = JSOP_NOP; \ + (pn)->pn_arity = PN_NULLARY; \ + JS_END_MACRO + +/* True if pn is a parsenode representing a literal constant. */ +#define PN_IS_CONSTANT(pn) \ + ((pn)->pn_type == TOK_NUMBER || \ + (pn)->pn_type == TOK_STRING || \ + ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS)) + +/* + * Compute a pointer to the last JSParseNode element in a singly-linked list. + * NB: list must be non-empty for correct PN_LAST usage! + */ +#define PN_LAST(list) \ + ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next))) + +#define PN_INIT_LIST(list) \ + JS_BEGIN_MACRO \ + (list)->pn_head = NULL; \ + (list)->pn_tail = &(list)->pn_head; \ + (list)->pn_count = 0; \ + JS_END_MACRO + +#define PN_INIT_LIST_1(list, pn) \ + JS_BEGIN_MACRO \ + (list)->pn_head = (pn); \ + (list)->pn_tail = &(pn)->pn_next; \ + (list)->pn_count = 1; \ + JS_END_MACRO + +#define PN_APPEND(list, pn) \ + JS_BEGIN_MACRO \ + *(list)->pn_tail = (pn); \ + (list)->pn_tail = &(pn)->pn_next; \ + (list)->pn_count++; \ + JS_END_MACRO + +/* + * Parse a top-level JS script. + * + * The caller must prevent the GC from running while this function is active, + * because atoms and function newborns are not rooted yet. + */ +extern JS_FRIEND_API(JSParseNode *) +js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts); + +extern JS_FRIEND_API(JSBool) +js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSCodeGenerator *cg); + +extern JSBool +js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); + +extern JSBool +js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); + +JS_END_EXTERN_C + +#endif /* jsparse_h___ */ diff --git a/src/extension/script/js/jsprf.c b/src/extension/script/js/jsprf.c new file mode 100644 index 000000000..36bf92428 --- /dev/null +++ b/src/extension/script/js/jsprf.c @@ -0,0 +1,1212 @@ +/* -*- 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 ***** */ + +/* +** Portable safe sprintf code. +** +** Author: Kipp E.B. Hickman +*/ +#include "jsstddef.h" +#include +#include +#include +#include +#include "jsprf.h" +#include "jslong.h" +#include "jsutil.h" /* Added by JSIFY */ + +/* +** Note: on some platforms va_list is defined as an array, +** and requires array notation. +*/ +#ifdef HAVE_VA_COPY +#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) +#elif defined(HAVE_VA_LIST_AS_ARRAY) +#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] +#else +#define VARARGS_ASSIGN(foo, bar) (foo) = (bar) +#endif + +/* +** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it) +*/ + +/* +** XXX This needs to be internationalized! +*/ + +typedef struct SprintfStateStr SprintfState; + +struct SprintfStateStr { + int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len); + + char *base; + char *cur; + JSUint32 maxlen; + + int (*func)(void *arg, const char *sp, JSUint32 len); + void *arg; +}; + +/* +** Numbered Arguement State +*/ +struct NumArgState{ + int type; /* type of the current ap */ + va_list ap; /* point to the corresponding position on ap */ +}; + +#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ + + +#define TYPE_INT16 0 +#define TYPE_UINT16 1 +#define TYPE_INTN 2 +#define TYPE_UINTN 3 +#define TYPE_INT32 4 +#define TYPE_UINT32 5 +#define TYPE_INT64 6 +#define TYPE_UINT64 7 +#define TYPE_STRING 8 +#define TYPE_DOUBLE 9 +#define TYPE_INTSTR 10 +#define TYPE_UNKNOWN 20 + +#define FLAG_LEFT 0x1 +#define FLAG_SIGNED 0x2 +#define FLAG_SPACED 0x4 +#define FLAG_ZEROS 0x8 +#define FLAG_NEG 0x10 + +/* +** Fill into the buffer using the data in src +*/ +static int fill2(SprintfState *ss, const char *src, int srclen, int width, + int flags) +{ + char space = ' '; + int rv; + + width -= srclen; + if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ + if (flags & FLAG_ZEROS) { + space = '0'; + } + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Copy out the source data */ + rv = (*ss->stuff)(ss, src, (JSUint32)srclen); + if (rv < 0) { + return rv; + } + + if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + return 0; +} + +/* +** Fill a number. The order is: optional-sign zero-filling conversion-digits +*/ +static int fill_n(SprintfState *ss, const char *src, int srclen, int width, + int prec, int type, int flags) +{ + int zerowidth = 0; + int precwidth = 0; + int signwidth = 0; + int leftspaces = 0; + int rightspaces = 0; + int cvtwidth; + int rv; + char sign; + + if ((type & 1) == 0) { + if (flags & FLAG_NEG) { + sign = '-'; + signwidth = 1; + } else if (flags & FLAG_SIGNED) { + sign = '+'; + signwidth = 1; + } else if (flags & FLAG_SPACED) { + sign = ' '; + signwidth = 1; + } + } + cvtwidth = signwidth + srclen; + + if (prec > 0) { + if (prec > srclen) { + precwidth = prec - srclen; /* Need zero filling */ + cvtwidth += precwidth; + } + } + + if ((flags & FLAG_ZEROS) && (prec < 0)) { + if (width > cvtwidth) { + zerowidth = width - cvtwidth; /* Zero filling */ + cvtwidth += zerowidth; + } + } + + if (flags & FLAG_LEFT) { + if (width > cvtwidth) { + /* Space filling on the right (i.e. left adjusting) */ + rightspaces = width - cvtwidth; + } + } else { + if (width > cvtwidth) { + /* Space filling on the left (i.e. right adjusting) */ + leftspaces = width - cvtwidth; + } + } + while (--leftspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + if (signwidth) { + rv = (*ss->stuff)(ss, &sign, 1); + if (rv < 0) { + return rv; + } + } + while (--precwidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + while (--zerowidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + rv = (*ss->stuff)(ss, src, (JSUint32)srclen); + if (rv < 0) { + return rv; + } + while (--rightspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + return 0; +} + +/* +** Convert a long into its printable form +*/ +static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (num == 0)) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (num) { + int digit = (((unsigned long)num) % radix) & 0xF; + *--cvt = hexp[digit]; + digits++; + num = (long)(((unsigned long)num) / radix); + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a 64-bit integer into its printable form +*/ +static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + JSInt64 rad; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (JSLL_IS_ZERO(num))) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + JSLL_I2L(rad, radix); + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (!JSLL_IS_ZERO(num)) { + JSInt32 digit; + JSInt64 quot, rem; + JSLL_UDIVMOD(", &rem, num, rad); + JSLL_L2I(digit, rem); + *--cvt = hexp[digit & 0xf]; + digits++; + num = quot; + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a double precision floating point number into its printable +** form. +** +** XXX stop using sprintf to convert floating point +*/ +static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) +{ + char fin[20]; + char fout[300]; + int amount = fmt1 - fmt0; + + JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); + if (amount >= (int)sizeof(fin)) { + /* Totally bogus % command to sprintf. Just ignore it */ + return 0; + } + memcpy(fin, fmt0, (size_t)amount); + fin[amount] = 0; + + /* Convert floating point using the native sprintf code */ +#ifdef DEBUG + { + const char *p = fin; + while (*p) { + JS_ASSERT(*p != 'L'); + p++; + } + } +#endif + sprintf(fout, fin, d); + + /* + ** This assert will catch overflow's of fout, when building with + ** debugging on. At least this way we can track down the evil piece + ** of calling code and fix it! + */ + JS_ASSERT(strlen(fout) < sizeof(fout)); + + return (*ss->stuff)(ss, fout, strlen(fout)); +} + +/* +** Convert a string into its printable form. "width" is the output +** width. "prec" is the maximum number of characters of "s" to output, +** where -1 means until NUL. +*/ +static int cvt_s(SprintfState *ss, const char *s, int width, int prec, + int flags) +{ + int slen; + + if (prec == 0) + return 0; + + /* Limit string length by precision value */ + slen = s ? strlen(s) : 6; + if (prec > 0) { + if (prec < slen) { + slen = prec; + } + } + + /* and away we go */ + return fill2(ss, s ? s : "(null)", slen, width, flags); +} + +/* +** BiuldArgArray stands for Numbered Argument list Sprintf +** for example, +** fmp = "%4$i, %2$d, %3s, %1d"; +** the number must start from 1, and no gap among them +*/ + +static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ) +{ + int number = 0, cn = 0, i; + const char* p; + char c; + struct NumArgState* nas; + + + /* + ** first pass: + ** detemine how many legal % I have got, then allocate space + */ + + p = fmt; + *rv = 0; + i = 0; + while( ( c = *p++ ) != 0 ){ + if( c != '%' ) + continue; + if( ( c = *p++ ) == '%' ) /* skip %% case */ + continue; + + while( c != 0 ){ + if( c > '9' || c < '0' ){ + if( c == '$' ){ /* numbered argument csae */ + if( i > 0 ){ + *rv = -1; + return NULL; + } + number++; + } else { /* non-numbered argument case */ + if( number > 0 ){ + *rv = -1; + return NULL; + } + i = 1; + } + break; + } + + c = *p++; + } + } + + if( number == 0 ){ + return NULL; + } + + + if( number > NAS_DEFAULT_NUM ){ + nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) ); + if( !nas ){ + *rv = -1; + return NULL; + } + } else { + nas = nasArray; + } + + for( i = 0; i < number; i++ ){ + nas[i].type = TYPE_UNKNOWN; + } + + + /* + ** second pass: + ** set nas[].type + */ + + p = fmt; + while( ( c = *p++ ) != 0 ){ + if( c != '%' ) continue; + c = *p++; + if( c == '%' ) continue; + + cn = 0; + while( c && c != '$' ){ /* should imporve error check later */ + cn = cn*10 + c - '0'; + c = *p++; + } + + if( !c || cn < 1 || cn > number ){ + *rv = -1; + break; + } + + /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ + cn--; + if( nas[cn].type != TYPE_UNKNOWN ) + continue; + + c = *p++; + + /* width */ + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + *rv = -1; + break; + } + + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + + /* precision */ + if (c == '.') { + c = *p++; + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + *rv = -1; + break; + } + + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + } + + /* size */ + nas[cn].type = TYPE_INTN; + if (c == 'h') { + nas[cn].type = TYPE_INT16; + c = *p++; + } else if (c == 'L') { + /* XXX not quite sure here */ + nas[cn].type = TYPE_INT64; + c = *p++; + } else if (c == 'l') { + nas[cn].type = TYPE_INT32; + c = *p++; + if (c == 'l') { + nas[cn].type = TYPE_INT64; + c = *p++; + } + } + + /* format */ + switch (c) { + case 'd': + case 'c': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + break; + + case 'e': + case 'f': + case 'g': + nas[ cn ].type = TYPE_DOUBLE; + break; + + case 'p': + /* XXX should use cpp */ + if (sizeof(void *) == sizeof(JSInt32)) { + nas[ cn ].type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(JSInt64)) { + nas[ cn ].type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(JSIntn)) { + nas[ cn ].type = TYPE_UINTN; + } else { + nas[ cn ].type = TYPE_UNKNOWN; + } + break; + + case 'C': + case 'S': + case 'E': + case 'G': + /* XXX not supported I suppose */ + JS_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + + case 's': + nas[ cn ].type = TYPE_STRING; + break; + + case 'n': + nas[ cn ].type = TYPE_INTSTR; + break; + + default: + JS_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + } + + /* get a legal para. */ + if( nas[ cn ].type == TYPE_UNKNOWN ){ + *rv = -1; + break; + } + } + + + /* + ** third pass + ** fill the nas[cn].ap + */ + + if( *rv < 0 ){ + if( nas != nasArray ) + JS_DELETE( nas ); + return NULL; + } + + cn = 0; + while( cn < number ){ + if( nas[cn].type == TYPE_UNKNOWN ){ + cn++; + continue; + } + + VARARGS_ASSIGN(nas[cn].ap, ap); + + switch( nas[cn].type ){ + case TYPE_INT16: + case TYPE_UINT16: + case TYPE_INTN: + case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break; + + case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break; + + case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break; + + case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break; + + case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break; + + case TYPE_STRING: (void)va_arg( ap, char* ); break; + + case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break; + + case TYPE_DOUBLE: (void)va_arg( ap, double ); break; + + default: + if( nas != nasArray ) + JS_DELETE( nas ); + *rv = -1; + return NULL; + } + + cn++; + } + + + return nas; +} + +/* +** The workhorse sprintf code. +*/ +static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) +{ + char c; + int flags, width, prec, radix, type; + union { + char ch; + int i; + long l; + JSInt64 ll; + double d; + const char *s; + int *ip; + } u; + const char *fmt0; + static char *hex = "0123456789abcdef"; + static char *HEX = "0123456789ABCDEF"; + char *hexp; + int rv, i; + struct NumArgState* nas = NULL; + struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; + char pattern[20]; + const char* dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ + + + /* + ** build an argument array, IF the fmt is numbered argument + ** list style, to contain the Numbered Argument list pointers + */ + + nas = BuildArgArray( fmt, ap, &rv, nasArray ); + if( rv < 0 ){ + /* the fmt contains error Numbered Argument format, jliu@netscape.com */ + JS_ASSERT(0); + return rv; + } + + while ((c = *fmt++) != 0) { + if (c != '%') { + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + fmt0 = fmt - 1; + + /* + ** Gobble up the % format string. Hopefully we have handled all + ** of the strange cases! + */ + flags = 0; + c = *fmt++; + if (c == '%') { + /* quoting a % with %% */ + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + + if( nas != NULL ){ + /* the fmt contains the Numbered Arguments feature */ + i = 0; + while( c && c != '$' ){ /* should imporve error check later */ + i = ( i * 10 ) + ( c - '0' ); + c = *fmt++; + } + + if( nas[i-1].type == TYPE_UNKNOWN ){ + if( nas && ( nas != nasArray ) ) + JS_DELETE( nas ); + return -1; + } + + ap = nas[i-1].ap; + dolPt = fmt; + c = *fmt++; + } + + /* + * Examine optional flags. Note that we do not implement the + * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is + * somewhat ambiguous and not ideal, which is perhaps why + * the various sprintf() implementations are inconsistent + * on this feature. + */ + while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { + if (c == '-') flags |= FLAG_LEFT; + if (c == '+') flags |= FLAG_SIGNED; + if (c == ' ') flags |= FLAG_SPACED; + if (c == '0') flags |= FLAG_ZEROS; + c = *fmt++; + } + if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; + if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; + + /* width */ + if (c == '*') { + c = *fmt++; + width = va_arg(ap, int); + } else { + width = 0; + while ((c >= '0') && (c <= '9')) { + width = (width * 10) + (c - '0'); + c = *fmt++; + } + } + + /* precision */ + prec = -1; + if (c == '.') { + c = *fmt++; + if (c == '*') { + c = *fmt++; + prec = va_arg(ap, int); + } else { + prec = 0; + while ((c >= '0') && (c <= '9')) { + prec = (prec * 10) + (c - '0'); + c = *fmt++; + } + } + } + + /* size */ + type = TYPE_INTN; + if (c == 'h') { + type = TYPE_INT16; + c = *fmt++; + } else if (c == 'L') { + /* XXX not quite sure here */ + type = TYPE_INT64; + c = *fmt++; + } else if (c == 'l') { + type = TYPE_INT32; + c = *fmt++; + if (c == 'l') { + type = TYPE_INT64; + c = *fmt++; + } + } + + /* format */ + hexp = hex; + switch (c) { + case 'd': case 'i': /* decimal/integer */ + radix = 10; + goto fetch_and_convert; + + case 'o': /* octal */ + radix = 8; + type |= 1; + goto fetch_and_convert; + + case 'u': /* unsigned decimal */ + radix = 10; + type |= 1; + goto fetch_and_convert; + + case 'x': /* unsigned hex */ + radix = 16; + type |= 1; + goto fetch_and_convert; + + case 'X': /* unsigned HEX */ + radix = 16; + hexp = HEX; + type |= 1; + goto fetch_and_convert; + + fetch_and_convert: + switch (type) { + case TYPE_INT16: + u.l = va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINT16: + u.l = va_arg(ap, int) & 0xffff; + goto do_long; + case TYPE_INTN: + u.l = va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINTN: + u.l = (long)va_arg(ap, unsigned int); + goto do_long; + + case TYPE_INT32: + u.l = va_arg(ap, JSInt32); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINT32: + u.l = (long)va_arg(ap, JSUint32); + do_long: + rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + + case TYPE_INT64: + u.ll = va_arg(ap, JSInt64); + if (!JSLL_GE_ZERO(u.ll)) { + JSLL_NEG(u.ll, u.ll); + flags |= FLAG_NEG; + } + goto do_longlong; + case TYPE_UINT64: + u.ll = va_arg(ap, JSUint64); + do_longlong: + rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + } + break; + + case 'e': + case 'E': + case 'f': + case 'g': + u.d = va_arg(ap, double); + if( nas != NULL ){ + i = fmt - dolPt; + if( i < (int)sizeof( pattern ) ){ + pattern[0] = '%'; + memcpy( &pattern[1], dolPt, (size_t)i ); + rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); + } + } else + rv = cvt_f(ss, u.d, fmt0, fmt); + + if (rv < 0) { + return rv; + } + break; + + case 'c': + u.ch = va_arg(ap, int); + if ((flags & FLAG_LEFT) == 0) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + rv = (*ss->stuff)(ss, &u.ch, 1); + if (rv < 0) { + return rv; + } + if (flags & FLAG_LEFT) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + break; + + case 'p': + if (sizeof(void *) == sizeof(JSInt32)) { + type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(JSInt64)) { + type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(int)) { + type = TYPE_UINTN; + } else { + JS_ASSERT(0); + break; + } + radix = 16; + goto fetch_and_convert; + +#if 0 + case 'C': + case 'S': + case 'E': + case 'G': + /* XXX not supported I suppose */ + JS_ASSERT(0); + break; +#endif + + case 's': + u.s = va_arg(ap, const char*); + rv = cvt_s(ss, u.s, width, prec, flags); + if (rv < 0) { + return rv; + } + break; + + case 'n': + u.ip = va_arg(ap, int*); + if (u.ip) { + *u.ip = ss->cur - ss->base; + } + break; + + default: + /* Not a % token after all... skip it */ +#if 0 + JS_ASSERT(0); +#endif + rv = (*ss->stuff)(ss, "%", 1); + if (rv < 0) { + return rv; + } + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Stuff trailing NUL */ + rv = (*ss->stuff)(ss, "\0", 1); + + if( nas && ( nas != nasArray ) ){ + JS_DELETE( nas ); + } + + return rv; +} + +/************************************************************************/ + +static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len) +{ + int rv; + + rv = (*ss->func)(ss->arg, sp, len); + if (rv < 0) { + return rv; + } + ss->maxlen += len; + return 0; +} + +JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, + const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = JS_vsxprintf(func, arg, fmt, ap); + va_end(ap); + return rv; +} + +JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, + const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = FuncStuff; + ss.func = func; + ss.arg = arg; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + return (rv < 0) ? (JSUint32)-1 : ss.maxlen; +} + +/* +** Stuff routine that automatically grows the malloc'd output buffer +** before it overflows. +*/ +static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len) +{ + ptrdiff_t off; + char *newbase; + JSUint32 newlen; + + off = ss->cur - ss->base; + if (off + len >= ss->maxlen) { + /* Grow the buffer */ + newlen = ss->maxlen + ((len > 32) ? len : 32); + if (ss->base) { + newbase = (char*) realloc(ss->base, newlen); + } else { + newbase = (char*) malloc(newlen); + } + if (!newbase) { + /* Ran out of memory */ + return -1; + } + ss->base = newbase; + ss->maxlen = newlen; + ss->cur = ss->base + off; + } + + /* Copy data */ + while (len) { + --len; + *ss->cur++ = *sp++; + } + JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen); + return 0; +} + +/* +** sprintf into a malloc'd buffer +*/ +JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = JS_vsmprintf(fmt, ap); + va_end(ap); + return rv; +} + +/* +** Free memory allocated, for the caller, by JS_smprintf +*/ +JS_PUBLIC_API(void) JS_smprintf_free(char *mem) +{ + JS_DELETE(mem); +} + +JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + JS_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + +/* +** Stuff routine that discards overflow data +*/ +static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len) +{ + JSUint32 limit = ss->maxlen - (ss->cur - ss->base); + + if (len > limit) { + len = limit; + } + while (len) { + --len; + *ss->cur++ = *sp++; + } + return 0; +} + +/* +** sprintf into a fixed size buffer. Make sure there is a NUL at the end +** when finished. +*/ +JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...) +{ + va_list ap; + int rv; + + JS_ASSERT((JSInt32)outlen > 0); + if ((JSInt32)outlen <= 0) { + return 0; + } + + va_start(ap, fmt); + rv = JS_vsnprintf(out, outlen, fmt, ap); + va_end(ap); + return rv; +} + +JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt, + va_list ap) +{ + SprintfState ss; + JSUint32 n; + + JS_ASSERT((JSInt32)outlen > 0); + if ((JSInt32)outlen <= 0) { + return 0; + } + + ss.stuff = LimitStuff; + ss.base = out; + ss.cur = out; + ss.maxlen = outlen; + (void) dosprintf(&ss, fmt, ap); + + /* If we added chars, and we didn't append a null, do it now. */ + if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) + *(--ss.cur) = '\0'; + + n = ss.cur - ss.base; + return n ? n - 1 : n; +} + +JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = JS_vsprintf_append(last, fmt, ap); + va_end(ap); + return rv; +} + +JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + if (last) { + int lastlen = strlen(last); + ss.base = last; + ss.cur = last + lastlen; + ss.maxlen = lastlen; + } else { + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + } + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + JS_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + diff --git a/src/extension/script/js/jsprf.h b/src/extension/script/js/jsprf.h new file mode 100644 index 000000000..e8cc46c0c --- /dev/null +++ b/src/extension/script/js/jsprf.h @@ -0,0 +1,148 @@ +/* -*- 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 jsprf_h___ +#define jsprf_h___ + +/* +** API for PR printf like routines. Supports the following formats +** %d - decimal +** %u - unsigned decimal +** %x - unsigned hex +** %X - unsigned uppercase hex +** %o - unsigned octal +** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above +** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above +** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above +** %s - string +** %c - character +** %p - pointer (deals with machine dependent pointer size) +** %f - float +** %g - float +*/ +#include "jstypes.h" +#include +#include + +JS_BEGIN_EXTERN_C + +/* +** sprintf into a fixed size buffer. Guarantees that a NUL is at the end +** of the buffer. Returns the length of the written output, NOT including +** the NUL, or (JSUint32)-1 if an error occurs. +*/ +extern JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...); + +/* +** sprintf into a malloc'd buffer. Return a pointer to the malloc'd +** buffer on success, NULL on failure. Call "JS_smprintf_free" to release +** the memory returned. +*/ +extern JS_PUBLIC_API(char*) JS_smprintf(const char *fmt, ...); + +/* +** Free the memory allocated, for the caller, by JS_smprintf +*/ +extern JS_PUBLIC_API(void) JS_smprintf_free(char *mem); + +/* +** "append" sprintf into a malloc'd buffer. "last" is the last value of +** the malloc'd buffer. sprintf will append data to the end of last, +** growing it as necessary using realloc. If last is NULL, JS_sprintf_append +** will allocate the initial string. The return value is the new value of +** last for subsequent calls, or NULL if there is a malloc failure. +*/ +extern JS_PUBLIC_API(char*) JS_sprintf_append(char *last, const char *fmt, ...); + +/* +** sprintf into a function. The function "f" is called with a string to +** place into the output. "arg" is an opaque pointer used by the stuff +** function to hold any state needed to do the storage of the output +** data. The return value is a count of the number of characters fed to +** the stuff function, or (JSUint32)-1 if an error occurs. +*/ +typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen); + +extern JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...); + +/* +** va_list forms of the above. +*/ +extern JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap); +extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); +extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); +extern JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); + +/* +*************************************************************************** +** FUNCTION: JS_sscanf +** DESCRIPTION: +** JS_sscanf() scans the input character string, performs data +** conversions, and stores the converted values in the data objects +** pointed to by its arguments according to the format control +** string. +** +** JS_sscanf() behaves the same way as the sscanf() function in the +** Standard C Library (stdio.h), with the following exceptions: +** - JS_sscanf() handles the NSPR integer and floating point types, +** such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas +** sscanf() handles the standard C types like short, int, long, +** and double. +** - JS_sscanf() has no multibyte character support, while sscanf() +** does. +** INPUTS: +** const char *buf +** a character string holding the input to scan +** const char *fmt +** the format control string for the conversions +** ... +** variable number of arguments, each of them is a pointer to +** a data object in which the converted value will be stored +** OUTPUTS: none +** RETURNS: JSInt32 +** The number of values converted and stored. +** RESTRICTIONS: +** Multibyte characters in 'buf' or 'fmt' are not allowed. +*************************************************************************** +*/ + +extern JS_PUBLIC_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...); + +JS_END_EXTERN_C + +#endif /* jsprf_h___ */ diff --git a/src/extension/script/js/jsprvtd.h b/src/extension/script/js/jsprvtd.h new file mode 100644 index 000000000..f5f1e77f6 --- /dev/null +++ b/src/extension/script/js/jsprvtd.h @@ -0,0 +1,174 @@ +/* -*- 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 jsprvtd_h___ +#define jsprvtd_h___ +/* + * JS private typename definitions. + * + * This header is included only in other .h files, for convenience and for + * simplicity of type naming. The alternative for structures is to use tags, + * which are named the same as their typedef names (legal in C/C++, and less + * noisy than suffixing the typedef name with "Struct" or "Str"). Instead, + * all .h files that include this file may use the same typedef name, whether + * declaring a pointer to struct type, or defining a member of struct type. + * + * A few fundamental scalar types are defined here too. Neither the scalar + * nor the struct typedefs should change much, therefore the nearly-global + * make dependency induced by this file should not prove painful. + */ + +#include "jspubtd.h" + +/* Scalar typedefs. */ +typedef uint8 jsbytecode; +typedef uint8 jssrcnote; +typedef uint32 jsatomid; + +/* Struct typedefs. */ +typedef struct JSArgumentFormatMap JSArgumentFormatMap; +typedef struct JSCodeGenerator JSCodeGenerator; +typedef struct JSDependentString JSDependentString; +typedef struct JSGCLockHashEntry JSGCLockHashEntry; +typedef struct JSGCRootHashEntry JSGCRootHashEntry; +typedef struct JSGCThing JSGCThing; +typedef struct JSParseNode JSParseNode; +typedef struct JSSharpObjectMap JSSharpObjectMap; +typedef struct JSToken JSToken; +typedef struct JSTokenPos JSTokenPos; +typedef struct JSTokenPtr JSTokenPtr; +typedef struct JSTokenStream JSTokenStream; +typedef struct JSTreeContext JSTreeContext; +typedef struct JSTryNote JSTryNote; + +/* Friend "Advanced API" typedefs. */ +typedef struct JSAtom JSAtom; +typedef struct JSAtomList JSAtomList; +typedef struct JSAtomListElement JSAtomListElement; +typedef struct JSAtomMap JSAtomMap; +typedef struct JSAtomState JSAtomState; +typedef struct JSCodeSpec JSCodeSpec; +typedef struct JSPrinter JSPrinter; +typedef struct JSRegExp JSRegExp; +typedef struct JSRegExpStatics JSRegExpStatics; +typedef struct JSScope JSScope; +typedef struct JSScopeOps JSScopeOps; +typedef struct JSScopeProperty JSScopeProperty; +typedef struct JSStackFrame JSStackFrame; +typedef struct JSStackHeader JSStackHeader; +typedef struct JSSubString JSSubString; + +/* "Friend" types used by jscntxt.h and jsdbgapi.h. */ +typedef enum JSTrapStatus { + JSTRAP_ERROR, + JSTRAP_CONTINUE, + JSTRAP_RETURN, + JSTRAP_THROW, + JSTRAP_LIMIT +} JSTrapStatus; + +typedef JSTrapStatus +(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure); + +typedef JSBool +(* JS_DLL_CALLBACK JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, + jsval old, jsval *newp, void *closure); + +/* called just after script creation */ +typedef void +(* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, + const char *filename, /* URL of script */ + uintN lineno, /* line script starts */ + JSScript *script, + JSFunction *fun, + void *callerdata); + +/* called just before script destruction */ +typedef void +(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, + JSScript *script, + void *callerdata); + +typedef void +(* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, + jschar *str, size_t length, + void **listenerTSData, void *closure); + +/* +* This hook captures high level script execution and function calls +* (JS or native). +* It is used by JS_SetExecuteHook to hook top level scripts and by +* JS_SetCallHook to hook function calls. +* It will get called twice per script or function call: +* just before execution begins and just after it finishes. In both cases +* the 'current' frame is that of the executing code. +* +* The 'before' param is JS_TRUE for the hook invocation before the execution +* and JS_FALSE for the invocation after the code has run. +* +* The 'ok' param is significant only on the post execution invocation to +* signify whether or not the code completed 'normally'. +* +* The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook +* for the 'before'invocation, but is whatever value is returned from that +* invocation for the 'after' invocation. Thus, the hook implementor *could* +* allocate a stucture in the 'before' invocation and return a pointer +* to that structure. The pointer would then be handed to the hook for +* the 'after' invocation. Alternately, the 'before' could just return the +* same value as in 'closure' to cause the 'after' invocation to be called +* with the same 'closure' value as the 'before'. +* +* Returning NULL in the 'before' hook will cause the 'after' hook to +* NOT be called. +*/ + +typedef void * +(* JS_DLL_CALLBACK JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, + JSBool *ok, void *closure); + +typedef void +(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, + void *closure); + +typedef JSBool +(* JS_DLL_CALLBACK JSDebugErrorHook)(JSContext *cx, const char *message, + JSErrorReport *report, void *closure); + +#endif /* jsprvtd_h___ */ diff --git a/src/extension/script/js/jspubtd.h b/src/extension/script/js/jspubtd.h new file mode 100644 index 000000000..e3a1a38b5 --- /dev/null +++ b/src/extension/script/js/jspubtd.h @@ -0,0 +1,564 @@ +/* -*- 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 jspubtd_h___ +#define jspubtd_h___ +/* + * JS public API typedefs. + */ +#include "jstypes.h" +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +/* Scalar typedefs. */ +typedef uint16 jschar; +typedef int32 jsint; +typedef uint32 jsuint; +typedef float64 jsdouble; +typedef jsword jsval; +typedef jsword jsid; +typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ + +/* + * Run-time version enumeration. See jsconfig.h for compile-time counterparts + * to these values that may be selected by the JS_VERSION macro, and tested by + * #if expressions. + */ +typedef enum JSVersion { + JSVERSION_1_0 = 100, + JSVERSION_1_1 = 110, + JSVERSION_1_2 = 120, + JSVERSION_1_3 = 130, + JSVERSION_1_4 = 140, + JSVERSION_ECMA_3 = 148, + JSVERSION_1_5 = 150, + JSVERSION_DEFAULT = 0, + JSVERSION_UNKNOWN = -1 +} JSVersion; + +#define JSVERSION_IS_ECMA(version) \ + ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) + +/* Result of typeof operator enumeration. */ +typedef enum JSType { + JSTYPE_VOID, /* undefined */ + JSTYPE_OBJECT, /* object */ + JSTYPE_FUNCTION, /* function */ + JSTYPE_STRING, /* string */ + JSTYPE_NUMBER, /* number */ + JSTYPE_BOOLEAN, /* boolean */ + JSTYPE_LIMIT +} JSType; + +/* JSObjectOps.checkAccess mode enumeration. */ +typedef enum JSAccessMode { + JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ + JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ + JSACC_IMPORT = 2, /* import foo.bar */ + JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ + JSACC_READ = 4, /* a "get" of foo.bar */ + JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ + JSACC_LIMIT +} JSAccessMode; + +#define JSACC_TYPEMASK (JSACC_WRITE - 1) + +/* + * This enum type is used to control the behavior of a JSObject property + * iterator function that has type JSNewEnumerate. + */ +typedef enum JSIterateOp { + JSENUMERATE_INIT, /* Create new iterator state */ + JSENUMERATE_NEXT, /* Iterate once */ + JSENUMERATE_DESTROY /* Destroy iterator state */ +} JSIterateOp; + +/* Struct typedefs. */ +typedef struct JSClass JSClass; +typedef struct JSConstDoubleSpec JSConstDoubleSpec; +typedef struct JSContext JSContext; +typedef struct JSErrorReport JSErrorReport; +typedef struct JSFunction JSFunction; +typedef struct JSFunctionSpec JSFunctionSpec; +typedef struct JSIdArray JSIdArray; +typedef struct JSProperty JSProperty; +typedef struct JSPropertySpec JSPropertySpec; +typedef struct JSObject JSObject; +typedef struct JSObjectMap JSObjectMap; +typedef struct JSObjectOps JSObjectOps; +typedef struct JSRuntime JSRuntime; +typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ +typedef struct JSScript JSScript; +typedef struct JSString JSString; +typedef struct JSXDRState JSXDRState; +typedef struct JSExceptionState JSExceptionState; +typedef struct JSLocaleCallbacks JSLocaleCallbacks; + +/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ + +/* + * Add, delete, get or set a property named by id in obj. Note the jsval id + * type -- id may be a string (Unicode property identifier) or an int (element + * index). The *vp out parameter, on success, is the new property value after + * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff + * obj[id] can't be deleted (because it's permanent). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, + jsval *vp); + +/* + * This function type is used for callbacks that enumerate the properties of + * a JSObject. The behavior depends on the value of enum_op: + * + * JSENUMERATE_INIT + * A new, opaque iterator state should be allocated and stored in *statep. + * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). + * + * The number of properties that will be enumerated should be returned as + * an integer jsval in *idp, if idp is non-null, and provided the number of + * enumerable properties is known. If idp is non-null and the number of + * enumerable properties can't be computed in advance, *idp should be set + * to JSVAL_ZERO. + * + * JSENUMERATE_NEXT + * A previously allocated opaque iterator state is passed in via statep. + * Return the next jsid in the iteration using *idp. The opaque iterator + * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL + * if there are no properties left to enumerate. + * + * JSENUMERATE_DESTROY + * Destroy the opaque iterator state previously allocated in *statep by a + * call to this function when enum_op was JSENUMERATE_INIT. + * + * The return value is used to indicate success, with a value of JS_FALSE + * indicating failure. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp); + +/* + * The old-style JSClass.enumerate op should define all lazy properties not + * yet reflected in obj. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); + +/* + * Resolve a lazy property named by id in obj by defining it directly in obj. + * Lazy properties are those reflected from some peer native property space + * (e.g., the DOM attributes for a given node reflected as obj) on demand. + * + * JS looks for a property in an object, and if not found, tries to resolve + * the given id. If resolve succeeds, the engine looks again in case resolve + * defined obj[id]. If no such property exists directly in obj, the process + * is repeated with obj's prototype, etc. + * + * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); + +/* + * Like JSResolveOp, but flags provide contextual information as follows: + * + * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id + * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment + * + * The *objp out parameter, on success, should be null to indicate that id + * was not resolved; and non-null, referring to obj or one of its prototypes, + * if id was resolved. + * + * This hook instead of JSResolveOp is called via the JSClass.resolve member + * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. + * + * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further + * extends this hook by passing in the starting object on the prototype chain + * via *objp. Thus a resolve hook implementation may define the property id + * being resolved in the object in which the id was first sought, rather than + * in a prototype object whose class led to the resolve hook being called. + * + * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore + * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no + * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. + * This is not good practice, but enough existing hook implementations count + * on it that we can't break compatibility by passing the starting object in + * *objp without a new JSClass flag. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, + uintN flags, JSObject **objp); + +/* + * Convert obj to the given type, returning true with the resulting value in + * *vp on success, and returning false on error or exception. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, + jsval *vp); + +/* + * Finalize obj, which the garbage collector has determined to be unreachable + * from other live objects or from GC roots. Obviously, finalizers must never + * store a reference to obj. + */ +typedef void +(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); + +/* + * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer + * to extend and reduce the set of string types finalized by the GC. + */ +typedef void +(* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); + +/* + * The signature for JSClass.getObjectOps, used by JS_NewObject's internals + * to discover the set of high-level object operations to use for new objects + * of the given class. All native objects have a JSClass, which is stored as + * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, + * all native and host objects have a JSObjectMap at obj->map, which may be + * shared among a number of objects, and which contains the JSObjectOps *ops + * pointer used to dispatch object operations from API calls. + * + * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level + * interface to class-specific code and data, while JSObjectOps allows for a + * higher level of operation, which does not use the object's class except to + * find the class's JSObjectOps struct, by calling clasp->getObjectOps. + * + * If this seems backwards, that's because it is! API compatibility requires + * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not + * need to implement the larger JSObjectOps, and can share the common JSScope + * code and data used by the native (js_ObjectOps, see jsobj.c) ops. + */ +typedef JSObjectOps * +(* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); + +/* + * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, + * returning false on error/exception, true on success with obj[id]'s last-got + * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id + * is either a string or an int jsval. + * + * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes + * a jsid (a tagged int or aligned, unique identifier pointer) rather than a + * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's + * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may + * specialize access checks. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp); + +/* + * Encode or decode an object, given an XDR state record representing external + * data. See jsxdrapi.h. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); + +/* + * Check whether v is an instance of obj. Return false on error or exception, + * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in + * *bp otherwise. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +/* + * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to + * scan live GC-things reachable from obj's private data structure. For each + * such thing, a mark implementation must call + * + * JS_MarkGCThing(cx, thing, name, arg); + * + * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap + * dumping and ref-path tracing. The mark function should pass a (typically + * literal) string naming the private data member for name, and it must pass + * the opaque arg parameter through from its caller. + * + * For the JSObjectOps.mark hook, the return value is the number of slots at + * obj->slots to scan. For JSClass.mark, the return value is ignored. + * + * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject + * called from a mark function will fail silently, e.g.). + */ +typedef uint32 +(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); + +/* JSObjectOps function pointer typedefs. */ + +/* + * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops + * members initialized from the same-named parameters, and with the nslots and + * freeslot members initialized according to ops and clasp. Return null on + * error, non-null on success. + * + * JSObjectMaps are reference-counted by generic code in the engine. Usually, + * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref + * returned to the caller on success. After a successful construction, some + * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs + * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will + * be called to dispose of the map. + */ +typedef JSObjectMap * +(* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, + JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +/* + * Generic type for an infallible JSObjectMap operation, used currently by + * JSObjectOps.destroyObjectMap. + */ +typedef void +(* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); + +/* + * Look for id in obj and its prototype chain, returning false on error or + * exception, true on success. On success, return null in *propp if id was + * not found. If id was found, return the first object searching from obj + * along its prototype chain in which id names a direct property in *objp, and + * return a non-null, opaque property pointer in *propp. + * + * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer + * may be passed as the prop parameter to a JSAttributesOp, as a short-cut + * that bypasses id re-lookup. In any case, a non-null *propp result after a + * successful lookup must be dropped via JSObjectOps.dropProperty. + * + * NB: successful return with non-null *propp means the implementation may + * have locked *objp and added a reference count associated with *propp, so + * callers should not risk deadlock by nesting or interleaving other lookups + * or any obj-bearing ops before dropping *propp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#endif + ); + +/* + * Define obj[id], a direct property of obj named id, having the given initial + * value, with the specified getter, setter, and attributes. If the propp out + * param is non-null, *propp on successful return contains an opaque property + * pointer usable as a speedup hint with JSAttributesOp. But note that propp + * may be null, indicating that the caller is not interested in recovering an + * opaque pointer to the newly-defined property. + * + * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure + * to drop *propp using JSObjectOps.dropProperty in short order, just as with + * JSLookupPropOp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, + jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs, JSProperty **propp); + +/* + * Get, set, or delete obj[id], returning false on error or exception, true + * on success. If getting or setting, the new value is returned in *vp on + * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is + * permanent, and JSVAL_TRUE if id named a direct property of obj that was in + * fact deleted, or if id names no direct property of obj (id could name a + * prototype property, or no property in obj or its prototype chain). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +/* + * Get or set attributes of the property obj[id]. Return false on error or + * exception, true with current attributes in *attrsp. If prop is non-null, + * it must come from the *propp out parameter of a prior JSDefinePropOp or + * JSLookupPropOp call. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, + JSProperty *prop, uintN *attrsp); + +/* + * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per + * mode, returning false on error/exception, true on success with obj[id]'s + * last-got value in *vp, and its attributes in *attrsp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, + JSAccessMode mode, jsval *vp, + uintN *attrsp); + +/* + * A generic type for functions mapping an object to another object, or null + * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject + * at present. + */ +typedef JSObject * +(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); + +/* + * A generic type for functions taking a context, object, and property, with + * no return value. Used by JSObjectOps.dropProperty currently (see above, + * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which + * dropProperty participates). + */ +typedef void +(* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, + JSProperty *prop); + +/* + * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These + * hooks must check for cycles without deadlocking, and otherwise take special + * steps. See jsobj.c, js_SetProtoOrParent, for an example. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, JSObject *pobj); + +/* + * Get and set a required slot, one that should already have been allocated. + * These operations are infallible, so required slots must be pre-allocated, + * or implementations must suppress out-of-memory errors. The native ops + * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to + * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. + * + * NB: the slot parameter is a zero-based index into obj->slots[], unlike the + * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry + * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) + * reserved slots that come after the initial well-known slots: proto, parent, + * class, and optionally, the private data slot. + */ +typedef jsval +(* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot); + +typedef void +(* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, jsval v); + +/* Typedef for native functions called by the JS VM. */ + +typedef JSBool +(* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval); + +/* Callbacks and their arguments. */ + +typedef enum JSGCStatus { + JSGC_BEGIN, + JSGC_END, + JSGC_MARK_END, + JSGC_FINALIZE_END +} JSGCStatus; + +typedef JSBool +(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); + +typedef JSBool +(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); + +typedef void +(* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, + JSErrorReport *report); + +typedef struct JSErrorFormatString { + const char *format; + uintN argCount; +} JSErrorFormatString; + +typedef const JSErrorFormatString * +(* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, + const uintN errorNumber); + +#ifdef va_start +#define JS_ARGUMENT_FORMATTER_DEFINED 1 + +typedef JSBool +(* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, + JSBool fromJS, jsval **vpp, + va_list *app); +#endif + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, + JSString *src1, JSString *src2, + jsval *rval); + +/* + * Security protocol types. + */ +typedef struct JSPrincipals JSPrincipals; + +/* + * XDR-encode or -decode a principals instance, based on whether xdr->mode is + * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, + * in which case implementations must return a held (via JSPRINCIPALS_HOLD), + * non-null *principalsp out parameter. Return true on success, false on any + * error, which the implementation must have reported. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, + JSPrincipals **principalsp); + +/* + * Return a weak reference to the principals associated with obj, possibly via + * the immutable parent chain leading from obj to a top-level container (e.g., + * a window object in the DOM level 0). If there are no principals associated + * with obj, return null. Therefore null does not mean an error was reported; + * in no event should an error be reported or an exception be thrown by this + * callback's implementation. + */ +typedef JSPrincipals * +(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jspubtd_h___ */ diff --git a/src/extension/script/js/jsregexp.c b/src/extension/script/js/jsregexp.c new file mode 100644 index 000000000..18ab92c35 --- /dev/null +++ b/src/extension/script/js/jsregexp.c @@ -0,0 +1,3773 @@ +/* -*- 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 regular expressions, after Perl. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.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 "jsconfig.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscan.h" +#include "jsstr.h" + +#ifdef XP_MAC +#include +#endif + +#if JS_HAS_REGEXPS + +/* Note : contiguity of 'simple opcodes' is important for simpleMatch() */ +typedef enum REOp { + REOP_EMPTY = 0, /* match rest of input against rest of r.e. */ + REOP_ALT = 1, /* alternative subexpressions in kid and next */ + REOP_SIMPLE_START = 2, /* start of 'simple opcodes' */ + REOP_BOL = 2, /* beginning of input (or line if multiline) */ + REOP_EOL = 3, /* end of input (or line if multiline) */ + REOP_WBDRY = 4, /* match "" at word boundary */ + REOP_WNONBDRY = 5, /* match "" at word non-boundary */ + REOP_DOT = 6, /* stands for any character */ + REOP_DIGIT = 7, /* match a digit char: [0-9] */ + REOP_NONDIGIT = 8, /* match a non-digit char: [^0-9] */ + REOP_ALNUM = 9, /* match an alphanumeric char: [0-9a-z_A-Z] */ + REOP_NONALNUM = 10, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */ + REOP_SPACE = 11, /* match a whitespace char */ + REOP_NONSPACE = 12, /* match a non-whitespace char */ + REOP_BACKREF = 13, /* back-reference (e.g., \1) to a parenthetical */ + REOP_FLAT = 14, /* match a flat string */ + REOP_FLAT1 = 15, /* match a single char */ + REOP_FLATi = 16, /* case-independent REOP_FLAT */ + REOP_FLAT1i = 17, /* case-independent REOP_FLAT1 */ + REOP_UCFLAT1 = 18, /* single Unicode char */ + REOP_UCFLAT1i = 19, /* case-independent REOP_UCFLAT1 */ + REOP_UCFLAT = 20, /* flat Unicode string; len immediate counts chars */ + REOP_UCFLATi = 21, /* case-independent REOP_UCFLAT */ + REOP_CLASS = 22, /* character class with index */ + REOP_NCLASS = 23, /* negated character class with index */ + REOP_SIMPLE_END = 23, /* end of 'simple opcodes' */ + REOP_QUANT = 25, /* quantified atom: atom{1,2} */ + REOP_STAR = 26, /* zero or more occurrences of kid */ + REOP_PLUS = 27, /* one or more occurrences of kid */ + REOP_OPT = 28, /* optional subexpression in kid */ + REOP_LPAREN = 29, /* left paren bytecode: kid is u.num'th sub-regexp */ + REOP_RPAREN = 30, /* right paren bytecode */ + REOP_JUMP = 31, /* for deoptimized closure loops */ + REOP_DOTSTAR = 32, /* optimize .* to use a single opcode */ + REOP_ANCHOR = 33, /* like .* but skips left context to unanchored r.e. */ + REOP_EOLONLY = 34, /* $ not preceded by any pattern */ + REOP_BACKREFi = 37, /* case-independent REOP_BACKREF */ + REOP_LPARENNON = 41, /* non-capturing version of REOP_LPAREN */ + REOP_ASSERT = 43, /* zero width positive lookahead assertion */ + REOP_ASSERT_NOT = 44, /* zero width negative lookahead assertion */ + REOP_ASSERTTEST = 45, /* sentinel at end of assertion child */ + REOP_ASSERTNOTTEST = 46, /* sentinel at end of !assertion child */ + REOP_MINIMALSTAR = 47, /* non-greedy version of * */ + REOP_MINIMALPLUS = 48, /* non-greedy version of + */ + REOP_MINIMALOPT = 49, /* non-greedy version of ? */ + REOP_MINIMALQUANT = 50, /* non-greedy version of {} */ + REOP_ENDCHILD = 51, /* sentinel at end of quantifier child */ + REOP_REPEAT = 52, /* directs execution of greedy quantifier */ + REOP_MINIMALREPEAT = 53, /* directs execution of non-greedy quantifier */ + REOP_ALTPREREQ = 54, /* prerequisite for ALT, either of two chars */ + REOP_ALTPREREQ2 = 55, /* prerequisite for ALT, a char or a class */ + REOP_ENDALT = 56, /* end of final alternate */ + REOP_CONCAT = 57, /* concatenation of terms (parse time only) */ + + REOP_END +} REOp; + +#define REOP_IS_SIMPLE(op) (((op) >= REOP_SIMPLE_START) && ((op) <= REOP_SIMPLE_END)) + +struct RENode { + REOp op; /* r.e. op bytecode */ + RENode *next; /* next in concatenation order */ + void *kid; /* first operand */ + union { + void *kid2; /* second operand */ + jsint num; /* could be a number */ + jsint parenIndex; /* or a parenthesis index */ + struct { /* or a quantifier range */ + uint16 min; + uint16 max; + JSBool greedy; + } range; + struct { /* or a character class */ + uint16 startIndex; + uint16 kidlen; /* length of string at kid, in jschars */ + uint16 bmsize; /* bitmap size, based on max char code */ + uint16 index; /* index into class list */ + JSBool sense; + } ucclass; + struct { /* or a literal sequence */ + jschar chr; /* of one character */ + uint16 length; /* or many (via the kid) */ + } flat; + struct { + RENode *kid2; /* second operand from ALT */ + jschar ch1; /* match char for ALTPREREQ */ + jschar ch2; /* ditto, or class index for ALTPREREQ2 */ + } altprereq; + } u; +}; + +#define RE_IS_LETTER(c) ( ((c >= 'A') && (c <= 'Z')) || \ + ((c >= 'a') && (c <= 'z')) ) +#define RE_IS_LINE_TERM(c) ( (c == '\n') || (c == '\r') || \ + (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) + +#define CLASS_CACHE_SIZE (4) +typedef struct CompilerState { + JSContext *context; + JSTokenStream *tokenStream; /* For reporting errors */ + const jschar *cpbegin; + const jschar *cpend; + const jschar *cp; + uintN flags; + uint16 parenCount; + uint16 classCount; /* number of [] encountered */ + size_t progLength; /* estimated bytecode length */ + uintN treeDepth; /* maximum depth of parse tree */ + RENode *result; + struct { + const jschar *start; /* small cache of class strings */ + uint16 length; /* since they're often the same */ + uint16 index; + } classCache[CLASS_CACHE_SIZE]; +} CompilerState; + +typedef struct RECapture { + int32 index; /* start of contents, -1 for empty */ + uint16 length; /* length of capture */ +} RECapture; + +typedef struct REMatchState { + const jschar *cp; + RECapture parens[1]; /* first of 're->parenCount' captures, + * allocated at end of this struct. + */ +} REMatchState; + +struct REBackTrackData; + +typedef struct REProgState { + jsbytecode *continue_pc; /* current continuation data */ + jsbytecode continue_op; + uint16 index; /* progress in text */ + uintN parenSoFar; /* highest indexed paren started */ + union { + struct { + uint16 min; /* current quantifier limits */ + uint16 max; + } quantifier; + struct { + size_t top; /* backtrack stack state */ + size_t sz; + } assertion; + } u; +} REProgState; + +typedef struct REBackTrackData { + size_t sz; /* size of previous stack entry */ + jsbytecode *backtrack_pc; /* where to backtrack to */ + jsbytecode backtrack_op; + const jschar *cp; /* index in text of match at backtrack */ + intN parenIndex; /* start index of saved paren contents */ + uint16 parenCount; /* # of saved paren contents */ + uint16 precedingStateTop; /* number of parent states */ + /* saved parent states follow */ + /* saved paren contents follow */ +} REBackTrackData; + +#define INITIAL_STATESTACK (100) +#define INITIAL_BACKTRACK (8000) + +typedef struct REGlobalData { + JSContext *cx; + JSRegExp *regexp; /* the RE in execution */ + JSBool ok; /* runtime error (out_of_memory only?) */ + size_t start; /* offset to start at */ + ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ + const jschar *cpbegin, *cpend; /* text base address and limit */ + + REProgState *stateStack; /* stack of state of current parents */ + uint16 stateStackTop; + uint16 maxStateStack; + + REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ + REBackTrackData *backTrackSP; + size_t maxBackTrack; + size_t cursz; /* size of current stack entry */ + + JSArenaPool pool; /* I don't understand but it's faster to + * use this than to malloc/free the three + * items that are allocated from this pool + */ + +} REGlobalData; + + +/* + * 1. If IgnoreCase is false, return ch. + * 2. Let u be ch converted to upper case as if by calling + * String.prototype.toUpperCase on the one-character string ch. + * 3. If u does not consist of a single character, return ch. + * 4. Let cu be u's character. + * 5. If ch's code point value is greater than or equal to decimal 128 and cu's + * code point value is less than decimal 128, then return ch. + * 6. Return cu. + */ +static jschar +upcase(jschar ch) +{ + jschar cu = JS_TOUPPER(ch); + if ((ch >= 128) && (cu < 128)) return ch; + return cu; +} + +static jschar +downcase(jschar ch) +{ + jschar cl = JS_TOLOWER(ch); + if ((cl >= 128) && (ch < 128)) return ch; + return cl; +} + +/* Construct and initialize an RENode, returning NULL for out-of-memory */ +static RENode * +NewRENode(CompilerState *state, REOp op) +{ + JSContext *cx; + RENode *ren; + + cx = state->context; + JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren); + if (!ren) { + JS_ReportOutOfMemory(cx); + return NULL; + } + ren->op = op; + ren->next = NULL; + ren->kid = NULL; + return ren; +} + +/* + * Validates and converts hex ascii value. + */ +static JSBool +isASCIIHexDigit(jschar c, uintN *digit) +{ + uintN cv = c; + + if (cv < '0') + return JS_FALSE; + if (cv <= '9') { + *digit = cv - '0'; + return JS_TRUE; + } + cv |= 0x20; + if (cv >= 'a' && cv <= 'f') { + *digit = cv - 'a' + 10; + return JS_TRUE; + } + return JS_FALSE; +} + + +typedef struct { + REOp op; + const jschar *errPos; + uint16 parenIndex; +} REOpData; + + +/* + * Process the op against the two top operands, reducing them to a single + * operand in the penultimate slot. Update progLength and treeDepth. + */ +static JSBool +processOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN operandSP) +{ + RENode *result; + + switch (opData->op) { + case REOP_ALT: + result = NewRENode(state, REOP_ALT); + if (!result) + return JS_FALSE; + result->kid = operandStack[operandSP - 2]; + result->u.kid2 = operandStack[operandSP - 1]; + operandStack[operandSP - 2] = result; + /* + * look at both alternates to see if there's a FLAT or a CLASS at + * the start of each. If so, use a prerequisite match + */ + ++state->treeDepth; + if ((((RENode *)(result->kid))->op == REOP_FLAT) + && (((RENode *)(result->u.kid2))->op == REOP_FLAT) + && ((state->flags & JSREG_FOLD) == 0) ) { + result->op = REOP_ALTPREREQ; + result->u.altprereq.ch1 + = ((RENode *)(result->kid))->u.flat.chr; + result->u.altprereq.ch2 + = ((RENode *)(result->u.kid2))->u.flat.chr; + /* ALTPREREQ, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if ((((RENode *)(result->kid))->op == REOP_CLASS) + && (((RENode *)(result->kid))->u.ucclass.index < 256) + && (((RENode *)(result->u.kid2))->op == REOP_FLAT) + && ((state->flags & JSREG_FOLD) == 0) ) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 + = ((RENode *)(result->u.kid2))->u.flat.chr; + result->u.altprereq.ch2 + = ((RENode *)(result->kid))->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if ((((RENode *)(result->kid))->op == REOP_FLAT) + && (((RENode *)(result->u.kid2))->op == REOP_CLASS) + && (((RENode *)(result->u.kid2))->u.ucclass.index < 256) + && ((state->flags & JSREG_FOLD) == 0) ) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 + = ((RENode *)(result->kid))->u.flat.chr; + result->u.altprereq.ch2 + = ((RENode *)(result->u.kid2))->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + /* ALT, , ..., JUMP, ... ENDALT */ + state->progLength += 7; + break; + case REOP_CONCAT: + result = operandStack[operandSP - 2]; + while (result->next) + result = result->next; + result->next = operandStack[operandSP - 1]; + break; + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPARENNON: + case REOP_LPAREN: + /* These should have been processed by a close paren. */ + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_MISSING_PAREN, opData->errPos); + return JS_FALSE; + default:; + } + return JS_TRUE; +} + +/* + * Parser forward declarations. + */ +static JSBool parseTerm(CompilerState *state); +static JSBool parseQuantifier(CompilerState *state); + +/* + * Top-down regular expression grammar, based closely on Perl4. + * + * regexp: altern A regular expression is one or more + * altern '|' regexp alternatives separated by vertical bar. + */ + +#define INITIAL_STACK_SIZE (128) +static JSBool +parseRegExp(CompilerState *state) +{ + uint16 parenIndex; + RENode *operand; + REOpData *operatorStack; + RENode **operandStack; + REOp op; + intN i; + JSBool result = JS_FALSE; + + intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; + intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; + + /* Watch out for empty regexp */ + if (state->cp == state->cpend) { + state->result = NewRENode(state, REOP_EMPTY); + return (state->result != NULL); + } + + operatorStack = (REOpData *)JS_malloc(state->context, + sizeof(REOpData) * operatorStackSize); + if (!operatorStack) + return JS_FALSE; + + operandStack = (RENode **)JS_malloc(state->context, + sizeof(RENode *) * operandStackSize); + if (!operandStack) + goto out; + + + while (JS_TRUE) { + if (state->cp == state->cpend) { + /* + * If we are at the end of the regexp and we're short an operand, + * the regexp must have the form /x|/ or some such. + */ + if (operatorSP == operandSP) { + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + } + } else { + switch (*state->cp) { + /* balance '(' */ + case '(': /* balance ')' */ + ++state->cp; + if ((state->cp < state->cpend) && (*state->cp == '?') + && ( (state->cp[1] == '=') + || (state->cp[1] == '!') + || (state->cp[1] == ':') )) { + ++state->cp; + if (state->cp == state->cpend) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_MISSING_PAREN); + goto out; + } + switch (*state->cp++) { + case '=': + op = REOP_ASSERT; + /* ASSERT, , ... ASSERTTEST */ + state->progLength += 4; + break; + case '!': + op = REOP_ASSERT_NOT; + /* ASSERTNOT, , ... ASSERTNOTTEST */ + state->progLength += 4; + break; + case ':': + op = REOP_LPARENNON; + break; + } + parenIndex = state->parenCount; + } + else { + op = REOP_LPAREN; + /* LPAREN, , ... RPAREN, */ + state->progLength += 6; + parenIndex = state->parenCount++; + if (state->parenCount == 65535) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_TOO_MANY_PARENS); + goto out; + } + } + goto pushOperator; + case ')': + /* If there's not a stacked open parenthesis, throw + * a syntax error. + */ + for (i = operatorSP - 1; i >= 0; i--) + if ((operatorStack[i].op == REOP_ASSERT) + || (operatorStack[i].op == REOP_ASSERT_NOT) + || (operatorStack[i].op == REOP_LPARENNON) + || (operatorStack[i].op == REOP_LPAREN)) + break; + if (i == -1) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + /* fall thru... */ + case '|': + /* Expected an operand before these, so make an empty one */ + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + default: + if (!parseTerm(state)) + goto out; + operand = state->result; +pushOperand: + if (operandSP == operandStackSize) { + operandStackSize += operandStackSize; + operandStack = + (RENode **)JS_realloc(state->context, operandStack, + sizeof(RENode *) * operandStackSize); + if (!operandStack) + goto out; + } + operandStack[operandSP++] = operand; + break; + } + } + /* At the end; process remaining operators */ +restartOperator: + if (state->cp == state->cpend) { + while (operatorSP) { + --operatorSP; + if (!processOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + } + JS_ASSERT(operandSP == 1); + state->result = operandStack[0]; + result = JS_TRUE; + goto out; + } + switch (*state->cp) { + case '|': + /* Process any stacked 'concat' operators */ + ++state->cp; + while (operatorSP + && (operatorStack[operatorSP - 1].op == REOP_CONCAT)) { + --operatorSP; + if (!processOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + } + op = REOP_ALT; + goto pushOperator; + + case ')': + /* If there's not a stacked open parenthesis,we + * accept the close as a flat. + */ + for (i = operatorSP - 1; i >= 0; i--) + if ((operatorStack[i].op == REOP_ASSERT) + || (operatorStack[i].op == REOP_ASSERT_NOT) + || (operatorStack[i].op == REOP_LPARENNON) + || (operatorStack[i].op == REOP_LPAREN)) + break; + if (i == -1) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + ++state->cp; + /* process everything on the stack until the open */ + while (JS_TRUE) { + JS_ASSERT(operatorSP); + --operatorSP; + switch (operatorStack[operatorSP].op) { + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPAREN: + operand = NewRENode(state, operatorStack[operatorSP].op); + if (!operand) + goto out; + operand->u.parenIndex + = operatorStack[operatorSP].parenIndex; + JS_ASSERT(operandSP); + operand->kid = operandStack[operandSP - 1]; + operandStack[operandSP - 1] = operand; + ++state->treeDepth; + /* fall thru... */ + case REOP_LPARENNON: + state->result = operandStack[operandSP - 1]; + if (!parseQuantifier(state)) + goto out; + operandStack[operandSP - 1] = state->result; + goto restartOperator; + default: + if (!processOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + break; + } + } + break; + default: + /* Anything else is the start of the next term */ + op = REOP_CONCAT; +pushOperator: + if (operatorSP == operatorStackSize) { + operatorStackSize += operatorStackSize; + operatorStack = + (REOpData *)JS_realloc(state->context, operatorStack, + sizeof(REOpData) * operatorStackSize); + if (!operatorStack) + goto out; + } + operatorStack[operatorSP].op = op; + operatorStack[operatorSP].errPos = state->cp; + operatorStack[operatorSP++].parenIndex = parenIndex; + break; + } + } +out: + if (operatorStack) + JS_free(state->context, operatorStack); + if (operandStack) + JS_free(state->context, operandStack); + return result; +} + +/* + * Extract and return a decimal value at state->cp, the + * initial character 'c' has already been read. + */ +static intN +getDecimalValue(jschar c, CompilerState *state) +{ + intN value = JS7_UNDEC(c); + while (state->cp < state->cpend) { + c = *state->cp; + if (!JS7_ISDEC(c)) + break; + value = (10 * value) + JS7_UNDEC(c); + ++state->cp; + } + return value; +} + +/* + * Calculate the total size of the bitmap required for a class expression. + */ +static JSBool +calculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, + const jschar *end) +{ + jschar rangeStart, c; + uintN n, digit, nDigits, i; + uintN max = 0; + JSBool inRange = JS_FALSE; + + target->u.ucclass.bmsize = 0; + target->u.ucclass.sense = JS_TRUE; + + if (src == end) + return JS_TRUE; + + if (*src == '^') { + ++src; + target->u.ucclass.sense = JS_FALSE; + } + + while (src != end) { + uintN localMax = 0; + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + localMax = 0x8; + break; + case 'f': + localMax = 0xC; + break; + case 'n': + localMax = 0xA; + break; + case 'r': + localMax = 0xD; + break; + case 't': + localMax = 0x9; + break; + case 'v': + localMax = 0xB; + break; + case 'c': + if (((src + 1) < end) && RE_IS_LETTER(src[1])) + localMax = (jschar)(*src++ & 0x1F); + else + localMax = '\\'; + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original + *'\' as a literal. + */ + src -= (i + 1); + n = '\\'; + break; + } + n = (n << 4) | digit; + } + localMax = n; + break; + case 'd': + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return JS_FALSE; + } + localMax = '9'; + break; + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return JS_FALSE; + } + target->u.ucclass.bmsize = 65535; + return JS_TRUE; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + * + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + localMax = n; + break; + + default: + localMax = c; + break; + } + break; + default: + localMax = *src++; + break; + } + if (inRange) { + if (rangeStart > localMax) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return JS_FALSE; + } + inRange = JS_FALSE; + } + else { + if (src < (end - 1)) { + if (*src == '-') { + ++src; + inRange = JS_TRUE; + rangeStart = (jschar)localMax; + continue; + } + } + } + if (state->flags & JSREG_FOLD) { + c = JS_MAX(upcase((jschar)localMax), downcase((jschar)localMax)); + if (c > localMax) + localMax = c; + } + if (localMax > max) + max = localMax; + } + target->u.ucclass.bmsize = max; + return JS_TRUE; +} + +/* + * item: assertion An item is either an assertion or + * quantatom a quantified atom. + * + * assertion: '^' Assertions match beginning of string + * (or line if the class static property + * RegExp.multiline is true). + * '$' End of string (or line if the class + * static property RegExp.multiline is + * true). + * '\b' Word boundary (between \w and \W). + * '\B' Word non-boundary. + * + * quantatom: atom An unquantified atom. + * quantatom '{' n ',' m '}' + * Atom must occur between n and m times. + * quantatom '{' n ',' '}' Atom must occur at least n times. + * quantatom '{' n '}' Atom must occur exactly n times. + * quantatom '*' Zero or more times (same as {0,}). + * quantatom '+' One or more times (same as {1,}). + * quantatom '?' Zero or one time (same as {0,1}). + * + * any of which can be optionally followed by '?' for ungreedy + * + * atom: '(' regexp ')' A parenthesized regexp (what matched + * can be addressed using a backreference, + * see '\' n below). + * '.' Matches any char except '\n'. + * '[' classlist ']' A character class. + * '[' '^' classlist ']' A negated character class. + * '\f' Form Feed. + * '\n' Newline (Line Feed). + * '\r' Carriage Return. + * '\t' Horizontal Tab. + * '\v' Vertical Tab. + * '\d' A digit (same as [0-9]). + * '\D' A non-digit. + * '\w' A word character, [0-9a-z_A-Z]. + * '\W' A non-word character. + * '\s' A whitespace character, [ \b\f\n\r\t\v]. + * '\S' A non-whitespace character. + * '\' n A backreference to the nth (n decimal + * and positive) parenthesized expression. + * '\' octal An octal escape sequence (octal must be + * two or three digits long, unless it is + * 0 for the null character). + * '\x' hex A hex escape (hex must be two digits). + * '\u' unicode A unicode escape (must be four digits). + * '\c' ctrl A control character, ctrl is a letter. + * '\' literalatomchar Any character except one of the above + * that follow '\' in an atom. + * otheratomchar Any character not first among the other + * atom right-hand sides. + */ +static JSBool +parseTerm(CompilerState *state) +{ + jschar c = *state->cp++; + uintN nDigits; + uintN num, tmp, n, i; + const jschar *termStart; + JSBool foundCachedCopy; + + switch (c) { + /* assertions and atoms */ + case '^': + state->result = NewRENode(state, REOP_BOL); + if (!state->result) + return JS_FALSE; + state->progLength++; + return JS_TRUE; + case '$': + state->result = NewRENode(state, REOP_EOL); + if (!state->result) + return JS_FALSE; + state->progLength++; + return JS_TRUE; + case '\\': + if (state->cp >= state->cpend) { + /* a trailing '\' is an error */ + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_TRAILING_SLASH); + return JS_FALSE; + } + c = *state->cp++; + switch (c) { + /* assertion escapes */ + case 'b' : + state->result = NewRENode(state, REOP_WBDRY); + if (!state->result) + return JS_FALSE; + state->progLength++; + return JS_TRUE; + case 'B': + state->result = NewRENode(state, REOP_WNONBDRY); + if (!state->result) + return JS_FALSE; + state->progLength++; + return JS_TRUE; + /* Decimal escape */ + case '0': + if (JS_HAS_STRICT_OPTION(state->context)) + c = 0; + else { + doOctal: + num = 0; + while (state->cp < state->cpend) { + if ('0' <= (c = *state->cp) && c <= '7') { + state->cp++; + tmp = 8 * num + (uintN)JS7_UNDEC(c); + if (tmp > 0377) + break; + num = tmp; + } + else + break; + } + c = (jschar)(num); + } + doFlat: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return JS_FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->progLength += 3; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + termStart = state->cp - 1; + num = (uintN)getDecimalValue(c, state); + if (num > 9 && + num > state->parenCount && + !JS_HAS_STRICT_OPTION(state->context)) { + state->cp = termStart; + goto doOctal; + } + state->result = NewRENode(state, REOP_BACKREF); + if (!state->result) + return JS_FALSE; + state->result->u.parenIndex = num - 1; + state->progLength += 3; + break; + /* Control escape */ + case 'f': + c = 0xC; + goto doFlat; + case 'n': + c = 0xA; + goto doFlat; + case 'r': + c = 0xD; + goto doFlat; + case 't': + c = 0x9; + goto doFlat; + case 'v': + c = 0xB; + goto doFlat; + /* Control letter */ + case 'c': + if (((state->cp + 1) < state->cpend) && + RE_IS_LETTER(state->cp[1])) + c = (jschar)(*state->cp++ & 0x1F); + else { + /* back off to accepting the original '\' as a literal */ + --state->cp; + c = '\\'; + } + goto doFlat; + /* HexEscapeSequence */ + case 'x': + nDigits = 2; + goto lexHex; + /* UnicodeEscapeSequence */ + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; (i < nDigits) + && (state->cp < state->cpend); i++) { + uintN digit; + c = *state->cp++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * back off to accepting the original + * 'u' or 'x' as a literal + */ + state->cp -= (i + 2); + n = *state->cp++; + break; + } + n = (n << 4) | digit; + } + c = (jschar)(n); + goto doFlat; + /* Character class escapes */ + case 'd': + state->result = NewRENode(state, REOP_DIGIT); +doSimple: + if (!state->result) + return JS_FALSE; + state->progLength++; + break; + case 'D': + state->result = NewRENode(state, REOP_NONDIGIT); + goto doSimple; + case 's': + state->result = NewRENode(state, REOP_SPACE); + goto doSimple; + case 'S': + state->result = NewRENode(state, REOP_NONSPACE); + goto doSimple; + case 'w': + state->result = NewRENode(state, REOP_ALNUM); + goto doSimple; + case 'W': + state->result = NewRENode(state, REOP_NONALNUM); + goto doSimple; + /* IdentityEscape */ + default: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return JS_FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *)(state->cp - 1); + state->progLength += 3; + break; + } + break; + case '[': + state->result = NewRENode(state, REOP_CLASS); + if (!state->result) + return JS_FALSE; + termStart = state->cp; + state->result->u.ucclass.startIndex = termStart - state->cpbegin; + while (JS_TRUE) { + if (state->cp == state->cpend) { + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_UNTERM_CLASS, termStart); + return JS_FALSE; + } + if (*state->cp == '\\') + state->cp++; + else { + if (*state->cp == ']') { + state->result->u.ucclass.kidlen = state->cp - termStart; + break; + } + } + state->cp++; + } + foundCachedCopy = JS_FALSE; + for (i = 0; i < CLASS_CACHE_SIZE; i++) { + if (state->classCache[i].start) { + if (state->classCache[i].length == state->result->u.ucclass.kidlen) { + foundCachedCopy = JS_TRUE; + for (n = 0; n < state->classCache[i].length; n++) { + if (state->classCache[i].start[n] != termStart[n]) { + foundCachedCopy = JS_FALSE; + break; + } + } + if (foundCachedCopy) { + state->result->u.ucclass.index = state->classCache[i].index; + break; + } + } + } + else { + state->classCache[i].start = termStart; + state->classCache[i].length = state->result->u.ucclass.kidlen; + state->classCache[i].index = state->classCount; + break; + } + } + if (!foundCachedCopy) + state->result->u.ucclass.index = state->classCount++; + /* + * Call calculateBitmapSize now as we want any errors it finds + * to be reported during the parse phase, not at execution. + */ + if (!calculateBitmapSize(state, state->result, termStart, state->cp++)) + return JS_FALSE; + state->progLength += 3; /* CLASS, */ + break; + + case '.': + state->result = NewRENode(state, REOP_DOT); + goto doSimple; + case '*': + case '+': + case '?': + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp - 1); + return JS_FALSE; +#if 0 + case '{': /* balance '}' */ + /* Treat left-curly in a non-quantifier context as an error only + * if it's followed immediately by a decimal digit. + * This is an Perl extension. + */ + if ((state->cp != state->cpend) && JS7_ISDEC(*state->cp)) { + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp - 1); + return JS_FALSE; + } + /* fall thru... */ +#endif + default: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return JS_FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *)(state->cp - 1); + state->progLength += 3; + break; + } + return parseQuantifier(state); +} + +static JSBool +parseQuantifier(CompilerState *state) +{ + RENode *term; + term = state->result; + if (state->cp < state->cpend) { + switch (*state->cp) { + case '+': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JS_FALSE; + state->result->u.range.min = 1; + state->result->u.range.max = -1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '*': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JS_FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = -1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '?': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JS_FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = 1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '{': /* balance '}' */ + { + intN err; + intN min = 0; + intN max = -1; + jschar c; + const jschar *errp = state->cp++; + + c = *state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + min = getDecimalValue(c, state); + c = *state->cp; + + if ((min + 1) >> 16) { + err = JSMSG_MIN_TOO_BIG; + goto quantError; + } + if (c == ',') { + c = *++state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + max = getDecimalValue(c, state); + c = *state->cp; + if ((max + 1) >> 16) { + err = JSMSG_MAX_TOO_BIG; + goto quantError; + } + if (min > max) { + err = JSMSG_OUT_OF_ORDER; + goto quantError; + } + } + } + else { + max = min; + } + if (c == '}') { + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JS_FALSE; + state->result->u.range.min = min; + state->result->u.range.max = max; + /* QUANT, , , ... */ + state->progLength += 8; + goto quantifier; + } + } + state->cp = errp; + return JS_TRUE; +quantError: + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + err, errp); + return JS_FALSE; + } + } + } + return JS_TRUE; + +quantifier: + ++state->treeDepth; + ++state->cp; + state->result->kid = term; + if ((state->cp < state->cpend) && (*state->cp == '?')) { + ++state->cp; + state->result->u.range.greedy = JS_FALSE; + } + else + state->result->u.range.greedy = JS_TRUE; + return JS_TRUE; +} + +#define CHECK_OFFSET(diff) (JS_ASSERT(((diff) >= -32768) && ((diff) <= 32767))) +#define SET_OFFSET(pc,off) ((pc)[0] = JUMP_OFFSET_HI(off), \ + (pc)[1] = JUMP_OFFSET_LO(off)) +#define GET_OFFSET(pc) ((int16)(((pc)[0] << 8) | (pc)[1])) +#define OFFSET_LEN (2) +#define GET_ARG(pc) GET_OFFSET(pc) +#define SET_ARG(pc,arg) SET_OFFSET(pc,arg) +#define ARG_LEN OFFSET_LEN + +/* + * Recursively generate bytecode for the tree rooted at t. Iteratively. + */ + +typedef struct { + RENode *nextAlt; + jsbytecode *nextAltFixup, *nextTermFixup, *endTermFixup; + RENode *continueNode; + REOp continueOp; +} EmitStateStackEntry; + +static jsbytecode * +emitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, + jsbytecode *pc, RENode *t) +{ + ptrdiff_t diff; + RECharSet *charSet; + EmitStateStackEntry *emitStateSP, *emitStateStack = NULL; + REOp op; + + if (treeDepth) { + emitStateStack = + (EmitStateStackEntry *)JS_malloc(state->context, + sizeof(EmitStateStackEntry) + * treeDepth); + if (!emitStateStack) + return NULL; + } + emitStateSP = emitStateStack; + op = t->op; + + while (JS_TRUE) { + *pc++ = op; + switch (op) { + case REOP_EMPTY: + --pc; + break; + + case REOP_ALTPREREQ2: + case REOP_ALTPREREQ: + JS_ASSERT(emitStateSP); + emitStateSP->endTermFixup = pc; + pc += OFFSET_LEN; + SET_ARG(pc, t->u.altprereq.ch1); + pc += ARG_LEN; + SET_ARG(pc, t->u.altprereq.ch2); + pc += ARG_LEN; + + emitStateSP->nextAltFixup = pc; /* address of next alternate */ + pc += OFFSET_LEN; + + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + + case REOP_JUMP: + emitStateSP->nextTermFixup = pc; /* address of following term */ + pc += OFFSET_LEN; + diff = pc - emitStateSP->nextAltFixup; + CHECK_OFFSET(diff); + SET_OFFSET(emitStateSP->nextAltFixup, diff); + emitStateSP->continueOp = REOP_ENDALT; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->u.kid2); + op = t->op; + continue; + + case REOP_ENDALT: + diff = pc - emitStateSP->nextTermFixup; + CHECK_OFFSET(diff); + SET_OFFSET(emitStateSP->nextTermFixup, diff); + if (t->op != REOP_ALT) { + diff = pc - emitStateSP->endTermFixup; + CHECK_OFFSET(diff); + SET_OFFSET(emitStateSP->endTermFixup, diff); + } + break; + + case REOP_ALT: + JS_ASSERT(emitStateSP); + emitStateSP->nextAltFixup = pc; /* address of pointer to next alternate */ + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + + case REOP_FLAT: + /* + * Consecutize FLAT's if possible. + */ + if (t->kid) { + while (t->next && (t->next->op == REOP_FLAT) + && (((jschar*)(t->kid) + t->u.flat.length) + == (jschar*)(t->next->kid))) { + t->u.flat.length += t->next->u.flat.length; + t->next = t->next->next; + } + } + if (t->kid && (t->u.flat.length > 1)) { + if (state->flags & JSREG_FOLD) + pc[-1] = REOP_FLATi; + else + pc[-1] = REOP_FLAT; + SET_ARG(pc, (jschar *)(t->kid) - state->cpbegin); + pc += ARG_LEN; + SET_ARG(pc, t->u.flat.length); + pc += ARG_LEN; + } + else { + if (t->u.flat.chr < 256) { + if (state->flags & JSREG_FOLD) + pc[-1] = REOP_FLAT1i; + else + pc[-1] = REOP_FLAT1; + *pc++ = (jsbytecode)(t->u.flat.chr); + } + else { + if (state->flags & JSREG_FOLD) + pc[-1] = REOP_UCFLAT1i; + else + pc[-1] = REOP_UCFLAT1; + SET_ARG(pc, t->u.flat.chr); + pc += ARG_LEN; + } + } + break; + + case REOP_LPAREN: + JS_ASSERT(emitStateSP); + SET_ARG(pc, t->u.parenIndex); + pc += ARG_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_RPAREN; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + case REOP_RPAREN: + SET_ARG(pc, t->u.parenIndex); + pc += ARG_LEN; + break; + + case REOP_BACKREF: + SET_ARG(pc, t->u.parenIndex); + pc += ARG_LEN; + break; + case REOP_ASSERT: + JS_ASSERT(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTTEST; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + case REOP_ASSERTTEST: + case REOP_ASSERTNOTTEST: + diff = pc - emitStateSP->nextTermFixup; + CHECK_OFFSET(diff); + SET_OFFSET(emitStateSP->nextTermFixup, diff); + break; + case REOP_ASSERT_NOT: + JS_ASSERT(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTNOTTEST; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + case REOP_QUANT: + JS_ASSERT(emitStateSP); + if ((t->u.range.min == 0) && (t->u.range.max == (uint16)(-1))) + pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; + else + if ((t->u.range.min == 0) && (t->u.range.max == 1)) + pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; + else + if ((t->u.range.min == 1) && (t->u.range.max == (uint16)(-1))) + pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; + else { + if (!t->u.range.greedy) pc[-1] = REOP_MINIMALQUANT; + SET_ARG(pc, t->u.range.min); + pc += ARG_LEN; + SET_ARG(pc, t->u.range.max); + pc += ARG_LEN; + } + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ENDCHILD; + ++emitStateSP; + JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *)(t->kid); + op = t->op; + continue; + case REOP_ENDCHILD: + diff = pc - emitStateSP->nextTermFixup; + CHECK_OFFSET(diff); + SET_OFFSET(emitStateSP->nextTermFixup, diff); + break; + case REOP_CLASS: + if (!t->u.ucclass.sense) + pc[-1] = REOP_NCLASS; + SET_ARG(pc, t->u.ucclass.index); + pc += ARG_LEN; + charSet = &re->classList[t->u.ucclass.index]; + charSet->converted = JS_FALSE; + charSet->length = t->u.ucclass.bmsize; + charSet->u.src.startIndex = t->u.ucclass.startIndex; + charSet->u.src.length = t->u.ucclass.kidlen; + charSet->sense = t->u.ucclass.sense; + break; + default: + break; + } + t = t->next; + if (t == NULL) { + if (emitStateSP == emitStateStack) + break; + --emitStateSP; + t = emitStateSP->continueNode; + op = emitStateSP->continueOp; + } + else + op = t->op; + } + if (emitStateStack) + JS_free(state->context, emitStateStack); + return pc; +} + + +JSRegExp * +js_NewRegExp(JSContext *cx, JSTokenStream *ts, + JSString *str, uintN flags, JSBool flat) +{ + JSRegExp *re; + void *mark; + CompilerState state; + size_t resize; + jsbytecode *endPC; + uint32 i; + size_t len; + + re = NULL; + mark = JS_ARENA_MARK(&cx->tempPool); + + state.context = cx; + state.tokenStream = ts; + state.cpbegin = state.cp = JSSTRING_CHARS(str); + state.cpend = state.cp + JSSTRING_LENGTH(str); + state.flags = flags; + state.parenCount = 0; + state.classCount = 0; + state.progLength = 0; + state.treeDepth = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + state.classCache[i].start = NULL; + + len = JSSTRING_LENGTH(str); + + if (len != 0 && flat) { + state.result = NewRENode(&state, REOP_FLAT); + state.result->u.flat.chr = *state.cpbegin; + state.result->u.flat.length = JSSTRING_LENGTH(str); + state.result->kid = (void *)(state.cpbegin); + state.progLength += 5; + } + else { + if (!parseRegExp(&state)) + goto out; + } + resize = sizeof *re + state.progLength + 1; + re = (JSRegExp *) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword))); + if (!re) + goto out; + + re->classCount = state.classCount; + if (state.classCount) { + re->classList = (RECharSet *)JS_malloc(cx, sizeof(RECharSet) + * state.classCount); + if (!re->classList) + goto out; + } + else + re->classList = NULL; + endPC = emitREBytecode(&state, re, state.treeDepth, re->program, state.result); + if (!endPC) { + re = NULL; + goto out; + } + *endPC++ = REOP_END; + JS_ASSERT(endPC <= (re->program + (state.progLength + 1))); + + re->nrefs = 1; + re->parenCount = state.parenCount; + re->flags = flags; + re->source = str; + +out: + JS_ARENA_RELEASE(&cx->tempPool, mark); + return re; +} + +JSRegExp * +js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, + JSString *str, JSString *opt, JSBool flat) +{ + uintN flags; + jschar *s; + size_t i, n; + char charBuf[2]; + + flags = 0; + if (opt) { + s = JSSTRING_CHARS(opt); + for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) { + switch (s[i]) { + case 'g': + flags |= JSREG_GLOB; + break; + case 'i': + flags |= JSREG_FOLD; + break; + case 'm': + flags |= JSREG_MULTILINE; + break; + default: + charBuf[0] = (char)s[i]; + charBuf[1] = '\0'; + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_FLAG, charBuf); + return NULL; + } + } + } + return js_NewRegExp(cx, ts, str, flags, flat); +} + + +#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) +#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) + +/* + * Save the current state of the match - the position in the input + * text as well as the position in the bytecode. The state of any + * parent expressions is also saved (preceding state). + * Contents of parenCount parentheses from parenIndex are also saved. + */ +static REBackTrackData * +pushBackTrackState(REGlobalData *gData, REOp op, + jsbytecode *target, REMatchState *x, const jschar *cp, + intN parenIndex, intN parenCount) +{ + intN i; + REBackTrackData *result + = (REBackTrackData *)((char *)(gData->backTrackSP) + gData->cursz); + + size_t sz = sizeof(REBackTrackData) + + gData->stateStackTop * sizeof(REProgState) + + parenCount * sizeof(RECapture); + + + if (((char *)result + sz) + > (char *)gData->backTrackStack + gData->maxBackTrack) { + ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; + gData->backTrackStack + = (REBackTrackData *)JS_ArenaGrow(&gData->pool, + gData->backTrackStack, + gData->maxBackTrack, + gData->maxBackTrack); + gData->maxBackTrack <<= 1; + if (!gData->backTrackStack) + return NULL; + result = (REBackTrackData *)((char *)gData->backTrackStack + offset); + } + gData->backTrackSP = result; + result->sz = gData->cursz; + gData->cursz = sz; + + result->backtrack_op = op; + result->backtrack_pc = target; + result->cp = cp; + result->parenCount = parenCount; + + result->precedingStateTop = gData->stateStackTop; + JS_ASSERT(gData->stateStackTop); + memcpy(result + 1, gData->stateStack, + sizeof(REProgState) * result->precedingStateTop); + + if (parenCount != -1) { + result->parenIndex = parenIndex; + memcpy((char *)(result + 1) + + sizeof(REProgState) * result->precedingStateTop, + &x->parens[parenIndex], + sizeof(RECapture) * parenCount); + for (i = 0; i < parenCount; i++) + x->parens[parenIndex + i].index = -1; + } + + return result; +} + + +/* + * Consecutive literal characters. + */ +#if 0 +static REMatchState * +flatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, + intN length) +{ + intN i; + if ((x->cp + length) > gData->cpend) + return NULL; + for (i = 0; i < length; i++) { + if (matchChars[i] != x->cp[i]) + return NULL; + } + x->cp += length; + return x; +} +#endif + +static REMatchState * +flatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, + intN length) +{ + intN i; + if ((x->cp + length) > gData->cpend) + return NULL; + for (i = 0; i < length; i++) { + if (upcase(matchChars[i]) != upcase(x->cp[i])) + return NULL; + } + x->cp += length; + return x; +} + +/* + * 1. Evaluate DecimalEscape to obtain an EscapeValue E. + * 2. If E is not a character then go to step 6. + * 3. Let ch be E's character. + * 4. Let A be a one-element RECharSet containing the character ch. + * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. + * 6. E must be an integer. Let n be that integer. + * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. + * 8. Return an internal Matcher closure that takes two arguments, a State x + * and a Continuation c, and performs the following: + * 1. Let cap be x's captures internal array. + * 2. Let s be cap[n]. + * 3. If s is undefined, then call c(x) and return its result. + * 4. Let e be x's endIndex. + * 5. Let len be s's length. + * 6. Let f be e+len. + * 7. If f>InputLength, return failure. + * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) + * such that Canonicalize(s[i]) is not the same character as + * Canonicalize(Input [e+i]), then return failure. + * 9. Let y be the State (f, cap). + * 10. Call c(y) and return its result. + */ +static REMatchState * +backrefMatcher(REGlobalData *gData, REMatchState *x, uintN parenIndex) +{ + uintN len; + uintN i; + const jschar *parenContent; + RECapture *s = &x->parens[parenIndex]; + if (s->index == -1) + return x; + + len = s->length; + if ((x->cp + len) > gData->cpend) + return NULL; + + parenContent = &gData->cpbegin[s->index]; + if (gData->regexp->flags & JSREG_FOLD) { + for (i = 0; i < len; i++) { + if (upcase(parenContent[i]) != upcase(x->cp[i])) + return NULL; + } + } + else { + for (i = 0; i < len; i++) { + if (parenContent[i] != x->cp[i]) + return NULL; + } + } + x->cp += len; + return x; +} + + +/* Add a single character to the RECharSet */ +static void +addCharacterToCharSet(RECharSet *cs, jschar c) +{ + uintN byteIndex = (uintN)(c / 8); + JS_ASSERT(c <= cs->length); + cs->u.bits[byteIndex] |= 1 << (c & 0x7); +} + + +/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ +static void +addCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2) +{ + uintN i; + + uintN byteIndex1 = (uintN)(c1 / 8); + uintN byteIndex2 = (uintN)(c2 / 8); + + JS_ASSERT((c2 <= cs->length) && (c1 <= c2)); + + c1 &= 0x7; + c2 &= 0x7; + + if (byteIndex1 == byteIndex2) + cs->u.bits[byteIndex1] |= ((uint8)(0xFF) >> (7 - (c2 - c1))) << c1; + else { + cs->u.bits[byteIndex1] |= 0xFF << c1; + for (i = byteIndex1 + 1; i < byteIndex2; i++) + cs->u.bits[i] = 0xFF; + cs->u.bits[byteIndex2] |= (uint8)(0xFF) >> (7 - c2); + } +} + +/* Compile the source of the class into a RECharSet */ +static JSBool +processCharSet(REGlobalData *gData, RECharSet *charSet) +{ + const jschar *src = JSSTRING_CHARS(gData->regexp->source) + + charSet->u.src.startIndex; + const jschar *end = src + charSet->u.src.length; + + jschar rangeStart, thisCh; + uintN byteLength; + jschar c; + uintN n; + intN nDigits; + intN i; + JSBool inRange = JS_FALSE; + + JS_ASSERT(!charSet->converted); + charSet->converted = JS_TRUE; + + byteLength = (charSet->length / 8) + 1; + charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); + if (!charSet->u.bits) + return JS_FALSE; + memset(charSet->u.bits, 0, byteLength); + + if (src == end) + return JS_TRUE; + + if (*src == '^') { + JS_ASSERT(charSet->sense == JS_FALSE); + ++src; + } + else + JS_ASSERT(charSet->sense == JS_TRUE); + + + while (src != end) { + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + thisCh = 0x8; + break; + case 'f': + thisCh = 0xC; + break; + case 'n': + thisCh = 0xA; + break; + case 'r': + thisCh = 0xD; + break; + case 't': + thisCh = 0x9; + break; + case 'v': + thisCh = 0xB; + break; + case 'c': + if (((src + 1) < end) && JS_ISWORD(src[1])) + thisCh = (jschar)(*src++ & 0x1F); + else { + --src; + thisCh = '\\'; + } + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + uintN digit; + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original '\' + * as a literal + */ + src -= (i + 1); + n = '\\'; + break; + } + n = (n << 4) | digit; + } + thisCh = (jschar)(n); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + * + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + thisCh = (jschar)(n); + break; + + case 'd': + addCharacterRangeToCharSet(charSet, '0', '9'); + continue; /* don't need range processing */ + case 'D': + addCharacterRangeToCharSet(charSet, 0, '0' - 1); + addCharacterRangeToCharSet(charSet, (jschar)('9' + 1), + (jschar)(charSet->length)); + continue; + case 's': + for (i = (intN)(charSet->length); i >= 0; i--) + if (JS_ISSPACE(i)) + addCharacterToCharSet(charSet, (jschar)(i)); + continue; + case 'S': + for (i = (intN)(charSet->length); i >= 0; i--) + if (!JS_ISSPACE(i)) + addCharacterToCharSet(charSet, (jschar)(i)); + continue; + case 'w': + for (i = (intN)(charSet->length); i >= 0; i--) + if (JS_ISWORD(i)) + addCharacterToCharSet(charSet, (jschar)(i)); + continue; + case 'W': + for (i = (intN)(charSet->length); i >= 0; i--) + if (!JS_ISWORD(i)) + addCharacterToCharSet(charSet, (jschar)(i)); + continue; + default: + thisCh = c; + break; + + } + break; + + default: + thisCh = *src++; + break; + + } + if (inRange) { + if (gData->regexp->flags & JSREG_FOLD) { + addCharacterRangeToCharSet(charSet, upcase(rangeStart), + upcase(thisCh)); + addCharacterRangeToCharSet(charSet, downcase(rangeStart), + downcase(thisCh)); + } else { + addCharacterRangeToCharSet(charSet, rangeStart, thisCh); + } + inRange = JS_FALSE; + } + else { + if (gData->regexp->flags & JSREG_FOLD) { + addCharacterToCharSet(charSet, upcase(thisCh)); + addCharacterToCharSet(charSet, downcase(thisCh)); + } else { + addCharacterToCharSet(charSet, thisCh); + } + if (src < (end - 1)) { + if (*src == '-') { + ++src; + inRange = JS_TRUE; + rangeStart = thisCh; + } + } + } + } + return JS_TRUE; +} + +void +js_DestroyRegExp(JSContext *cx, JSRegExp *re) +{ + uintN i; + if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { + if (re->classList) { + for (i = 0; i < re->classCount; i++) { + if (re->classList[i].converted) + JS_free(cx, re->classList[i].u.bits); + re->classList[i].u.bits = NULL; + } + JS_free(cx, re->classList); + } + JS_free(cx, re); + } +} + +static JSBool +reallocStateStack(REGlobalData *gData) +{ + size_t sz = sizeof(REProgState) * gData->maxStateStack; + gData->maxStateStack <<= 1; + gData->stateStack + = (REProgState *)JS_ArenaGrow(&gData->pool, gData->stateStack, sz, sz); + if (!gData->stateStack) { + gData->ok = JS_FALSE; + return JS_FALSE; + } + return JS_TRUE; +} + +/* +* Apply the current op against the given input to see if +* it's going to match or fail. Return false if we don't +* get a match, true if we do and update the state of the +* input and pc if the update flag is true. +*/ +static REMatchState *simpleMatch(REGlobalData *gData, REMatchState *x, + REOp op, jsbytecode **startpc, JSBool update) +{ + REMatchState *result = NULL; + jschar matchCh; + intN parenIndex; + intN offset, length, index; + jsbytecode *pc = *startpc; /* pc has already been incremented past op */ + jschar *source; + const jschar *startcp = x->cp; + jschar ch; + RECharSet *charSet; + + + switch (op) { + default: + JS_ASSERT(JS_FALSE); + case REOP_BOL: + if (x->cp != gData->cpbegin) { + if (gData->cx->regExpStatics.multiline || + (gData->regexp->flags & JSREG_MULTILINE)) { + if (!RE_IS_LINE_TERM(x->cp[-1])) + break; + } + else + break; + } + result = x; + break; + case REOP_EOL: + if (x->cp != gData->cpend) { + if (gData->cx->regExpStatics.multiline || + (gData->regexp->flags & JSREG_MULTILINE)) { + if (!RE_IS_LINE_TERM(*x->cp)) + break; + } + else + break; + } + result = x; + break; + case REOP_WBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) + ^ !((x->cp != gData->cpend) && JS_ISWORD(*x->cp))) + result = x; + break; + case REOP_WNONBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) + ^ ((x->cp != gData->cpend) && JS_ISWORD(*x->cp))) + result = x; + break; + case REOP_DOT: + if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_DIGIT: + if (x->cp != gData->cpend && JS_ISDIGIT(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONDIGIT: + if (x->cp != gData->cpend && !JS_ISDIGIT(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_ALNUM: + if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONALNUM: + if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_SPACE: + if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONSPACE: + if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_BACKREF: + parenIndex = GET_ARG(pc); + pc += ARG_LEN; + result = backrefMatcher(gData, x, parenIndex); + break; + case REOP_FLAT: + offset = GET_ARG(pc); + pc += ARG_LEN; + length = GET_ARG(pc); + pc += ARG_LEN; + source = JSSTRING_CHARS(gData->regexp->source) + offset; + if ((x->cp + length) <= gData->cpend) { + for (index = 0; index < length; index++) { + if (source[index] != x->cp[index]) + return NULL; + } + x->cp += length; + result = x; + } + break; + case REOP_FLAT1: + matchCh = *pc++; + if ((x->cp != gData->cpend) && (*x->cp == matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_FLATi: + offset = GET_ARG(pc); + pc += ARG_LEN; + length = GET_ARG(pc); + pc += ARG_LEN; + source = JSSTRING_CHARS(gData->regexp->source); + result = flatNIMatcher(gData, x, source + offset, length); + break; + case REOP_FLAT1i: + matchCh = *pc++; + if ((x->cp != gData->cpend) && (upcase(*x->cp) == upcase(matchCh))) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1: + matchCh = GET_ARG(pc); + pc += ARG_LEN; + if ((x->cp != gData->cpend) && (*x->cp == matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1i: + matchCh = GET_ARG(pc); + pc += ARG_LEN; + if ((x->cp != gData->cpend) && (upcase(*x->cp) == upcase(matchCh))) { + result = x; + result->cp++; + } + break; + case REOP_CLASS: + index = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + JS_ASSERT(charSet->converted); + ch = *x->cp; + index = ch / 8; + if ((charSet->length != 0) && + ( (ch <= charSet->length) + && ((charSet->u.bits[index] & (1 << (ch & 0x7))) != 0) )) { + result = x; + result->cp++; + } + } + break; + case REOP_NCLASS: + index = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + JS_ASSERT(charSet->converted); + ch = *x->cp; + index = ch / 8; + if ((charSet->length == 0) || + ( (ch > charSet->length) + || ((charSet->u.bits[index] & (1 << (ch & 0x7))) == 0) )) { + result = x; + result->cp++; + } + } + break; + } + if (result != NULL) { + if (update) + *startpc = pc; + else + x->cp = startcp; + return result; + } + x->cp = startcp; + return NULL; +} + +static REMatchState * +executeREBytecode(REGlobalData *gData, REMatchState *x) +{ + REMatchState *result; + REBackTrackData *backTrackData; + intN offset; + jsbytecode *nextpc; + REOp nextop; + RECapture *cap; + REProgState *curState; + const jschar *startcp; + uintN parenIndex, k; + uintN parenSoFar = 0; + + jschar matchCh1, matchCh2; + RECharSet *charSet; + + JSBool anchor; + jsbytecode *pc = gData->regexp->program; + REOp op = (REOp)(*pc++); + + /* + * If the first node is a simple match, step the index into + * the string until that match is made, or fail if it can't be + * found at all. + */ + if (REOP_IS_SIMPLE(op)) { + anchor = JS_FALSE; + while (x->cp <= gData->cpend) { + nextpc = pc; /* reset back to start each time */ + result = simpleMatch(gData, x, op, &nextpc, JS_TRUE); + if (result) { + anchor = JS_TRUE; + x = result; + pc = nextpc; /* accept skip to next opcode */ + op = (REOp)(*pc++); + break; + } + else { + gData->skipped++; + x->cp++; + } + } + if (!anchor) + return NULL; + } + + while (JS_TRUE) { + if (REOP_IS_SIMPLE(op)) + result = simpleMatch(gData, x, op, &pc, JS_TRUE); + else { + curState = &gData->stateStack[gData->stateStackTop]; + switch (op) { + case REOP_EMPTY: + result = x; + break; + + case REOP_ALTPREREQ2: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + k = GET_ARG(pc); + pc += ARG_LEN; + + if (x->cp != gData->cpend) { + if (*x->cp == matchCh2) + goto doAlt; + + charSet = &gData->regexp->classList[k]; + if (!charSet->converted) + if (!processCharSet(gData, charSet)) + return NULL; + matchCh1 = *x->cp; + k = matchCh1 / 8; + if ((charSet->length == 0 || + matchCh1 > charSet->length || + (charSet->u.bits[k] & (1 << (matchCh1 & 0x7))) == 0) + ^ charSet->sense) { + goto doAlt; + } + } + result = NULL; + break; + + case REOP_ALTPREREQ: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh1 = GET_ARG(pc); + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + if ((x->cp == gData->cpend) + || ((*x->cp != matchCh1) && (*x->cp != matchCh2))) { + result = NULL; + break; + } + /* else false thru... */ + + case REOP_ALT: +doAlt: + nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ + pc += ARG_LEN; /* start of this alternate */ + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + op = (REOp)(*pc++); + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!simpleMatch(gData, x, op, &pc, JS_TRUE)) { + op = (REOp)(*nextpc++); + pc = nextpc; + continue; + } + else { /* accept the match and move on */ + result = x; + op = (REOp)(*pc++); + } + } + nextop = (REOp)(*nextpc++); + if (!pushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) + return NULL; + continue; + + /* + * Occurs at (succesful) end of REOP_ALT, + */ + case REOP_JUMP: + --gData->stateStackTop; + offset = GET_OFFSET(pc); + pc += offset; + op = (REOp)(*pc++); + continue; + + /* + * Occurs at last (succesful) end of REOP_ALT, + */ + case REOP_ENDALT: + --gData->stateStackTop; + op = (REOp)(*pc++); + continue; + + case REOP_LPAREN: + parenIndex = GET_ARG(pc); + if ((parenIndex + 1) > parenSoFar) + parenSoFar = parenIndex + 1; + pc += ARG_LEN; + x->parens[parenIndex].index = x->cp - gData->cpbegin; + x->parens[parenIndex].length = 0; + op = (REOp)(*pc++); + continue; + case REOP_RPAREN: + parenIndex = GET_ARG(pc); + pc += ARG_LEN; + cap = &x->parens[parenIndex]; + cap->length = x->cp - (gData->cpbegin + cap->index); + op = (REOp)(*pc++); + continue; + + case REOP_ASSERT: + nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ + pc += ARG_LEN; /* start of ASSERT child */ + op = (REOp)(*pc++); + if (REOP_IS_SIMPLE(op) + && !simpleMatch(gData, x, op, &pc, JS_FALSE)) { + result = NULL; + break; + } + else { + curState->u.assertion.top + = (char *)gData->backTrackSP + - (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (!pushBackTrackState(gData, REOP_ASSERTTEST, + nextpc, x, x->cp, 0, 0)) + return NULL; + } + continue; + case REOP_ASSERT_NOT: + nextpc = pc + GET_OFFSET(pc); + pc += ARG_LEN; + op = (REOp)(*pc++); + if (REOP_IS_SIMPLE(op) + /* Note - fail to fail! */ + && simpleMatch(gData, x, op, &pc, JS_FALSE)) { + result = NULL; + break; + } + else { + curState->u.assertion.top + = (char *)gData->backTrackSP + - (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (!pushBackTrackState(gData, REOP_ASSERTNOTTEST, + nextpc, x, x->cp, 0, 0)) + return NULL; + } + continue; + case REOP_ASSERTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP + = (REBackTrackData *)((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + if (result != NULL) + result = x; + break; + case REOP_ASSERTNOTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP + = (REBackTrackData *)((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + if (result == NULL) + result = x; + else + result = NULL; + break; + + case REOP_END: + if (x != NULL) + return x; + break; + + case REOP_STAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = -1; + goto quantcommon; + case REOP_PLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = -1; + goto quantcommon; + case REOP_OPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto quantcommon; + case REOP_QUANT: + curState->u.quantifier.min = GET_ARG(pc); + pc += ARG_LEN; + curState->u.quantifier.max = GET_ARG(pc); + pc += ARG_LEN; +quantcommon: + if (curState->u.quantifier.max == 0) { + pc = pc + GET_OFFSET(pc); + op = (REOp)(*pc++); + result = x; + continue; + } + /* Step over */ + nextpc = pc + ARG_LEN; + op = (REOp)(*nextpc++); + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!simpleMatch(gData, x, op, &nextpc, JS_TRUE)) { + if (curState->u.quantifier.min == 0) + result = x; + else + result = NULL; + pc = pc + GET_OFFSET(pc); + break; + } + else { + op = (REOp)(*nextpc++); + result = x; + } + } + curState->index = startcp - gData->cpbegin; + curState->continue_op = REOP_REPEAT; + curState->continue_pc = pc; + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (curState->u.quantifier.min == 0) + if (!pushBackTrackState(gData, REOP_REPEAT, + pc, x, startcp, 0, 0)) + return NULL; + pc = nextpc; + continue; + + case REOP_ENDCHILD: /* marks the end of a quantifier child */ + pc = curState[-1].continue_pc; + op = curState[-1].continue_op; + continue; + + case REOP_REPEAT: + --curState; +repeatAgain: + --gData->stateStackTop; + if (result == NULL) { + /* + * There's been a failure, see if we have enough children. + */ + if (curState->u.quantifier.min == 0) { + result = x; + goto repeatDone; + } + break; + } + else { + if ((curState->u.quantifier.min == 0) + && (x->cp == gData->cpbegin + curState->index)) { + /* matched an empty string, that'll get us nowhere */ + result = NULL; + break; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (uint16)(-1)) + curState->u.quantifier.max--; + if (curState->u.quantifier.max == 0) { + result = x; + goto repeatDone; + } + nextpc = pc + ARG_LEN; + nextop = (REOp)(*nextpc); + startcp = x->cp; + if (REOP_IS_SIMPLE(nextop)) { + nextpc++; + if (!simpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) { + if (curState->u.quantifier.min == 0) { + result = x; + goto repeatDone; + } + else + result = NULL; + break; + } + result = x; + } + curState->index = startcp - gData->cpbegin; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (curState->u.quantifier.min == 0) + if (!pushBackTrackState(gData, REOP_REPEAT, + pc, x, startcp, + curState->parenSoFar, + parenSoFar + - curState->parenSoFar)) + return NULL; + if (*nextpc == REOP_ENDCHILD) + goto repeatAgain; + pc = nextpc; + op = (REOp)(*pc++); + parenSoFar = curState->parenSoFar; + } + continue; +repeatDone: + pc = pc + GET_OFFSET(pc); + break; + + + case REOP_MINIMALSTAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = -1; + goto minimalquantcommon; + case REOP_MINIMALPLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = -1; + goto minimalquantcommon; + case REOP_MINIMALOPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto minimalquantcommon; + case REOP_MINIMALQUANT: + curState->u.quantifier.min = GET_ARG(pc); + pc += ARG_LEN; + curState->u.quantifier.max = GET_ARG(pc); + pc += ARG_LEN; +minimalquantcommon: + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (curState->u.quantifier.min != 0) { + curState->continue_op = REOP_MINIMALREPEAT; + curState->continue_pc = pc; + /* step over */ + pc += ARG_LEN; + op = (REOp)(*pc++); + } + else { + if (!pushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, 0, 0)) + return NULL; + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp)(*pc++); + } + continue; + + case REOP_MINIMALREPEAT: + --gData->stateStackTop; + --curState; + + if (result == NULL) { + /* + * Non-greedy failure - try to consume another child. + */ + if ((curState->u.quantifier.max == (uint16)(-1)) + || (curState->u.quantifier.max > 0)) { + curState->index = x->cp - gData->cpbegin; + curState->continue_op = REOP_MINIMALREPEAT; + curState->continue_pc = pc; + pc += ARG_LEN; + for (k = curState->parenSoFar; k < parenSoFar; k++) + x->parens[k].index = -1; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + op = (REOp)(*pc++); + continue; + } + else { + /* Don't need to adjust pc since we're going to pop. */ + break; + } + } + else { + if ((curState->u.quantifier.min == 0) + && (x->cp == gData->cpbegin + curState->index)) { + /* Matched an empty string, that'll get us nowhere. */ + result = NULL; + break; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (uint16)(-1)) + curState->u.quantifier.max--; + if (curState->u.quantifier.min != 0) { + curState->continue_op = REOP_MINIMALREPEAT; + curState->continue_pc = pc; + pc += ARG_LEN; + for (k = curState->parenSoFar; k < parenSoFar; k++) + x->parens[k].index = -1; + curState->index = x->cp - gData->cpbegin; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + op = (REOp)(*pc++); + continue; + } + else { + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + ++gData->stateStackTop; + if (gData->stateStackTop == gData->maxStateStack) + if (!reallocStateStack(gData)) + return NULL; + if (!pushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, + curState->parenSoFar, + parenSoFar + - curState->parenSoFar)) + return NULL; + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp)(*pc++); + continue; + } + } + + default: + JS_ASSERT(JS_FALSE); + result = NULL; + } + } + /* + * If the match failed and there's a backtrack option, take it. + * Otherwise this is a complete and utter failure. + */ + if (result == NULL) { + if (gData->cursz > 0) { + backTrackData = gData->backTrackSP; + gData->cursz = backTrackData->sz; + gData->backTrackSP + = (REBackTrackData *)((char *)backTrackData + - backTrackData->sz); + x->cp = backTrackData->cp; + pc = backTrackData->backtrack_pc; + op = backTrackData->backtrack_op; + gData->stateStackTop = backTrackData->precedingStateTop; + JS_ASSERT(gData->stateStackTop); + + memcpy(gData->stateStack, backTrackData + 1, + sizeof(REProgState) * backTrackData->precedingStateTop); + curState = &gData->stateStack[gData->stateStackTop - 1]; + + if (backTrackData->parenCount) { + memcpy(&x->parens[backTrackData->parenIndex], + (char *)(backTrackData + 1) + sizeof(REProgState) * backTrackData->precedingStateTop, + sizeof(RECapture) * backTrackData->parenCount); + parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; + } + else { + for (k = curState->parenSoFar; k < parenSoFar; k++) + x->parens[k].index = -1; + parenSoFar = curState->parenSoFar; + } + continue; + } + else + return NULL; + } + else + x = result; + + /* + * Continue with the expression. + */ + op = (REOp)*pc++; + } + return NULL; +} + +static REMatchState * +MatchRegExp(REGlobalData *gData, REMatchState *x) +{ + REMatchState *result; + const jschar *cp = x->cp; + const jschar *cp2; + uintN j; + + /* + * Have to include the position beyond the last character + * in order to detect end-of-input/line condition. + */ + for (cp2 = cp; cp2 <= gData->cpend; cp2++) { + gData->skipped = cp2 - cp; + x->cp = cp2; + for (j = 0; j < gData->regexp->parenCount; j++) + x->parens[j].index = -1; + result = executeREBytecode(gData, x); + if (!gData->ok || result) + return result; + gData->backTrackSP = gData->backTrackStack; + gData->cursz = 0; + gData->stateStackTop = 0; + cp2 = cp + gData->skipped; + } + return NULL; +} + + +static REMatchState * +initMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) +{ + REMatchState *result; + uintN i; + + gData->maxBackTrack = INITIAL_BACKTRACK; + JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *, + &gData->pool, + INITIAL_BACKTRACK); + if (!gData->backTrackStack) + return NULL; + gData->backTrackSP = gData->backTrackStack; + gData->cursz = 0; + + + gData->maxStateStack = INITIAL_STATESTACK; + JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, + &gData->pool, + sizeof(REProgState) * INITIAL_STATESTACK); + if (!gData->stateStack) + return NULL; + gData->stateStackTop = 0; + + gData->cx = cx; + gData->regexp = re; + gData->ok = JS_TRUE; + + JS_ARENA_ALLOCATE_CAST(result, REMatchState *, + &gData->pool, + sizeof(REMatchState) + + (re->parenCount - 1) * sizeof(RECapture)); + if (!result) + return NULL; + + for (i = 0; i < re->classCount; i++) + if (!re->classList[i].converted) + if (!processCharSet(gData, &re->classList[i])) + return NULL; + + return result; +} + +JSBool +js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, + JSBool test, jsval *rval) +{ + REGlobalData gData; + REMatchState *x, *result; + + const jschar *cp, *ep; + size_t i, length, start; + JSSubString *morepar; + JSBool ok; + JSRegExpStatics *res; + ptrdiff_t matchlen; + uintN num, morenum; + JSString *parstr, *matchstr; + JSObject *obj; + + RECapture *parsub; + + /* + * It's safe to load from cp because JSStrings have a zero at the end, + * and we never let cp get beyond cpend. + */ + start = *indexp; + length = JSSTRING_LENGTH(str); + if (start > length) + start = length; + cp = JSSTRING_CHARS(str); + gData.cpbegin = cp; + gData.cpend = cp + length; + cp += start; + gData.start = start; + gData.skipped = 0; + + JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4); + x = initMatch(cx, &gData, re); + if (!x) + return JS_FALSE; + x->cp = cp; + + /* + * Call the recursive matcher to do the real work. Return null on mismatch + * whether testing or not. On match, return an extended Array object. + */ + result = MatchRegExp(&gData, x); + if (!(ok = gData.ok)) goto out; + if (!result) { + *rval = JSVAL_NULL; + goto out; + } + cp = result->cp; + i = PTRDIFF(cp, gData.cpbegin, jschar); + *indexp = i; + matchlen = i - (start + gData.skipped); + ep = cp; + cp -= matchlen; + + if (test) { + /* + * Testing for a match and updating cx->regExpStatics: don't allocate + * an array object, do return true. + */ + *rval = JSVAL_TRUE; + + /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */ + obj = NULL; + } else { + /* + * The array returned on match has element 0 bound to the matched + * string, elements 1 through state.parenCount bound to the paren + * matches, an index property telling the length of the left context, + * and an input property referring to the input string. + */ + obj = js_NewArrayObject(cx, 0, NULL); + if (!obj) { + ok = JS_FALSE; + goto out; + } + *rval = OBJECT_TO_JSVAL(obj); + +#define DEFVAL(val, id) { \ + ok = js_DefineProperty(cx, obj, id, val, \ + JS_PropertyStub, JS_PropertyStub, \ + JSPROP_ENUMERATE, NULL); \ + if (!ok) { \ + cx->newborn[GCX_OBJECT] = NULL; \ + cx->newborn[GCX_STRING] = NULL; \ + goto out; \ + } \ +} + + matchstr = js_NewStringCopyN(cx, cp, matchlen, 0); + if (!matchstr) { + cx->newborn[GCX_OBJECT] = NULL; + ok = JS_FALSE; + goto out; + } + DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0)); + } + + res = &cx->regExpStatics; + res->input = str; + res->parenCount = re->parenCount; + if (re->parenCount == 0) + res->lastParen = js_EmptySubString; + else { + for (num = 0; num < re->parenCount; num++) { + parsub = &result->parens[num]; + if (num < 9) { + if (parsub->index == -1) { + res->parens[num].chars = NULL; + res->parens[num].length = 0; + } + else { + res->parens[num].chars = gData.cpbegin + parsub->index; + res->parens[num].length = parsub->length; + } + } else { + morenum = num - 9; + morepar = res->moreParens; + if (!morepar) { + res->moreLength = 10; + morepar = (JSSubString*) JS_malloc(cx, + 10 * sizeof(JSSubString)); + } else if (morenum >= res->moreLength) { + res->moreLength += 10; + morepar = (JSSubString*) JS_realloc(cx, morepar, + res->moreLength * sizeof(JSSubString)); + } + if (!morepar) { + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; + ok = JS_FALSE; + goto out; + } + res->moreParens = morepar; + if (parsub->index == -1) { + morepar[morenum].chars = NULL; + morepar[morenum].length = 0; + } + else { + morepar[morenum].chars = gData.cpbegin + parsub->index; + morepar[morenum].length = parsub->length; + } + } + if (test) + continue; + if (parsub->index == -1) + ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + JSVAL_VOID, NULL, NULL, + JSPROP_ENUMERATE, NULL); + else { + parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index, + parsub->length, 0); + if (!parstr) { + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; + ok = JS_FALSE; + goto out; + } + ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + STRING_TO_JSVAL(parstr), NULL, NULL, + JSPROP_ENUMERATE, NULL); + } + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; + goto out; + } + } + if (parsub->index == -1) { + res->lastParen.chars = NULL; + res->lastParen.length = 0; + } + else { + res->lastParen.chars = gData.cpbegin + parsub->index; + res->lastParen.length = parsub->length; + } + } + + if (!test) { + /* + * Define the index and input properties last for better for/in loop + * order (so they come after the elements). + */ + DEFVAL(INT_TO_JSVAL(start + gData.skipped), + (jsid)cx->runtime->atomState.indexAtom); + DEFVAL(STRING_TO_JSVAL(str), + (jsid)cx->runtime->atomState.inputAtom); + } + +#undef DEFVAL + + res->lastMatch.chars = cp; + res->lastMatch.length = matchlen; + if (cx->version == JSVERSION_1_2) { + /* + * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used + * in scalar contexts, and unintentionally for the string.match "list" + * psuedo-context. On "hi there bye", the following would result: + * + * Language while(/ /g){print("$`");} s/ /$`/g + * perl4.036 "hi", "there" "hihitherehi therebye" + * perl5 "hi", "hi there" "hihitherehi therebye" + * js1.2 "hi", "there" "hihitheretherebye" + */ + res->leftContext.chars = JSSTRING_CHARS(str) + start; + res->leftContext.length = gData.skipped; + } else { + /* + * For JS1.3 and ECMAv2, emulate Perl5 exactly: + * + * js1.3 "hi", "hi there" "hihitherehi therebye" + */ + res->leftContext.chars = JSSTRING_CHARS(str); + res->leftContext.length = start + gData.skipped; + } + res->rightContext.chars = ep; + res->rightContext.length = gData.cpend - ep; + +out: + JS_FinishArenaPool(&gData.pool); + return ok; +} + +/************************************************************************/ + +enum regexp_tinyid { + REGEXP_SOURCE = -1, + REGEXP_GLOBAL = -2, + REGEXP_IGNORE_CASE = -3, + REGEXP_LAST_INDEX = -4, + REGEXP_MULTILINE = -5 +}; + +#define REGEXP_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED) + +static JSPropertySpec regexp_props[] = { + {"source", REGEXP_SOURCE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, + {"global", REGEXP_GLOBAL, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, + {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, + {"lastIndex", REGEXP_LAST_INDEX, REGEXP_PROP_ATTRS,0,0}, + {"multiline", REGEXP_MULTILINE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, + {0,0,0,0,0} +}; + +static JSBool +regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSRegExp *re; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + if (slot == REGEXP_LAST_INDEX) + return JS_GetReservedSlot(cx, obj, 0, vp); + + JS_LOCK_OBJ(cx, obj); + re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); + if (re) { + switch (slot) { + case REGEXP_SOURCE: + *vp = STRING_TO_JSVAL(re->source); + break; + case REGEXP_GLOBAL: + *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0); + break; + case REGEXP_IGNORE_CASE: + *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0); + break; + case REGEXP_MULTILINE: + *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0); + break; + } + } + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; +} + +static JSBool +regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok; + jsint slot; + jsdouble lastIndex; + + ok = JS_TRUE; + if (!JSVAL_IS_INT(id)) + return ok; + slot = JSVAL_TO_INT(id); + if (slot == REGEXP_LAST_INDEX) { + if (!js_ValueToNumber(cx, *vp, &lastIndex)) + return JS_FALSE; + lastIndex = js_DoubleToInteger(lastIndex); + ok = js_NewNumberValue(cx, lastIndex, vp) && + JS_SetReservedSlot(cx, obj, 0, *vp); + } + return ok; +} + +/* + * RegExp class static properties and their Perl counterparts: + * + * RegExp.input $_ + * RegExp.multiline $* + * RegExp.lastMatch $& + * RegExp.lastParen $+ + * RegExp.leftContext $` + * RegExp.rightContext $' + */ +enum regexp_static_tinyid { + REGEXP_STATIC_INPUT = -1, + REGEXP_STATIC_MULTILINE = -2, + REGEXP_STATIC_LAST_MATCH = -3, + REGEXP_STATIC_LAST_PAREN = -4, + REGEXP_STATIC_LEFT_CONTEXT = -5, + REGEXP_STATIC_RIGHT_CONTEXT = -6 +}; + +JSBool +js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) +{ + JS_ClearRegExpStatics(cx); + return js_AddRoot(cx, &res->input, "res->input"); +} + +void +js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) +{ + if (res->moreParens) { + JS_free(cx, res->moreParens); + res->moreParens = NULL; + } + js_RemoveRoot(cx->runtime, &res->input); +} + +static JSBool +regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsint slot; + JSRegExpStatics *res; + JSString *str; + JSSubString *sub; + + res = &cx->regExpStatics; + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + slot = JSVAL_TO_INT(id); + switch (slot) { + case REGEXP_STATIC_INPUT: + *vp = res->input ? STRING_TO_JSVAL(res->input) + : JS_GetEmptyStringValue(cx); + return JS_TRUE; + case REGEXP_STATIC_MULTILINE: + *vp = BOOLEAN_TO_JSVAL(res->multiline); + return JS_TRUE; + case REGEXP_STATIC_LAST_MATCH: + sub = &res->lastMatch; + break; + case REGEXP_STATIC_LAST_PAREN: + sub = &res->lastParen; + break; + case REGEXP_STATIC_LEFT_CONTEXT: + sub = &res->leftContext; + break; + case REGEXP_STATIC_RIGHT_CONTEXT: + sub = &res->rightContext; + break; + default: + sub = REGEXP_PAREN_SUBSTRING(res, slot); + break; + } + str = js_NewStringCopyN(cx, sub->chars, sub->length, 0); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSRegExpStatics *res; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + res = &cx->regExpStatics; + /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */ + if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) { + if (!JSVAL_IS_STRING(*vp) && + !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) { + return JS_FALSE; + } + res->input = JSVAL_TO_STRING(*vp); + } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) { + if (!JSVAL_IS_BOOLEAN(*vp) && + !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) { + return JS_FALSE; + } + res->multiline = JSVAL_TO_BOOLEAN(*vp); + } + return JS_TRUE; +} + +static JSPropertySpec regexp_static_props[] = { + {"input", + REGEXP_STATIC_INPUT, + JSPROP_ENUMERATE|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_setProperty}, + {"multiline", + REGEXP_STATIC_MULTILINE, + JSPROP_ENUMERATE|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_setProperty}, + {"lastMatch", + REGEXP_STATIC_LAST_MATCH, + JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"lastParen", + REGEXP_STATIC_LAST_PAREN, + JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"leftContext", + REGEXP_STATIC_LEFT_CONTEXT, + JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"rightContext", + REGEXP_STATIC_RIGHT_CONTEXT, + JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + + /* XXX should have block scope and local $1, etc. */ + {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, + regexp_static_getProperty, regexp_static_getProperty}, + + {0,0,0,0,0} +}; + +static void +regexp_finalize(JSContext *cx, JSObject *obj) +{ + JSRegExp *re; + + re = (JSRegExp *) JS_GetPrivate(cx, obj); + if (!re) + return; + js_DestroyRegExp(cx, re); +} + +/* Forward static prototype. */ +static JSBool +regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +} + +#if JS_HAS_XDR + +#include "jsxdrapi.h" + +static JSBool +regexp_xdrObject(JSXDRState *xdr, JSObject **objp) +{ + JSRegExp *re; + JSString *source; + uint8 flags; + + if (xdr->mode == JSXDR_ENCODE) { + re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp); + if (!re) + return JS_FALSE; + source = re->source; + flags = (uint8) re->flags; + } + if (!JS_XDRString(xdr, &source) || + !JS_XDRUint8(xdr, &flags)) { + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + *objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); + if (!*objp) + return JS_FALSE; + re = js_NewRegExp(xdr->cx, NULL, source, flags, JS_FALSE); + if (!re) + return JS_FALSE; + if (!JS_SetPrivate(xdr->cx, *objp, re) || + !js_SetLastIndex(xdr->cx, *objp, 0)) { + js_DestroyRegExp(xdr->cx, re); + return JS_FALSE; + } + } + return JS_TRUE; +} + +#else /* !JS_HAS_XDR */ + +#define regexp_xdrObject NULL + +#endif /* !JS_HAS_XDR */ + +static uint32 +regexp_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj); + if (re) + JS_MarkGCThing(cx, re->source, "source", arg); + return 0; +} + +JSClass js_RegExpClass = { + js_RegExp_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, regexp_getProperty, regexp_setProperty, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, regexp_finalize, + NULL, NULL, regexp_call, NULL, + regexp_xdrObject, NULL, regexp_mark, 0 +}; + +static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; + +static JSBool +regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSRegExp *re; + const jschar *source; + jschar *chars; + size_t length, nflags; + uintN flags; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) + return JS_FALSE; + JS_LOCK_OBJ(cx, obj); + re = (JSRegExp *) JS_GetPrivate(cx, obj); + if (!re) { + JS_UNLOCK_OBJ(cx, obj); + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + source = JSSTRING_CHARS(re->source); + length = JSSTRING_LENGTH(re->source); + if (length == 0) { + source = empty_regexp_ucstr; + length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1; + } + length += 2; + nflags = 0; + for (flags = re->flags; flags != 0; flags &= flags - 1) + nflags++; + chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); + if (!chars) { + JS_UNLOCK_OBJ(cx, obj); + return JS_FALSE; + } + + chars[0] = '/'; + js_strncpy(&chars[1], source, length - 2); + chars[length-1] = '/'; + if (nflags) { + if (re->flags & JSREG_GLOB) + chars[length++] = 'g'; + if (re->flags & JSREG_FOLD) + chars[length++] = 'i'; + if (re->flags & JSREG_MULTILINE) + chars[length++] = 'm'; + } + JS_UNLOCK_OBJ(cx, obj); + chars[length] = 0; + + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *opt, *str; + JSRegExp *oldre, *re; + JSBool ok; + JSObject *obj2; + + if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) + return JS_FALSE; + opt = NULL; + if (argc == 0) { + str = cx->runtime->emptyString; + } else { + if (JSVAL_IS_OBJECT(argv[0])) { + /* + * If we get passed in a RegExp object we construct a new + * RegExp that is a duplicate of it by re-compiling the + * original source code. ECMA requires that it be an error + * here if the flags are specified. (We must use the flags + * from the original RegExp also). + */ + obj2 = JSVAL_TO_OBJECT(argv[0]); + if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) { + if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NEWREGEXP_FLAGGED); + return JS_FALSE; + } + JS_LOCK_OBJ(cx, obj2); + re = (JSRegExp *) JS_GetPrivate(cx, obj2); + if (!re) { + JS_UNLOCK_OBJ(cx, obj2); + return JS_FALSE; + } + re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); + JS_UNLOCK_OBJ(cx, obj2); + goto madeit; + } + } + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + if (argc > 1) { + if (JSVAL_IS_VOID(argv[1])) { + opt = NULL; + } else { + opt = js_ValueToString(cx, argv[1]); + if (!opt) + return JS_FALSE; + argv[1] = STRING_TO_JSVAL(opt); + } + } + } + re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); +madeit: + if (!re) + return JS_FALSE; + JS_LOCK_OBJ(cx, obj); + oldre = (JSRegExp *) JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, re) && js_SetLastIndex(cx, obj, 0); + JS_UNLOCK_OBJ(cx, obj); + if (!ok) { + js_DestroyRegExp(cx, re); + } else { + if (oldre) + js_DestroyRegExp(cx, oldre); + *rval = OBJECT_TO_JSVAL(obj); + } + return ok; +} + +static JSBool +regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + JSBool test, jsval *rval) +{ + JSBool ok; + JSRegExp *re; + jsdouble lastIndex; + JSString *str; + size_t i; + + ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv); + if (!ok) + return JS_FALSE; + JS_LOCK_OBJ(cx, obj); + re = (JSRegExp *) JS_GetPrivate(cx, obj); + if (!re) { + JS_UNLOCK_OBJ(cx, obj); + return JS_TRUE; + } + + /* NB: we must reach out: after this paragraph, in order to drop re. */ + HOLD_REGEXP(cx, re); + if (re->flags & JSREG_GLOB) { + ok = js_GetLastIndex(cx, obj, &lastIndex); + } else { + lastIndex = 0; + } + JS_UNLOCK_OBJ(cx, obj); + if (!ok) + goto out; + + /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */ + if (argc == 0) { + str = cx->regExpStatics.input; + if (!str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NO_INPUT, + JS_GetStringBytes(re->source), + (re->flags & JSREG_GLOB) ? "g" : "", + (re->flags & JSREG_FOLD) ? "i" : "", + (re->flags & JSREG_MULTILINE) ? "m" : ""); + ok = JS_FALSE; + goto out; + } + } else { + str = js_ValueToString(cx, argv[0]); + if (!str) + goto out; + argv[0] = STRING_TO_JSVAL(str); + } + + if (lastIndex < 0 || JSSTRING_LENGTH(str) < lastIndex) { + ok = js_SetLastIndex(cx, obj, 0); + *rval = JSVAL_NULL; + } else { + i = (size_t) lastIndex; + ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); + if (ok && (re->flags & JSREG_GLOB)) + ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i); + } + +out: + DROP_REGEXP(cx, re); + return ok; +} + +static JSBool +regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval); +} + +static JSBool +regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval)) + return JS_FALSE; + if (*rval != JSVAL_TRUE) + *rval = JSVAL_FALSE; + return JS_TRUE; +} + +static JSFunctionSpec regexp_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, regexp_toString, 0,0,0}, +#endif + {js_toString_str, regexp_toString, 0,0,0}, + {"compile", regexp_compile, 1,0,0}, + {"exec", regexp_exec, 0,0,0}, + {"test", regexp_test, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* + * If first arg is regexp and no flags are given, just return the arg. + * (regexp_compile detects the regexp + flags case and throws a + * TypeError.) See 10.15.3.1. + */ + if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && JSVAL_IS_OBJECT(argv[0]) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) { + *rval = argv[0]; + return JS_TRUE; + } + + /* Otherwise, replace obj with a new RegExp object. */ + obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + return regexp_compile(cx, obj, argc, argv, rval); +} + +JSObject * +js_InitRegExpClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *ctor; + jsval rval; + + proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, + regexp_props, regexp_methods, + regexp_static_props, NULL); + + if (!proto || !(ctor = JS_GetConstructor(cx, proto))) + return NULL; + if (!JS_AliasProperty(cx, ctor, "input", "$_") || + !JS_AliasProperty(cx, ctor, "multiline", "$*") || + !JS_AliasProperty(cx, ctor, "lastMatch", "$&") || + !JS_AliasProperty(cx, ctor, "lastParen", "$+") || + !JS_AliasProperty(cx, ctor, "leftContext", "$`") || + !JS_AliasProperty(cx, ctor, "rightContext", "$'")) { + goto bad; + } + + /* Give RegExp.prototype private data so it matches the empty string. */ + if (!regexp_compile(cx, proto, 0, NULL, &rval)) + goto bad; + return proto; + +bad: + JS_DeleteProperty(cx, obj, js_RegExpClass.name); + return NULL; +} + +JSObject * +js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, + jschar *chars, size_t length, uintN flags) +{ + JSString *str; + JSObject *obj; + JSRegExp *re; + + str = js_NewStringCopyN(cx, chars, length, 0); + if (!str) + return NULL; + re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); + if (!re) + return NULL; + obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, re) || !js_SetLastIndex(cx, obj, 0)) { + js_DestroyRegExp(cx, re); + return NULL; + } + return obj; +} + +JSObject * +js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) +{ + JSObject *clone; + JSRegExp *re; + + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); + clone = js_NewObject(cx, &js_RegExpClass, NULL, parent); + if (!clone) + return NULL; + re = JS_GetPrivate(cx, obj); + if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + HOLD_REGEXP(cx, re); + return clone; +} + +JSBool +js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex) +{ + jsval v; + + return JS_GetReservedSlot(cx, obj, 0, &v) && + js_ValueToNumber(cx, v, lastIndex); +} + +JSBool +js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex) +{ + jsval v; + + return js_NewNumberValue(cx, lastIndex, &v) && + JS_SetReservedSlot(cx, obj, 0, v); +} + +#endif /* JS_HAS_REGEXPS */ diff --git a/src/extension/script/js/jsregexp.h b/src/extension/script/js/jsregexp.h new file mode 100644 index 000000000..0485c2860 --- /dev/null +++ b/src/extension/script/js/jsregexp.h @@ -0,0 +1,168 @@ +/* -*- 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 jsregexp_h___ +#define jsregexp_h___ +/* + * JS regular expression interface. + */ +#include +#include "jspubtd.h" +#include "jsstr.h" + +#ifdef JS_THREADSAFE +#include "jsdhash.h" +#endif + +struct JSRegExpStatics { + JSString *input; /* input string to match (perl $_, GC root) */ + JSBool multiline; /* whether input contains newlines (perl $*) */ + uintN parenCount; /* number of valid elements in parens[] */ + uintN moreLength; /* number of allocated elements in moreParens */ + JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */ + JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */ + JSSubString lastMatch; /* last string matched (perl $&) */ + JSSubString lastParen; /* last paren matched (perl $+) */ + JSSubString leftContext; /* input to left of last match (perl $`) */ + JSSubString rightContext; /* input to right of last match (perl $') */ +}; + +/* + * This struct holds a bitmap representation of a class from a regexp. + * There's a list of these referenced by the classList field in the JSRegExp + * struct below. The initial state has startIndex set to the offset in the + * original regexp source of the beginning of the class contents. The first + * use of the class converts the source representation into a bitmap. + * + */ +typedef struct RECharSet { + JSBool converted; + JSBool sense; + uint16 length; + union { + uint8 *bits; + struct { + uint16 startIndex; + uint16 length; + } src; + } u; +} RECharSet; + +/* + * This macro is safe because moreParens is guaranteed to be allocated and big + * enough to hold parenCount, or else be null when parenCount is 0. + */ +#define REGEXP_PAREN_SUBSTRING(res, num) \ + (((jsuint)(num) < (jsuint)(res)->parenCount) \ + ? ((jsuint)(num) < 9) \ + ? &(res)->parens[num] \ + : &(res)->moreParens[(num) - 9] \ + : &js_EmptySubString) + +typedef struct RENode RENode; + +struct JSRegExp { + jsrefcount nrefs; /* reference count */ + uint32 parenCount:24, /* number of parenthesized submatches */ + flags:8; /* flags, see jsapi.h's JSREG_* defines */ + uint32 classCount; /* count [...] bitmaps */ + RECharSet *classList; /* list of [...] bitmaps */ + JSString *source; /* locked source string, sans // */ + jsbytecode program[1]; /* regular expression bytecode */ +}; + +extern JSRegExp * +js_NewRegExp(JSContext *cx, JSTokenStream *ts, + JSString *str, uintN flags, JSBool flat); + +extern JSRegExp * +js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, + JSString *str, JSString *opt, JSBool flat); + +extern void +js_DestroyRegExp(JSContext *cx, JSRegExp *re); + +/* + * Execute re on input str at *indexp, returning null in *rval on mismatch. + * On match, return true if test is true, otherwise return an array object. + * Update *indexp and cx->regExpStatics always on match. + */ +extern JSBool +js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, + JSBool test, jsval *rval); + +/* + * These two add and remove GC roots, respectively, so their calls must be + * well-ordered. + */ +extern JSBool +js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); + +extern void +js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); + +#define JSVAL_IS_REGEXP(cx, v) \ + (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) + +extern JSClass js_RegExpClass; + +extern JSObject * +js_InitRegExpClass(JSContext *cx, JSObject *obj); + +/* + * Create a new RegExp object. + */ +extern JSObject * +js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, + jschar *chars, size_t length, uintN flags); + +extern JSBool +js_XDRRegExp(JSXDRState *xdr, JSObject **objp); + +extern JSObject * +js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent); + +extern JSBool +js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex); + +extern JSBool +js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex); + +#endif /* jsregexp_h___ */ diff --git a/src/extension/script/js/jsscan.c b/src/extension/script/js/jsscan.c new file mode 100644 index 000000000..e0bc5cd92 --- /dev/null +++ b/src/extension/script/js/jsscan.c @@ -0,0 +1,1315 @@ +/* -*- 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 lexical scanner. + */ +#include "jsstddef.h" +#include /* first to avoid trouble on some systems */ +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdtoa.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsemit.h" +#include "jsexn.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsscan.h" + +#define RESERVE_JAVA_KEYWORDS +#define RESERVE_ECMA_KEYWORDS + +static struct keyword { + const char *name; + JSTokenType tokentype; /* JSTokenType */ + JSOp op; /* JSOp */ + JSVersion version; /* JSVersion */ +} keywords[] = { + {"break", TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT}, + {"case", TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT}, + {"continue", TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT}, + {"default", TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT}, + {js_delete_str, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT}, + {"do", TOK_DO, JSOP_NOP, JSVERSION_DEFAULT}, + {"else", TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT}, + {"export", TOK_EXPORT, JSOP_NOP, JSVERSION_1_2}, + {js_false_str, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT}, + {"for", TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT}, + {js_function_str, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT}, + {"if", TOK_IF, JSOP_NOP, JSVERSION_DEFAULT}, + {js_in_str, TOK_IN, JSOP_IN, JSVERSION_DEFAULT}, + {js_new_str, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT}, + {js_null_str, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT}, + {"return", TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT}, + {"switch", TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT}, + {js_this_str, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT}, + {js_true_str, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT}, + {js_typeof_str, TOK_UNARYOP, JSOP_TYPEOF,JSVERSION_DEFAULT}, + {"var", TOK_VAR, JSOP_DEFVAR,JSVERSION_DEFAULT}, + {js_void_str, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT}, + {"while", TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT}, + {"with", TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT}, + +#if JS_HAS_CONST + {js_const_str, TOK_VAR, JSOP_DEFCONST,JSVERSION_DEFAULT}, +#else + {js_const_str, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#if JS_HAS_EXCEPTIONS + {"try", TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT}, + {"catch", TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT}, + {"finally", TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT}, + {"throw", TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT}, +#else + {"try", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"catch", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"finally", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"throw", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#if JS_HAS_INSTANCEOF + {js_instanceof_str, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_1_4}, +#else + {js_instanceof_str, TOK_RESERVED, JSOP_NOP, JSVERSION_1_4}, +#endif + +#ifdef RESERVE_JAVA_KEYWORDS + {"abstract", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"boolean", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"byte", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"char", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"class", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"double", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"extends", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"final", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"float", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"goto", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"implements", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"import", TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT}, + {"int", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"interface", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"long", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"native", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"package", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"private", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"protected", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"public", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"short", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"static", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"super", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"synchronized", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"throws", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"transient", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"volatile", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#ifdef RESERVE_ECMA_KEYWORDS + {"enum", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, +#endif + +#if JS_HAS_DEBUGGER_KEYWORD + {"debugger", TOK_DEBUGGER, JSOP_NOP, JSVERSION_1_3}, +#elif defined(RESERVE_ECMA_KEYWORDS) + {"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, +#endif + {0, TOK_EOF, JSOP_NOP, JSVERSION_DEFAULT} +}; + +JSBool +js_InitScanner(JSContext *cx) +{ + struct keyword *kw; + JSAtom *atom; + + for (kw = keywords; kw->name; kw++) { + atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED); + if (!atom) + return JS_FALSE; + ATOM_SET_KEYWORD(atom, kw); + } + return JS_TRUE; +} + +JS_FRIEND_API(void) +js_MapKeywords(void (*mapfun)(const char *)) +{ + struct keyword *kw; + + for (kw = keywords; kw->name; kw++) + mapfun(kw->name); +} + +JSTokenStream * +js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, + const char *filename, uintN lineno, + JSPrincipals *principals) +{ + JSTokenStream *ts; + + ts = js_NewBufferTokenStream(cx, base, length); + if (!ts) + return NULL; + ts->filename = filename; + ts->lineno = lineno; + if (principals) + JSPRINCIPALS_HOLD(cx, principals); + ts->principals = principals; + return ts; +} + +JS_FRIEND_API(JSTokenStream *) +js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) +{ + size_t nb; + JSTokenStream *ts; + + nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar); + JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb); + if (!ts) { + JS_ReportOutOfMemory(cx); + return NULL; + } + memset(ts, 0, nb); + ts->lineno = 1; + ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1); + ts->userbuf.base = (jschar *)base; + ts->userbuf.limit = (jschar *)base + length; + ts->userbuf.ptr = (jschar *)base; + ts->listener = cx->runtime->sourceHandler; + ts->listenerData = cx->runtime->sourceHandlerData; + return ts; +} + +JS_FRIEND_API(JSTokenStream *) +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp) +{ + jschar *base; + JSTokenStream *ts; + FILE *file; + + JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool, + JS_LINE_LIMIT * sizeof(jschar)); + if (!base) + return NULL; + ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT); + if (!ts) + return NULL; + if (!filename || strcmp(filename, "-") == 0) { + file = defaultfp; + } else { + file = fopen(filename, "r"); + if (!file) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN, + filename, "No such file or directory"); + return NULL; + } + } + ts->userbuf.ptr = ts->userbuf.limit; + ts->file = file; + ts->filename = filename; + return ts; +} + +JS_FRIEND_API(JSBool) +js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) +{ + if (ts->principals) + JSPRINCIPALS_DROP(cx, ts->principals); + return !ts->file || fclose(ts->file) == 0; +} + +static int +my_fgets(char *buf, int size, FILE *file) +{ + int n, i, c; + JSBool crflag; + + n = size - 1; + if (n < 0) + return -1; + + crflag = JS_FALSE; + for (i = 0; i < n && (c = getc(file)) != EOF; i++) { + buf[i] = c; + if (c == '\n') { /* any \n ends a line */ + i++; /* keep the \n; we know there is room for \0 */ + break; + } + if (crflag) { /* \r not followed by \n ends line at the \r */ + ungetc(c, file); + break; /* and overwrite c in buf with \0 */ + } + crflag = (c == '\r'); + } + + buf[i] = '\0'; + return i; +} + +static int32 +GetChar(JSTokenStream *ts) +{ + int32 c; + ptrdiff_t i, j, len, olen; + JSBool crflag; + char cbuf[JS_LINE_LIMIT]; + jschar *ubuf, *nl; + + if (ts->ungetpos != 0) { + c = ts->ungetbuf[--ts->ungetpos]; + } else { + do { + if (ts->linebuf.ptr == ts->linebuf.limit) { + len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar); + if (len <= 0) { + if (!ts->file) { + ts->flags |= TSF_EOF; + return EOF; + } + + /* Fill ts->userbuf so that \r and \r\n convert to \n. */ + crflag = (ts->flags & TSF_CRFLAG) != 0; + len = my_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); + if (len <= 0) { + ts->flags |= TSF_EOF; + return EOF; + } + olen = len; + ubuf = ts->userbuf.base; + i = 0; + if (crflag) { + ts->flags &= ~TSF_CRFLAG; + if (cbuf[0] != '\n') { + ubuf[i++] = '\n'; + len++; + ts->linepos--; + } + } + for (j = 0; i < len; i++, j++) + ubuf[i] = (jschar) (unsigned char) cbuf[j]; + ts->userbuf.limit = ubuf + len; + ts->userbuf.ptr = ubuf; + } + if (ts->listener) { + ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len, + &ts->listenerTSData, ts->listenerData); + } + + /* + * Any one of \n, \r, or \r\n ends a line (longest match wins). + * Also allow the Unicode line and paragraph separators. + */ + for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { + /* + * Try to prevent value-testing on most characters by + * filtering out characters that aren't 000x or 202x. + */ + if ((*nl & 0xDFD0) == 0) { + if (*nl == '\n') + break; + if (*nl == '\r') { + if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') + nl++; + break; + } + if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) + break; + } + } + + /* + * If there was a line terminator, copy thru it into linebuf. + * Else copy JS_LINE_LIMIT-1 bytes into linebuf. + */ + if (nl < ts->userbuf.limit) + len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1; + if (len >= JS_LINE_LIMIT) + len = JS_LINE_LIMIT - 1; + js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); + ts->userbuf.ptr += len; + olen = len; + + /* + * Make sure linebuf contains \n for EOL (don't do this in + * userbuf because the user's string might be readonly). + */ + if (nl < ts->userbuf.limit) { + if (*nl == '\r') { + if (ts->linebuf.base[len-1] == '\r') { + /* + * Does the line segment end in \r? We must check + * for a \n at the front of the next segment before + * storing a \n into linebuf. This case matters + * only when we're reading from a file. + */ + if (nl + 1 == ts->userbuf.limit && ts->file) { + len--; + ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */ + if (len == 0) { + /* + * This can happen when a segment ends in + * \r\r. Start over. ptr == limit in this + * case, so we'll fall into buffer-filling + * code. + */ + return GetChar(ts); + } + } else { + ts->linebuf.base[len-1] = '\n'; + } + } + } else if (*nl == '\n') { + if (nl > ts->userbuf.base && + nl[-1] == '\r' && + ts->linebuf.base[len-2] == '\r') { + len--; + JS_ASSERT(ts->linebuf.base[len] == '\n'); + ts->linebuf.base[len-1] = '\n'; + } + } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) { + ts->linebuf.base[len-1] = '\n'; + } + } + + /* Reset linebuf based on adjusted segment length. */ + ts->linebuf.limit = ts->linebuf.base + len; + ts->linebuf.ptr = ts->linebuf.base; + + /* Update position of linebuf within physical userbuf line. */ + if (!(ts->flags & TSF_NLFLAG)) + ts->linepos += ts->linelen; + else + ts->linepos = 0; + if (ts->linebuf.limit[-1] == '\n') + ts->flags |= TSF_NLFLAG; + else + ts->flags &= ~TSF_NLFLAG; + + /* Update linelen from original segment length. */ + ts->linelen = olen; + } + c = *ts->linebuf.ptr++; + } while (JS_ISFORMAT(c)); + } + if (c == '\n') + ts->lineno++; + return c; +} + +static void +UngetChar(JSTokenStream *ts, int32 c) +{ + if (c == EOF) + return; + JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]); + if (c == '\n') + ts->lineno--; + ts->ungetbuf[ts->ungetpos++] = (jschar)c; +} + +static int32 +PeekChar(JSTokenStream *ts) +{ + int32 c; + + c = GetChar(ts); + UngetChar(ts, c); + return c; +} + +static JSBool +PeekChars(JSTokenStream *ts, intN n, jschar *cp) +{ + intN i, j; + int32 c; + + for (i = 0; i < n; i++) { + c = GetChar(ts); + if (c == EOF) + break; + cp[i] = (jschar)c; + } + for (j = i - 1; j >= 0; j--) + UngetChar(ts, cp[j]); + return i == n; +} + +static void +SkipChars(JSTokenStream *ts, intN n) +{ + while (--n >= 0) + GetChar(ts); +} + +static JSBool +MatchChar(JSTokenStream *ts, int32 expect) +{ + int32 c; + + c = GetChar(ts); + if (c == expect) + return JS_TRUE; + UngetChar(ts, c); + return JS_FALSE; +} + +JSBool +js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, + JSCodeGenerator *cg, uintN flags, + const uintN errorNumber, ...) +{ + va_list ap; + JSErrorReporter onError; + JSErrorReport report; + jschar *tokenptr; + JSString *linestr = NULL; + char *message; + JSBool warning; + + if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) + return JS_TRUE; + + memset(&report, 0, sizeof (struct JSErrorReport)); + report.flags = flags; + report.errorNumber = errorNumber; + message = NULL; + + va_start(ap, errorNumber); + if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, + errorNumber, &message, &report, &warning, + JS_TRUE, ap)) { + return JS_FALSE; + } + va_end(ap); + + js_AddRoot(cx, &linestr, "error line buffer"); + + JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); + onError = cx->errorReporter; + if (onError) { + /* + * We are typically called with non-null ts and null cg from jsparse.c. + * We can be called with null ts from the regexp compilation functions. + * The code generator (jsemit.c) may pass null ts and non-null cg. + */ + if (ts) { + report.filename = ts->filename; + report.lineno = ts->lineno; + linestr = js_NewStringCopyN(cx, ts->linebuf.base, + ts->linebuf.limit - ts->linebuf.base, + 0); + report.linebuf = linestr + ? JS_GetStringBytes(linestr) + : NULL; + tokenptr = + ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr; + report.tokenptr = linestr + ? report.linebuf + (tokenptr - ts->linebuf.base) + : NULL; + report.uclinebuf = linestr + ? JS_GetStringChars(linestr) + : NULL; + report.uctokenptr = linestr + ? report.uclinebuf + (tokenptr - ts->linebuf.base) + : NULL; + } else if (cg) { + report.filename = cg->filename; + report.lineno = CG_CURRENT_LINE(cg); + } + +#if JS_HAS_ERROR_EXCEPTIONS + /* + * If there's a runtime exception type associated with this error + * number, set that as the pending exception. For errors occuring at + * compile time, this is very likely to be a JSEXN_SYNTAXERR. + * + * If an exception is thrown but not caught, the JSREPORT_EXCEPTION + * flag will be set in report.flags. Proper behavior for an error + * reporter is to ignore a report with this flag for all but top-level + * compilation errors. The exception will remain pending, and so long + * as the non-top-level "load", "eval", or "compile" native function + * returns false, the top-level reporter will eventually receive the + * uncaught exception report. + * + * XXX it'd probably be best if there was only one call to this + * function, but there seem to be two error reporter call points. + */ + + /* + * Only try to raise an exception if there isn't one already set - + * otherwise the exception will describe only the last compile error, + * which is likely spurious. + */ + if (!(ts && (ts->flags & TSF_ERROR))) + if (js_ErrorToException(cx, message, &report)) + onError = NULL; + + /* + * Suppress any compiletime errors that don't occur at the top level. + * This may still fail, as interplevel may be zero in contexts where we + * don't really want to call the error reporter, as when js is called + * by other code which could catch the error. + */ + if (cx->interpLevel != 0) + onError = NULL; +#endif + if (cx->runtime->debugErrorHook && onError) { + JSDebugErrorHook hook = cx->runtime->debugErrorHook; + /* test local in case debugErrorHook changed on another thread */ + if (hook && !hook(cx, message, &report, + cx->runtime->debugErrorHookData)) { + onError = NULL; + } + } + if (onError) + (*onError)(cx, message, &report); + } + if (message) + JS_free(cx, message); + if (report.messageArgs) { + int i = 0; + while (report.messageArgs[i]) + JS_free(cx, (void *)report.messageArgs[i++]); + JS_free(cx, (void *)report.messageArgs); + } + if (report.ucmessage) + JS_free(cx, (void *)report.ucmessage); + + js_RemoveRoot(cx->runtime, &linestr); + + if (ts && !JSREPORT_IS_WARNING(flags)) { + /* Set the error flag to suppress spurious reports. */ + ts->flags |= TSF_ERROR; + } + return warning; +} + +JSTokenType +js_PeekToken(JSContext *cx, JSTokenStream *ts) +{ + JSTokenType tt; + + if (ts->lookahead != 0) { + tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type; + } else { + tt = js_GetToken(cx, ts); + js_UngetToken(ts); + } + return tt; +} + +JSTokenType +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) +{ + JSTokenType tt; + + JS_ASSERT(ts->lookahead == 0 || + ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)); + ts->flags |= TSF_NEWLINES; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_NEWLINES; + return tt; +} + +#define TBMIN 64 + +static JSBool +GrowTokenBuf(JSContext *cx, JSTokenBuf *tb) +{ + jschar *base; + ptrdiff_t offset, length; + size_t tbsize; + JSArenaPool *pool; + + base = tb->base; + offset = PTRDIFF(tb->ptr, base, jschar); + pool = &cx->tempPool; + if (!base) { + tbsize = TBMIN * sizeof(jschar); + length = TBMIN; + JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); + } else { + length = PTRDIFF(tb->limit, base, jschar); + tbsize = length * sizeof(jschar); + length <<= 1; + JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); + } + if (!base) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + tb->base = base; + tb->limit = base + length; + tb->ptr = base + offset; + return JS_TRUE; +} + +static JSBool +AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c) +{ + if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb)) + return JS_FALSE; + *tb->ptr++ = c; + return JS_TRUE; +} + +/* + * We have encountered a '\': check for a Unicode escape sequence after it, + * returning the character code value if we found a Unicode escape sequence. + * Otherwise, non-destructively return the original '\'. + */ +static int32 +GetUnicodeEscape(JSTokenStream *ts) +{ + jschar cp[5]; + int32 c; + + if (PeekChars(ts, 5, cp) && cp[0] == 'u' && + JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && + JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) + { + c = (((((JS7_UNHEX(cp[1]) << 4) + + JS7_UNHEX(cp[2])) << 4) + + JS7_UNHEX(cp[3])) << 4) + + JS7_UNHEX(cp[4]); + SkipChars(ts, 5); + return c; + } + return '\\'; +} + +JSTokenType +js_GetToken(JSContext *cx, JSTokenStream *ts) +{ + JSTokenType tt; + JSToken *tp; + int32 c; + JSAtom *atom; + JSBool hadUnicodeEscape; + +#define INIT_TOKENBUF(tb) ((tb)->ptr = (tb)->base) +#define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR) +#define TOKEN_LENGTH(tb) ((tb)->ptr - (tb)->base - 1) +#define RETURN(tt) { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR; \ + tp->pos.end.index = ts->linepos + \ + (ts->linebuf.ptr - ts->linebuf.base) - \ + ts->ungetpos; \ + return (tp->type = tt); } + + /* If there was a fatal error, keep returning TOK_ERROR. */ + if (ts->flags & TSF_ERROR) + return TOK_ERROR; + + /* Check for a pushed-back token resulting from mismatching lookahead. */ + while (ts->lookahead != 0) { + ts->lookahead--; + ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; + tt = CURRENT_TOKEN(ts).type; + if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES)) + return tt; + } + +retry: + do { + c = GetChar(ts); + if (c == '\n') { + ts->flags &= ~TSF_DIRTYLINE; + if (ts->flags & TSF_NEWLINES) + break; + } + } while (JS_ISSPACE(c)); + + ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; + tp = &CURRENT_TOKEN(ts); + tp->ptr = ts->linebuf.ptr - 1; + tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base); + tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno; + + if (c == EOF) + RETURN(TOK_EOF); + if (c != '-' && c != '\n') + ts->flags |= TSF_DIRTYLINE; + + hadUnicodeEscape = JS_FALSE; + if (JS_ISIDENT_START(c) || + (c == '\\' && + (c = GetUnicodeEscape(ts), + hadUnicodeEscape = JS_ISIDENT_START(c)))) { + INIT_TOKENBUF(&ts->tokenbuf); + for (;;) { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + if (c == '\\') { + c = GetUnicodeEscape(ts); + if (!JS_ISIDENT(c)) + break; + hadUnicodeEscape = JS_TRUE; + } else { + if (!JS_ISIDENT(c)) + break; + } + } + UngetChar(ts, c); + FINISH_TOKENBUF(&ts->tokenbuf); + + atom = js_AtomizeChars(cx, + ts->tokenbuf.base, + TOKEN_LENGTH(&ts->tokenbuf), + 0); + if (!atom) + RETURN(TOK_ERROR); + if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) { + struct keyword *kw = ATOM_KEYWORD(atom); + + if (JSVERSION_IS_ECMA(cx->version) || kw->version <= cx->version) { + tp->t_op = (JSOp) kw->op; + RETURN(kw->tokentype); + } + } + tp->t_op = JSOP_NAME; + tp->t_atom = atom; + RETURN(TOK_NAME); + } + + if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { + jsint radix; + const jschar *endptr; + jsdouble dval; + + radix = 10; + INIT_TOKENBUF(&ts->tokenbuf); + + if (c == '0') { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + if (JS_TOLOWER(c) == 'x') { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + radix = 16; + } else if (JS7_ISDEC(c)) { + radix = 8; + } + } + + while (JS7_ISHEX(c)) { + if (radix < 16) { + if (JS7_ISLET(c)) + break; + + /* + * We permit 08 and 09 as decimal numbers, which makes our + * behaviour a superset of the ECMA numeric grammar. We might + * not always be so permissive, so we warn about it. + */ + if (radix == 8 && c >= '8') { + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING, + JSMSG_BAD_OCTAL, + c == '8' ? "08" : "09")) { + RETURN(TOK_ERROR); + } + radix = 10; + } + } + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + } + + if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { + if (c == '.') { + do { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + } while (JS7_ISDEC(c)); + } + if (JS_TOLOWER(c) == 'e') { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + if (c == '+' || c == '-') { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + } + if (!JS7_ISDEC(c)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_MISSING_EXPONENT); + RETURN(TOK_ERROR); + } + do { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + } while (JS7_ISDEC(c)); + } + } + + UngetChar(ts, c); + FINISH_TOKENBUF(&ts->tokenbuf); + + if (radix == 10) { + if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_OUT_OF_MEMORY); + RETURN(TOK_ERROR); + } + } else { + if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_OUT_OF_MEMORY); + RETURN(TOK_ERROR); + } + } + tp->t_dval = dval; + RETURN(TOK_NUMBER); + } + + if (c == '"' || c == '\'') { + int32 val, qc = c; + + INIT_TOKENBUF(&ts->tokenbuf); + while ((c = GetChar(ts)) != qc) { + if (c == '\n' || c == EOF) { + UngetChar(ts, c); + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_STRING); + RETURN(TOK_ERROR); + } + if (c == '\\') { + switch (c = GetChar(ts)) { + 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; + case 'v': c = '\v'; break; + + default: + if ('0' <= c && c < '8') { + val = JS7_UNDEC(c); + c = PeekChar(ts); + if ('0' <= c && c < '8') { + val = 8 * val + JS7_UNDEC(c); + GetChar(ts); + c = PeekChar(ts); + if ('0' <= c && c < '8') { + int32 save = val; + val = 8 * val + JS7_UNDEC(c); + if (val <= 0377) + GetChar(ts); + else + val = save; + } + } + c = (jschar)val; + } else if (c == 'u') { + jschar cp[4]; + if (PeekChars(ts, 4, cp) && + JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && + JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { + c = (((((JS7_UNHEX(cp[0]) << 4) + + JS7_UNHEX(cp[1])) << 4) + + JS7_UNHEX(cp[2])) << 4) + + JS7_UNHEX(cp[3]); + SkipChars(ts, 4); + } + } else if (c == 'x') { + jschar cp[2]; + if (PeekChars(ts, 2, cp) && + JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) { + c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); + SkipChars(ts, 2); + } + } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) { + /* ECMA follows C by removing escaped newlines. */ + continue; + } + break; + } + } + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + } + FINISH_TOKENBUF(&ts->tokenbuf); + atom = js_AtomizeChars(cx, + ts->tokenbuf.base, + TOKEN_LENGTH(&ts->tokenbuf), + 0); + if (!atom) + RETURN(TOK_ERROR); + tp->pos.end.lineno = (uint16)ts->lineno; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + RETURN(TOK_STRING); + } + + switch (c) { + case '\n': + c = TOK_EOL; + break; + + case ';': c = TOK_SEMI; break; + case '.': c = TOK_DOT; break; + case '[': c = TOK_LB; break; + case ']': c = TOK_RB; break; + case '{': c = TOK_LC; break; + case '}': c = TOK_RC; break; + case '(': c = TOK_LP; break; + case ')': c = TOK_RP; break; + case ',': c = TOK_COMMA; break; + case '?': c = TOK_HOOK; break; + + case ':': + /* + * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an + * object initializer, likewise for setter. + */ + tp->t_op = JSOP_NOP; + c = TOK_COLON; + break; + + case '|': + if (MatchChar(ts, c)) { + c = TOK_OR; + } else if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITOR; + c = TOK_ASSIGN; + } else { + c = TOK_BITOR; + } + break; + + case '^': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITXOR; + c = TOK_ASSIGN; + } else { + c = TOK_BITXOR; + } + break; + + case '&': + if (MatchChar(ts, c)) { + c = TOK_AND; + } else if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITAND; + c = TOK_ASSIGN; + } else { + c = TOK_BITAND; + } + break; + + case '=': + if (MatchChar(ts, c)) { +#if JS_HAS_TRIPLE_EQOPS + tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; +#else + tp->t_op = cx->jsop_eq; +#endif + c = TOK_EQOP; + } else { + tp->t_op = JSOP_NOP; + c = TOK_ASSIGN; + } + break; + + case '!': + if (MatchChar(ts, '=')) { +#if JS_HAS_TRIPLE_EQOPS + tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; +#else + tp->t_op = cx->jsop_ne; +#endif + c = TOK_EQOP; + } else { + tp->t_op = JSOP_NOT; + c = TOK_UNARYOP; + } + break; + + case '<': + /* NB: treat HTML begin-comment as comment-till-end-of-line */ + if (MatchChar(ts, '!')) { + if (MatchChar(ts, '-')) { + if (MatchChar(ts, '-')) + goto skipline; + UngetChar(ts, '-'); + } + UngetChar(ts, '!'); + } + if (MatchChar(ts, c)) { + tp->t_op = JSOP_LSH; + c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; + } else { + tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT; + c = TOK_RELOP; + } + break; + + case '>': + if (MatchChar(ts, c)) { + tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH; + c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; + } else { + tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT; + c = TOK_RELOP; + } + break; + + case '*': + tp->t_op = JSOP_MUL; + c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR; + break; + + case '/': + if (MatchChar(ts, '/')) { +skipline: + while ((c = GetChar(ts)) != EOF && c != '\n') + /* skip to end of line */; + UngetChar(ts, c); + goto retry; + } + if (MatchChar(ts, '*')) { + while ((c = GetChar(ts)) != EOF && + !(c == '*' && MatchChar(ts, '/'))) { + /* Ignore all characters until comment close. */ + } + if (c == EOF) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_COMMENT); + RETURN(TOK_ERROR); + } + goto retry; + } + +#if JS_HAS_REGEXPS + if (ts->flags & TSF_REGEXP) { + JSObject *obj; + uintN flags; + + INIT_TOKENBUF(&ts->tokenbuf); + while ((c = GetChar(ts)) != '/') { + if (c == '\n' || c == EOF) { + UngetChar(ts, c); + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_REGEXP); + RETURN(TOK_ERROR); + } + if (c == '\\') { + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + c = GetChar(ts); + } + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) + RETURN(TOK_ERROR); + } + FINISH_TOKENBUF(&ts->tokenbuf); + for (flags = 0; ; ) { + if (MatchChar(ts, 'g')) + flags |= JSREG_GLOB; + else if (MatchChar(ts, 'i')) + flags |= JSREG_FOLD; + else if (MatchChar(ts, 'm')) + flags |= JSREG_MULTILINE; + else + break; + } + c = PeekChar(ts); + if (JS7_ISLET(c)) { + tp->ptr = ts->linebuf.ptr - 1; + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_REGEXP_FLAG); + (void) GetChar(ts); + RETURN(TOK_ERROR); + } + obj = js_NewRegExpObject(cx, ts, + ts->tokenbuf.base, + TOKEN_LENGTH(&ts->tokenbuf), + flags); + if (!obj) + RETURN(TOK_ERROR); + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) + RETURN(TOK_ERROR); + tp->t_op = JSOP_OBJECT; + tp->t_atom = atom; + RETURN(TOK_OBJECT); + } +#endif /* JS_HAS_REGEXPS */ + + tp->t_op = JSOP_DIV; + c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; + break; + + case '%': + tp->t_op = JSOP_MOD; + c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; + break; + + case '~': + tp->t_op = JSOP_BITNOT; + c = TOK_UNARYOP; + break; + + case '+': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_ADD; + c = TOK_ASSIGN; + } else if (MatchChar(ts, c)) { + c = TOK_INC; + } else { + tp->t_op = JSOP_POS; + c = TOK_PLUS; + } + break; + + case '-': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_SUB; + c = TOK_ASSIGN; + } else if (MatchChar(ts, c)) { + if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) + goto skipline; + c = TOK_DEC; + } else { + tp->t_op = JSOP_NEG; + c = TOK_MINUS; + } + ts->flags |= TSF_DIRTYLINE; + break; + +#if JS_HAS_SHARP_VARS + case '#': + { + uint32 n; + + c = GetChar(ts); + if (!JS7_ISDEC(c)) { + UngetChar(ts, c); + goto badchar; + } + n = (uint32)JS7_UNDEC(c); + for (;;) { + c = GetChar(ts); + if (!JS7_ISDEC(c)) + break; + n = 10 * n + JS7_UNDEC(c); + if (n >= ATOM_INDEX_LIMIT) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SHARPVAR_TOO_BIG); + RETURN(TOK_ERROR); + } + } + tp->t_dval = (jsdouble) n; + if (JS_HAS_STRICT_OPTION(cx) && + (c == '=' || c == '#')) { + char buf[20]; + JS_snprintf(buf, sizeof buf, "#%u%c", n, c); + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DEPRECATED_USAGE, + buf)) { + RETURN(TOK_ERROR); + } + } + if (c == '=') + RETURN(TOK_DEFSHARP); + if (c == '#') + RETURN(TOK_USESHARP); + goto badchar; + } + + badchar: +#endif /* JS_HAS_SHARP_VARS */ + + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_ILLEGAL_CHARACTER); + RETURN(TOK_ERROR); + } + + JS_ASSERT(c < TOK_LIMIT); + RETURN((JSTokenType)c); + +#undef INIT_TOKENBUF +#undef FINISH_TOKENBUF +#undef TOKEN_LENGTH +#undef RETURN +} + +void +js_UngetToken(JSTokenStream *ts) +{ + JS_ASSERT(ts->lookahead < NTOKENS_MASK); + if (ts->flags & TSF_ERROR) + return; + ts->lookahead++; + ts->cursor = (ts->cursor - 1) & NTOKENS_MASK; +} + +JSBool +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt) +{ + if (js_GetToken(cx, ts) == tt) + return JS_TRUE; + js_UngetToken(ts); + return JS_FALSE; +} diff --git a/src/extension/script/js/jsscan.h b/src/extension/script/js/jsscan.h new file mode 100644 index 000000000..c61eff926 --- /dev/null +++ b/src/extension/script/js/jsscan.h @@ -0,0 +1,264 @@ +/* -*- 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 jsscan_h___ +#define jsscan_h___ +/* + * JS lexical scanner interface. + */ +#include +#include +#include "jsopcode.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +JS_BEGIN_EXTERN_C + +typedef enum JSTokenType { + TOK_ERROR = -1, /* well-known as the only code < EOF */ + TOK_EOF = 0, /* end of file */ + TOK_EOL = 1, /* end of line */ + TOK_SEMI = 2, /* semicolon */ + TOK_COMMA = 3, /* comma operator */ + TOK_ASSIGN = 4, /* assignment ops (= += -= etc.) */ + TOK_HOOK = 5, TOK_COLON = 6, /* conditional (?:) */ + TOK_OR = 7, /* logical or (||) */ + TOK_AND = 8, /* logical and (&&) */ + TOK_BITOR = 9, /* bitwise-or (|) */ + TOK_BITXOR = 10, /* bitwise-xor (^) */ + TOK_BITAND = 11, /* bitwise-and (&) */ + TOK_EQOP = 12, /* equality ops (== !=) */ + TOK_RELOP = 13, /* relational ops (< <= > >=) */ + TOK_SHOP = 14, /* shift ops (<< >> >>>) */ + TOK_PLUS = 15, /* plus */ + TOK_MINUS = 16, /* minus */ + TOK_STAR = 17, TOK_DIVOP = 18, /* multiply/divide ops (* / %) */ + TOK_UNARYOP = 19, /* unary prefix operator */ + TOK_INC = 20, TOK_DEC = 21, /* increment/decrement (++ --) */ + TOK_DOT = 22, /* member operator (.) */ + TOK_LB = 23, TOK_RB = 24, /* left and right brackets */ + TOK_LC = 25, TOK_RC = 26, /* left and right curlies (braces) */ + TOK_LP = 27, TOK_RP = 28, /* left and right parentheses */ + TOK_NAME = 29, /* identifier */ + TOK_NUMBER = 30, /* numeric constant */ + TOK_STRING = 31, /* string constant */ + TOK_OBJECT = 32, /* RegExp or other object constant */ + TOK_PRIMARY = 33, /* true, false, null, this, super */ + TOK_FUNCTION = 34, /* function keyword */ + TOK_EXPORT = 35, /* export keyword */ + TOK_IMPORT = 36, /* import keyword */ + TOK_IF = 37, /* if keyword */ + TOK_ELSE = 38, /* else keyword */ + TOK_SWITCH = 39, /* switch keyword */ + TOK_CASE = 40, /* case keyword */ + TOK_DEFAULT = 41, /* default keyword */ + TOK_WHILE = 42, /* while keyword */ + TOK_DO = 43, /* do keyword */ + TOK_FOR = 44, /* for keyword */ + TOK_BREAK = 45, /* break keyword */ + TOK_CONTINUE = 46, /* continue keyword */ + TOK_IN = 47, /* in keyword */ + TOK_VAR = 48, /* var keyword */ + TOK_WITH = 49, /* with keyword */ + TOK_RETURN = 50, /* return keyword */ + TOK_NEW = 51, /* new keyword */ + TOK_DELETE = 52, /* delete keyword */ + TOK_DEFSHARP = 53, /* #n= for object/array initializers */ + TOK_USESHARP = 54, /* #n# for object/array initializers */ + TOK_TRY = 55, /* try keyword */ + TOK_CATCH = 56, /* catch keyword */ + TOK_FINALLY = 57, /* finally keyword */ + TOK_THROW = 58, /* throw keyword */ + TOK_INSTANCEOF = 59, /* instanceof keyword */ + TOK_DEBUGGER = 60, /* debugger keyword */ + TOK_RESERVED, /* reserved keywords */ + TOK_LIMIT /* domain size */ +} JSTokenType; + +#define IS_PRIMARY_TOKEN(tt) \ + ((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME)) + +struct JSTokenPtr { + uint16 index; /* index of char in physical line */ + uint16 lineno; /* physical line number */ +}; + +struct JSTokenPos { + JSTokenPtr begin; /* first character and line of token */ + JSTokenPtr end; /* index 1 past last char, last line */ +}; + +struct JSToken { + JSTokenType type; /* char value or above enumerator */ + JSTokenPos pos; /* token position in file */ + jschar *ptr; /* beginning of token in line buffer */ + union { + struct { + JSOp op; /* operator, for minimal parser */ + JSAtom *atom; /* atom table entry */ + } s; + jsdouble dval; /* floating point number */ + } u; +}; + +#define t_op u.s.op +#define t_atom u.s.atom +#define t_dval u.dval + +typedef struct JSTokenBuf { + jschar *base; /* base of line or stream buffer */ + jschar *limit; /* limit for quick bounds check */ + jschar *ptr; /* next char to get, or slot to use */ +} JSTokenBuf; + +#define JS_LINE_LIMIT 256 /* logical line buffer size limit -- + physical line length is unlimited */ +#define NTOKENS 4 /* 1 current + 2 lookahead, rounded */ +#define NTOKENS_MASK (NTOKENS-1) /* to power of 2 to avoid divmod by 3 */ + +struct JSTokenStream { + JSToken tokens[NTOKENS];/* circular token buffer */ + uintN cursor; /* index of last parsed token */ + uintN lookahead; /* count of lookahead tokens */ + uintN lineno; /* current line number */ + uintN ungetpos; /* next free char slot in ungetbuf */ + jschar ungetbuf[6]; /* at most 6, for \uXXXX lookahead */ + uintN flags; /* flags -- see below */ + ptrdiff_t linelen; /* physical linebuf segment length */ + ptrdiff_t linepos; /* linebuf offset in physical line */ + JSTokenBuf linebuf; /* line buffer for diagnostics */ + JSTokenBuf userbuf; /* user input buffer if !file */ + JSTokenBuf tokenbuf; /* current token string buffer */ + const char *filename; /* input filename or null */ + FILE *file; /* stdio stream if reading from file */ + JSPrincipals *principals; /* principals associated with source */ + JSSourceHandler listener; /* callback for source; eg debugger */ + void *listenerData; /* listener 'this' data */ + void *listenerTSData;/* listener data for this TokenStream */ +}; + +#define CURRENT_TOKEN(ts) ((ts)->tokens[(ts)->cursor]) +#define ON_CURRENT_LINE(ts,pos) ((uint16)(ts)->lineno == (pos).end.lineno) + +/* JSTokenStream flags */ +#define TSF_ERROR 0x01 /* fatal error while compiling */ +#define TSF_EOF 0x02 /* hit end of file */ +#define TSF_NEWLINES 0x04 /* tokenize newlines */ +#define TSF_REGEXP 0x08 /* looking for a regular expression */ +#define TSF_NLFLAG 0x20 /* last linebuf ended with \n */ +#define TSF_CRFLAG 0x40 /* linebuf would have ended with \r */ +#define TSF_DIRTYLINE 0x80 /* stuff other than whitespace since start of line */ + +/* Unicode separators that are treated as line terminators, in addition to \n, \r */ +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +/* + * Create a new token stream, either from an input buffer or from a file. + * Return null on file-open or memory-allocation failure. + * + * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient + * memory in the current context's temp pool. This memory is deallocated via + * JS_ARENA_RELEASE() after parsing is finished. + */ +extern JSTokenStream * +js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, + const char *filename, uintN lineno, JSPrincipals *principals); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); + +extern JS_FRIEND_API(JSBool) +js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); + +/* + * Initialize the scanner, installing JS keywords into cx's global scope. + */ +extern JSBool +js_InitScanner(JSContext *cx); + +/* + * 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 *)); + +/* + * Report a compile-time error by its number, using ts or cg to show context. + * Return true for a warning, false for an error. + */ +extern JSBool +js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, + JSCodeGenerator *cg, uintN flags, + const uintN errorNumber, ...); + +/* + * 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/src/extension/script/js/jsscope.c b/src/extension/script/js/jsscope.c new file mode 100644 index 000000000..20243e9b7 --- /dev/null +++ b/src/extension/script/js/jsscope.c @@ -0,0 +1,1581 @@ +/* -*- 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 symbol tables. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.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" + +JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj) +{ + JSScope *scope, *newscope; + + scope = OBJ_SCOPE(obj); + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + if (scope->object == obj) + return scope; + newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), + obj); + if (!newscope) + return NULL; + JS_LOCK_SCOPE(cx, newscope); + obj->map = js_HoldObjectMap(cx, &newscope->map); + scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + 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 *)) + +static void +InitMinimalScope(JSScope *scope) +{ + scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; + scope->entryCount = scope->removedCount = 0; + scope->table = NULL; + scope->lastProp = NULL; +} + +static JSBool +CreateScopeTable(JSScope *scope) +{ + int sizeLog2; + JSScopeProperty *sprop, **spp; + + JS_ASSERT(!scope->table); + JS_ASSERT(scope->lastProp); + + if (scope->entryCount > SCOPE_HASH_THRESHOLD) { + /* + * Ouch: calloc failed at least once already -- let's try again, + * overallocating to hold at least twice the current population. + */ + sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); + scope->hashShift = JS_DHASH_BITS - sizeLog2; + } else { + JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); + sizeLog2 = MIN_SCOPE_SIZE_LOG2; + } + + scope->table = (JSScopeProperty **) + calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); + if (!scope->table) + return JS_FALSE; + + scope->hashShift = JS_DHASH_BITS - sizeLog2; + for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + } + return JS_TRUE; +} + +JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj) +{ + JSScope *scope; + + scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); + if (!scope) + return NULL; + + js_InitObjectMap(&scope->map, nrefs, ops, clasp); + scope->object = obj; + scope->flags = 0; + InitMinimalScope(scope); + +#ifdef JS_THREADSAFE + scope->ownercx = cx; + memset(&scope->lock, 0, sizeof scope->lock); + + /* + * Set u.link = NULL, not u.count = 0, in case the target architecture's + * null pointer has a non-zero integer representation. + */ + scope->u.link = NULL; + +#ifdef DEBUG + scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; + scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; +#endif +#endif + + JS_RUNTIME_METER(cx->runtime, liveScopes); + JS_RUNTIME_METER(cx->runtime, totalScopes); + return scope; +} + +#ifdef DEBUG_SCOPE_COUNT +extern void +js_unlog_scope(JSScope *scope); +#endif + +void +js_DestroyScope(JSContext *cx, JSScope *scope) +{ +#ifdef DEBUG_SCOPE_COUNT + js_unlog_scope(scope); +#endif + +#ifdef JS_THREADSAFE + /* Scope must be single-threaded at this point, so set scope->ownercx. */ + JS_ASSERT(scope->u.count == 0); + scope->ownercx = cx; + js_FinishLock(&scope->lock); +#endif + if (scope->table) + JS_free(cx, scope->table); + +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + JS_RUNTIME_UNMETER(cx->runtime, liveScopes); + JS_free(cx, scope); +} + +#ifdef DEBUG_brendan +typedef struct JSScopeStats { + jsrefcount searches; + jsrefcount steps; + jsrefcount hits; + jsrefcount misses; + jsrefcount stepHits; + jsrefcount stepMisses; + jsrefcount adds; + jsrefcount redundantAdds; + jsrefcount addFailures; + jsrefcount changeFailures; + jsrefcount compresses; + jsrefcount grows; + jsrefcount removes; + jsrefcount removeFrees; + jsrefcount uselessRemoves; + jsrefcount shrinks; +} JSScopeStats; + +JS_FRIEND_DATA(JSScopeStats) js_scope_stats; + +# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) +#else +# define METER(x) /* nothing */ +#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 int + * property index or named property's atom number (observe that most objects + * have either no indexed properties, or almost all indexed and a few names, + * so collisions between index and atom number are unlikely). + */ +#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) + +JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding) +{ + JSHashNumber hash0, hash1, hash2; + int hashShift, sizeLog2; + JSScopeProperty *stored, *sprop, **spp, **firstRemoved; + uint32 sizeMask; + + METER(searches); + if (!scope->table) { + /* Not enough properties to justify hashing: search from lastProp. */ + JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); + for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { + if (sprop->id == id) { + METER(hits); + return spp; + } + } + METER(misses); + return spp; + } + + /* Compute the primary hash address. */ + hash0 = SCOPE_HASH0(id); + hashShift = scope->hashShift; + hash1 = SCOPE_HASH1(hash0, hashShift); + spp = scope->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 = scope->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; +} + +static JSBool +ChangeScope(JSContext *cx, JSScope *scope, int change) +{ + int oldlog2, newlog2; + uint32 oldsize, newsize, nbytes; + JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; + + /* Grow, shrink, or compress by changing scope->table. */ + oldlog2 = JS_DHASH_BITS - scope->hashShift; + newlog2 = oldlog2 + change; + oldsize = JS_BIT(oldlog2); + newsize = JS_BIT(newlog2); + nbytes = SCOPE_TABLE_NBYTES(newsize); + table = (JSScopeProperty **) calloc(nbytes, 1); + if (!table) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + /* Now that we have a new table allocated, update scope members. */ + scope->hashShift = JS_DHASH_BITS - newlog2; + scope->removedCount = 0; + oldtable = scope->table; + scope->table = table; + + /* Copy only live entries, leaving removed and free ones behind. */ + for (oldspp = oldtable; oldsize != 0; oldspp++) { + sprop = SPROP_FETCH(oldspp); + if (sprop) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + JS_ASSERT(SPROP_IS_FREE(*spp)); + *spp = sprop; + } + oldsize--; + } + + /* Finally, free the old table storage. */ + JS_free(cx, oldtable); + return JS_TRUE; +} + +/* + * Take care to exclude the mark and duplicate bits, in case we're called from + * the GC, or we are searching for a property that has not yet been flagged as + * a duplicate when making a duplicate formal parameter. + */ +#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) + +JS_STATIC_DLL_CALLBACK(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; + gsop = sprop->getter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + gsop = sprop->setter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) + ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (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) \ + ((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)) + +JS_STATIC_DLL_CALLBACK(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_DHashGetKeyStub, + 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 the runtime lock 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 + +typedef struct PropTreeKidsChunk PropTreeKidsChunk; + +struct PropTreeKidsChunk { + JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; + PropTreeKidsChunk *next; +}; + +static PropTreeKidsChunk * +NewPropTreeKidsChunk(JSRuntime *rt) +{ + PropTreeKidsChunk *chunk; + + chunk = calloc(1, 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); + free(chunk); +} + +/* NB: Called with the runtime lock held. */ +static JSBool +InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, + JSScopeProperty *child) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty **childp, *kids, *sprop; + PropTreeKidsChunk *chunk, **chunkp; + uintN i; + + JS_ASSERT(!parent || child->parent != parent); + + if (!parent) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + return JS_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 { + childp = &parent->kids; + kids = *childp; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + 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); + + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_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); + } + + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + parent->kids = CHUNK_TO_KIDS(chunk); + chunk->kids[0] = sprop; + childp = &chunk->kids[1]; + } + } + insert: + *childp = child; + } + + child->parent = parent; + return JS_TRUE; +} + +/* NB: Called with the runtime lock held. */ +static void +RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty *parent, *kids, *kid; + PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; + uintN i, j; + + 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. + */ + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); + + if (entry->child == child) + JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); + } else { + kids = parent->kids; + if (KIDS_IS_CHUNKY(kids)) { + list = chunk = KIDS_TO_CHUNK(kids); + chunkp = &list; + + 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; + DestroyPropTreeKidsChunk(rt, lastChunk); + } + return; + } + } + + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + } else { + kid = kids; + if (kid == child) + parent->kids = NULL; + } + } +} + +/* + * Called *without* the runtime lock 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! + */ +static JSScopeProperty * +GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, + JSScopeProperty *child) +{ + JSRuntime *rt; + JSPropertyTreeEntry *entry; + JSScopeProperty *sprop; + PropTreeKidsChunk *chunk; + uintN i; + + rt = cx->runtime; + if (!parent) { + JS_LOCK_RUNTIME(rt); + + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + goto out_of_memory; + + sprop = entry->child; + if (sprop) + goto out; + } else { + /* + * Because chunks are appended at the end and never deleted except by + * the GC, we can search without taking the runtime 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. + * + * If workload changes so as to increase fan-out significantly below + * the property tree root, we'll want to add another tag bit stored in + * parent->kids that indicates a JSDHashTable pointer. + */ + entry = NULL; + sprop = parent->kids; + if (sprop) { + if (KIDS_IS_CHUNKY(sprop)) { + chunk = KIDS_TO_CHUNK(sprop); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) + goto not_found; + + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } while ((chunk = chunk->next) != NULL); + } else { + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } + + not_found: + JS_LOCK_RUNTIME(rt); + } + + 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; + if (!parent) { + entry->child = sprop; + } else { + if (!InsertPropertyTreeChild(rt, parent, sprop)) + goto out_of_memory; + } + +out: + JS_UNLOCK_RUNTIME(rt); + return sprop; + +out_of_memory: + JS_UNLOCK_RUNTIME(rt); + JS_ReportOutOfMemory(cx); + return NULL; +} + +#ifdef DEBUG_notbrendan +#define CHECK_ANCESTOR_LINE(scope, sparse) \ + JS_BEGIN_MACRO \ + if ((scope)->table) CheckAncestorLine(scope, sparse); \ + JS_END_MACRO + +static void +CheckAncestorLine(JSScope *scope, JSBool sparse) +{ + uint32 size; + JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; + uint32 entryCount, ancestorCount; + + ancestorLine = SCOPE_LAST_PROP(scope); + if (ancestorLine) + JS_ASSERT(SCOPE_HAS_PROPERTY(scope, 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) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && + !SCOPE_HAS_PROPERTY(scope, sprop)) { + JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); + continue; + } + ancestorCount++; + } + JS_ASSERT(ancestorCount == scope->entryCount); +} +#else +#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ +#endif + +static void +ReportReadOnlyScope(JSContext *cx, JSScope *scope) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, + str + ? JS_GetStringBytes(str) + : LOCKED_OBJ_GET_CLASS(scope->object)->name); +} + +JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; + uint32 size, splen, i; + int change; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* + * 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 (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return NULL; + } + + /* + * Normalize stub getter and setter values for faster is-stub testing in + * the SPROP_CALL_[GS]ETTER macros. + */ + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + + /* + * Search for id in order to claim its entry, allocating a property tree + * node if one doesn't already exist for our parameters. + */ + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + if (!sprop) { + /* Check whether we need to grow, if the load factor is >= .75. */ + size = SCOPE_CAPACITY(scope); + if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { + if (scope->removedCount >= size >> 2) { + METER(compresses); + change = 0; + } else { + METER(grows); + change = 1; + } + if (!ChangeScope(cx, scope, change) && + scope->entryCount + scope->removedCount == size - 1) { + METER(addFailures); + return NULL; + } + spp = js_SearchScope(scope, id, JS_TRUE); + JS_ASSERT(!SPROP_FETCH(spp)); + } + } else { + /* Property exists: js_SearchScope must have returned a valid entry. */ + JS_ASSERT(!SPROP_IS_REMOVED(*spp)); + + /* + * 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, scope)) { + slot = sprop->slot; + } + if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, + flags, shortid)) { + METER(redundantAdds); + return sprop; + } + + /* + * Duplicate formal parameters require us to leave the old property + * on the ancestor line, so the decompiler can find it, even though + * its entry in scope->table is overwritten to point at a new property + * descending from the old one. The SPROP_IS_DUPLICATE flag helps us + * cope with the consequent disparity between ancestor line height and + * scope->entryCount. + */ + if (flags & SPROP_IS_DUPLICATE) { + sprop->flags |= SPROP_IS_DUPLICATE; + } else { + /* + * If we are clearing sprop to force an existing property to be + * overwritten (apart from a duplicate formal parameter), we must + * unlink it from the ancestor line at scope->lastProp, lazily if + * sprop is not lastProp. And we must remove the entry at *spp, + * precisely so the lazy "middle delete" fixup code further below + * won't find sprop in scope->table, in spite of sprop being on + * the ancestor line. + * + * When we finally succeed in finding or creating a new sprop + * and storing its pointer at *spp, we'll use the |overwriting| + * local saved when we first looked up id to decide whether we're + * indeed creating a new entry, or merely overwriting an existing + * property. + */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + /* + * If we have no hash table yet, we need one now. The middle + * delete code is simple-minded that way! + */ + if (!scope->table) { + if (!CreateScopeTable(scope)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + } + SCOPE_SET_MIDDLE_DELETE(scope); + } + } + + /* + * If we fail later on trying to find or create a new sprop, we will + * goto fail_overwrite and restore *spp from |overwriting|. Note that + * we don't bother to keep scope->removedCount in sync, because we'll + * fix up *spp and scope->entryCount shortly, no matter how control + * flow returns from this function. + */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, NULL); + scope->entryCount--; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + sprop = NULL; + } + + if (!sprop) { + /* + * If properties were deleted from the middle of the list starting at + * scope->lastProp, we may need to fork the property tree and squeeze + * all deleted properties out of scope's ancestor line. Otherwise we + * risk adding a node with the same id as a "middle" node, violating + * the rule that properties along an ancestor line have distinct ids + * (unless flagged SPROP_IS_DUPLICATE). + */ + if (SCOPE_HAD_MIDDLE_DELETE(scope)) { + JS_ASSERT(scope->table); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + splen = scope->entryCount; + if (splen == 0) { + JS_ASSERT(scope->lastProp == NULL); + } else { + /* + * Enumerate live entries in scope->table using a temporary + * vector, by walking the (possibly sparse, due to deletions) + * ancestor line from scope->lastProp. + */ + spvec = (JSScopeProperty **) + JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); + if (!spvec) + goto fail_overwrite; + i = splen; + sprop = SCOPE_LAST_PROP(scope); + JS_ASSERT(sprop); + do { + /* + * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- + * the latter insists that sprop->id maps to sprop, while + * the former simply tests whether sprop->id is bound in + * scope. We must allow for duplicate formal parameters + * along the ancestor line, and fork them as needed. + */ + if (!SCOPE_GET_PROPERTY(scope, sprop->id)) + continue; + + JS_ASSERT(sprop != overwriting); + if (i == 0) { + /* + * If our original splen estimate, scope->entryCount, + * is less than the ancestor line height, there must + * be duplicate formal parameters in this (function + * object) scope. Count remaining ancestors in order + * to realloc spvec. + */ + JSScopeProperty *tmp = sprop; + do { + if (SCOPE_GET_PROPERTY(scope, tmp->id)) + i++; + } while ((tmp = tmp->parent) != NULL); + spp2 = (JSScopeProperty **) + JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); + if (!spp2) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spvec = spp2; + memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); + splen += i; + } + + spvec[--i] = sprop; + } while ((sprop = sprop->parent) != NULL); + JS_ASSERT(i == 0); + + /* + * Now loop forward through spvec, forking the property tree + * whenever we see a "parent gap" due to deletions from scope. + * NB: sprop is null on first entry to the loop body. + */ + do { + if (spvec[i]->parent == sprop) { + sprop = spvec[i]; + } else { + sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); + if (!sprop) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); + SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); + } + } while (++i < splen); + JS_free(cx, spvec); + + /* + * Now sprop points to the last property in scope, where the + * ancestor line from sprop to the root is dense w.r.t. scope: + * it contains no nodes not mapped by scope->table, apart from + * any stinking ECMA-mandated duplicate formal parameters. + */ + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); + } + + SCOPE_CLR_MIDDLE_DELETE(scope); + } + + /* + * Aliases share another property's slot, passed in the |slot| param. + * Shared properties have no slot. Unshared properties that do not + * alias another property's slot get one here, but may lose it due to + * a JS_ClearScope call. + */ + if (!(flags & SPROP_IS_ALIAS)) { + if (attrs & JSPROP_SHARED) { + 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. + * Callers should therefore pass SPROP_INVALID_SLOT for all + * non-alias, unshared property adds. + */ + if (slot != SPROP_INVALID_SLOT) + JS_ASSERT(overwriting); + else if (!js_AllocSlot(cx, scope->object, &slot)) + goto fail_overwrite; + } + } + + /* + * Check for a watchpoint on a deleted property; if one exists, change + * setter to js_watch_set. + * 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) + goto fail_overwrite; + } + + /* 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 = GetPropertyTreeChild(cx, scope->lastProp, &child); + if (!sprop) + goto fail_overwrite; + + /* Store the tree node pointer in the table entry for id. */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + scope->entryCount++; + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + if (!overwriting) { + JS_RUNTIME_METER(cx->runtime, liveScopeProps); + JS_RUNTIME_METER(cx->runtime, totalScopeProps); + } + + /* + * If we reach the hashing threshold, try to allocate scope->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 !scope->table and + * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the + * entry count just reached the threshold. + */ + if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) + (void) CreateScopeTable(scope); + } + + METER(adds); + return sprop; + +fail_overwrite: + if (overwriting) { + /* + * We may or may not have forked overwriting out of scope's ancestor + * line, so we must check (the alternative is to set a flag above, but + * that hurts the common, non-error case). If we did fork overwriting + * out, we'll add it back at scope->lastProp. This means enumeration + * order can change due to a failure to overwrite an id. + * XXXbe very minor incompatibility + */ + for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { + if (!sprop) { + sprop = SCOPE_LAST_PROP(scope); + if (overwriting->parent == sprop) { + scope->lastProp = overwriting; + } else { + sprop = GetPropertyTreeChild(cx, sprop, overwriting); + if (sprop) { + JS_ASSERT(sprop != overwriting); + scope->lastProp = sprop; + } + overwriting = sprop; + } + break; + } + if (sprop == overwriting) + break; + } + if (overwriting) { + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); + scope->entryCount++; + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + METER(addFailures); + return NULL; +} + +JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter) +{ + JSScopeProperty child, *newsprop, **spp; + + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Allow only shared (slot-less) => unshared (slot-full) transition. */ + attrs |= sprop->attrs & mask; + JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || + !(attrs & JSPROP_SHARED)); + 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 (SCOPE_LAST_PROP(scope) == sprop) { + /* + * Optimize the case where the last property added to scope is changed + * to have a different attrs, getter, or setter. In the last property + * case, we need not fork the property tree. But since we do not call + * js_AddScopeProperty, we may need to allocate a new slot directly. + */ + if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { + JS_ASSERT(child.slot == SPROP_INVALID_SLOT); + if (!js_AllocSlot(cx, scope->object, &child.slot)) + return NULL; + } + + newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); + if (newsprop) { + spp = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp) == sprop); + + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); + scope->lastProp = newsprop; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + } else { + /* + * Let js_AddScopeProperty handle this |overwriting| case, including + * the conservation of sprop->slot (if it's valid). We must not call + * js_RemoveScopeProperty here, it will free a valid sprop->slot and + * js_AddScopeProperty won't re-allocate it. + */ + newsprop = js_AddScopeProperty(cx, scope, child.id, + child.getter, child.setter, child.slot, + child.attrs, child.flags, child.shortid); + } + +#ifdef DEBUG_brendan + if (!newsprop) + METER(changeFailures); +#endif + return newsprop; +} + +JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) +{ + JSScopeProperty **spp, *stored, *sprop; + uint32 size; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + if (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return JS_FALSE; + } + METER(removes); + + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + if (!sprop) { + METER(uselessRemoves); + return JS_TRUE; + } + + /* Convert from a list to a hash so we can handle "middle deletes". */ + if (!scope->table && sprop != scope->lastProp) { + if (!CreateScopeTable(scope)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + } + + /* First, if sprop is unshared and not cleared, free its slot number. */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) + js_FreeSlot(cx, scope->object, sprop->slot); + + /* Next, remove id by setting its entry to a removed or free sentinel. */ + if (SPROP_HAD_COLLISION(stored)) { + JS_ASSERT(scope->table); + *spp = SPROP_REMOVED; + scope->removedCount++; + } else { + METER(removeFrees); + if (scope->table) + *spp = NULL; + } + scope->entryCount--; + JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); + + /* Update scope->lastProp directly, or set its deferred update flag. */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + SCOPE_SET_MIDDLE_DELETE(scope); + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Last, consider shrinking scope's table if its load factor is <= .25. */ + size = SCOPE_CAPACITY(scope); + if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { + METER(shrinks); + (void) ChangeScope(cx, scope, -1); + } + + return JS_TRUE; +} + +void +js_ClearScope(JSContext *cx, JSScope *scope) +{ + CHECK_ANCESTOR_LINE(scope, JS_TRUE); +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + + if (scope->table) + free(scope->table); + SCOPE_CLR_MIDDLE_DELETE(scope); + InitMinimalScope(scope); +} + +#ifdef DEBUG_brendan + +#include +#include + +uint32 js_nkids_max; +uint32 js_nkids_sum; +double js_nkids_sqsum; +uint32 js_nkids_hist[11]; + +static void +MeterKidCount(uintN nkids) +{ + if (nkids) { + js_nkids_sum += nkids; + js_nkids_sqsum += (double)nkids * nkids; + if (nkids > js_nkids_max) + js_nkids_max = nkids; + } + js_nkids_hist[JS_MIN(nkids, 10)]++; +} + +static void +MeterPropertyTree(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(kid); + nkids++; + } + } + } else { + MeterPropertyTree(kids); + nkids = 1; + } + } + + MeterKidCount(nkids); +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; + + MeterPropertyTree(entry->child); + return JS_DHASH_NEXT; +} + +#endif /* DEBUG_brendan */ + +void +js_SweepScopeProperties(JSRuntime *rt) +{ + JSArena **ap, *a; + JSScopeProperty *limit, *sprop, *parent, *kids, *kid; + uintN liveCount; + PropTreeKidsChunk *chunk, *nextChunk; + uintN i; + +#ifdef DEBUG_brendan + uint32 livePropCapacity = 0, totalLiveCount = 0; + static FILE *logfp; + if (!logfp) + logfp = fopen("/tmp/proptree.stats", "a"); + + MeterKidCount(rt->propertyTreeHash.entryCount); + JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); + + { + double mean = 0., var = 0., sigma = 0.; + double nodesum = rt->livePropTreeNodes; + double kidsum = js_nkids_sum; + if (nodesum > 0 && kidsum >= 0) { + mean = kidsum / nodesum; + var = nodesum * js_nkids_sqsum - kidsum * kidsum; + if (var < 0.0 || nodesum <= 1) + var = 0.0; + else + var /= nodesum * (nodesum - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + + fprintf(logfp, + "props %u nodes %g beta %g meankids %g sigma %g max %u", + rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, + mean, sigma, js_nkids_max); + } + + fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", + js_nkids_hist[0], js_nkids_hist[1], + js_nkids_hist[2], js_nkids_hist[3], + js_nkids_hist[4], js_nkids_hist[5], + js_nkids_hist[6], js_nkids_hist[7], + js_nkids_hist[8], js_nkids_hist[9], + js_nkids_hist[10]); + js_nkids_sum = js_nkids_max = 0; + js_nkids_sqsum = 0; + memset(js_nkids_hist, 0, sizeof js_nkids_hist); +#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 we skip it. */ + if (sprop->flags & SPROP_MARK) { + sprop->flags &= ~SPROP_MARK; + liveCount++; + continue; + } + + /* Ok, sprop is garbage to collect: unlink it from its parent. */ + RemovePropertyTreeChild(rt, sprop); + + /* Take care to reparent all sprop's kids to their grandparent. */ + kids = sprop->kids; + if (kids) { + sprop->kids = NULL; + parent = sprop->parent; + 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); + InsertPropertyTreeChild(rt, parent, kid); + } + nextChunk = chunk->next; + DestroyPropTreeKidsChunk(rt, chunk); + } while ((chunk = nextChunk) != NULL); + } else { + kid = kids; + InsertPropertyTreeChild(rt, parent, kid); + } + } + + /* 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_brendan + livePropCapacity += limit - (JSScopeProperty *) a->base; + totalLiveCount += liveCount; +#endif + ap = &a->next; + } + } + +#ifdef DEBUG_brendan + fprintf(logfp, " arenautil %g%%\n", + (totalLiveCount * 100.) / livePropCapacity); + fflush(logfp); +#endif +} + +JSBool +js_InitPropertyTree(JSRuntime *rt) +{ + if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { + rt->propertyTreeHash.ops = NULL; + return JS_FALSE; + } + JS_InitArenaPool(&rt->propertyArenaPool, "properties", + 256 * sizeof(JSScopeProperty), sizeof(void *)); + return JS_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/src/extension/script/js/jsscope.h b/src/extension/script/js/jsscope.h new file mode 100644 index 000000000..4f66441b4 --- /dev/null +++ b/src/extension/script/js/jsscope.h @@ -0,0 +1,386 @@ +/* -*- 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 jsscope_h___ +#define jsscope_h___ +/* + * JS symbol tables. + */ +#include "jstypes.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#ifdef JS_THREADSAFE +# include "jslock.h" +#endif + +/* + * 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. Because delete is + * "bursty", we should not fork eagerly. Delaying a fork till we are at risk + * of adding Y after it was deleted already requires a flag in the JSScope, to + * wit, SCOPE_MIDDLE_DELETE. + * + * 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. Note + * also that the stable storage of the property nodes during active requests + * permits the property cache (see jsinterp.h) to dereference JSScopeProperty + * weak references safely. + * + * 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, or + * until a "middle delete" occurs, we use linear search from scope->lastProp + * to find a given id, and save on the space overhead of a hash table. + */ + +struct JSScope { + JSObjectMap map; /* base class state */ + JSObject *object; /* object that owns this scope */ + uint16 flags; /* flags, see below */ + int16 hashShift; /* multiplicative hash shift */ + uint32 entryCount; /* number of entries in table */ + uint32 removedCount; /* removed entry sentinels in table */ + JSScopeProperty **table; /* table of ptrs to shared tree nodes */ + JSScopeProperty *lastProp; /* pointer to last property added */ +#ifdef JS_THREADSAFE + JSContext *ownercx; /* creating context, NULL if shared */ + JSThinLock lock; /* binary semaphore protecting scope */ + union { /* union lockful and lock-free state: */ + jsrefcount count; /* lock entry count for reentrancy */ + JSScope *link; /* next link in rt->scopeSharingTodo */ + } u; +#ifdef DEBUG + const char *file[4]; /* file where lock was (re-)taken */ + unsigned int line[4]; /* line where lock was (re-)taken */ +#endif +#endif +}; + +#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) + +/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ +#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) + +/* Scope flags and some macros to hide them from other files than jsscope.c. */ +#define SCOPE_MIDDLE_DELETE 0x0001 +#define SCOPE_SEALED 0x0002 + +#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) +#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) +#define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) + +#define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) +#define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) +#if 0 +/* + * Don't define this, 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. + */ +#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) +#endif + +/* + * A little information hiding for scope->lastProp, in case it ever becomes + * a tagged pointer again. + */ +#define SCOPE_LAST_PROP(scope) ((scope)->lastProp) +#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ + (scope)->lastProp->parent) + +struct JSScopeProperty { + jsid id; /* int-tagged jsval/untagged JSAtom* */ + JSPropertyOp getter; /* getter and setter hooks or objects */ + JSPropertyOp setter; + uint32 slot; /* index in obj->slots vector */ + 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 */ + JSScopeProperty *kids; /* null, single child, or a tagged ptr + to many-kids data structure */ +}; + +/* 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)))) + +/* Bits stored in sprop->flags. */ +#define SPROP_MARK 0x01 +#define SPROP_IS_DUPLICATE 0x02 +#define SPROP_IS_ALIAS 0x04 +#define SPROP_HAS_SHORTID 0x08 + +/* + * 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 SPROP_INVALID_SLOT 0xffffffff + +#define SPROP_HAS_VALID_SLOT(sprop, scope) \ + ((sprop)->slot < (scope)->map.freeslot) + +#define SPROP_CALL_GETTER(cx,sprop,getter,obj,obj2,vp) \ + (!(getter) || \ + (getter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) +#define SPROP_CALL_SETTER(cx,sprop,setter,obj,obj2,vp) \ + (!(setter) || \ + (setter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + +#define SPROP_GET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_GETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ + 0, 0, vp) \ + : SPROP_CALL_GETTER(cx, sprop, (sprop)->getter, obj, obj2, vp)) + +#define SPROP_SET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_SETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ + 1, vp, vp) \ + : ((sprop)->attrs & JSPROP_GETTER) \ + ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ + JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ + : SPROP_CALL_SETTER(cx, sprop, (sprop)->setter, obj, obj2, 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 JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +extern void +js_DestroyScope(JSContext *cx, JSScope *scope); + +#define ID_TO_VALUE(id) (((id) & JSVAL_INT) ? id : ATOM_KEY((JSAtom *)(id))) +#define HASH_ID(id) (((id) & JSVAL_INT) \ + ? (jsatomid) JSVAL_TO_INT(id) \ + : ((JSAtom *)id)->number) + +extern JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding); + +#define SCOPE_GET_PROPERTY(scope, id) \ + SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) + +#define SCOPE_HAS_PROPERTY(scope, sprop) \ + (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) + +extern JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +extern JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter); + +extern JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); + +extern void +js_ClearScope(JSContext *cx, JSScope *scope); + +#define MARK_SCOPE_PROPERTY(sprop) ((sprop)->flags |= SPROP_MARK) + +extern void +js_SweepScopeProperties(JSRuntime *rt); + +extern JSBool +js_InitPropertyTree(JSRuntime *rt); + +extern void +js_FinishPropertyTree(JSRuntime *rt); + +#endif /* jsscope_h___ */ diff --git a/src/extension/script/js/jsscript.c b/src/extension/script/js/jsscript.c new file mode 100644 index 000000000..504cbbd6d --- /dev/null +++ b/src/extension/script/js/jsscript.c @@ -0,0 +1,1287 @@ +/* -*- 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 script operations. + */ +#include "jsstddef.h" +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsscript.h" +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif + +#if JS_HAS_SCRIPT_OBJECT + +#if JS_HAS_TOSOURCE +static JSBool +script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSScript *script; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + uint32 indent; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + + /* 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 { + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + 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; + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n += k; + } + + /* Allocate the source string and copy into it. */ + t = (jschar *) JS_malloc(cx, (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) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSScript *script; + uint32 indent; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + str = JS_DecompileScript(cx, script, "Script.prototype.toString", + (uintN)indent); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSScript *oldscript, *script; + JSString *str; + JSStackFrame *fp, *caller; + JSObject *scopeobj; + const char *file; + uintN line; + JSPrincipals *principals; + + /* 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; + + /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + if (caller) { + if (!scopeobj) + scopeobj = caller->scopeChain; + + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); + principals = JS_EvalFramePrincipals(cx, fp, caller); + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* XXXbe set only for the compiler, which does not currently test it */ + fp->flags |= JSFRAME_EVAL; + + /* Compile the new script using the caller's scope chain, a la eval(). */ + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, + JSSTRING_CHARS(str), + JSSTRING_LENGTH(str), + file, line); + if (!script) + return JS_FALSE; + + /* Swap script for obj's old script, if any. */ + oldscript = (JSScript *) JS_GetPrivate(cx, obj); + if (!JS_SetPrivate(cx, obj, script)) { + js_DestroyScript(cx, script); + return JS_FALSE; + } + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; +out: + /* Return the object. */ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + JSObject *scopeobj, *parent; + JSStackFrame *fp, *caller; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; + + scopeobj = NULL; + if (argc) { + 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, thisp, 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. + */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + if (caller && !caller->varobj) { + /* Called from a lightweight function. */ + JS_ASSERT(caller->fun && !(caller->fun->flags & JSFUN_HEAVYWEIGHT)); + + /* Scope chain links from Call object to callee's parent. */ + parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); + if (!js_GetCallObject(cx, caller, parent)) + 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 = caller->scopeChain; + } else { + /* + * Called from native code, so we don't know what scope object to + * use. We could use parent (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; + } + } + + return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); +} + +#if JS_HAS_XDR + +static JSBool +XDRAtomListElement(JSXDRState *xdr, JSAtomListElement *ale) +{ + jsval value; + jsatomid index; + + if (xdr->mode == JSXDR_ENCODE) + value = ATOM_KEY(ALE_ATOM(ale)); + + index = ALE_INDEX(ale); + if (!JS_XDRUint32(xdr, &index)) + return JS_FALSE; + ALE_SET_INDEX(ale, index); + + if (!JS_XDRValue(xdr, &value)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + if (!ALE_SET_ATOM(ale, js_AtomizeValue(xdr->cx, value, 0))) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +{ + uint32 length; + uintN i; + JSBool ok; + + if (xdr->mode == JSXDR_ENCODE) + length = map->length; + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + JSContext *cx; + void *mark; + JSAtomList al; + JSAtomListElement *ale; + + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + ATOM_LIST_INIT(&al); + for (i = 0; i < length; i++) { + JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); + if (!ale || + !XDRAtomListElement(xdr, ale)) { + if (!ale) + JS_ReportOutOfMemory(cx); + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + ALE_SET_NEXT(ale, al.list); + al.count++; + al.list = ale; + } + ok = js_InitAtomMap(cx, map, &al); + JS_ARENA_RELEASE(&cx->tempPool, mark); + return ok; + } + + if (xdr->mode == JSXDR_ENCODE) { + JSAtomListElement ale; + + for (i = 0; i < map->length; i++) { + ALE_SET_ATOM(&ale, map->vector[i]); + ALE_SET_INDEX(&ale, i); + if (!XDRAtomListElement(xdr, &ale)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) +{ + JSContext *cx; + JSScript *script, *newscript; + uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; + uint32 prologLength, version; + JSBool filenameWasSaved; + jssrcnote *notes, *sn; + + cx = xdr->cx; + script = *scriptp; + nsrcnotes = ntrynotes = 0; + filenameWasSaved = JS_FALSE; + notes = NULL; + + /* + * Encode prologLength and version after script->length (_2 or greater), + * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. + * Version _3 supports principals serialization. Version _4 reorders the + * nsrcnotes and ntrynotes fields to come before everything except magic, + * length, prologLength, and version, so that srcnote and trynote storage + * can be allocated as part of the JSScript (along with bytecode storage). + */ + if (xdr->mode == JSXDR_ENCODE) + magic = JSXDR_MAGIC_SCRIPT_CURRENT; + if (!JS_XDRUint32(xdr, &magic)) + return JS_FALSE; + if (magic != JSXDR_MAGIC_SCRIPT_4 && + magic != JSXDR_MAGIC_SCRIPT_3 && + magic != JSXDR_MAGIC_SCRIPT_2 && + magic != JSXDR_MAGIC_SCRIPT_1) { + 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; + + if (xdr->mode == JSXDR_ENCODE) { + length = script->length; + prologLength = PTRDIFF(script->main, script->code, jsbytecode); + version = (int32)script->version; + lineno = (uint32)script->lineno; + depth = (uint32)script->depth; + + /* Count the srcnotes, keeping notes pointing at the first one. */ + notes = SCRIPT_NOTES(script); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nsrcnotes = PTRDIFF(sn, notes, jssrcnote); + nsrcnotes++; /* room for the terminator */ + + /* Count the trynotes. */ + if (script->trynotes) { + while (script->trynotes[ntrynotes].catchStart) + ntrynotes++; + ntrynotes++; /* room for the end marker */ + } + } + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + if (!JS_XDRUint32(xdr, &prologLength)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &version)) + return JS_FALSE; + + /* To fuse allocations, we need srcnote and trynote counts early. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &ntrynotes)) + return JS_FALSE; + } + } + + if (xdr->mode == JSXDR_DECODE) { + script = js_NewScript(cx, length, nsrcnotes, ntrynotes); + if (!script) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + script->main += prologLength; + script->version = (JSVersion) version; + + /* If we know nsrcnotes, we allocated space for notes in script. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) + notes = SCRIPT_NOTES(script); + } + *scriptp = script; + } + + /* + * Control hereafter must goto error on failure, in order for the DECODE + * case to destroy script and conditionally free notes, which if non-null + * in the (DECODE and version < _4) case must point at a temporary vector + * allocated just below. + */ + if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || + !XDRAtomMap(xdr, &script->atomMap)) { + goto error; + } + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + goto error; + if (xdr->mode == JSXDR_DECODE) { + notes = (jssrcnote *) JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); + if (!notes) + goto error; + } + } + + if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || + !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || + !JS_XDRUint32(xdr, &lineno) || + !JS_XDRUint32(xdr, &depth) || + (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { + goto error; + } + + /* Script principals transcoding support comes with versions >= _3. */ + if (magic >= JSXDR_MAGIC_SCRIPT_3) { + JSPrincipals *principals; + uint32 encodeable; + + if (xdr->mode == JSXDR_ENCODE) { + principals = script->principals; + encodeable = (cx->runtime->principalsTranscoder != NULL); + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable && + !cx->runtime->principalsTranscoder(xdr, &principals)) { + goto error; + } + } else { + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable) { + if (!cx->runtime->principalsTranscoder) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DECODE_PRINCIPALS); + goto error; + } + if (!cx->runtime->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; + JS_free(cx, (void *) script->filename); + script->filename = filename; + filenameWasSaved = JS_TRUE; + } + script->lineno = (uintN)lineno; + script->depth = (uintN)depth; + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + /* + * Argh, we have to reallocate script, copy notes into the extra + * space after the bytecodes, and free the temporary notes vector. + * First, add enough slop to nsrcnotes so we can align the address + * after the srcnotes of the first trynote. + */ + uint32 osrcnotes = nsrcnotes; + + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + newscript = (JSScript *) JS_realloc(cx, script, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!newscript) + goto error; + + *scriptp = script = newscript; + script->code = (jsbytecode *)(script + 1); + script->main = script->code + prologLength; + memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); + JS_free(cx, (void *) notes); + notes = NULL; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + } + } + } + + while (ntrynotes) { + JSTryNote *tn = &script->trynotes[--ntrynotes]; + uint32 start = (uint32) tn->start, + catchLength = (uint32) tn->length, + catchStart = (uint32) tn->catchStart; + + if (!JS_XDRUint32(xdr, &start) || + !JS_XDRUint32(xdr, &catchLength) || + !JS_XDRUint32(xdr, &catchStart)) { + goto error; + } + tn->start = (ptrdiff_t) start; + tn->length = (ptrdiff_t) catchLength; + tn->catchStart = (ptrdiff_t) catchStart; + } + return JS_TRUE; + + error: + if (xdr->mode == JSXDR_DECODE) { + if (script->filename && !filenameWasSaved) { + JS_free(cx, (void *) script->filename); + script->filename = NULL; + } + if (notes && magic < JSXDR_MAGIC_SCRIPT_4) + JS_free(cx, (void *) notes); + js_DestroyScript(cx, script); + *scriptp = NULL; + } + return JS_FALSE; +} + +#if 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, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSScript *script; + JSBool ok, hasMagic; + uint32 len; + void *buf; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + 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, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = 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); + str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); + if (!str) { + ok = JS_FALSE; + goto out; + } + +#if IS_BIG_ENDIAN + { + jschar *chars; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + chars = JS_GetStringChars(str); + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(chars[i]); + } +#endif + *rval = STRING_TO_JSVAL(str); + +out: + JS_XDRDestroy(xdr); + return ok; +} + +static JSBool +script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSString *str; + void *buf; + uint32 len; + JSScript *script, *oldscript; + JSBool ok, hasMagic; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + if (argc == 0) + return JS_TRUE; + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!xdr) + return JS_FALSE; + + buf = JS_GetStringChars(str); + len = JS_GetStringLength(str); +#if IS_BIG_ENDIAN + { + jschar *from, *to; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + from = (jschar *)buf; + to = (jschar *) JS_malloc(cx, 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, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = JSVAL_FALSE; + goto out; + } + + /* Swap script for obj's old script, if any. */ + oldscript = (JSScript *) JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, script); + if (!ok) { + JS_free(cx, script); + goto out; + } + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->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 (argv[0]). + */ + JS_XDRMemSetData(xdr, NULL, 0); + JS_XDRDestroy(xdr); +#if IS_BIG_ENDIAN + JS_free(cx, buf); +#endif + *rval = JSVAL_TRUE; + return ok; +} + +static const char js_thaw_str[] = "thaw"; + +#endif /* JS_HAS_XDR_FREEZE_THAW */ +#endif /* JS_HAS_XDR */ + +static JSFunctionSpec script_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, script_toSource, 0,0,0}, +#endif + {js_toString_str, script_toString, 0,0,0}, + {"compile", script_compile, 2,0,0}, + {"exec", script_exec, 1,0,0}, +#if JS_HAS_XDR_FREEZE_THAW + {"freeze", script_freeze, 0,0,0}, + {js_thaw_str, script_thaw, 1,0,0}, +#endif /* JS_HAS_XDR_FREEZE_THAW */ + {0,0,0,0,0} +}; + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static void +script_finalize(JSContext *cx, JSObject *obj) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + 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(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +#else + return JS_FALSE; +#endif +} + +static uint32 +script_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + if (script) + js_MarkScript(cx, script, arg); + return 0; +} + +JS_FRIEND_DATA(JSClass) js_ScriptClass = { + js_Script_str, + JSCLASS_HAS_PRIVATE, + 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, script_mark, 0 +}; + +#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 (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + } + return script_compile(cx, obj, argc, argv, rval); +} + +#if JS_HAS_XDR_FREEZE_THAW + +static JSBool +script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + if (!script_thaw(cx, obj, argc, argv, rval)) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec script_static_methods[] = { + {js_thaw_str, script_static_thaw, 1,0,0}, + {0,0,0,0,0} +}; + +#else /* !JS_HAS_XDR_FREEZE_THAW */ + +#define script_static_methods NULL + +#endif /* !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 JSHashTable *script_filename_table; +#ifdef JS_THREADSAFE +static JSLock *script_filename_table_lock; +#endif + +JS_STATIC_DLL_CALLBACK(int) +js_compare_strings(const void *k1, const void *k2) +{ + return strcmp(k1, k2) == 0; +} + +/* Shared with jsatom.c to save code space. */ +extern void * JS_DLL_CALLBACK +js_alloc_table_space(void *priv, size_t size); + +extern void JS_DLL_CALLBACK +js_free_table_space(void *priv, void *item); + +/* 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 */ + JSPackedBool mark; /* mark flag, for GC */ + char filename[3]; /* two or more bytes, NUL-terminated */ +} ScriptFilenameEntry; + +JS_STATIC_DLL_CALLBACK(JSHashEntry *) +js_alloc_entry(void *priv, const void *key) +{ + size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; + + return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_entry(void *priv, JSHashEntry *he, uintN flag) +{ + if (flag != HT_FREE_ENTRY) + return; + free(he); +} + +static JSHashAllocOps table_alloc_ops = { + js_alloc_table_space, js_free_table_space, + js_alloc_entry, js_free_entry +}; + +JSBool +js_InitScriptGlobals() +{ +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!script_filename_table_lock) { + script_filename_table_lock = JS_NEW_LOCK(); + if (!script_filename_table_lock) + return JS_FALSE; + } +#endif + if (!script_filename_table) { + JS_ACQUIRE_LOCK(script_filename_table_lock); + if (!script_filename_table) { + script_filename_table = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &table_alloc_ops, NULL); + } + JS_RELEASE_LOCK(script_filename_table_lock); + if (!script_filename_table) + return JS_FALSE; + } + return JS_TRUE; +} + +void +js_FreeScriptGlobals() +{ + if (script_filename_table) { + JS_HashTableDestroy(script_filename_table); + script_filename_table = NULL; + } +#ifdef JS_THREADSAFE + if (script_filename_table_lock) { + JS_DESTROY_LOCK(script_filename_table_lock); + script_filename_table_lock = NULL; + } +#endif +} + +#ifdef DEBUG_brendan +size_t sft_savings = 0; +#endif + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep; + ScriptFilenameEntry *sfe; + + JS_ACQUIRE_LOCK(script_filename_table_lock); + table = script_filename_table; + hash = JS_HashString(filename); + hep = JS_HashTableRawLookup(table, hash, filename); + sfe = (ScriptFilenameEntry *) *hep; +#ifdef DEBUG_brendan + if (sfe) + sft_savings += strlen(sfe->filename); +#endif + if (!sfe) { + sfe = (ScriptFilenameEntry *) + JS_HashTableRawAdd(table, hep, hash, filename, NULL); + if (sfe) { + sfe->key = strcpy(sfe->filename, filename); + JS_ASSERT(!sfe->mark); + } + } + JS_RELEASE_LOCK(script_filename_table_lock); + if (!sfe) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return sfe->filename; +} + +void +js_MarkScriptFilename(const char *filename) +{ + ScriptFilenameEntry *sfe; + + /* + * Back up from filename by its offset within its hash table entry. + * 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. + */ + sfe = (ScriptFilenameEntry *) + (filename - offsetof(ScriptFilenameEntry, filename)); + JS_ASSERT(sfe->key == sfe->filename); + sfe->mark = JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(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) +{ + JS_HashTableEnumerateEntries(script_filename_table, + js_script_filename_sweeper, + rt); +#ifdef DEBUG_brendan + printf("script filename table savings so far: %u\n", sft_savings); +#endif +} + +JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) +{ + JSScript *script; + + /* Round up source note count to align script->trynotes for its type. */ + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + script = (JSScript *) JS_malloc(cx, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!script) + return NULL; + memset(script, 0, sizeof(JSScript)); + script->code = script->main = (jsbytecode *)(script + 1); + script->length = length; + script->version = cx->version; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + } + return script; +} + +JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) +{ + uint32 mainLength, prologLength, nsrcnotes, ntrynotes; + JSScript *script; + const char *filename; + + mainLength = CG_OFFSET(cg); + prologLength = CG_PROLOG_OFFSET(cg); + CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); + CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); + script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, 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)); + if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) + goto bad; + + filename = cg->filename; + if (filename) { + script->filename = js_SaveScriptFilename(cx, filename); + if (!script->filename) + goto bad; + } + script->lineno = cg->firstLine; + script->depth = cg->maxStackDepth; + if (cg->principals) { + script->principals = cg->principals; + JSPRINCIPALS_HOLD(cx, script->principals); + } + + if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) + goto bad; + if (script->trynotes) + js_FinishTakingTryNotes(cx, cg, script->trynotes); + + /* Tell the debugger about this compiled script. */ + js_CallNewScriptHook(cx, script, fun); + return script; + +bad: + js_DestroyScript(cx, script); + return NULL; +} + +JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) +{ + JSRuntime *rt; + JSNewScriptHook hook; + + rt = cx->runtime; + hook = rt->newScriptHook; + if (hook) { + /* + * We use a dummy stack frame to protect the script from a GC caused + * by debugger-hook execution. + * + * XXX We really need a way to manage local roots and such more + * XXX automatically, at which point we can remove this one-off hack + * XXX and others within the engine. See bug 40757 for discussion. + */ + JSStackFrame dummy; + + memset(&dummy, 0, sizeof dummy); + dummy.down = cx->fp; + dummy.script = script; + cx->fp = &dummy; + + hook(cx, script->filename, script->lineno, script, fun, + rt->newScriptHookData); + + cx->fp = dummy.down; + } +} + +void +js_DestroyScript(JSContext *cx, JSScript *script) +{ + JSRuntime *rt; + JSDestroyScriptHook hook; + + rt = cx->runtime; + hook = rt->destroyScriptHook; + if (hook) + hook(cx, script, rt->destroyScriptHookData); + + JS_ClearScriptTraps(cx, script); + js_FreeAtomMap(cx, &script->atomMap); + if (script->principals) + JSPRINCIPALS_DROP(cx, script->principals); + JS_free(cx, script); +} + +void +js_MarkScript(JSContext *cx, JSScript *script, void *arg) +{ + JSAtomMap *map; + uintN i, length; + JSAtom **vector; + + map = &script->atomMap; + length = map->length; + vector = map->vector; + for (i = 0; i < length; i++) + GC_MARK_ATOM(cx, vector[i], arg); + + if (script->filename) + js_MarkScriptFilename(script->filename); +} + +jssrcnote * +js_GetSrcNote(JSScript *script, jsbytecode *pc) +{ + jssrcnote *sn; + ptrdiff_t offset, target; + + target = PTRDIFF(pc, script->code, jsbytecode); + if ((uint32)target >= script->length) + return NULL; + offset = 0; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + offset += SN_DELTA(sn); + if (offset == target && SN_IS_GETTABLE(sn)) + return sn; + } + return NULL; +} + +uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSAtom *atom; + JSFunction *fun; + uintN lineno; + ptrdiff_t offset, target; + jssrcnote *sn; + JSSrcNoteType type; + + /* + * Special case: function definition needs no line number note because + * the function's script contains its starting line number. + */ + if (*pc == JSOP_DEFFUN) { + atom = GET_ATOM(cx, script, pc); + fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); + return fun->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 = PTRDIFF(pc, script->code, jsbytecode); + for (sn = SCRIPT_NOTES(script); !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; +} + +jsbytecode * +js_LineNumberToPC(JSScript *script, uintN target) +{ + ptrdiff_t offset; + uintN lineno; + jssrcnote *sn; + JSSrcNoteType type; + + offset = 0; + lineno = script->lineno; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + if (lineno >= target) + break; + 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++; + } + } + return script->code + offset; +} + +uintN +js_GetScriptLineExtent(JSScript *script) +{ + uintN lineno; + jssrcnote *sn; + JSSrcNoteType type; + + lineno = script->lineno; + for (sn = SCRIPT_NOTES(script); !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/src/extension/script/js/jsscript.h b/src/extension/script/js/jsscript.h new file mode 100644 index 000000000..ffcc40de7 --- /dev/null +++ b/src/extension/script/js/jsscript.h @@ -0,0 +1,178 @@ +/* -*- 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 jsscript_h___ +#define jsscript_h___ +/* + * JS script descriptor. + */ +#include "jsatom.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Exception handling runtime information. + * + * All fields except length are code offsets, relative to the beginning of + * the script. If script->trynotes is not null, it points to a vector of + * these structs terminated by one with catchStart == 0. + */ +struct JSTryNote { + ptrdiff_t start; /* start of try statement */ + ptrdiff_t length; /* count of try statement bytecodes */ + ptrdiff_t catchStart; /* start of catch block (0 if end) */ +}; + +#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) +#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) + +struct JSScript { + jsbytecode *code; /* bytecodes and their immediate operands */ + uint32 length; /* length of code vector */ + jsbytecode *main; /* main entry point, after predef'ing prolog */ + JSVersion version; /* JS version under which script was compiled */ + JSAtomMap atomMap; /* maps immediate index to literal struct */ + const char *filename; /* source filename or null */ + uintN lineno; /* base line number of script */ + uintN depth; /* maximum stack depth in slots */ + JSTryNote *trynotes; /* exception table for this script */ + JSPrincipals *principals; /* principals for this script */ + JSObject *object; /* optional Script-class object wrapper */ +}; + +/* No need to store script->notes now that it is allocated right after code. */ +#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) + +#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ + JS_BEGIN_MACRO \ + JSTryNote *tn_ = (script)->trynotes; \ + jsbytecode *catchpc_ = NULL; \ + if (tn_) { \ + ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ + if (off_ >= 0) { \ + while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ + ++tn_; \ + if (tn_->catchStart) \ + catchpc_ = (script)->main + tn_->catchStart; \ + } \ + } \ + catchpc = catchpc_; \ + JS_END_MACRO + +extern JS_FRIEND_DATA(JSClass) js_ScriptClass; + +extern JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj); + +extern JSBool +js_InitScriptGlobals(); + +extern void +js_FreeScriptGlobals(); + +extern const char * +js_SaveScriptFilename(JSContext *cx, const char *filename); + +extern void +js_MarkScriptFilename(const char *filename); + +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. + */ +extern JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); + +extern JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); + +/* + * 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 void +js_DestroyScript(JSContext *cx, JSScript *script); + +extern void +js_MarkScript(JSContext *cx, JSScript *script, void *arg); + +extern jssrcnote * +js_GetSrcNote(JSScript *script, jsbytecode *pc); + +/* XXX need cx to lock function objects declared by prolog bytecodes. */ +extern uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern jsbytecode * +js_LineNumberToPC(JSScript *script, uintN lineno); + +extern uintN +js_GetScriptLineExtent(JSScript *script); + +/* + * 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: callers must call js_CallNewScriptHook after successful JSXDR_DECODE + * and subsequent set-up of owning function or script object, if any. + */ +extern JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); + +JS_END_EXTERN_C + +#endif /* jsscript_h___ */ diff --git a/src/extension/script/js/jsshell.msg b/src/extension/script/js/jsshell.msg new file mode 100644 index 000000000..4b811ac01 --- /dev/null +++ b/src/extension/script/js/jsshell.msg @@ -0,0 +1,50 @@ +/* -*- 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") diff --git a/src/extension/script/js/jsstddef.h b/src/extension/script/js/jsstddef.h new file mode 100644 index 000000000..0d87b0c0c --- /dev/null +++ b/src/extension/script/js/jsstddef.h @@ -0,0 +1,83 @@ +/* -*- 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 ***** */ + +/* + * stddef inclusion here to first declare ptrdif as a signed long instead of a + * signed int. + */ + +#ifdef _WINDOWS +# ifndef XP_WIN +# define XP_WIN +# endif +#if defined(_WIN32) || defined(WIN32) +# ifndef XP_WIN32 +# define XP_WIN32 +# endif +#else +# ifndef XP_WIN16 +# define XP_WIN16 +# endif +#endif +#endif + +#ifdef XP_WIN16 +#ifndef _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; + +/* + * The Win16 compiler treats pointer differences as 16-bit signed values. + * This macro allows us to treat them as 17-bit signed values, stored in + * a 32-bit type. + */ +#define PTRDIFF(p1, p2, type) \ + ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) + +#define _PTRDIFF_T_DEFINED +#endif /*_PTRDIFF_T_DEFINED*/ +#else /*WIN16*/ + +#define PTRDIFF(p1, p2, type) \ + ((p1) - (p2)) + +#endif + +#include + + diff --git a/src/extension/script/js/jsstr.c b/src/extension/script/js/jsstr.c new file mode 100644 index 000000000..e143ab8df --- /dev/null +++ b/src/extension/script/js/jsstr.c @@ -0,0 +1,4502 @@ +/* -*- 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 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. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.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 "jscntxt.h" +#include "jsconfig.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsstr.h" + +#if JS_HAS_REPLACE_LAMBDA +#include "jsinterp.h" +#endif + +#define JSSTRDEP_RECURSION_LIMIT 100 + +size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) +{ + JSString *base; + size_t start, length; + + JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); + base = JSSTRDEP_BASE(str); + start = JSSTRDEP_START(str); + if (JSSTRING_IS_DEPENDENT(base)) { + if (level < JSSTRDEP_RECURSION_LIMIT) { + start += js_MinimizeDependentStrings(base, level + 1, &base); + } else { + do { + start += JSSTRDEP_START(base); + base = JSSTRDEP_BASE(base); + } while (JSSTRING_IS_DEPENDENT(base)); + } + if (start == 0) { + JS_ASSERT(JSSTRING_IS_PREFIX(str)); + JSPREFIX_SET_BASE(str, base); + } else if (start <= JSSTRDEP_START_MASK) { + length = JSSTRDEP_LENGTH(str); + JSSTRDEP_SET_START_AND_LENGTH(str, start, length); + JSSTRDEP_SET_BASE(str, base); + } + } + *basep = base; + return start; +} + +jschar * +js_GetDependentStringChars(JSString *str) +{ + size_t start; + JSString *base; + + start = js_MinimizeDependentStrings(str, 0, &base); + JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); + JS_ASSERT(start < base->length); + return base->chars + start; +} + +jschar * +js_GetStringChars(JSString *str) +{ + if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) + return NULL; + + *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; + return str->chars; +} + +JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + size_t rn, ln, lrdist, n; + jschar *rs, *ls, *s; + JSDependentString *ldep; /* non-null if left should become dependent */ + JSString *str; + + if (JSSTRING_IS_DEPENDENT(right)) { + rn = JSSTRDEP_LENGTH(right); + rs = JSSTRDEP_CHARS(right); + } else { + rn = right->length; + rs = right->chars; + } + if (rn == 0) + return left; + + if (JSSTRING_IS_DEPENDENT(left) || + !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { + /* We must copy if left does not own a buffer to realloc. */ + ln = JSSTRING_LENGTH(left); + if (ln == 0) + return right; + ls = JSSTRING_CHARS(left); + s = (jschar *) JS_malloc(cx, (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. */ + ln = left->length; + if (ln == 0) + return right; + ls = left->chars; + s = (jschar *) JS_realloc(cx, 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->chars = ls = s; + ldep = JSSTRDEP(left); + } + + js_strncpy(s + ln, rs, rn); + n = ln + rn; + s[n] = 0; + str = js_NewString(cx, s, n, GCF_MUTABLE); + if (!str) { + /* Out of memory: clean up any space we (re-)allocated. */ + if (!ldep) { + JS_free(cx, s); + } else { + s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); + if (s) + left->chars = s; + } + } else { + /* Morph left into a dependent prefix if we realloc'd its buffer. */ + if (ldep) { + JSPREFIX_SET_LENGTH(ldep, ln); + JSPREFIX_SET_BASE(ldep, str); +#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; +} + +/* + * May be called with null cx by js_GetStringChars, above; and by the jslock.c + * MAKE_STRING_IMMUTABLE file-local macro. + */ +const jschar * +js_UndependString(JSContext *cx, JSString *str) +{ + size_t n, size; + jschar *s; + + if (JSSTRING_IS_DEPENDENT(str)) { + n = JSSTRDEP_LENGTH(str); + size = (n + 1) * sizeof(jschar); + s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!s) + return NULL; + + js_strncpy(s, JSSTRDEP_CHARS(str), n); + s[n] = 0; + str->length = n; + str->chars = s; + +#ifdef DEBUG + if (cx) { + 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->chars; +} + +/* + * Forward declarations for URI encode/decode and helper routines + */ +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static int +OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); + +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 15.1.2.4. */ +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) { + if (!js_ValueToNumber(cx, argv[1], &d)) + 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 = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = newlength = JSSTRING_LENGTH(str); + + /* 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 */ + } + } + + newchars = (jschar *) JS_malloc(cx, (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, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#undef IS_OK + +/* See ECMA-262 15.1.2.5 */ +static JSBool +str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length; + const jschar *chars; + jschar *newchars; + jschar ch; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + + /* Don't bother allocating less space for the new string. */ + newchars = (jschar *) JS_malloc(cx, (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, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_UNEVAL +static JSBool +str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToSource(cx, argv[0]); + if (!str) + return JS_FALSE; + *rval = 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_escape_str, js_str_escape, 1,0,0}, + {js_unescape_str, str_unescape, 1,0,0}, +#if JS_HAS_UNEVAL + {js_uneval_str, str_uneval, 1,0,0}, +#endif + {js_decodeURI_str, str_decodeURI, 1,0,0}, + {js_encodeURI_str, str_encodeURI, 1,0,0}, + {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, + {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, + + {0,0,0,0,0} +}; + +jschar js_empty_ucstr[] = {0}; +JSSubString js_EmptySubString = {0, js_empty_ucstr}; + +enum string_tinyid { + STRING_LENGTH = -1 +}; + +static JSPropertySpec string_props[] = { + {js_length_str, STRING_LENGTH, + JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, + {0,0,0,0,0} +}; + +static JSBool +str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSString *str; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + slot = JSVAL_TO_INT(id); + if (slot == STRING_LENGTH) + *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); + return JS_TRUE; +} + +#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) + +static JSBool +str_enumerate(JSContext *cx, JSObject *obj) +{ + JSString *str, *str1; + size_t i, length; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + for (i = 0; i < length; i++) { + str1 = js_NewDependentString(cx, str, i, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(i), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +str_resolve(JSContext *cx, JSObject *obj, jsval id) +{ + JSString *str, *str1; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + slot = JSVAL_TO_INT(id); + if ((size_t)slot < JSSTRING_LENGTH(str)) { + str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(slot), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSClass string_class = { + js_String_str, + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, + str_enumerate, str_resolve, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#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, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '"'); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *str; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + + if (!JS_InstanceOf(cx, obj, &string_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!str) + return JS_FALSE; + j = JS_snprintf(buf, sizeof buf, "(new %s(", string_class.name); + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n = j + k + 2; + t = (jschar *) JS_malloc(cx, (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, 0); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + + if (!JS_InstanceOf(cx, obj, &string_class, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toString(cx, obj, argc, argv, rval); + *rval = v; + return JS_TRUE; +} + +static JSBool +str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (!JS_InstanceOf(cx, obj, &string_class, argv)) + return JS_FALSE; + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + return JS_TRUE; +} + +/* + * Java-like string native methods. + */ +static JSBool +str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) + begin = 0; + else if (begin > length) + begin = length; + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + else if (end > length) + end = length; + if (end < begin) { + if (cx->version != JSVERSION_1_2) { + /* XXX emulate old JDK1.0 java.lang.String.substring. */ + jsdouble tmp = begin; + begin = end; + end = tmp; + } else { + end = begin; + } + } + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOLOWER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + 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) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + return cx->localeCallbacks->localeToLowerCase(cx, str, rval); + } + return str_toLowerCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOUPPER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toUpperCase(), + * ECMA has reserved that argument, presumbaly for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + return cx->localeCallbacks->localeToUpperCase(cx, str, rval); + } + return str_toUpperCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *thatStr; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + *rval = JSVAL_ZERO; + } else { + thatStr = js_ValueToString(cx, argv[0]); + if (!thatStr) + return JS_FALSE; + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) + return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); + *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); + } + return JS_TRUE; +} + +static JSBool +str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetEmptyStringValue(cx); + } else { + index = (size_t)d; + str = js_NewDependentString(cx, str, index, 1, 0); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +static JSBool +str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetNaNValue(cx); + } else { + index = (size_t)d; + *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); + } + return JS_TRUE; +} + +jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start) +{ + jsint i, j, k, m; + uint8 skip[BMH_CHARSET_SIZE]; + jschar c; + + JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); + for (i = 0; i < BMH_CHARSET_SIZE; i++) + skip[i] = (uint8)patlen; + m = patlen - 1; + for (i = 0; i < m; i++) { + c = pat[i]; + if (c >= BMH_CHARSET_SIZE) + return BMH_BAD_PATTERN; + skip[c] = (uint8)(m - i); + } + for (k = start + m; + k < textlen; + k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { + for (i = k, j = m; ; i--, j--) { + if (j < 0) + return i + 1; + if (text[i] != pat[j]) + break; + } + } + return -1; +} + +static JSBool +str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + jsint i, j, index, textlen, patlen; + const jschar *text, *pat; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen) + i = textlen; + else + i = (jsint)d; + } else { + i = 0; + } + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + /* XXX tune the BMH threshold (512) */ + if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { + index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); + if (index != BMH_BAD_PATTERN) + goto out; + } + + index = -1; + j = 0; + while (i + j < textlen) { + if (text[i + j] == pat[j]) { + if (++j == patlen) { + index = i; + break; + } + } else { + i++; + j = 0; + } + } + +out: + *rval = INT_TO_JSVAL(index); + return JS_TRUE; +} + +static JSBool +str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *str2; + const jschar *text, *pat; + jsint i, j, textlen, patlen; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(d)) { + i = textlen; + } else { + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen - patlen) + i = textlen - patlen; + else + i = (jsint)d; + } + } else { + i = textlen; + } + + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + j = 0; + while (i >= 0) { + /* Don't assume that text is NUL-terminated: it could be dependent. */ + if (i + j < textlen && text[i + j] == pat[j]) { + if (++j == patlen) + break; + } else { + i--; + j = 0; + } + } + *rval = INT_TO_JSVAL(i); + return JS_TRUE; +} + +/* + * Perl-inspired string functions. + */ +#if JS_HAS_REGEXPS +typedef struct GlobData { + uintN flags; /* inout: mode and flag bits, see below */ + uintN optarg; /* in: index of optional flags argument */ + JSString *str; /* out: 'this' parameter object as string */ + JSRegExp *regexp; /* out: regexp parameter object private data */ +} GlobData; + +/* + * Mode and flag bit definitions for match_or_replace's GlobData.flags field. + */ +#define MODE_MATCH 0x00 /* in: return match array on success */ +#define MODE_REPLACE 0x01 /* in: match and replace */ +#define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ +#define GET_MODE(f) ((f) & 0x03) +#define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ +#define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller + of match_or_replace; if set on input + but clear on output, regexp ownership + does not pass to caller */ +#define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ + +static JSBool +match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), + GlobData *data, jsval *rval) +{ + JSString *str, *src, *opt; + JSObject *reobj; + JSRegExp *re; + size_t index, length; + JSBool ok, test; + jsint count; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + data->str = str; + + if (JSVAL_IS_REGEXP(cx, argv[0])) { + reobj = JSVAL_TO_OBJECT(argv[0]); + re = (JSRegExp *) JS_GetPrivate(cx, reobj); + } else { + src = js_ValueToString(cx, argv[0]); + if (!src) + return JS_FALSE; + if (data->optarg < argc) { + argv[0] = STRING_TO_JSVAL(src); + opt = js_ValueToString(cx, argv[data->optarg]); + if (!opt) + return JS_FALSE; + } else { + opt = NULL; + } + re = js_NewRegExpOpt(cx, NULL, src, opt, + (data->flags & FORCE_FLAT) != 0); + if (!re) + return JS_FALSE; + reobj = NULL; + } + data->regexp = re; + + if (re->flags & JSREG_GLOB) + data->flags |= GLOBAL_REGEXP; + index = 0; + if (GET_MODE(data->flags) == MODE_SEARCH) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (ok) { + *rval = (*rval == JSVAL_TRUE) + ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) + : INT_TO_JSVAL(-1); + } + } else if (data->flags & GLOBAL_REGEXP) { + if (reobj) { + /* Set the lastIndex property's reserved slot to 0. */ + ok = js_SetLastIndex(cx, reobj, 0); + if (!ok) + return JS_FALSE; + } else { + ok = JS_TRUE; + } + length = JSSTRING_LENGTH(str); + for (count = 0; index <= length; count++) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (!ok || *rval != JSVAL_TRUE) + break; + ok = glob(cx, count, data); + if (!ok) + break; + if (cx->regExpStatics.lastMatch.length == 0) { + if (index == length) + break; + index++; + } + } + } else { + if (GET_MODE(data->flags) == MODE_REPLACE) { + test = JS_TRUE; + } else { + /* + * MODE_MATCH implies str_match is being called from a script or a + * scripted function. If the caller cares only about testing null + * vs. non-null return value, optimize away the array object that + * would normally be returned in *rval. + */ + JS_ASSERT(*cx->fp->down->pc == JSOP_CALL || + *cx->fp->down->pc == JSOP_NEW); + JS_ASSERT(js_CodeSpec[*cx->fp->down->pc].length == 3); + switch (cx->fp->down->pc[3]) { + case JSOP_POP: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_IFEQX: + case JSOP_IFNEX: + test = JS_TRUE; + break; + default: + test = JS_FALSE; + break; + } + } + ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); + } + + if (reobj) { + /* Tell our caller that it doesn't need to destroy data->regexp. */ + data->flags &= ~KEEP_REGEXP; + } else if (!(data->flags & KEEP_REGEXP)) { + /* Caller didn't want to keep data->regexp, so null and destroy it. */ + data->regexp = NULL; + js_DestroyRegExp(cx, re); + } + return ok; +} + +typedef struct MatchData { + GlobData base; + jsval *arrayval; /* NB: local root pointer */ +} MatchData; + +static JSBool +match_glob(JSContext *cx, jsint count, GlobData *data) +{ + MatchData *mdata; + JSObject *arrayobj; + JSSubString *matchsub; + JSString *matchstr; + jsval v; + + mdata = (MatchData *)data; + arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); + if (!arrayobj) { + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); + } + matchsub = &cx->regExpStatics.lastMatch; + matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); + if (!matchstr) + return JS_FALSE; + v = STRING_TO_JSVAL(matchstr); + return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v); +} + +static JSBool +str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + MatchData mdata; + JSBool ok; + + mdata.base.flags = MODE_MATCH; + mdata.base.optarg = 1; + mdata.arrayval = &argv[2]; + *mdata.arrayval = JSVAL_NULL; + ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); + if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) + *rval = *mdata.arrayval; + return ok; +} + +static JSBool +str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GlobData data; + + data.flags = MODE_SEARCH; + data.optarg = 1; + return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); +} + +typedef struct ReplaceData { + GlobData base; /* base struct state */ + 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 */ + jschar *chars; /* result chars, null initially */ + size_t length; /* result length, 0 initially */ + jsint index; /* index in result of next replacement */ + jsint leftIndex; /* left context index in base.str->chars */ + JSSubString dollarStr; /* for "$$" interpret_dollar result */ +} ReplaceData; + +static JSSubString * +interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) +{ + JSRegExpStatics *res; + jschar dc, *cp; + uintN num, tmp; + JSString *str; + + JS_ASSERT(*dp == '$'); + + /* + * Allow a real backslash (literal "\\" before "$1") to escape "$1", e.g. + * Do this only for versions strictly less than ECMAv3. + */ + if (cx->version != JSVERSION_DEFAULT && cx->version <= JSVERSION_1_4) { + if (dp > JSSTRING_CHARS(rdata->repstr) && dp[-1] == '\\') + return NULL; + } + + /* Interpret all Perl match-induced dollar variables. */ + res = &cx->regExpStatics; + dc = dp[1]; + if (JS7_ISDEC(dc)) { + if (cx->version != JSVERSION_DEFAULT && cx->version <= JSVERSION_1_4) { + if (dc == '0') + return NULL; + + /* Check for overflow to avoid gobbling arbitrary decimal digits. */ + num = 0; + cp = dp; + while ((dc = *++cp) != 0 && JS7_ISDEC(dc)) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp < num) + break; + num = tmp; + } + } else { /* ECMA 3, 1-9 or 01-99 */ + num = JS7_UNDEC(dc); + if (num > res->parenCount) + return NULL; + cp = dp + 2; + dc = *cp; + if ((dc != 0) && 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 '`': + if (cx->version == JSVERSION_1_2) { + /* + * JS1.2 imitated the Perl4 bug where left context at each step + * in an iterative use of a global regexp started from last match, + * not from the start of the target string. But Perl4 does start + * $` at the beginning of the target string when it is used in a + * substitution, so we emulate that special case here. + */ + str = rdata->base.str; + res->leftContext.chars = JSSTRING_CHARS(str); + res->leftContext.length = res->lastMatch.chars + - JSSTRING_CHARS(str); + } + return &res->leftContext; + case '\'': + return &res->rightContext; + } + return NULL; +} + +static JSBool +find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) +{ + JSString *repstr; + size_t replen, skip; + jschar *dp, *ep; + JSSubString *sub; +#if JS_HAS_REPLACE_LAMBDA + JSObject *lambda; + + lambda = rdata->lambda; + if (lambda) { + uintN argc, i, j, m, n, p; + jsval *sp, *oldsp, rval; + void *mark; + JSStackFrame *fp; + JSBool ok; + + /* + * Save the rightContext from the current regexp, since it + * gets stuck at the end of the replacement string and may + * be clobbered by a RegExp usage in the lambda function. + */ + JSSubString saveRightContext = cx->regExpStatics.rightContext; + + /* + * 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 + * do_replace. 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. + */ + p = rdata->base.regexp->parenCount; + argc = 1 + p + 2; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push lambda and its 'this' parameter. */ + *sp++ = OBJECT_TO_JSVAL(lambda); + *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); + +#define PUSH_REGEXP_STATIC(sub) \ + JS_BEGIN_MACRO \ + JSString *str = js_NewStringCopyN(cx, \ + cx->regExpStatics.sub.chars, \ + cx->regExpStatics.sub.length, \ + 0); \ + if (!str) { \ + ok = JS_FALSE; \ + goto lambda_out; \ + } \ + *sp++ = STRING_TO_JSVAL(str); \ + JS_END_MACRO + + /* Push $&, $1, $2, ... */ + PUSH_REGEXP_STATIC(lastMatch); + i = 0; + m = cx->regExpStatics.parenCount; + n = JS_MIN(m, 9); + for (j = 0; i < n; i++, j++) + PUSH_REGEXP_STATIC(parens[j]); + for (j = 0; i < m; i++, j++) + PUSH_REGEXP_STATIC(moreParens[j]); + +#undef PUSH_REGEXP_STATIC + + /* 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->base.str); + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); + rval = fp->sp[-1]; + fp->sp = oldsp; + + if (ok) { + /* + * 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 do_replace. + */ + repstr = js_ValueToString(cx, rval); + if (!repstr) { + ok = JS_FALSE; + } else { + rdata->repstr = repstr; + *sizep = JSSTRING_LENGTH(repstr); + } + } + + lambda_out: + js_FreeStack(cx, mark); + cx->regExpStatics.rightContext = saveRightContext; + return ok; + } +#endif /* JS_HAS_REPLACE_LAMBDA */ + + repstr = rdata->repstr; + replen = JSSTRING_LENGTH(repstr); + for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + sub = interpret_dollar(cx, dp, rdata, &skip); + if (sub) { + replen += sub->length - skip; + dp += skip; + } + else + dp++; + } + *sizep = replen; + return JS_TRUE; +} + +static void +do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) +{ + JSString *repstr; + jschar *bp, *cp, *dp, *ep; + size_t len, skip; + JSSubString *sub; + + repstr = rdata->repstr; + bp = cp = JSSTRING_CHARS(repstr); + 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 = interpret_dollar(cx, dp, 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, JSSTRING_LENGTH(repstr) - (cp - bp)); +} + +static JSBool +replace_glob(JSContext *cx, jsint count, GlobData *data) +{ + ReplaceData *rdata; + JSString *str; + size_t leftoff, leftlen, replen, growth; + const jschar *left; + jschar *chars; + + rdata = (ReplaceData *)data; + str = data->str; + leftoff = rdata->leftIndex; + left = JSSTRING_CHARS(str) + leftoff; + leftlen = cx->regExpStatics.lastMatch.chars - left; + rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); + rdata->leftIndex += cx->regExpStatics.lastMatch.length; + if (!find_replen(cx, rdata, &replen)) + return JS_FALSE; + growth = leftlen + replen; + chars = (jschar *) + (rdata->chars + ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) + * sizeof(jschar)) + : JS_malloc(cx, (growth + 1) * sizeof(jschar))); + if (!chars) { + JS_free(cx, rdata->chars); + rdata->chars = NULL; + return JS_FALSE; + } + rdata->chars = chars; + rdata->length += growth; + chars += rdata->index; + rdata->index += growth; + js_strncpy(chars, left, leftlen); + chars += leftlen; + do_replace(cx, rdata, chars); + return JS_TRUE; +} + +static JSBool +str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *lambda; + JSString *repstr, *str; + ReplaceData rdata; + JSBool ok; + jschar *chars; + size_t leftlen, rightlen, length; + +#if JS_HAS_REPLACE_LAMBDA + if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { + lambda = JSVAL_TO_OBJECT(argv[1]); + repstr = NULL; + } else +#endif + { + if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) + return JS_FALSE; + repstr = JSVAL_TO_STRING(argv[1]); + lambda = NULL; + } + + /* + * For ECMA Edition 3, the first argument is to be converted to a string + * to match in a "flat" sense (without regular expression metachars having + * special meanings) UNLESS the first arg is a RegExp object. + */ + rdata.base.flags = MODE_REPLACE | KEEP_REGEXP; + if (cx->version == JSVERSION_DEFAULT || cx->version > JSVERSION_1_4) + rdata.base.flags |= FORCE_FLAT; + rdata.base.optarg = 2; + + rdata.lambda = lambda; + rdata.repstr = repstr; + if (repstr) { + rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); + rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', + rdata.dollarEnd); + } else { + rdata.dollar = rdata.dollarEnd = NULL; + } + rdata.chars = NULL; + rdata.length = 0; + rdata.index = 0; + rdata.leftIndex = 0; + + ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); + if (!ok) + return JS_FALSE; + + if (!rdata.chars) { + if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { + /* Didn't match even once. */ + *rval = STRING_TO_JSVAL(rdata.base.str); + goto out; + } + leftlen = cx->regExpStatics.leftContext.length; + ok = find_replen(cx, &rdata, &length); + if (!ok) + goto out; + length += leftlen; + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) { + ok = JS_FALSE; + goto out; + } + js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); + do_replace(cx, &rdata, chars + leftlen); + rdata.chars = chars; + rdata.length = length; + } + + rightlen = cx->regExpStatics.rightContext.length; + length = rdata.length + rightlen; + chars = (jschar *) + JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); + if (!chars) { + JS_free(cx, rdata.chars); + ok = JS_FALSE; + goto out; + } + js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, + rightlen); + chars[length] = 0; + + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + ok = JS_FALSE; + goto out; + } + *rval = STRING_TO_JSVAL(str); + +out: + /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ + if (rdata.base.flags & KEEP_REGEXP) + js_DestroyRegExp(cx, rdata.base.regexp); + return ok; +} +#endif /* JS_HAS_REGEXPS */ + +/* + * 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; + jschar *chars; + size_t length; + + /* + * 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; + if ((size_t)i > JSSTRING_LENGTH(str)) + return -1; + + /* + * Perl4 special case for str.split(' '), only if the user has selected + * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s. + * Strange but true, apparently modeled after awk. + * + * NB: we set sep->length to the length of the w/s run, so we must test + * sep->chars[1] == 0 to make sure sep is just one space. + */ + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + if (cx->version == JSVERSION_1_2 && + !re && *sep->chars == ' ' && sep->chars[1] == 0) { + + /* Skip leading whitespace if at front of str. */ + if (i == 0) { + while (JS_ISSPACE(chars[i])) + i++; + *ip = i; + } + + /* Don't delimit whitespace at end of string. */ + if ((size_t)i == length) + return -1; + + /* Skip over the non-whitespace chars. */ + while ((size_t)i < length && !JS_ISSPACE(chars[i])) + i++; + + /* Now skip the next run of whitespace. */ + j = i; + while ((size_t)j < length && JS_ISSPACE(chars[j])) + j++; + + /* Update sep->length to count delimiter chars. */ + sep->length = (size_t)(j - i); + return i; + } + +#if JS_HAS_REGEXPS + /* + * 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 match_or_replace. + */ + 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) { + if (cx->version == JSVERSION_1_2) { + sep->length = 1; + return i; + } + return -1; + } + i++; + goto again; + } + } + JS_ASSERT((size_t)i >= sep->length); + return i - sep->length; + } +#endif /* JS_HAS_REGEXPS */ + + /* + * Deviate from ECMA by never splitting an empty string by any separator + * string into a non-empty array (an array of length 1 that contains the + * empty string). + */ + if (!JSVERSION_IS_ECMA(cx->version) && length == 0) + return -1; + + /* + * 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. + * + * For 1.2 compatibility, at the end of the string, we return the length as + * the result, and set the separator length to 1 -- this allows the caller + * to include an additional null string at the end of the substring list. + */ + if (sep->length == 0) { + if (cx->version == JSVERSION_1_2) { + if ((size_t)i == length) { + sep->length = 1; + return i; + } + return i + 1; + } + 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, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *sub; + JSObject *arrayobj; + jsval v; + JSBool ok, limited; + JSRegExp *re; + JSSubString *sep, tmp; + jsdouble d; + jsint i, j; + uint32 len, limit; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + + if (argc == 0) { + v = STRING_TO_JSVAL(str); + ok = JS_SetElement(cx, arrayobj, 0, &v); + } else { +#if JS_HAS_REGEXPS + if (JSVAL_IS_REGEXP(cx, argv[0])) { + re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + sep = &tmp; + + /* Set a magic value so we can detect a successful re match. */ + sep->chars = NULL; + } else +#endif + { + JSString *str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + + /* + * Point sep at a local copy of str2's header because find_split + * will modify sep->length. + */ + tmp.length = JSSTRING_LENGTH(str2); + tmp.chars = JSSTRING_CHARS(str2); + sep = &tmp; + re = NULL; + } + + /* Use the second argument as the split limit, if given. */ + limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); + limit = 0; /* Avoid warning. */ + if (limited) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + + /* Clamp limit between 0 and 1 + string length. */ + if (!js_DoubleToECMAUint32(cx, d, &limit)) + return JS_FALSE; + if (limit > JSSTRING_LENGTH(str)) + limit = 1 + JSSTRING_LENGTH(str); + } + + 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), 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; +#if JS_HAS_REGEXPS + /* + * 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, + 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + } + sep->chars = NULL; + } +#endif + i = j + sep->length; + if (!JSVERSION_IS_ECMA(cx->version)) { + /* + * Deviate from ECMA to imitate Perl, which omits a final + * split unless a limit argument is given and big enough. + */ + if (!limited && (size_t)i == JSSTRING_LENGTH(str)) + break; + } + } + ok = (j != -2); + } + return ok; +} + +#if JS_HAS_PERL_SUBSTR +static JSBool +str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + 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 { + if (!js_ValueToNumber(cx, argv[1], &d)) + 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), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_PERL_SUBSTR */ + +#if JS_HAS_SEQUENCE_OPS +/* + * Python-esque sequence operations. + */ +static JSBool +str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + uintN i; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + for (i = 0; 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; + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + 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 { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + 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), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_SEQUENCE_OPS */ + +#if JS_HAS_STR_HTML_HELPERS +/* + * HTML composition aids. + */ +static JSBool +tagify(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, const jschar *param, const char *end, + jsval *rval) +{ + JSString *str; + jschar *tagbuf; + size_t beglen, endlen, parlen, taglen; + size_t i, j; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (!end) + end = begin; + + beglen = strlen(begin); + taglen = 1 + beglen + 1; /* '' */ + parlen = 0; /* Avoid warning. */ + if (param) { + parlen = js_strlen(param); + taglen += 2 + parlen + 1; /* '="param"' */ + } + endlen = strlen(end); + taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ + + tagbuf = (jschar *) JS_malloc(cx, (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, parlen); + j += parlen; + tagbuf[j++] = '"'; + } + tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); + j += JSSTRING_LENGTH(str); + 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, 0); + if (!str) { + free((char *)tagbuf); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +tagify_value(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, const char *end, + jsval *rval) +{ + JSString *param; + + param = js_ValueToString(cx, argv[0]); + if (!param) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(param); + return tagify(cx, obj, argv, begin, JSSTRING_CHARS(param), end, rval); +} + +static JSBool +str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "b", NULL, NULL, rval); +} + +static JSBool +str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "i", NULL, NULL, rval); +} + +static JSBool +str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "tt", NULL, NULL, rval); +} + +static JSBool +str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "font size", "font", rval); +} + +static JSBool +str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return tagify_value(cx, obj, argv, "font color", "font", rval); +} + +static JSBool +str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a href", "a", rval); +} + +static JSBool +str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a name", "a", rval); +} + +static JSBool +str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "strike", NULL, NULL, rval); +} + +static JSBool +str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "small", NULL, NULL, rval); +} + +static JSBool +str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "big", NULL, NULL, rval); +} + +static JSBool +str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "blink", NULL, NULL, rval); +} + +static JSBool +str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sup", NULL, NULL, rval); +} + +static JSBool +str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sub", NULL, NULL, rval); +} +#endif /* JS_HAS_STR_HTML_HELPERS */ + +static JSFunctionSpec string_methods[] = { +#if JS_HAS_TOSOURCE + {"quote", str_quote, 0,0,0}, + {js_toSource_str, str_toSource, 0,0,0}, +#endif + + /* Java-like methods. */ + {js_toString_str, str_toString, 0,0,0}, + {js_valueOf_str, str_valueOf, 0,0,0}, + {"substring", str_substring, 2,0,0}, + {"toLowerCase", str_toLowerCase, 0,0,0}, + {"toUpperCase", str_toUpperCase, 0,0,0}, + {"charAt", str_charAt, 1,0,0}, + {"charCodeAt", str_charCodeAt, 1,0,0}, + {"indexOf", str_indexOf, 1,0,0}, + {"lastIndexOf", str_lastIndexOf, 1,0,0}, + {"toLocaleLowerCase", str_toLocaleLowerCase, 0,0,0}, + {"toLocaleUpperCase", str_toLocaleUpperCase, 0,0,0}, + {"localeCompare", str_localeCompare, 1,0,0}, + + /* Perl-ish methods (search is actually Python-esque). */ +#if JS_HAS_REGEXPS + {"match", str_match, 1,0,2}, + {"search", str_search, 1,0,0}, + {"replace", str_replace, 2,0,0}, + {"split", str_split, 2,0,0}, +#endif +#if JS_HAS_PERL_SUBSTR + {"substr", str_substr, 2,0,0}, +#endif + + /* Python-esque sequence methods. */ +#if JS_HAS_SEQUENCE_OPS + {"concat", str_concat, 0,0,0}, + {"slice", str_slice, 0,0,0}, +#endif + + /* HTML string methods. */ +#if JS_HAS_STR_HTML_HELPERS + {"bold", str_bold, 0,0,0}, + {"italics", str_italics, 0,0,0}, + {"fixed", str_fixed, 0,0,0}, + {"fontsize", str_fontsize, 1,0,0}, + {"fontcolor", str_fontcolor, 1,0,0}, + {"link", str_link, 1,0,0}, + {"anchor", str_anchor, 1,0,0}, + {"strike", str_strike, 0,0,0}, + {"small", str_small, 0,0,0}, + {"big", str_big, 0,0,0}, + {"blink", str_blink, 0,0,0}, + {"sup", str_sup, 0,0,0}, + {"sub", str_sub, 0,0,0}, +#endif + + {0,0,0,0,0} +}; + +static JSBool +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; + } else { + str = cx->runtime->emptyString; + } + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return JS_TRUE; +} + +static JSBool +str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jschar *chars; + uintN i; + uint16 code; + JSString *str; + + chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!js_ValueToUint16(cx, argv[i], &code)) { + JS_free(cx, chars); + return JS_FALSE; + } + chars[i] = (jschar)code; + } + chars[i] = 0; + str = js_NewString(cx, chars, argc, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec string_static_methods[] = { + {"fromCharCode", str_fromCharCode, 1,0,0}, + {0,0,0,0,0} +}; + +static JSHashTable *deflated_string_cache; +#ifdef DEBUG +static uint32 deflated_string_cache_bytes; +#endif +#ifdef JS_THREADSAFE +static JSLock *deflated_string_cache_lock; +#endif + +JSBool +js_InitStringGlobals(void) +{ +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!deflated_string_cache_lock) { + deflated_string_cache_lock = JS_NEW_LOCK(); + if (!deflated_string_cache_lock) + return JS_FALSE; + } +#endif + return JS_TRUE; +} + +void +js_FreeStringGlobals() +{ + if (deflated_string_cache) { + JS_HashTableDestroy(deflated_string_cache); + deflated_string_cache = NULL; + } +#ifdef JS_THREADSAFE + if (deflated_string_cache_lock) { + JS_DESTROY_LOCK(deflated_string_cache_lock); + deflated_string_cache_lock = NULL; + } +#endif +} + +JSBool +js_InitRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt; + JSString *empty; + + rt = cx->runtime; + JS_ASSERT(!rt->emptyString); + + /* Make a permanently locked empty string. */ + empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); + if (!empty) + return JS_FALSE; + + /* Atomize it for scripts that use '' + x to convert x to string. */ + if (!js_AtomizeString(cx, empty, ATOM_PINNED)) + return JS_FALSE; + + rt->emptyString = empty; + return JS_TRUE; +} + +void +js_FinishRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + js_UnlockGCThingRT(rt, rt->emptyString); + rt->emptyString = NULL; +} + +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, &string_class, String, 1, + string_props, string_methods, + NULL, string_static_methods); + if (!proto) + return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, + STRING_TO_JSVAL(cx->runtime->emptyString)); + return proto; +} + +JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) +{ + JSString *str; + + if (length > JSSTRING_LENGTH_MASK) { + JS_ReportOutOfMemory(cx); + return NULL; + } + + str = (JSString *) js_AllocGCThing(cx, gcflag | GCX_STRING); + if (!str) + return NULL; + str->length = length; + str->chars = chars; +#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; +} + +JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag) +{ + JSDependentString *ds; + + if (length == 0) + return cx->runtime->emptyString; + + if (start > JSSTRDEP_START_MASK || + (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { + return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, + gcflag); + } + + ds = (JSDependentString *) js_AllocGCThing(cx, gcflag | GCX_MUTABLE_STRING); + if (!ds) + return NULL; + if (start == 0) { + JSPREFIX_SET_LENGTH(ds, length); + JSPREFIX_SET_BASE(ds, base); + } else { + JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); + JSSTRDEP_SET_BASE(ds, base); + } +#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 (JSString *)ds; +} + +#ifdef DEBUG +#include + +void printJSStringStats(JSRuntime *rt) { + double mean = 0., var = 0., sigma = 0.; + jsrefcount count = rt->totalStrings; + if (count > 0 && rt->lengthSum >= 0) { + mean = rt->lengthSum / count; + var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); + + mean = var = sigma = 0.; + count = rt->totalDependentStrings; + if (count > 0 && rt->strdepLengthSum >= 0) { + mean = rt->strdepLengthSum / count; + var = count * rt->strdepLengthSquaredSum + - rt->strdepLengthSum * rt->strdepLengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); +} +#endif + +JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) +{ + jschar *news; + JSString *str; + + news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return NULL; + js_strncpy(news, s, n); + news[n] = 0; + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) +{ + size_t n, m; + jschar *news; + JSString *str; + + n = js_strlen(s); + m = (n + 1) * sizeof(jschar); + news = (jschar *) JS_malloc(cx, m); + if (!news) + return NULL; + memcpy(news, s, m); + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_string_pointer(const void *key) +{ + return (JSHashNumber)key >> JSVAL_TAGBITS; +} + +void +js_PurgeDeflatedStringCache(JSString *str) +{ + JSHashNumber hash; + JSHashEntry *he, **hep; + + if (!deflated_string_cache) + return; + + hash = js_hash_string_pointer(str); + JS_ACQUIRE_LOCK(deflated_string_cache_lock); + hep = JS_HashTableRawLookup(deflated_string_cache, hash, str); + he = *hep; + if (he) { +#ifdef DEBUG + deflated_string_cache_bytes -= JSSTRING_LENGTH(str); +#endif + free(he->value); + JS_HashTableRawRemove(deflated_string_cache, hep, he); + } + JS_RELEASE_LOCK(deflated_string_cache_lock); +} + +void +js_FinalizeString(JSContext *cx, JSString *str) +{ + js_FinalizeStringRT(cx->runtime, str); +} + +void +js_FinalizeStringRT(JSRuntime *rt, JSString *str) +{ + JSBool valid; + + JS_RUNTIME_UNMETER(rt, liveStrings); + if (JSSTRING_IS_DEPENDENT(str)) { + /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ + JS_ASSERT(JSSTRDEP_BASE(str)); + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + valid = JS_TRUE; + } else { + /* A stillborn string has null chars, so is not valid. */ + valid = (str->chars != NULL); + if (valid) + free(str->chars); + } + if (valid) { + js_PurgeDeflatedStringCache(str); + str->chars = NULL; + } + str->length = 0; +} + +JSObject * +js_StringToObject(JSContext *cx, JSString *str) +{ + JSObject *obj; + + obj = js_NewObject(cx, &string_class, NULL, NULL); + if (!obj) + return NULL; + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return obj; +} + +JSString * +js_ValueToString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + + if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + if (!OBJ_DEFAULT_VALUE(cx, obj, 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 { + str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } + return str; +} + +JSString * +js_ValueToSource(JSContext *cx, jsval v) +{ + 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, 0); + } + } else { + if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), + cx->runtime->atomState.toSourceAtom, + 0, NULL, &v)) { + return NULL; + } + } + return js_ValueToString(cx, v); +} + +JSHashNumber +js_HashString(JSString *str) +{ + JSHashNumber h; + const jschar *s; + size_t n; + + h = 0; + for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) + h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; + return h; +} + +intN +js_CompareStrings(JSString *str1, JSString *str2) +{ + size_t l1, l2, n, i; + const jschar *s1, *s2; + intN cmp; + + l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + 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); +} + +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; +} + +const jschar * +js_SkipWhiteSpace(const jschar *s) +{ + /* JS_ISSPACE is false on a null. */ + while (JS_ISSPACE(*s)) + s++; + return s; +} + +#define INFLATE_STRING_BODY \ + for (i = 0; i < length; i++) \ + chars[i] = (unsigned char) bytes[i]; \ + chars[i] = 0; + +void +js_InflateStringToBuffer(jschar *chars, const char *bytes, size_t length) +{ + size_t i; + + INFLATE_STRING_BODY +} + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t length) +{ + jschar *chars; + size_t i; + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + INFLATE_STRING_BODY + + return chars; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length) +{ + size_t i, size; + char *bytes; + + size = (length + 1) * sizeof(char); + bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!bytes) + return NULL; + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + bytes[i] = 0; + return bytes; +} + +static JSHashTable * +GetDeflatedStringCache(void) +{ + JSHashTable *cache; + + cache = deflated_string_cache; + if (!cache) { + cache = JS_NewHashTable(8, js_hash_string_pointer, + JS_CompareValues, JS_CompareValues, + NULL, NULL); + deflated_string_cache = cache; + } + return cache; +} + +JSBool +js_SetStringBytes(JSString *str, char *bytes, size_t length) +{ + JSHashTable *cache; + JSBool ok; + JSHashNumber hash; + JSHashEntry **hep; + + JS_ACQUIRE_LOCK(deflated_string_cache_lock); + + cache = GetDeflatedStringCache(); + if (!cache) { + ok = JS_FALSE; + } else { + 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; +#ifdef DEBUG + if (ok) + deflated_string_cache_bytes += length; +#endif + } + + JS_RELEASE_LOCK(deflated_string_cache_lock); + return ok; +} + +char * +js_GetStringBytes(JSString *str) +{ + JSHashTable *cache; + char *bytes; + JSHashNumber hash; + JSHashEntry *he, **hep; + + JS_ACQUIRE_LOCK(deflated_string_cache_lock); + + cache = GetDeflatedStringCache(); + if (!cache) { + bytes = NULL; + } else { + 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. */ + JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || + *bytes == (char) JSSTRING_CHARS(str)[0]); + } else { + bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), + JSSTRING_LENGTH(str)); + if (bytes) { + if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { +#ifdef DEBUG + deflated_string_cache_bytes += JSSTRING_LENGTH(str); +#endif + } else { + free(bytes); + bytes = NULL; + } + } + } + } + + JS_RELEASE_LOCK(deflated_string_cache_lock); + 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 + * 4 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 */ +0x00036009, /* 9 Nd, identifier part, decimal 16 */ +0x0827FE01, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ +0x0000001B, /* 11 Sk */ +0x00050017, /* 12 Pc, underscore */ +0x0817FE02, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ +0x0000000C, /* 14 Zs */ +0x0000001C, /* 15 So */ +0x00070002, /* 16 Ll, identifier start */ +0x0000600B, /* 17 No, decimal 16 */ +0x0000500B, /* 18 No, decimal 8 */ +0x0000800B, /* 19 No, strange */ +0x08270001, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170002, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70002, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670001, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570002, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670001, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170002, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70001, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170002, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70001, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70001, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670001, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70001, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70001, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70001, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70001, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670001, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670001, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070001, /* 38 Lu, identifier start */ +0x36A70001, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070005, /* 40 Lo, identifier start */ +0x36670001, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70001, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0001, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0003, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0002, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ +0x00000000, /* 46 unassigned */ +0x34970002, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970002, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570002, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970002, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70002, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70002, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570002, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70002, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570002, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970002, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570002, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70002, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070004, /* 59 Lm, identifier start */ +0x00030006, /* 60 Mn, identifier part */ +0x09A70001, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670001, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270001, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70001, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970002, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570002, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170002, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70002, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970002, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570002, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70002, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970002, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970002, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170002, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270001, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270001, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170002, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034009, /* 78 Nd, identifier part, decimal 0 */ +0x00000007, /* 79 Me */ +0x00030008, /* 80 Mc, identifier part */ +0x00037409, /* 81 Nd, identifier part, decimal 26 */ +0x00005A0B, /* 82 No, decimal 13 */ +0x00006E0B, /* 83 No, decimal 23 */ +0x0000740B, /* 84 No, decimal 26 */ +0x0000000B, /* 85 No */ +0xFE170002, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270001, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970002, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970002, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170002, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170002, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170002, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970002, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70002, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70001, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70001, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70001, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270001, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570002, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270001, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670001, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270001, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70001, /* 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 */ +0x0427420A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427800A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417620A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417800A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007800A, /* 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 */ +0x0007720A, /* 120 Nl, identifier start, decimal 25 */ +0x0007400A, /* 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}; + +#define URI_CHUNK 64U + +/* Concatenate jschars onto an unshared/newborn JSString. */ +static JSBool +AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) +{ + size_t total; + + JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); + total = str->length + length + 1; + if (!str->chars || + JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { + total = JS_ROUNDUP(total, URI_CHUNK); + str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); + if (!str->chars) + return JS_FALSE; + } + js_strncpy(str->chars + str->length, chars, length); + str->length += length; + str->chars[str->length] = 0; + return JS_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; + jschar *chars, C, C2; + uint32 V; + uint8 utf8buf[6]; + jschar hexBuf[4]; + static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ + JSString *R; + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + hexBuf[0] = '%'; + hexBuf[3] = 0; + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + for (k = 0; k < length; k++) { + C = chars[k]; + if (js_strchr(unescapedSet, C) || + (unescapedSet2 && js_strchr(unescapedSet2, C))) { + if (!AddCharsToURI(cx, R, &C, 1)) + 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 = OneUcs4ToUtf8Char(utf8buf, V); + for (j = 0; j < L; j++) { + hexBuf[1] = HexDigits[utf8buf[j] >> 4]; + hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; + if (!AddCharsToURI(cx, R, hexBuf, 3)) + return JS_FALSE; + } + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; +} + +static JSBool +Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) +{ + size_t length, start, k; + jschar *chars, C, H; + uint32 V; + jsuint B; + uint8 octets[6]; + JSString *R; + intN j, n; + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + for (k = 0; k < length; k++) { + C = chars[k]; + if (C == '%') { + start = k; + if ((k + 2) >= length) + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + 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 > 6) + goto bad; + octets[0] = (uint8)B; + if (k + 3 * (n - 1) >= length) + goto bad; + for (j = 1; j < n; j++) { + k++; + if (chars[k] != '%') + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + if ((B & 0xC0) != 0x80) + goto bad; + k += 2; + octets[j] = (char)B; + } + V = Utf8ToOneUcs4Char(octets, n); + if (V >= 0x10000) { + V -= 0x10000; + if (V > 0xFFFFF) + goto bad; + C = (jschar)((V & 0x3FF) + 0xDC00); + H = (jschar)((V >> 10) + 0xD800); + if (!AddCharsToURI(cx, R, &H, 1)) + return JS_FALSE; + } else { + C = (jschar)V; + } + } + if (js_strchr(reservedSet, C)) { + if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) + return JS_FALSE; + } else { + if (!AddCharsToURI(cx, R, &C, 1)) + return JS_FALSE; + } + } else { + if (!AddCharsToURI(cx, R, &C, 1)) + return JS_FALSE; + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); + return JS_FALSE; +} + +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); +} + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + return Decode(cx, str, js_empty_ucstr, rval); +} + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, + rval); +} + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); +} + +/* + * 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. + */ +static int +OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) +{ + int utf8Length = 1; + + JS_ASSERT(ucs4Char <= 0x7FFFFFFF); + 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, 0x0001000, 0x0020000, 0x0400000 + }; + + JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); + 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 (ucs4Char < minucs4Char || + ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { + ucs4Char = 0xFFFD; + } + } + return ucs4Char; +} diff --git a/src/extension/script/js/jsstr.h b/src/extension/script/js/jsstr.h new file mode 100644 index 000000000..94ebe97b9 --- /dev/null +++ b/src/extension/script/js/jsstr.h @@ -0,0 +1,439 @@ +/* -*- 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. + * + * When a string is treated as an object (by following it with . or []), the + * runtime wraps it with a JSObject whose valueOf method returns the unwrapped + * string descriptor. + */ +#include +#include "jspubtd.h" +#include "jsprvtd.h" +#include "jshash.h" + +JS_BEGIN_EXTERN_C + +/* + * The original GC-thing "string" type, a flat character string owned by its + * GC-thing descriptor. The chars member points to a vector having byte size + * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. + * The terminator is purely a backstop, in case the chars pointer flows out to + * native code that requires \u0000 termination. + * + * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, + * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). + */ +struct JSString { + size_t length; + jschar *chars; +}; + +/* + * Overlay structure for a string that depends on another string's characters. + * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base + * member may point to another dependent string if JSSTRING_CHARS has not been + * called yet. The length chars in a dependent string are stored starting at + * base->chars + start, and are not necessarily zero-terminated. If start is + * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in + * the high two positions), and the JSSTRFLAG_PREFIX flag is set. + */ +struct JSDependentString { + size_t length; + JSString *base; +}; + +/* Definitions for flags stored in the high order bits of JSString.length. */ +#define JSSTRFLAG_BITS 2 +#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) +#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) +#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) +#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) + +/* Universal JSString type inquiry and accessor macros. */ +#define JSSTRING_BIT(n) ((size_t)1 << (n)) +#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) +#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) +#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) +#define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) +#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_CHARS(str) \ + : (str)->chars) +#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_LENGTH(str) \ + : (str)->length) +#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ + - JSSTRFLAG_BITS) +#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) + +/* Specific JSDependentString shift/mask accessor and mutator macros. */ +#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) +#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS +#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) +#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) +#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) + +#define JSSTRDEP(str) ((JSDependentString *)(str)) +#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ + : ((JSSTRDEP(str)->length \ + >> JSSTRDEP_START_SHIFT) \ + & JSSTRDEP_START_MASK)) +#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ + & (JSSTRING_IS_PREFIX(str) \ + ? JSSTRING_LENGTH_MASK \ + : JSSTRDEP_LENGTH_MASK)) + +#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ + | ((off) << JSSTRDEP_START_SHIFT) \ + | (len)) +#define JSPREFIX_SET_LENGTH(str,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) + +#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) +#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) +#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) +#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) + +#define JSSTRDEP_CHARS(str) \ + (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ + ? js_GetDependentStringChars(str) \ + : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) + +extern size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); + +extern jschar * +js_GetDependentStringChars(JSString *str); + +extern jschar * +js_GetStringChars(JSString *str); + +extern JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +extern const jschar * +js_UndependString(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_ISUC_LETTER(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_ISID_PART(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) + +#define JS_ISWORD(c) (JS_ISALNUM(c) || (c) == '_') + +/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ +#define JS_ISIDENT_START(c) (JS_ISUC_LETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISID_PART(c) || (c) == '_' || (c) == '$') + +#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) + +/* XXXbe fs, etc. ? */ +#define JS_ISSPACE(c) ((JS_CCODE(c) & 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) ((JS_CCODE(c) & 0x00100000) ? (c) - ((int32)JS_CCODE(c) >> 22) : (c)) +#define JS_TOLOWER(c) ((JS_CCODE(c) & 0x00200000) ? (c) + ((int32)JS_CCODE(c) >> 22) : (c)) + +#define JS_TOCTRL(c) ((c) ^ 64) /* XXX unsafe! requires uppercase c */ + +/* Shorthands for ASCII (7-bit) decimal and hex conversion. */ +#define JS7_ISDEC(c) ((c) < 128 && isdigit(c)) +#define JS7_UNDEC(c) ((c) - '0') +#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) +#define JS7_UNHEX(c) (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a') +#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) + +/* Initialize truly global state associated with JS strings. */ +extern JSBool +js_InitStringGlobals(void); + +extern void +js_FreeStringGlobals(void); + +extern void +js_PurgeDeflatedStringCache(JSString *str); + +/* Initialize per-runtime string state for the first context in the runtime. */ +extern JSBool +js_InitRuntimeStringState(JSContext *cx); + +extern void +js_FinishRuntimeStringState(JSContext *cx); + +/* Initialize the String class, returning its prototype object. */ +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, uintN gcflag); + +extern JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag); + +/* Copy a counted string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); + +/* Copy a C string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); + +/* Free the chars held by str when it is finalized by the GC. */ +extern void +js_FinalizeString(JSContext *cx, JSString *str); + +extern void +js_FinalizeStringRT(JSRuntime *rt, JSString *str); + +/* Wrap a string value in a String object. */ +extern JSObject * +js_StringToObject(JSContext *cx, JSString *str); + +/* + * Convert a value to a string, returning null after reporting an error, + * otherwise returning a new string reference. + */ +extern JSString * +js_ValueToString(JSContext *cx, jsval v); + +/* + * Convert a value to its source expression, returning null after reporting + * an error, otherwise returning a new string reference. + */ +extern JSString * +js_ValueToSource(JSContext *cx, jsval v); + +#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ +/* + * Compute a hash function from str. + */ +extern JSHashNumber +js_HashString(JSString *str); +#endif + +/* + * Return less than, equal to, or greater than zero depending on whether + * str1 is less than, equal to, or greater than str2. + */ +extern intN +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 BMH_PATLEN_MAX. + * The start argument tells where in text to begin the search. + * + * Return the index of pat in text, or -1 if not found. + */ +#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ +#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ + +#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ + +extern jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start); + +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. + */ +extern const jschar * +js_SkipWhiteSpace(const jschar *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. + */ +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'+1 jschars. + */ +extern void +js_InflateStringToBuffer(jschar *chars, const 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(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 char * +js_GetStringBytes(JSString *str); + +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +JS_END_EXTERN_C + +#endif /* jsstr_h___ */ diff --git a/src/extension/script/js/jstypes.h b/src/extension/script/js/jstypes.h new file mode 100644 index 000000000..1709c6461 --- /dev/null +++ b/src/extension/script/js/jstypes.h @@ -0,0 +1,388 @@ +/* -*- 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 + +/*********************************************************************** +** 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 + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(WIN16) + +#ifdef _WINDLL +#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds +#define JS_EXPORT_API(__type) __type _cdecl _export _loadds +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK + +#else /* this must be .EXE */ +#define JS_EXTERN_API(__type) extern __type _cdecl _export +#define JS_EXPORT_API(__type) __type _cdecl _export +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK +#endif /* _WINDLL */ + +#elif defined(XP_MAC) +#define JS_EXTERN_API(__type) extern __declspec(export) __type +#define JS_EXPORT_API(__type) __declspec(export) __type +#define JS_EXTERN_DATA(__type) extern __declspec(export) __type +#define JS_EXPORT_DATA(__type) __declspec(export) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#else /* Unix */ + +#define JS_EXTERN_API(__type) extern __type +#define JS_EXPORT_API(__type) __type +#define JS_EXTERN_DATA(__type) extern __type +#define JS_EXPORT_DATA(__type) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#endif + +#ifdef _WIN32 +# if defined(__MWERKS__) || defined(__GNUC__) +# define JS_IMPORT_API(__x) __x +# else +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +# endif +#else +# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) +#endif + +#if defined(_WIN32) && !defined(__MWERKS__) &&!defined(__GNUC__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_DATA(__x) __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. + */ +#ifdef 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) + +#ifdef _WIN32 +# define JS_INLINE __inline +#elif defined(__GNUC__) +# define JS_INLINE +#else +# define JS_INLINE +#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 { +#define JS_END_MACRO } while (0) + +/*********************************************************************** +** 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_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)) + +#if (defined(XP_MAC) || defined(XP_WIN)) && !defined(CROSS_COMPILE) +# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ +#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) +# include "jsautocfg.h" /* Use auto-detected configuration */ +# include "jsosdep.h" /* ...and platform-specific flags */ +#else +# error "Must define one of XP_BEOS, XP_MAC, XP_OS2, XP_WIN or XP_UNIX" +#endif + +JS_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: JSUint8 +** JSInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if JS_BYTES_PER_BYTE == 1 +typedef unsigned char JSUint8; +typedef signed char JSInt8; +#else +#error No suitable type for JSInt8/JSUint8 +#endif + +/************************************************************************ +** TYPES: JSUint16 +** JSInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if JS_BYTES_PER_SHORT == 2 +typedef unsigned short JSUint16; +typedef short JSInt16; +#else +#error No suitable type for JSInt16/JSUint16 +#endif + +/************************************************************************ +** TYPES: JSUint32 +** JSInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if JS_BYTES_PER_INT == 4 +typedef unsigned int JSUint32; +typedef int JSInt32; +#define JS_INT32(x) x +#define JS_UINT32(x) x ## U +#elif JS_BYTES_PER_LONG == 4 +typedef unsigned long JSUint32; +typedef long JSInt32; +#define JS_INT32(x) x ## L +#define JS_UINT32(x) x ## UL +#else +#error No suitable type for JSInt32/JSUint32 +#endif + +/************************************************************************ +** TYPES: JSUint64 +** JSInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type JSUint64 or JSInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the JSLL_ macros (see jslong.h). +************************************************************************/ +#ifdef JS_HAVE_LONG_LONG +#if JS_BYTES_PER_LONG == 8 +typedef long JSInt64; +typedef unsigned long JSUint64; +#elif defined(WIN16) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#else +typedef long long JSInt64; +typedef unsigned long long JSUint64; +#endif /* JS_BYTES_PER_LONG == 8 */ +#else /* !JS_HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + JSUint32 lo, hi; +#else + JSUint32 hi, lo; +#endif +} JSInt64; +typedef JSInt64 JSUint64; +#endif /* !JS_HAVE_LONG_LONG */ + +/************************************************************************ +** 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. +************************************************************************/ +#if JS_BYTES_PER_INT >= 2 +typedef int JSIntn; +typedef unsigned int JSUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** 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 unsigned long 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 consistant overhead matters. +************************************************************************/ +typedef JSUint8 JSPackedBool; + +/* +** A JSWord is an integer that is the same size as a void* +*/ +typedef long JSWord; +typedef unsigned long JSUword; + +#include "jsotypes.h" + +JS_END_EXTERN_C + +#endif /* jstypes_h___ */ + diff --git a/src/extension/script/js/jsutil.c b/src/extension/script/js/jsutil.c new file mode 100644 index 000000000..f64718ad4 --- /dev/null +++ b/src/extension/script/js/jsutil.c @@ -0,0 +1,157 @@ +/* -*- 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 ***** */ + +/* + * PR assertion checker. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#ifdef WIN32 +# include +#endif + +#ifdef XP_MAC +# include +# include +# include "jsprf.h" +#endif + +#ifdef XP_MAC +/* + * PStrFromCStr converts the source C string to a destination + * pascal string as it copies. The dest string will + * be truncated to fit into an Str255 if necessary. + * If the C String pointer is NULL, the pascal string's length is + * set to zero. + */ +static void PStrFromCStr(const char* src, Str255 dst) +{ + short length = 0; + + /* handle case of overlapping strings */ + if ( (void*)src == (void*)dst ) + { + unsigned char* curdst = &dst[1]; + unsigned char thisChar; + + thisChar = *(const unsigned char*)src++; + while ( thisChar != '\0' ) + { + unsigned char nextChar; + + /* + * Use nextChar so we don't overwrite what we + * are about to read + */ + nextChar = *(const unsigned char*)src++; + *curdst++ = thisChar; + thisChar = nextChar; + + if ( ++length >= 255 ) + break; + } + } + else if ( src != NULL ) + { + unsigned char* curdst = &dst[1]; + /* count down so test it loop is faster */ + short overflow = 255; + register char temp; + + /* + * Can't do the K&R C thing of while (*s++ = *t++) + * because it will copy trailing zero which might + * overrun pascal buffer. Instead we use a temp variable. + */ + while ( (temp = *src++) != 0 ) + { + *(char*)curdst++ = temp; + + if ( --overflow <= 0 ) + break; + } + length = 255 - overflow; + } + dst[0] = length; +} + +static void jsdebugstr(const char *debuggerMsg) +{ + Str255 pStr; + + PStrFromCStr(debuggerMsg, pStr); + DebugStr(pStr); +} + +static void dprintf(const char *format, ...) +{ + va_list ap; + char *buffer; + + va_start(ap, format); + buffer = (char *)JS_vsmprintf(format, ap); + va_end(ap); + + jsdebugstr(buffer); + JS_DELETE(buffer); +} +#endif /* XP_MAC */ + +JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) +{ +#ifdef XP_MAC + dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln); +#else + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#endif +#if defined(WIN32) + DebugBreak(); +#endif +#if defined(XP_OS2) + asm("int $3"); +#endif +#ifndef XP_MAC + abort(); +#endif +} diff --git a/src/extension/script/js/jsutil.h b/src/extension/script/js/jsutil.h new file mode 100644 index 000000000..d4edb919c --- /dev/null +++ b/src/extension/script/js/jsutil.h @@ -0,0 +1,106 @@ +/* -*- 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___ + +JS_BEGIN_EXTERN_C + +/*********************************************************************** +** FUNCTION: JS_MALLOC() +** DESCRIPTION: +** JS_NEW() allocates an untyped item of size _size from the heap. +** INPUTS: _size: size in bytes of item to be allocated +** OUTPUTS: untyped pointer to the node allocated +** RETURN: pointer to node or error returned from malloc(). +***********************************************************************/ +#define JS_MALLOC(_bytes) (malloc((_bytes))) + +/*********************************************************************** +** FUNCTION: JS_DELETE() +** DESCRIPTION: +** JS_DELETE() unallocates an object previosly allocated via JS_NEW() +** or JS_NEWZAP() to the heap. +** INPUTS: pointer to previously allocated object +** OUTPUTS: the referenced object is returned to the heap +** RETURN: void +***********************************************************************/ +#define JS_DELETE(_ptr) { free(_ptr); (_ptr) = NULL; } + +/*********************************************************************** +** FUNCTION: JS_NEW() +** DESCRIPTION: +** JS_NEW() allocates an item of type _struct from the heap. +** INPUTS: _struct: a data type +** OUTPUTS: pointer to _struct +** RETURN: pointer to _struct or error returns from malloc(). +***********************************************************************/ +#define JS_NEW(_struct) ((_struct *) JS_MALLOC(sizeof(_struct))) + +#ifdef DEBUG + +extern JS_PUBLIC_API(void) +JS_Assert(const char *s, const char *file, JSIntn ln); +#define JS_ASSERT(_expr) \ + ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) + +#define JS_NOT_REACHED(_reasonStr) \ + JS_Assert(_reasonStr,__FILE__,__LINE__) + +#else + +#define JS_ASSERT(expr) ((void) 0) +#define JS_NOT_REACHED(reasonStr) + +#endif /* defined(DEBUG) */ + +/* +** 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); + +JS_END_EXTERN_C + +#endif /* jsutil_h___ */ diff --git a/src/extension/script/js/jsxdrapi.c b/src/extension/script/js/jsxdrapi.c new file mode 100644 index 000000000..1b092924d --- /dev/null +++ b/src/extension/script/js/jsxdrapi.c @@ -0,0 +1,690 @@ +/* -*- 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 "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XDR + +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.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_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ + if (!data_) \ + return 0; \ + MEM_BASE(xdr) = 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) +{ + JS_free(xdr->cx, 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; +} + +JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode) +{ + JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); + if (!xdr) + return NULL; + JS_XDRInitBase(xdr, mode, cx); + if (mode == JSXDR_ENCODE) { + if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { + JS_free(cx, 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) = 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) { + JS_free(cx, xdr->registry); + if (xdr->reghash) + JS_DHashTableDestroy(xdr->reghash); + } + JS_free(cx, 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 *) JS_malloc(xdr->cx, len + 1))) + return JS_FALSE; + } + if (!JS_XDRBytes(xdr, *sp, len)) { + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, *sp); + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + (*sp)[len] = '\0'; + } else if (xdr->mode == JSXDR_FREE) { + JS_free(xdr->cx, *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); +} + +/* + * Convert between a JS (Unicode) string and the XDR representation. + */ +JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp) +{ + uint32 i, len, padlen, nbytes; + jschar *chars = NULL, *raw; + + if (xdr->mode == JSXDR_ENCODE) + len = JSSTRING_LENGTH(*strp); + if (!JS_XDRUint32(xdr, &len)) + return JS_FALSE; + nbytes = len * sizeof(jschar); + + if (xdr->mode == JSXDR_DECODE) { + if (!(chars = (jschar *) JS_malloc(xdr->cx, nbytes + sizeof(jschar)))) + return JS_FALSE; + } else { + chars = JSSTRING_CHARS(*strp); + } + + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) + goto bad; + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i < len; 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 < len; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + chars[len] = 0; + + if (!(*strp = JS_NewUCString(xdr->cx, chars, len))) + goto bad; + } + return JS_TRUE; + +bad: + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, 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); +} + +JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp) +{ + jsdouble d; + if (xdr->mode == JSXDR_ENCODE) + d = **dp; +#if IS_BIG_ENDIAN + if (!JS_XDRUint32(xdr, (uint32 *)&d + 1) || + !JS_XDRUint32(xdr, (uint32 *)&d)) +#else + if (!JS_XDRUint32(xdr, (uint32 *)&d) || + !JS_XDRUint32(xdr, (uint32 *)&d + 1)) +#endif + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + *dp = JS_NewDouble(xdr->cx, d); + if (!*dp) + 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 + +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); + } + if (!JS_XDRUint32(xdr, &type)) + return JS_FALSE; + + 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; + if (xdr->mode == JSXDR_ENCODE) + dp = JSVAL_TO_DOUBLE(*vp); + 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_BOOLEAN: { + 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((JSBool) 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_XDRScript(JSXDRState *xdr, JSScript **scriptp) +{ + if (!js_XDRScript(xdr, scriptp, 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 **) + JS_realloc(xdr->cx, 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(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), + numclasses); + if (xdr->reghash) { + for (i = 0; i < numclasses; i++) { + JSClass *clasp = xdr->registry[i]; + entry = (JSRegHashEntry *) + JS_DHashTableOperate(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(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/src/extension/script/js/jsxdrapi.h b/src/extension/script/js/jsxdrapi.h new file mode 100644 index 000000000..874a62eeb --- /dev/null +++ b/src/extension/script/js/jsxdrapi.h @@ -0,0 +1,193 @@ +/* -*- 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 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; +}; + +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_CURRENT JSXDR_MAGIC_SCRIPT_4 + +JS_END_EXTERN_C + +#endif /* ! jsxdrapi_h___ */ diff --git a/src/extension/script/js/prmjtime.c b/src/extension/script/js/prmjtime.c new file mode 100644 index 000000000..0a53f76bf --- /dev/null +++ b/src/extension/script/js/prmjtime.c @@ -0,0 +1,646 @@ +/* -*- 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. + */ +#include "jsstddef.h" +#ifdef SOLARIS +#define _REENTRANT 1 +#endif +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#include "jsprf.h" +#include "prmjtime.h" + +#define PRMJ_DO_MILLISECONDS 1 + +#ifdef XP_OS2 +#include +#endif +#ifdef XP_WIN +#include +#include +#endif + +#ifdef XP_MAC +#include +#include +#include +#include +#include +#include +#include +#if !TARGET_CARBON +#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 */ + +#ifdef XP_MAC +static uint64 dstLocalBaseMicroseconds; +static unsigned long gJanuaryFirst1970Seconds; + +static void MacintoshInitializeTime(void) +{ + uint64 upTime; + unsigned long currentLocalTimeSeconds, + startupTimeSeconds; + uint64 startupTimeMicroSeconds; + uint32 upTimeSeconds; + uint64 oneMillion, upTimeSecondsLong, microSecondsToSeconds; + DateTimeRec firstSecondOfUnixTime; + + /* + * Figure out in local time what time the machine started up. This information can be added to + * upTime to figure out the current local time as well as GMT. + */ + + Microseconds((UnsignedWide*)&upTime); + + GetDateTime(¤tLocalTimeSeconds); + + JSLL_I2L(microSecondsToSeconds, PRMJ_USEC_PER_SEC); + JSLL_DIV(upTimeSecondsLong, upTime, microSecondsToSeconds); + JSLL_L2I(upTimeSeconds, upTimeSecondsLong); + + startupTimeSeconds = currentLocalTimeSeconds - upTimeSeconds; + + /* Make sure that we normalize the macintosh base seconds to the unix base of January 1, 1970. + */ + + firstSecondOfUnixTime.year = 1970; + firstSecondOfUnixTime.month = 1; + firstSecondOfUnixTime.day = 1; + firstSecondOfUnixTime.hour = 0; + firstSecondOfUnixTime.minute = 0; + firstSecondOfUnixTime.second = 0; + firstSecondOfUnixTime.dayOfWeek = 0; + + DateToSeconds(&firstSecondOfUnixTime, &gJanuaryFirst1970Seconds); + + startupTimeSeconds -= gJanuaryFirst1970Seconds; + + /* Now convert the startup time into a wide so that we can figure out GMT and DST. + */ + + JSLL_I2L(startupTimeMicroSeconds, startupTimeSeconds); + JSLL_I2L(oneMillion, PRMJ_USEC_PER_SEC); + JSLL_MUL(dstLocalBaseMicroseconds, oneMillion, startupTimeMicroSeconds); +} + +static SleepQRec gSleepQEntry = { NULL, sleepQType, NULL, 0 }; +static JSBool gSleepQEntryInstalled = JS_FALSE; + +static pascal long MySleepQProc(long message, SleepQRecPtr sleepQ) +{ + /* just woke up from sleeping, so must recompute dstLocalBaseMicroseconds. */ + if (message == kSleepWakeUp) + MacintoshInitializeTime(); + return 0; +} + +/* Because serial port and SLIP conflict with ReadXPram calls, + * we cache the call here + */ + +static void MyReadLocation(MachineLocation * loc) +{ + static MachineLocation storedLoc; /* InsideMac, OSUtilities, page 4-20 */ + static JSBool didReadLocation = JS_FALSE; + if (!didReadLocation) + { + MacintoshInitializeTime(); + ReadLocation(&storedLoc); + /* install a sleep queue routine, so that when the machine wakes up, time can be recomputed. */ + if (&SleepQInstall != (void*)kUnresolvedCFragSymbolAddress +#if !TARGET_CARBON + && NGetTrapAddress(0xA28A, OSTrap) != NGetTrapAddress(_Unimplemented, ToolTrap) +#endif + ) { + if ((gSleepQEntry.sleepQProc = NewSleepQUPP(MySleepQProc)) != NULL) { + SleepQInstall(&gSleepQEntry); + gSleepQEntryInstalled = JS_TRUE; + } + } + didReadLocation = JS_TRUE; + } + *loc = storedLoc; +} + + +#ifndef XP_MACOSX + +/* CFM library init and terminate routines. We'll use the terminate routine + to clean up the sleep Q entry. On Mach-O, the sleep Q entry gets cleaned + up for us, so nothing to do there. +*/ + +extern pascal OSErr __NSInitialize(const CFragInitBlock* initBlock); +extern pascal void __NSTerminate(); + +pascal OSErr __JSInitialize(const CFragInitBlock* initBlock); +pascal void __JSTerminate(void); + +pascal OSErr __JSInitialize(const CFragInitBlock* initBlock) +{ + return __NSInitialize(initBlock); +} + +pascal void __JSTerminate() +{ + /* clean up the sleepQ entry */ + if (gSleepQEntryInstalled) + SleepQRemove(&gSleepQEntry); + + __NSTerminate(); +} +#endif /* XP_MACOSX */ + +#endif /* XP_MAC */ + +#define IS_LEAP(year) \ + (year != 0 && ((((year & 0x3) == 0) && \ + ((year - ((year/100) * 100)) != 0)) || \ + (year - ((year/400) * 400)) == 0)) + +#define PRMJ_HOUR_SECONDS 3600L +#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) +#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) +#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() +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm ltime; + + /* get the difference between this time zone and GMT */ + memset((char *)<ime,0,sizeof(ltime)); + ltime.tm_mday = 2; + ltime.tm_year = 70; +#ifdef SUNOS4 + ltime.tm_zone = 0; + ltime.tm_gmtoff = 0; + return timelocal(<ime) - (24 * 3600); +#else + return mktime(<ime) - (24L * 3600L); +#endif +#endif +#if defined(XP_MAC) + static JSInt32 zone = -1L; + MachineLocation machineLocation; + JSInt32 gmtOffsetSeconds; + + /* difference has been set no need to recalculate */ + if (zone != -1) + return zone; + + /* Get the information about the local machine, including + * its GMT offset and its daylight savings time info. + * Convert each into wides that we can add to + * startupTimeMicroSeconds. + */ + + MyReadLocation(&machineLocation); + + /* Mask off top eight bits of gmtDelta, sign extend lower three. */ + gmtOffsetSeconds = (machineLocation.u.gmtDelta << 8); + gmtOffsetSeconds >>= 8; + + /* Backout OS adjustment for DST, to give consistent GMT offset. */ + if (machineLocation.u.dlsDelta != 0) + gmtOffsetSeconds -= PRMJ_HOUR_SECONDS; + return (zone = -gmtOffsetSeconds); +#endif +} + +/* 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); +#ifndef JS_HAVE_LONG_LONG + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); +#else + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); +#endif + JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); + + JSLL_I2L(exttime,base_time); + JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); + JSLL_SUB(exttime,exttime,tmp); + return exttime; +} + +JSInt64 +PRMJ_Now(void) +{ +#ifdef XP_OS2 + JSInt64 s, us, ms2us, s2us; + struct timeb b; +#endif +#ifdef XP_WIN + JSInt64 s, us, + win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), + ten = JSLL_INIT(0, 10); + FILETIME time, midnight; +#endif +#if defined(XP_UNIX) || defined(XP_BEOS) + struct timeval tv; + JSInt64 s, us, s2us; +#endif /* XP_UNIX */ +#ifdef XP_MAC + JSUint64 upTime; + JSInt64 localTime; + JSInt64 gmtOffset; + JSInt64 dstOffset; + JSInt32 gmtDiff; + JSInt64 s2us; +#endif /* XP_MAC */ + +#ifdef XP_OS2 + 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; +#endif +#ifdef XP_WIN + /* 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 precise than the JS time unit) */ + GetSystemTimeAsFileTime(&time); + /* Win9x gets confused at midnight + http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 + So if the low part (precision <8mins) is 0 then we get the time + again. */ + if (!time.dwLowDateTime) { + GetSystemTimeAsFileTime(&midnight); + time.dwHighDateTime = midnight.dwHighDateTime; + } + JSLL_UI2L(s, time.dwHighDateTime); + JSLL_UI2L(us, time.dwLowDateTime); + JSLL_SHL(s, s, 32); + JSLL_ADD(s, s, us); + JSLL_SUB(s, s, win2un); + JSLL_DIV(s, s, ten); + return s; +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) +#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; +#endif /* XP_UNIX */ +#ifdef XP_MAC + JSLL_UI2L(localTime,0); + gmtDiff = PRMJ_LocalGMTDifference(); + JSLL_I2L(gmtOffset,gmtDiff); + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_MUL(gmtOffset,gmtOffset,s2us); + + /* don't adjust for DST since it sets ctime and gmtime off on the MAC */ + Microseconds((UnsignedWide*)&upTime); + JSLL_ADD(localTime,localTime,gmtOffset); + JSLL_ADD(localTime,localTime, dstLocalBaseMicroseconds); + JSLL_ADD(localTime,localTime, upTime); + + dstOffset = PRMJ_DSTOffset(localTime); + JSLL_SUB(localTime,localTime,dstOffset); + + return *((JSUint64 *)&localTime); +#endif /* XP_MAC */ +} + +/* Get the DST timezone offset for the time passed in */ +JSInt64 +PRMJ_DSTOffset(JSInt64 local_time) +{ + JSInt64 us2s; +#ifdef XP_MAC + /* + * Convert the local time passed in to Macintosh epoch seconds. Use UTC utilities to convert + * to UTC time, then compare difference with our GMT offset. If they are the same, then + * DST must not be in effect for the input date/time. + */ + UInt32 macLocalSeconds = (local_time / PRMJ_USEC_PER_SEC) + gJanuaryFirst1970Seconds, utcSeconds; + ConvertLocalTimeToUTC(macLocalSeconds, &utcSeconds); + if ((utcSeconds - macLocalSeconds) == PRMJ_LocalGMTDifference()) + return 0; + else { + JSInt64 dlsOffset; + JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); + JSLL_UI2L(dlsOffset, PRMJ_HOUR_SECONDS); + JSLL_MUL(dlsOffset, dlsOffset, us2s); + return dlsOffset; + } +#else + 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); + } + JSLL_L2UI(local,local_time); + PRMJ_basetime(local_time,&prtm); +#ifndef HAVE_LOCALTIME_R + ptm = localtime(&local); + if(!ptm){ + return JSLL_ZERO; + } + 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); +#endif +} + +/* Format a time value into a buffer. Same semantics as strftime() */ +size_t +PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC) || defined(XP_BEOS) + struct tm a; + + /* 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; + 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.) + + */ + +#if defined(SUNOS4) + if (mktime(&a) == -1) { + /* Seems to fail whenever the requested date is outside of the 32-bit + * UNIX epoch. We could proceed at this point (setting a.tm_zone to + * "") but then strftime returns a string with a 2-digit field of + * garbage for the year. So we return 0 and hope jsdate.c + * will fall back on toString. + */ + return 0; + } +#endif + + return strftime(buf, buflen, fmt, &a); +#endif +} + +/* 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; + JSInt64 result; + JSInt64 result1; + JSInt64 result2; + JSInt64 base; + + JSLL_UI2L(result,0); + JSLL_UI2L(result1,0); + JSLL_UI2L(result2,0); + + /* 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); + + JSLL_UI2L(result, PRMJ_YEAR_SECONDS); + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + JSLL_ADD(result2,result,result1); + + /* get the year */ + while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { + /* subtract a year from tsecs */ + JSLL_SUB(tsecs,tsecs,result); + days += 365; + /* is it a leap year ? */ + if(IS_LEAP(year)){ + JSLL_SUB(tsecs,tsecs,result1); + days++; + } + year++; + isleap = IS_LEAP(year); + } + + 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/src/extension/script/js/prmjtime.h b/src/extension/script/js/prmjtime.h new file mode 100644 index 000000000..6a94a11b1 --- /dev/null +++ b/src/extension/script/js/prmjtime.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 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, ... */ + JSInt16 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); + +/* 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, 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/src/extension/script/js/resource.h b/src/extension/script/js/resource.h new file mode 100644 index 000000000..9301810e4 --- /dev/null +++ b/src/extension/script/js/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/src/extension/script/makefile.in b/src/extension/script/makefile.in new file mode 100644 index 000000000..7ebfd5217 --- /dev/null +++ b/src/extension/script/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd ../.. && $(MAKE) extension/script/all + +clean %.a %.o: + cd ../.. && $(MAKE) extension/script/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/extension/script/quotefile.pl b/src/extension/script/quotefile.pl new file mode 100644 index 000000000..9eb769a25 --- /dev/null +++ b/src/extension/script/quotefile.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl +############################################################################ +# +# Quote all of the lines of a text file, so that it can be loaded +# into C/C++ +# +############################################################################ + +# +# main - top level code +# + + +if ( $#ARGV != 1 ) { # parse command line args + print "usage: perl quotefile.pl infile outfile\n\n"; + exit 1; +} + +$inName = $ARGV[0]; +$outName = $ARGV[1]; + +print "#######################################################\n"; +print "## Quoting $inName to $outName\n"; +print "#######################################################\n"; + +&doQuoteFile(); #Do your magic! + +print "#######################################################\n"; +print "## DONE\n"; +print "#######################################################\n"; + +exit 0; + + + +############################################################################ +# +# +# +# +############################################################################ +sub doQuoteFile +{ + my $line; #current line from input file + my $datestr; #Current date + local(*INFILE); + local(*OUTFILE); + + $datestr = gmtime(); + + if ( -r $inName ) + { + open INFILE, $inName or + die "$inName: $!"; + open OUTFILE, ">$outName" or + die "$outName: $!"; + print OUTFILE "\n"; + print OUTFILE "/* ###################################################\n"; + print OUTFILE "## This file generated by quotefile.pl from\n"; + print OUTFILE "## $inName on $datestr\n"; + print OUTFILE "## DO NOT EDIT\n"; + print OUTFILE "################################################### */\n"; + print OUTFILE "\n"; + print OUTFILE "static char *inkscape_module_script =\n"; + while () + { + $line = $_; + #Escape existing quotes + $line =~ s/\"/\\"/g; + #Add outer quotes + $line =~ s/^/\"/; + $line =~ s/$/\\n\"/; + + print OUTFILE $line + } + close INFILE; + print OUTFILE "\"\";\n"; + close OUTFILE; + } + +} + diff --git a/src/extension/script/runme.py b/src/extension/script/runme.py new file mode 100644 index 000000000..706676064 --- /dev/null +++ b/src/extension/script/runme.py @@ -0,0 +1,8 @@ + +import inkscape_py + +inkscape = inkscape_py.getInkscape() +desktop = inkscape.getDesktop() +document = desktop.getDocument() +document.hello() + diff --git a/src/extension/script/wrap_swig_module.sh b/src/extension/script/wrap_swig_module.sh new file mode 100644 index 000000000..8c6236f2d --- /dev/null +++ b/src/extension/script/wrap_swig_module.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +inf=$1 +outf=$2 +datestr=`date` + +echo "" > ${outf} +echo "/* ###################################################" >> ${outf} +echo "## This file generated by wrap_swig_module.sh from" >> ${outf} +echo "## ${inf} on ${datestr}" >> ${outf} +echo "## DO NOT EDIT" >> ${outf} +echo "################################################### */" >> ${outf} +echo "" >> ${outf} + +echo "static char *inkscape_module_script =" >> ${outf} +echo "\"\n\"" >> ${outf} + +cat ${inf} | \ +sed -e 's/\"/\\"/g' -e 's/^/\"/g' -e 's/$/\\n\"/g' >> ${outf} + +echo "\"\n\";" >> ${outf} + + + + + + diff --git a/src/extension/system.cpp b/src/extension/system.cpp new file mode 100644 index 000000000..34855cd4e --- /dev/null +++ b/src/extension/system.cpp @@ -0,0 +1,476 @@ +/* + * This is file is kind of the junk file. Basically everything that + * didn't fit in one of the other well defined areas, well, it's now + * here. Which is good in someways, but this file really needs some + * definition. Hopefully that will come ASAP. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "db.h" +#include "input.h" +#include "output.h" +#include "effect.h" +#include "print.h" +#include "implementation/script.h" +/* #include "implementation/plugin.h" */ + +namespace Inkscape { +namespace Extension { + +static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data); +static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data); +static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp); + +/** + * \return A new document created from the filename passed in + * \brief This is a generic function to use the open function of + * a module (including Autodetect) + * \param key Identifier of which module to use + * \param filename The file that should be opened + * + * First things first, are we looking at an autodetection? Well if that's the case then the module + * needs to be found, and that is done with a database lookup through the module DB. The foreach + * function is called, with the parameter being a gpointer array. It contains both the filename + * (to find its extension) and where to write the module when it is found. + * + * If there is no autodetection, then the module database is queried with the key given. + * + * If everything is cool at this point, the module is loaded, and there is possibility for + * preferences. If there is a function, then it is executed to get the dialog to be displayed. + * After it is finished the function continues. + * + * Lastly, the open function is called in the module itself. + */ +SPDocument * +open(Extension *key, gchar const *filename) +{ + Input *imod = NULL; + if (key == NULL) { + gpointer parray[2]; + parray[0] = (gpointer)filename; + parray[1] = (gpointer)&imod; + db.foreach(open_internal, (gpointer)&parray); + } else { + imod = dynamic_cast(key); + } + + bool last_chance_svg = false; + if (key == NULL && imod == NULL) { + last_chance_svg = true; + imod = dynamic_cast(db.get(SP_MODULE_KEY_INPUT_SVG)); + } + + if (imod == NULL) { + throw Input::no_extension_found(); + } + + imod->set_state(Extension::STATE_LOADED); + + if (!imod->loaded()) { + throw Input::open_failed(); + } + + if (!imod->prefs(filename)) + return NULL; + + SPDocument *doc = imod->open(filename); + if (!doc) { + return NULL; + } + + if (last_chance_svg) { + /* We can't call sp_ui_error_dialog because we may be + running from the console, in which case calling sp_ui + routines will cause a segfault. See bug 1000350 - bryce */ + // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG.")); + g_warning(_("Format autodetect failed. The file is being opened as SVG.")); + } + + /* This kinda overkill as most of these are already set, but I want + to make sure for this release -- TJG */ + Inkscape::XML::Node *repr = sp_document_repr_root(doc); + gboolean saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive(doc, FALSE); + repr->setAttribute("sodipodi:modified", NULL); + sp_document_set_undo_sensitive(doc, saved); + + sp_document_set_uri(doc, filename); + + return doc; +} + +/** + * \return none + * \brief This is the function that searches each module to see + * if it matches the filename for autodetection. + * \param in_plug The module to be tested + * \param in_data An array of pointers containing the filename, and + * the place to put a successfully found module. + * + * Basically this function only looks at input modules as it is part of the open function. If the + * module is an input module, it then starts to take it apart, and the data that is passed in. + * Because the data being passed in is in such a weird format, there are a few casts to make it + * easier to use. While it looks like a lot of local variables, they'll all get removed by the + * compiler. + * + * First thing that is checked is if the filename is shorter than the extension itself. There is + * no way for a match in that case. If it's long enough then there is a string compare of the end + * of the filename (for the length of the extension), and the extension itself. If this passes + * then the pointer passed in is set to the current module. + */ +static void +open_internal(Extension *in_plug, gpointer in_data) +{ + if (!in_plug->deactivated() && dynamic_cast(in_plug)) { + gpointer *parray = (gpointer *)in_data; + gchar const *filename = (gchar const *)parray[0]; + Input **pimod = (Input **)parray[1]; + + // skip all the rest if we already found a function to open it + // since they're ordered by preference now. + if (!*pimod) { + gchar const *ext = dynamic_cast(in_plug)->get_extension(); + + gchar *filenamelower = g_utf8_strdown(filename, -1); + gchar *extensionlower = g_utf8_strdown(ext, -1); + + if (g_str_has_suffix(filenamelower, extensionlower)) { + *pimod = dynamic_cast(in_plug); + } + + g_free(filenamelower); + g_free(extensionlower); + } + } + + return; +} + +/** + * \return None + * \brief This is a generic function to use the save function of + * a module (including Autodetect) + * \param key Identifier of which module to use + * \param doc The document to be saved + * \param filename The file that the document should be saved to + * \param official (optional) whether to set :output_module and :modified in the + * document; is true for normal save, false for temporary saves + * + * First things first, are we looking at an autodetection? Well if that's the case then the module + * needs to be found, and that is done with a database lookup through the module DB. The foreach + * function is called, with the parameter being a gpointer array. It contains both the filename + * (to find its extension) and where to write the module when it is found. + * + * If there is no autodetection the module database is queried with the key given. + * + * If everything is cool at this point, the module is loaded, and there is possibility for + * preferences. If there is a function, then it is executed to get the dialog to be displayed. + * After it is finished the function continues. + * + * Lastly, the save function is called in the module itself. + */ +void +save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official) +{ + Output *omod; + if (key == NULL) { + gpointer parray[2]; + parray[0] = (gpointer)filename; + parray[1] = (gpointer)&omod; + omod = NULL; + db.foreach(save_internal, (gpointer)&parray); + + /* This is a nasty hack, but it is required to ensure that + autodetect will always save with the Inkscape extensions + if they are available. */ + if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) { + omod = dynamic_cast(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); + } + /* If autodetect fails, save as Inkscape SVG */ + if (omod == NULL) { + omod = dynamic_cast(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); + } + } else { + omod = dynamic_cast(key); + } + + if (!dynamic_cast(omod)) { + g_warning("Unable to find output module to handle file: %s\n", filename); + throw Output::no_extension_found(); + return; + } + + omod->set_state(Extension::STATE_LOADED); + if (!omod->loaded()) { + throw Output::save_failed(); + } + + if (!omod->prefs()) + return; + + gchar *fileName = NULL; + if (setextension) { + gchar *lowerfile = g_utf8_strdown(filename, -1); + gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1); + + if (!g_str_has_suffix(lowerfile, lowerext)) { + fileName = g_strdup_printf("%s%s", filename, omod->get_extension()); + } + + g_free(lowerfile); + g_free(lowerext); + } + + if (fileName == NULL) { + fileName = g_strdup(filename); + } + + if (check_overwrite && !sp_ui_overwrite_file(fileName)) { + g_free(fileName); + throw Output::no_overwrite(); + } + + if (official) { + sp_document_set_uri(doc, fileName); + } + + omod->save(doc, fileName); + + g_free(fileName); + return; +} + +/** + * \return none + * \brief This is the function that searches each module to see + * if it matches the filename for autodetection. + * \param in_plug The module to be tested + * \param in_data An array of pointers containing the filename, and + * the place to put a successfully found module. + * + * Basically this function only looks at output modules as it is part of the open function. If the + * module is an output module, it then starts to take it apart, and the data that is passed in. + * Because the data being passed in is in such a weird format, there are a few casts to make it + * easier to use. While it looks like a lot of local variables, they'll all get removed by the + * compiler. + * + * First thing that is checked is if the filename is shorter than the extension itself. There is + * no way for a match in that case. If it's long enough then there is a string compare of the end + * of the filename (for the length of the extension), and the extension itself. If this passes + * then the pointer passed in is set to the current module. + */ +static void +save_internal(Extension *in_plug, gpointer in_data) +{ + if (!in_plug->deactivated() && dynamic_cast(in_plug)) { + gpointer *parray = (gpointer *)in_data; + gchar const *filename = (gchar const *)parray[0]; + Output **pomod = (Output **)parray[1]; + + // skip all the rest if we already found someone to save it + // since they're ordered by preference now. + if (!*pomod) { + gchar const *ext = dynamic_cast(in_plug)->get_extension(); + + gchar *filenamelower = g_utf8_strdown(filename, -1); + gchar *extensionlower = g_utf8_strdown(ext, -1); + + if (g_str_has_suffix(filenamelower, extensionlower)) { + *pomod = dynamic_cast(in_plug); + } + + g_free(filenamelower); + g_free(extensionlower); + } + } + + return; +} + +Print * +get_print(gchar const *key) +{ + return dynamic_cast(db.get(key)); +} + +/** + * \return The built module + * \brief Creates a module from a Inkscape::XML::Document describing the module + * \param doc The XML description of the module + * + * This function basically has two segments. The first is that it goes through the Repr tree + * provided, and determines what kind of of module this is, and what kind of implementation to use. + * All of these are then stored in two enums that are defined in this function. This makes it + * easier to add additional types (which will happen in the future, I'm sure). + * + * Second, there is case statements for these enums. The first one is the type of module. This is + * the one where the module is actually created. After that, then the implementation is applied to + * get the load and unload functions. If there is no implementation then these are not set. This + * case could apply to modules that are built in (like the SVG load/save functions). + */ +static Extension * +build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp) +{ + enum { + MODULE_EXTENSION, + /* MODULE_PLUGIN, */ + MODULE_UNKNOWN_IMP + } module_implementation_type = MODULE_UNKNOWN_IMP; + enum { + MODULE_INPUT, + MODULE_OUTPUT, + MODULE_FILTER, + MODULE_PRINT, + MODULE_UNKNOWN_FUNC + } module_functional_type = MODULE_UNKNOWN_FUNC; + + g_return_val_if_fail(doc != NULL, NULL); + + Inkscape::XML::Node *repr = sp_repr_document_root(doc); + + /* sp_repr_print(repr); */ + + if (strcmp(repr->name(), "inkscape-extension")) { + g_warning("Extension definition started with <%s> instead of . Extension will not be created.\n", repr->name()); + return NULL; + } + + Inkscape::XML::Node *child_repr = sp_repr_children(repr); + while (child_repr != NULL) { + char const *element_name = child_repr->name(); + /* printf("Child: %s\n", child_repr->name()); */ + if (!strcmp(element_name, "input")) { + module_functional_type = MODULE_INPUT; + } else if (!strcmp(element_name, "output")) { + module_functional_type = MODULE_OUTPUT; + } else if (!strcmp(element_name, "effect")) { + module_functional_type = MODULE_FILTER; + } else if (!strcmp(element_name, "print")) { + module_functional_type = MODULE_PRINT; + } else if (!strcmp(element_name, "script")) { + module_implementation_type = MODULE_EXTENSION; +#if 0 + } else if (!strcmp(element_name, "plugin")) { + module_implementation_type = MODULE_PLUGIN; +#endif + } + + //Inkscape::XML::Node *old_repr = child_repr; + child_repr = sp_repr_next(child_repr); + //Inkscape::GC::release(old_repr); + } + + Implementation::Implementation *imp; + if (in_imp == NULL) { + switch (module_implementation_type) { + case MODULE_EXTENSION: { + Implementation::Script *script = new Implementation::Script(); + imp = static_cast(script); + break; + } +#if 0 + case MODULE_PLUGIN: { + Implementation::Plugin *plugin = new Implementation::Plugin(); + imp = static_cast(plugin); + break; + } +#endif + default: { + imp = NULL; + break; + } + } + } else { + imp = in_imp; + } + + Extension *module = NULL; + switch (module_functional_type) { + case MODULE_INPUT: { + module = new Input(repr, imp); + break; + } + case MODULE_OUTPUT: { + module = new Output(repr, imp); + break; + } + case MODULE_FILTER: { + module = new Effect(repr, imp); + break; + } + case MODULE_PRINT: { + module = new Print(repr, imp); + break; + } + default: { + break; + } + } + + return module; +} + +/** + * \return The module created + * \brief This function creates a module from a filename of an + * XML description. + * \param filename The file holding the XML description of the module. + * + * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc. + */ +Extension * +build_from_file(gchar const *filename) +{ + /* TODO: Need to define namespace here, need to write the + DTD in general for this stuff */ + Inkscape::XML::Document *doc = sp_repr_read_file(filename, NULL); + Extension *ext = build_from_reprdoc(doc, NULL); + Inkscape::GC::release(doc); + if (ext == NULL) + g_warning("Unable to create extension from definition file %s.\n", filename); + return ext; +} + +/** + * \return The module created + * \brief This function creates a module from a buffer holding an + * XML description. + * \param buffer The buffer holding the XML description of the module. + * + * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc. It + * finds the length of the buffer using strlen. + */ +Extension * +build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp) +{ + Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL); + Extension *ext = build_from_reprdoc(doc, in_imp); + Inkscape::GC::release(doc); + return ext; +} + + +} } /* namespace Inkscape::Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/system.h b/src/extension/system.h new file mode 100644 index 000000000..6c23b2f0d --- /dev/null +++ b/src/extension/system.h @@ -0,0 +1,44 @@ +/* + * This is file is kind of the junk file. Basically everything that + * didn't fit in one of the other well defined areas, well, it's now + * here. Which is good in someways, but this file really needs some + * definition. Hopefully that will come ASAP. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2002-2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_SYSTEM_H__ +#define INKSCAPE_EXTENSION_SYSTEM_H__ + +#include "document.h" +#include "extension/extension.h" + +namespace Inkscape { +namespace Extension { + +SPDocument *open(Extension *key, gchar const *filename); +void save(Extension *key, SPDocument *doc, gchar const *filename, + bool setextension, bool check_overwrite, bool official); +Print *get_print(gchar const *key); +Extension *build_from_file(gchar const *filename); +Extension *build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp); + +} } /* namespace Inkscape::Extension */ + +#endif /* INKSCAPE_EXTENSION_SYSTEM_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/timer.cpp b/src/extension/timer.cpp new file mode 100644 index 000000000..0e7a6ad11 --- /dev/null +++ b/src/extension/timer.cpp @@ -0,0 +1,214 @@ +/* + * Here is where the extensions can get timed on when they load and + * unload. All of the timing is done in here. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + + +#include "extension.h" +#include "timer.h" + +namespace Inkscape { +namespace Extension { + +#define TIMER_SCALE_VALUE 20 + +ExpirationTimer * ExpirationTimer::timer_list = NULL; +ExpirationTimer * ExpirationTimer::idle_start = NULL; +long ExpirationTimer::timeout = 240; +bool ExpirationTimer::timer_started = false; + +/** \brief Create a new expiration timer + \param in_extension Which extension this timer is related to + + This function creates the timer, and sets the time to the current + time, plus what ever the current timeout is. Also, if this is + the first timer extension, the timer is kicked off. This function + also sets up teh circularly linked list of all the timers. +*/ +ExpirationTimer::ExpirationTimer (Extension * in_extension) +{ + locked = false; + extension = in_extension; + + /* Fix Me! */ + if (timer_list == NULL) { + next = this; + timer_list = this; + } else { + next = timer_list->next; + timer_list->next = this; + } + + expiration.assign_current_time(); + expiration += timeout; + + if (!timer_started) { + Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE); + timer_started = true; + } + + return; +} + +/** \brief Deletes a \c ExpirationTimer + + The most complex thing that this function does is remove the timer + from the circularly linked list. If this is the only entry in the + list that is easy, otherwise all the entries must be found, and this + one removed from the list. +*/ +ExpirationTimer::~ExpirationTimer(void) +{ + if (this != next) { + /* This will remove this entry from the circularly linked + list. */ + ExpirationTimer * prev; + for (prev = timer_list; + prev->next != this; + prev = prev->next); + prev->next = next; + + if (idle_start == this) + idle_start = next; + + /* This may occur more than you think, just because the guy + doing most of the deletions is the idle function, who tracks + where it is looking using the \c timer_list variable. */ + if (timer_list == this) + timer_list = next; + } else { + /* If we're the only entry in the list, the list needs to go + to NULL */ + timer_list = NULL; + idle_start = NULL; + } + + return; +} + +/** \brief Touches the timer to extend the length before it expires + + Basically it adds more time to the timer. One thing that is kinda + tricky is that it adds half the time remaining back into the timer. + This allows for some extensions that are used regularly to having + extended expiration times. So, in the end, they stay loaded longer. + Extensions that are only used once will expire at a standard rate + set by \c timeout. +*/ +void +ExpirationTimer::touch (void) +{ + Glib::TimeVal current; + current.assign_current_time(); + + long time_left = (long)(expiration.as_double() - current.as_double()); + if (time_left < 0) time_left = 0; + time_left /= 2; + + expiration = current + timeout + time_left; + return; +} + +/** \brief Check to see if the timer has expired + + Checks the time against the current time. +*/ +bool +ExpirationTimer::expired (void) const +{ + if (locked) return false; + + Glib::TimeVal current; + current.assign_current_time(); + return expiration < current; +} + +// int idle_cnt = 0; + +/** \brief This function goes in the idle loop to find expired extensions + \return Whether the function should be requeued or not + + This function first insures that there is a timer list, and then checks + to see if the one on the top of the list has expired. If it has + expired it unloads the module. By unloading the module, the timer + gets deleted (happens in the unload function). If the list is + no empty, the function returns that it should be dequeued and sets + the \c timer_started variable so that the timer will be reissued when + a timer is added. If there is entries left, but the next one is + where this function started, then the timer is set up. The timer + will then re-add the idle loop function when it runs. +*/ +bool +ExpirationTimer::idle_func (void) +{ + // std::cout << "Idle func pass: " << idle_cnt++ << " timer list: " << timer_list << std::endl; + + /* see if this is the last */ + if (timer_list == NULL) { + timer_started = false; + return false; + } + + /* evalutate current */ + if (timer_list->expired()) { + timer_list->extension->set_state(Extension::STATE_UNLOADED); + } + + /* see if this is the last */ + if (timer_list == NULL) { + timer_started = false; + return false; + } + + if (timer_list->next == idle_start) { + /* if so, set up the timer and return FALSE */ + /* Note: This may cause one to be missed on the evaluation if + the one before it expires and it is last in the list. + While this could be taken care of, it isn't worth the + complexity for this lazy removal that we're doing. It + should get picked up next time */ + Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE); + return false; + } + + /* If nothing else, continue on */ + timer_list = timer_list->next; + return true; +} + +/** \brief A timer function to set up the idle function + \return Always false -- to disable the timer + + This function sets up the idle loop when it runs. The idle loop is + the one that unloads all the extensions. +*/ +bool +ExpirationTimer::timer_func (void) +{ + // std::cout << "Timer func" << std::endl; + idle_start = timer_list; + // idle_cnt = 0; + Glib::signal_idle().connect(sigc::ptr_fun(&idle_func)); + return false; +} + +}; }; /* namespace Inkscape, Extension */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/extension/timer.h b/src/extension/timer.h new file mode 100644 index 000000000..b9649e899 --- /dev/null +++ b/src/extension/timer.h @@ -0,0 +1,72 @@ +/* + * Here is where the extensions can get timed on when they load and + * unload. All of the timing is done in here. + * + * Authors: + * Ted Gould + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_TIMER_H__ +#define INKSCAPE_EXTENSION_TIMER_H__ + +#include +#include +#include "extension-forward.h" + +namespace Inkscape { +namespace Extension { + +class ExpirationTimer { + /** \brief Circularly linked list of all timers */ + static ExpirationTimer * timer_list; + /** \brief Which timer was on top when we started the idle loop */ + static ExpirationTimer * idle_start; + /** \brief What the current timeout is */ + static long timeout; + /** \brief Has the timer been started? */ + static bool timer_started; + + /** \brief Is this extension locked from being unloaded? */ + bool locked; + /** \brief Next entry in the list */ + ExpirationTimer * next; + /** \brief When this timer expires */ + Glib::TimeVal expiration; + /** \brief What extension this function relates to */ + Extension * extension; + + bool expired(void) const; + + static bool idle_func (void); + static bool timer_func (void); + +public: + ExpirationTimer(Extension * in_extension); + ~ExpirationTimer(void); + + void touch (void); + void lock (void) { locked = true; }; + void unlock (void) { locked = false; }; + + /** \brief Set the timeout variable */ + static void set_timeout (long in_seconds) { timeout = in_seconds; }; +}; + +}; }; /* namespace Inkscape, Extension */ + +#endif /* INKSCAPE_EXTENSION_TIMER_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : -- cgit v1.2.3