From: ishmal Date: Wed, 12 Apr 2006 13:25:21 +0000 (+0000) Subject: Add new rearranged /dom directory X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=6232b46968ade11dd56fdb9627a2bda45d0dda7d;p=inkscape.git Add new rearranged /dom directory --- diff --git a/src/dom/Makefile.mingw b/src/dom/Makefile.mingw new file mode 100644 index 000000000..cdae967fa --- /dev/null +++ b/src/dom/Makefile.mingw @@ -0,0 +1,352 @@ +########################################################################### +# +# Makefile for testing DOM code +# +########################################################################### + +####### Sense whether we are on a DOS box or cross-compiling +ifdef ComSpec +BUILD=native +DOSSHELL=CMD_EXE +else +ifdef COMSPEC +BUILD=native +DOSSHELL=COMMAND_COM +else +BUILD=cross +endif +endif + + + + +########################################################################## +# FILE SEPARATORS +# $(S) will be set to one of these +########################################################################## +BSLASH := \\# +FSLASH := / + + + +########################################################################## +# CROSS / NATIVE SWITCHES +########################################################################## +ifeq ($(BUILD),cross) + + + +########################################################################## +# CROSS COMPILER SETTINGS +########################################################################## + +CC = i686-pc-mingw32-gcc +CXX = i686-pc-mingw32-g++ +AS = i686-pc-mingw32-as +AR = i686-pc-mingw32-ar +RANLIB = i686-pc-mingw32-ranlib +WINDRES = i686-pc-mingw32-windres +LD = i686-pc-mingw32-ld +DLLWRAP = i686-pc-mingw32-dllwrap +DLLTOOL = i686-pc-mingw32-dlltool + +####### file separator +S = $(FSLASH) + +####### escape character for echo +E = / + +####### file manipulation programs +CP = cp +RMDIR = rm -rf +MKDIR = mkdir +CPDIR = cp -rf +MSGFMT = msgfmt +RMREC = find ./inkscape -type f -name +RMREC1 = |xargs $(RM) +RMDIRREC = find ./inkscape -type d -name +RMDIRREC1 = |xargs $(RMDIR) + +####### Where is your GTK directory? +GTK=/target + +####### Same thing, file system style +GTKDOS=$(GTK) + +DTG := $(shell date +%y%m%d.%H%M) + +else + +########################################################################## +# NATIVE COMPILER SETTINGS +########################################################################## + +CC = mingw32-gcc +CXX = mingw32-g++ +AS = as +AR = mingw32-ar +RANLIB = ranlib +WINDRES = windres +DLLWRAP = dllwrap +DLLTOOL = dlltool + +####### file separator +S = $(BSLASH) + +####### escape character for echo +E = + +####### file manipulation programs +CP = copy + +####### are we on WinNt and beyond? +ifeq ($(DOSSHELL),CMD_EXE) +RMDIR = rmdir /s /q +RM = del +else +RMDIR = deltree /y +RM = del +endif +MKDIR = mkdir +CPDIR = xcopy /e /i +RMREC = cd inkscape & $(RM) /s /q +RMREC1 = & cd .. +RMDIRREC = cd inkscape & $(RMDIR) /s +RMDIRREC1 = & cd .. + +####### Where is your GTK directory? +GTK=c:/gtk28 + +####### Same thing, DOS style +GTKDOS=c:\gtk28 + +####### Command to process .po files --> .mo +MSGFMT = $(GTKDOS)$(S)bin$(S)msgfmt + +####### change me!! +DTG := 20050626 + +endif +########################################################################## +# END CROSS / NATIVE SWITCHES +########################################################################## + + + + +INC = -I. +CFLAGS = -Wall -g -DXP_WIN +LIBS = -lws2_32 + +DOMOBJ = \ +charclass.o \ +cssparser.o \ +domimpl.o \ +domstring.o \ +lsimpl.o \ +smilimpl.o \ +uri.o \ +xmlreader.o \ +xpathimpl.o \ +xpathparser.o \ +xpathtoken.o \ +io/base64stream.o \ +io/domstream.o \ +io/bufferstream.o \ +io/gzipstream.o \ +io/httpclient.o \ +io/stringstream.o \ +io/uristream.o \ +io/socket.o \ +odf/odfdocument.o \ +svg/svgimpl.o \ +svg/svglsimpl.o \ +svg/svgparser.o \ +util/thread.o \ +util/ziptool.o + +JSOBJ = \ +js/js.o \ +js/jsapi.o \ +js/jsarena.o \ +js/jsarray.o \ +js/jsatom.o \ +js/jsbool.o \ +js/jscntxt.o \ +js/jscpucfg.o \ +js/jsdate.o \ +js/jsdbgapi.o \ +js/jsdhash.o \ +js/jsdtoa.o \ +js/jsemit.o \ +js/jsexn.o \ +js/jsfile.o \ +js/jsfun.o \ +js/jsgc.o \ +js/jshash.o \ +js/jsinterp.o \ +js/jslock.o \ +js/jslog2.o \ +js/jslong.o \ +js/jsmath.o \ +js/jsnum.o \ +js/jsobj.o \ +js/jsopcode.o \ +js/jsparse.o \ +js/jsprf.o \ +js/jsregexp.o \ +js/jsscan.o \ +js/jsscope.o \ +js/jsscript.o \ +js/jsstr.o \ +js/jsutil.o \ +js/jsxdrapi.o \ +js/prmjtime.o \ +js/fdlibm/e_acos.o \ +js/fdlibm/e_acosh.o \ +js/fdlibm/e_asin.o \ +js/fdlibm/e_atan2.o \ +js/fdlibm/e_atanh.o \ +js/fdlibm/e_cosh.o \ +js/fdlibm/e_exp.o \ +js/fdlibm/e_fmod.o \ +js/fdlibm/e_gamma.o \ +js/fdlibm/e_gamma_r.o \ +js/fdlibm/e_hypot.o \ +js/fdlibm/e_j0.o \ +js/fdlibm/e_j1.o \ +js/fdlibm/e_jn.o \ +js/fdlibm/e_lgamma.o \ +js/fdlibm/e_lgamma_r.o \ +js/fdlibm/e_log.o \ +js/fdlibm/e_log10.o \ +js/fdlibm/e_pow.o \ +js/fdlibm/e_remainder.o \ +js/fdlibm/e_rem_pio2.o \ +js/fdlibm/e_scalb.o \ +js/fdlibm/e_sinh.o \ +js/fdlibm/e_sqrt.o \ +js/fdlibm/k_cos.o \ +js/fdlibm/k_rem_pio2.o \ +js/fdlibm/k_sin.o \ +js/fdlibm/k_standard.o \ +js/fdlibm/k_tan.o \ +js/fdlibm/s_asinh.o \ +js/fdlibm/s_atan.o \ +js/fdlibm/s_cbrt.o \ +js/fdlibm/s_ceil.o \ +js/fdlibm/s_copysign.o \ +js/fdlibm/s_cos.o \ +js/fdlibm/s_erf.o \ +js/fdlibm/s_expm1.o \ +js/fdlibm/s_fabs.o \ +js/fdlibm/s_finite.o \ +js/fdlibm/s_floor.o \ +js/fdlibm/s_frexp.o \ +js/fdlibm/s_ilogb.o \ +js/fdlibm/s_isnan.o \ +js/fdlibm/s_ldexp.o \ +js/fdlibm/s_lib_version.o \ +js/fdlibm/s_log1p.o \ +js/fdlibm/s_logb.o \ +js/fdlibm/s_matherr.o \ +js/fdlibm/s_modf.o \ +js/fdlibm/s_nextafter.o \ +js/fdlibm/s_rint.o \ +js/fdlibm/s_scalbn.o \ +js/fdlibm/s_signgam.o \ +js/fdlibm/s_significand.o \ +js/fdlibm/s_sin.o \ +js/fdlibm/s_tan.o \ +js/fdlibm/s_tanh.o \ +js/fdlibm/w_acos.o \ +js/fdlibm/w_acosh.o \ +js/fdlibm/w_asin.o \ +js/fdlibm/w_atan2.o \ +js/fdlibm/w_atanh.o \ +js/fdlibm/w_cosh.o \ +js/fdlibm/w_exp.o \ +js/fdlibm/w_fmod.o \ +js/fdlibm/w_gamma.o \ +js/fdlibm/w_gamma_r.o \ +js/fdlibm/w_hypot.o \ +js/fdlibm/w_j0.o \ +js/fdlibm/w_j1.o \ +js/fdlibm/w_jn.o \ +js/fdlibm/w_lgamma.o \ +js/fdlibm/w_lgamma_r.o \ +js/fdlibm/w_log.o \ +js/fdlibm/w_log10.o \ +js/fdlibm/w_pow.o \ +js/fdlibm/w_remainder.o \ +js/fdlibm/w_scalb.o \ +js/fdlibm/w_sinh.o \ +js/fdlibm/w_sqrt.o + + + +TESTOBJ = \ +work/testdom.o \ +work/testodf.o \ +work/testsvg.o \ +work/testuri.o \ +work/testxpath.o \ +work/testzip.o + +OBJ = $(DOMOBJ) $(JSOBJ) + +all: testdom.exe testhttp.exe testodf.exe \ +testsvg.exe testuri.exe testxpath.exe testzip.exe + +testdom.exe: libdom.a work/testdom.o + $(CXX) -o $@ work/testdom.o libdom.a $(LIBS) + +testhttp.exe: libdom.a work/testhttp.o + $(CXX) -o $@ work/testhttp.o libdom.a $(LIBS) + +testodf.exe: libdom.a work/testodf.o + $(CXX) -o $@ work/testodf.o libdom.a $(LIBS) + +testsvg.exe: libdom.a work/testsvg.o + $(CXX) -o $@ work/testsvg.o libdom.a $(LIBS) + +testuri.exe: libdom.a work/testuri.o + $(CXX) -o $@ work/testuri.o libdom.a $(LIBS) + +testxpath.exe: libdom.a work/testxpath.o + $(CXX) -o $@ work/testxpath.o libdom.a $(LIBS) + +testzip.exe: libdom.a work/testzip.o + $(CXX) -o $@ work/testzip.o libdom.a $(LIBS) + + +libdom.a: $(OBJ) + ar crv $@ $(OBJ) + + +.cpp.o: + $(CXX) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + $(foreach a, $(OBJ), $(shell $(RM) $(subst /,$(S), $(a)))) + $(foreach a, $(TESTOBJ), $(shell $(RM) $(subst /,$(S), $(a)))) + -$(RM) *.a + -$(RM) *.gch + -$(RM) testdom + -$(RM) testdom.exe + -$(RM) testodf + -$(RM) testodf.exe + -$(RM) testsvg + -$(RM) testsvg.exe + -$(RM) testuri + -$(RM) testuri.exe + -$(RM) testxpath + -$(RM) testxpath.exe + -$(RM) testzip + -$(RM) testzip.exe + -$(RM) core.* + +########################################################################### +# E N D O F F I L E +########################################################################### + diff --git a/src/dom/Makefile_insert b/src/dom/Makefile_insert new file mode 100644 index 000000000..ae7609d5e --- /dev/null +++ b/src/dom/Makefile_insert @@ -0,0 +1,59 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +dom/all: dom/libdom.a + +dom/clean: + rm -f dom/libdom.a $(dom_libdom_a_OBJECTS) + +dom_libdom_a_SOURCES = \ + dom/charclass.cpp \ + dom/charclass.h \ + dom/css.h \ + dom/cssparser.cpp \ + dom/cssparser.h \ + dom/dom.h \ + dom/domimpl.cpp \ + dom/domimpl.h \ + dom/domstream.cpp \ + dom/domstream.h \ + dom/domstring.cpp \ + dom/domstring.h \ + dom/domstringimpl.h \ + dom/events.h \ + dom/ls.h \ + dom/lsimpl.cpp \ + dom/lsimpl.h \ + dom/phoebedom.h \ + dom/prop-css2.cpp \ + dom/prop-css.cpp \ + dom/prop-svg.cpp \ + dom/smil.h \ + dom/smilimpl.cpp \ + dom/smilimpl.h \ + dom/stringstream.cpp \ + dom/stringstream.h \ + dom/stylesheets.h \ + dom/svg.h \ + dom/svgimpl.cpp \ + dom/svgimpl.h \ + dom/svglsimpl.cpp \ + dom/svglsimpl.h \ + dom/svgparser.cpp \ + dom/svgparser.h \ + dom/svgtypes.h \ + dom/traversal.h \ + dom/uri.cpp \ + dom/uri.h \ + dom/uristream.cpp \ + dom/uristream.h \ + dom/views.h \ + dom/xmlreader.cpp \ + dom/xmlreader.h \ + dom/xpath.h \ + dom/xpathimpl.cpp \ + dom/xpathimpl.h \ + dom/xpathparser.cpp \ + dom/xpathparser.h \ + dom/ziptool.h \ + dom/ziptool.cpp + diff --git a/src/dom/charclass.cpp b/src/dom/charclass.cpp new file mode 100644 index 000000000..132402f34 --- /dev/null +++ b/src/dom/charclass.cpp @@ -0,0 +1,545 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "charclass.h" + + +/** + * (impl) LetterOrDigit ::= + * Letter | Digit + */ +bool isLetterOrDigit(int ch) +{ + if (isLetter(ch)) + return true; + if (isDigit(ch)) + return true; + return false; +} + +/** + * (84) Letter ::= + * BaseChar | Ideographic + */ +bool isLetter(int ch) +{ + if (isBaseChar(ch)) + return true; + if (isIdeographic(ch)) + return true; + return false; +} + + +/** + * (85) BaseChar ::= + */ +bool isBaseChar(int ch) +{ + + if ( (0x0041 <= ch && ch <= 0x005A) | + (0x0061 <= ch && ch <= 0x007A) | + (0x00C0 <= ch && ch <= 0x00D6) | + (0x00D8 <= ch && ch <= 0x00F6) | + (0x00F8 <= ch && ch <= 0x00FF) | + (0x0100 <= ch && ch <= 0x0131) | + (0x0134 <= ch && ch <= 0x013E) | + (0x0141 <= ch && ch <= 0x0148) | + (0x014A <= ch && ch <= 0x017E) | + (0x0180 <= ch && ch <= 0x01C3) | + (0x01CD <= ch && ch <= 0x01F0) | + (0x01F4 <= ch && ch <= 0x01F5) | + (0x01FA <= ch && ch <= 0x0217) | + (0x0250 <= ch && ch <= 0x02A8) | + (0x02BB <= ch && ch <= 0x02C1) | + ch == 0x0386 | + (0x0388 <= ch && ch <= 0x038A) | + ch == 0x038C | + (0x038E <= ch && ch <= 0x03A1) | + (0x03A3 <= ch && ch <= 0x03CE) | + (0x03D0 <= ch && ch <= 0x03D6) | + ch == 0x03DA | + ch == 0x03DC | + ch == 0x03DE | + ch == 0x03E0 | + (0x03E2 <= ch && ch <= 0x03F3) | + (0x0401 <= ch && ch <= 0x040C) | + (0x040E <= ch && ch <= 0x044F) | + (0x0451 <= ch && ch <= 0x045C) | + (0x045E <= ch && ch <= 0x0481) | + (0x0490 <= ch && ch <= 0x04C4) | + (0x04C7 <= ch && ch <= 0x04C8) | + (0x04CB <= ch && ch <= 0x04CC) | + (0x04D0 <= ch && ch <= 0x04EB) | + (0x04EE <= ch && ch <= 0x04F5) | + (0x04F8 <= ch && ch <= 0x04F9) | + (0x0531 <= ch && ch <= 0x0556) | + ch == 0x0559 | + (0x0561 <= ch && ch <= 0x0586) | + (0x05D0 <= ch && ch <= 0x05EA) | + (0x05F0 <= ch && ch <= 0x05F2) | + (0x0621 <= ch && ch <= 0x063A) | + (0x0641 <= ch && ch <= 0x064A) | + (0x0671 <= ch && ch <= 0x06B7) | + (0x06BA <= ch && ch <= 0x06BE) | + (0x06C0 <= ch && ch <= 0x06CE) | + (0x06D0 <= ch && ch <= 0x06D3) | + ch == 0x06D5 | + (0x06E5 <= ch && ch <= 0x06E6) | + (0x0905 <= ch && ch <= 0x0939) | + ch == 0x093D | + (0x0958 <= ch && ch <= 0x0961) | + (0x0985 <= ch && ch <= 0x098C) | + (0x098F <= ch && ch <= 0x0990) | + (0x0993 <= ch && ch <= 0x09A8) | + (0x09AA <= ch && ch <= 0x09B0) | + ch == 0x09B2 | + (0x09B6 <= ch && ch <= 0x09B9) | + (0x09DC <= ch && ch <= 0x09DD) | + (0x09DF <= ch && ch <= 0x09E1) | + (0x09F0 <= ch && ch <= 0x09F1) | + (0x0A05 <= ch && ch <= 0x0A0A) | + (0x0A0F <= ch && ch <= 0x0A10) | + (0x0A13 <= ch && ch <= 0x0A28) | + (0x0A2A <= ch && ch <= 0x0A30) | + (0x0A32 <= ch && ch <= 0x0A33) | + (0x0A35 <= ch && ch <= 0x0A36) | + (0x0A38 <= ch && ch <= 0x0A39) | + (0x0A59 <= ch && ch <= 0x0A5C) | + ch == 0x0A5E | + (0x0A72 <= ch && ch <= 0x0A74) | + (0x0A85 <= ch && ch <= 0x0A8B) | + ch == 0x0A8D | + (0x0A8F <= ch && ch <= 0x0A91) | + (0x0A93 <= ch && ch <= 0x0AA8) | + (0x0AAA <= ch && ch <= 0x0AB0) | + (0x0AB2 <= ch && ch <= 0x0AB3) | + (0x0AB5 <= ch && ch <= 0x0AB9) | + ch == 0x0ABD | + ch == 0x0AE0 | + (0x0B05 <= ch && ch <= 0x0B0C) | + (0x0B0F <= ch && ch <= 0x0B10) | + (0x0B13 <= ch && ch <= 0x0B28) | + (0x0B2A <= ch && ch <= 0x0B30) | + (0x0B32 <= ch && ch <= 0x0B33) | + (0x0B36 <= ch && ch <= 0x0B39) | + ch == 0x0B3D | + (0x0B5C <= ch && ch <= 0x0B5D) | + (0x0B5F <= ch && ch <= 0x0B61) | + (0x0B85 <= ch && ch <= 0x0B8A) | + (0x0B8E <= ch && ch <= 0x0B90) | + (0x0B92 <= ch && ch <= 0x0B95) | + (0x0B99 <= ch && ch <= 0x0B9A) | + ch == 0x0B9C | + (0x0B9E <= ch && ch <= 0x0B9F) | + (0x0BA3 <= ch && ch <= 0x0BA4) | + (0x0BA8 <= ch && ch <= 0x0BAA) | + (0x0BAE <= ch && ch <= 0x0BB5) | + (0x0BB7 <= ch && ch <= 0x0BB9) | + (0x0C05 <= ch && ch <= 0x0C0C) | + (0x0C0E <= ch && ch <= 0x0C10) | + (0x0C12 <= ch && ch <= 0x0C28) | + (0x0C2A <= ch && ch <= 0x0C33) | + (0x0C35 <= ch && ch <= 0x0C39) | + (0x0C60 <= ch && ch <= 0x0C61) | + (0x0C85 <= ch && ch <= 0x0C8C) | + (0x0C8E <= ch && ch <= 0x0C90) | + (0x0C92 <= ch && ch <= 0x0CA8) | + (0x0CAA <= ch && ch <= 0x0CB3) | + (0x0CB5 <= ch && ch <= 0x0CB9) | + ch == 0x0CDE | + (0x0CE0 <= ch && ch <= 0x0CE1) | + (0x0D05 <= ch && ch <= 0x0D0C) | + (0x0D0E <= ch && ch <= 0x0D10) | + (0x0D12 <= ch && ch <= 0x0D28) | + (0x0D2A <= ch && ch <= 0x0D39) | + (0x0D60 <= ch && ch <= 0x0D61) | + (0x0E01 <= ch && ch <= 0x0E2E) | + ch == 0x0E30 | + (0x0E32 <= ch && ch <= 0x0E33) | + (0x0E40 <= ch && ch <= 0x0E45) | + (0x0E81 <= ch && ch <= 0x0E82) | + ch == 0x0E84 | + (0x0E87 <= ch && ch <= 0x0E88) | + ch == 0x0E8A | + ch == 0x0E8D | + (0x0E94 <= ch && ch <= 0x0E97) | + (0x0E99 <= ch && ch <= 0x0E9F) | + (0x0EA1 <= ch && ch <= 0x0EA3) | + ch == 0x0EA5 | + ch == 0x0EA7 | + (0x0EAA <= ch && ch <= 0x0EAB) | + (0x0EAD <= ch && ch <= 0x0EAE) | + ch == 0x0EB0 | + (0x0EB2 <= ch && ch <= 0x0EB3) | + ch == 0x0EBD | + (0x0EC0 <= ch && ch <= 0x0EC4) | + (0x0F40 <= ch && ch <= 0x0F47) | + (0x0F49 <= ch && ch <= 0x0F69) | + (0x10A0 <= ch && ch <= 0x10C5) | + (0x10D0 <= ch && ch <= 0x10F6) | + ch == 0x1100 | + (0x1102 <= ch && ch <= 0x1103) | + (0x1105 <= ch && ch <= 0x1107) | + ch == 0x1109 | + (0x110B <= ch && ch <= 0x110C) | + (0x110E <= ch && ch <= 0x1112) | + ch == 0x113C | + ch == 0x113E | + ch == 0x1140 | + ch == 0x114C | + ch == 0x114E | + ch == 0x1150 | + (0x1154 <= ch && ch <= 0x1155) | + ch == 0x1159 | + (0x115F <= ch && ch <= 0x1161) | + ch == 0x1163 | + ch == 0x1165 | + ch == 0x1167 | + ch == 0x1169 | + (0x116D <= ch && ch <= 0x116E) | + (0x1172 <= ch && ch <= 0x1173) | + ch == 0x1175 | + ch == 0x119E | + ch == 0x11A8 | + ch == 0x11AB | + (0x11AE <= ch && ch <= 0x11AF) | + (0x11B7 <= ch && ch <= 0x11B8) | + ch == 0x11BA | + (0x11BC <= ch && ch <= 0x11C2) | + ch == 0x11EB | + ch == 0x11F0 | + ch == 0x11F9 | + (0x1E00 <= ch && ch <= 0x1E9B) | + (0x1EA0 <= ch && ch <= 0x1EF9) | + (0x1F00 <= ch && ch <= 0x1F15) | + (0x1F18 <= ch && ch <= 0x1F1D) | + (0x1F20 <= ch && ch <= 0x1F45) | + (0x1F48 <= ch && ch <= 0x1F4D) | + (0x1F50 <= ch && ch <= 0x1F57) | + ch == 0x1F59 | + ch == 0x1F5B | + ch == 0x1F5D | + (0x1F5F <= ch && ch <= 0x1F7D) | + (0x1F80 <= ch && ch <= 0x1FB4) | + (0x1FB6 <= ch && ch <= 0x1FBC) | + ch == 0x1FBE | + (0x1FC2 <= ch && ch <= 0x1FC4) | + (0x1FC6 <= ch && ch <= 0x1FCC) | + (0x1FD0 <= ch && ch <= 0x1FD3) | + (0x1FD6 <= ch && ch <= 0x1FDB) | + (0x1FE0 <= ch && ch <= 0x1FEC) | + (0x1FF2 <= ch && ch <= 0x1FF4) | + (0x1FF6 <= ch && ch <= 0x1FFC) | + ch == 0x2126 | + (0x212A <= ch && ch <= 0x212B) | + ch == 0x212E | + (0x2180 <= ch && ch <= 0x2182) | + (0x3041 <= ch && ch <= 0x3094) | + (0x30A1 <= ch && ch <= 0x30FA) | + (0x3105 <= ch && ch <= 0x312C) | + (0xAC00 <= ch && ch <= 0xD7A3) ) + return true; + return false; +} + + + +/** + * (86) Ideographic ::= + */ +bool isIdeographic(int ch) +{ + if ( (0x4E00 <= ch && ch <=0x9FA5) | + ch == 0x3007 | + (0x3021 <= ch && ch <=0x3029) ) + return true; + return false; +} + +/** + * (87) CombiningChar ::= + */ +bool isCombiningChar(int ch) +{ + if ( (0x0300 <= ch && ch <= 0x0345) | + (0x0360 <= ch && ch <= 0x0361) | + (0x0483 <= ch && ch <= 0x0486) | + (0x0591 <= ch && ch <= 0x05A1) | + (0x05A3 <= ch && ch <= 0x05B9) | + (0x05BB <= ch && ch <= 0x05BD) | + ch == 0x05BF | + (0x05C1 <= ch && ch <= 0x05C2) | + ch == 0x05C4 | + (0x064B <= ch && ch <= 0x0652) | + ch == 0x0670 | + (0x06D6 <= ch && ch <= 0x06DC) | + (0x06DD <= ch && ch <= 0x06DF) | + (0x06E0 <= ch && ch <= 0x06E4) | + (0x06E7 <= ch && ch <= 0x06E8) | + (0x06EA <= ch && ch <= 0x06ED) | + (0x0901 <= ch && ch <= 0x0903) | + ch == 0x093C | + (0x093E <= ch && ch <= 0x094C) | + ch == 0x094D | + (0x0951 <= ch && ch <= 0x0954) | + (0x0962 <= ch && ch <= 0x0963) | + (0x0981 <= ch && ch <= 0x0983) | + ch == 0x09BC | + ch == 0x09BE | + ch == 0x09BF | + (0x09C0 <= ch && ch <= 0x09C4) | + (0x09C7 <= ch && ch <= 0x09C8) | + (0x09CB <= ch && ch <= 0x09CD) | + ch == 0x09D7 | + (0x09E2 <= ch && ch <= 0x09E3) | + ch == 0x0A02 | + ch == 0x0A3C | + ch == 0x0A3E | + ch == 0x0A3F | + (0x0A40 <= ch && ch <= 0x0A42) | + (0x0A47 <= ch && ch <= 0x0A48) | + (0x0A4B <= ch && ch <= 0x0A4D) | + (0x0A70 <= ch && ch <= 0x0A71) | + (0x0A81 <= ch && ch <= 0x0A83) | + ch == 0x0ABC | + (0x0ABE <= ch && ch <= 0x0AC5) | + (0x0AC7 <= ch && ch <= 0x0AC9) | + (0x0ACB <= ch && ch <= 0x0ACD) | + (0x0B01 <= ch && ch <= 0x0B03) | + ch == 0x0B3C | + (0x0B3E <= ch && ch <= 0x0B43) | + (0x0B47 <= ch && ch <= 0x0B48) | + (0x0B4B <= ch && ch <= 0x0B4D) | + (0x0B56 <= ch && ch <= 0x0B57) | + (0x0B82 <= ch && ch <= 0x0B83) | + (0x0BBE <= ch && ch <= 0x0BC2) | + (0x0BC6 <= ch && ch <= 0x0BC8) | + (0x0BCA <= ch && ch <= 0x0BCD) | + ch == 0x0BD7 | + (0x0C01 <= ch && ch <= 0x0C03) | + (0x0C3E <= ch && ch <= 0x0C44) | + (0x0C46 <= ch && ch <= 0x0C48) | + (0x0C4A <= ch && ch <= 0x0C4D) | + (0x0C55 <= ch && ch <= 0x0C56) | + (0x0C82 <= ch && ch <= 0x0C83) | + (0x0CBE <= ch && ch <= 0x0CC4) | + (0x0CC6 <= ch && ch <= 0x0CC8) | + (0x0CCA <= ch && ch <= 0x0CCD) | + (0x0CD5 <= ch && ch <= 0x0CD6) | + (0x0D02 <= ch && ch <= 0x0D03) | + (0x0D3E <= ch && ch <= 0x0D43) | + (0x0D46 <= ch && ch <= 0x0D48) | + (0x0D4A <= ch && ch <= 0x0D4D) | + ch == 0x0D57 | + ch == 0x0E31 | + (0x0E34 <= ch && ch <= 0x0E3A) | + (0x0E47 <= ch && ch <= 0x0E4E) | + ch == 0x0EB1 | + (0x0EB4 <= ch && ch <= 0x0EB9) | + (0x0EBB <= ch && ch <= 0x0EBC) | + (0x0EC8 <= ch && ch <= 0x0ECD) | + (0x0F18 <= ch && ch <= 0x0F19) | + ch == 0x0F35 | + ch == 0x0F37 | + ch == 0x0F39 | + ch == 0x0F3E | + ch == 0x0F3F | + (0x0F71 <= ch && ch <= 0x0F84) | + (0x0F86 <= ch && ch <= 0x0F8B) | + (0x0F90 <= ch && ch <= 0x0F95) | + ch == 0x0F97 | + (0x0F99 <= ch && ch <= 0x0FAD) | + (0x0FB1 <= ch && ch <= 0x0FB7) | + ch == 0x0FB9 | + (0x20D0 <= ch && ch <= 0x20DC) | + ch == 0x20E1 | + (0x302A <= ch && ch <= 0x302F) | + ch == 0x3099 | + ch == 0x309A ) + return true; + return false; +} + + +/** + * (88) Digit ::= + */ +bool isDigit(int ch) +{ + if ( (0x0030 <= ch && ch <= 0x0039) | + (0x0660 <= ch && ch <= 0x0669) | + (0x06F0 <= ch && ch <= 0x06F9) | + (0x0966 <= ch && ch <= 0x096F) | + (0x09E6 <= ch && ch <= 0x09EF) | + (0x0A66 <= ch && ch <= 0x0A6F) | + (0x0AE6 <= ch && ch <= 0x0AEF) | + (0x0B66 <= ch && ch <= 0x0B6F) | + (0x0BE7 <= ch && ch <= 0x0BEF) | + (0x0C66 <= ch && ch <= 0x0C6F) | + (0x0CE6 <= ch && ch <= 0x0CEF) | + (0x0D66 <= ch && ch <= 0x0D6F) | + (0x0E50 <= ch && ch <= 0x0E59) | + (0x0ED0 <= ch && ch <= 0x0ED9) | + (0x0F20 <= ch && ch <= 0x0F29) ) + return true; + return false; +} + + +/** + * (89) Extender ::= + */ +bool isExtender(int ch) +{ + if ( ch == 0x00B7 | + ch == 0x02D0 | + ch == 0x02D1 | + ch == 0x0387 | + ch == 0x0640 | + ch == 0x0E46 | + ch == 0x0EC6 | + ch == 0x3005 | + (0x3031 <= ch && ch <= 0x3035) | + (0x309D <= ch && ch <= 0x309E) | + (0x30FC <= ch && ch <= 0x30FE) ) + return true; + return false; +} + + + + + + +/** + * + * Following are from unicode.org, in the UnicodeData file + * in the Unicode Database + */ + +/** + * UNICODE general class Zs + */ +bool isSpaceSeparator(int ch) +{ + if (ch == 0x0020 || + ch == 0x200A || + ch == 0x2003 || + ch == 0x205F || + ch == 0x2005 || + ch == 0x202F || + ch == 0x2000 || + ch == 0x180E || + ch == 0x2001 || + ch == 0x2004 || + ch == 0x3000 || + ch == 0x2008 || + ch == 0x2006 || + ch == 0x2002 || + ch == 0x2007 || + ch == 0x2009 || + ch == 0x00A0 || + ch == 0x1680) + return true; + return false; +} + +/** + * UNICODE general class Zl + */ +bool isLineSeparator(int ch) +{ + if (ch == 0x2028) + return true; + return false; +} + +/** + * UNICODE general class Zp + */ +bool isParagraphSeparator(int ch) +{ + if (ch == 0x2029) + return true; + return false; +} + +/** + * The union of the 3 space types. + */ +bool isSpaceChar(int ch) +{ + if ( isSpaceSeparator(ch) || + isLineSeparator(ch) || + isParagraphSeparator(ch)) + return true; + return false; +} + +/** + * 3 spaces in isSpaceChar() which don't break + */ +bool isNonBreakingSpace(int ch) +{ + if (ch == 0x00A0 || ch == 0x2007 || ch == 0x202F) + return true; + return false; +} + +/** + * + */ +bool isWhitespace(int ch) +{ + if (isSpaceChar(ch) && !isNonBreakingSpace(ch)) + return true; + if (ch == 0x0009 || // HORIZONTAL TABULATION + ch == 0x000A || // LINE FEED. + ch == 0x000B || // VERTICAL TABULATION. + ch == 0x000C || // FORM FEED. + ch == 0x000D || // CARRIAGE RETURN. + ch == 0x001C || // FILE SEPARATOR. + ch == 0x001D || // GROUP SEPARATOR. + ch == 0x001E || // RECORD SEPARATOR. + ch == 0x001F) // UNIT SEPARATOR. + return true; + return false; +} + + + + + + + + + + + diff --git a/src/dom/charclass.h b/src/dom/charclass.h new file mode 100644 index 000000000..8b3546ea7 --- /dev/null +++ b/src/dom/charclass.h @@ -0,0 +1,220 @@ +#ifndef __CHARCLASS_H__ +#define __CHARCLASS_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + + +/** + * Utility classes to test characters for their class, as specified in + * http://www.w3.org/TR/REC-xml/#CharClasses + */ + +/** + * Convenience method. Not in spec + * [impl] LetterOrDigit ::= + * Letter | Digit + */ +bool isLetterOrDigit(int ch); + + +/** + * [84] Letter ::= + * BaseChar | Ideographic + */ +bool isLetter(int ch); + + +/** + * [85] BaseChar ::= + * [#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | + * [#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] | [#x0141-#x0148] | + * [#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] | + * [#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 | + * [#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | + * [#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | + * [#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | + * [#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | + * [#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | + * [#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] | [#x0621-#x063A] | + * [#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] | [#x06C0-#x06CE] | + * [#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | + * [#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] | + * [#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] | + * [#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | + * [#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] | + * [#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | + * [#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | + * [#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | + * [#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] | [#x0B2A-#x0B30] | + * [#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D | [#x0B5C-#x0B5D] | + * [#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | + * [#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | + * [#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] | + * [#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | + * [#x0C35-#x0C39] | [#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | + * [#x0C92-#x0CA8] | [#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | + * [#x0CE0-#x0CE1] | [#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | + * [#x0D2A-#x0D39] | [#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | + * [#x0E32-#x0E33] | [#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | + * [#x0E87-#x0E88] | #x0E8A | #x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] | + * [#x0EA1-#x0EA3] | #x0EA5 | #x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | + * #x0EB0 | [#x0EB2-#x0EB3] | #x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | + * [#x0F49-#x0F69] | [#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | + * [#x1102-#x1103] | [#x1105-#x1107] | #x1109 | [#x110B-#x110C] | + * [#x110E-#x1112] | #x113C | #x113E | #x1140 | #x114C | #x114E | + * #x1150 | [#x1154-#x1155] | #x1159 | [#x115F-#x1161] | #x1163 | + * #x1165 | #x1167 | #x1169 | [#x116D-#x116E] | [#x1172-#x1173] | #x1175 | + * #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | [#x11B7-#x11B8] | #x11BA | + * [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 | [#x1E00-#x1E9B] | + * [#x1EA0-#x1EF9] | [#x1F00-#x1F15] | [#x1F18-#x1F1D] | [#x1F20-#x1F45] | + * [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 | #x1F5B | #x1F5D | + * [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | + * [#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | + * [#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | + * [#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] | + * [#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3] + */ +bool isBaseChar(int ch); + + +/** + * [86] Ideographic ::= + * [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029] + */ +bool isIdeographic(int ch); + + + + + +/** + * [87] CombiningChar ::= + * [#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] | [#x0591-#x05A1] | + * [#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 | + * [#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] | + * [#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | + * #x093C | [#x093E-#x094C] | #x094D | [#x0951-#x0954] |[#x0962-#x0963] | + * [#x0981-#x0983] | #x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | + * [#x09C7-#x09C8] | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | + * #x0A02 | #x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] | + * [#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC | + * [#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | + * #x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | + * [#x0B56-#x0B57] | [#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | + * [#x0BCA-#x0BCD] | #x0BD7 | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | + * [#x0C46-#x0C48] | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] | + * [#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | + * [#x0D02-#x0D03] | [#x0D3E-#x0D43] | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | + * #x0D57 | #x0E31 | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | + * [#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | + * #x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] | + * [#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | + * [#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | + * #x3099 | #x309A + */ +bool isCombiningChar(int ch); + + + +/** + * [88] Digit ::= + * [#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | + * [#x0966-#x096F] | [#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | + * [#x0B66-#x0B6F] | [#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | + * [#x0D66-#x0D6F] | [#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29] + */ +bool isDigit(int ch); + + + + +/** + * [89] Extender ::= + * #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | + * #x3005 | [#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE] + */ +bool isExtender(int ch); + + + +/** + * + * Following are from unicode.org, in the UnicodeData file + * in the Unicode Database + */ + +/** + * + */ +bool isSpaceSeparator(int ch); + +/** + * + */ +bool isLineSeparator(int ch); + +/** + * + */ +bool isParagraphSeparator(int ch); + +/** + * + */ +bool isSpaceChar(int ch); + +/** + * + */ +bool isNonBreakingSpace(int ch); + +/** + * + */ +bool isWhitespace(int ch); + +#endif /* __CHARCLASS_H__ */ + + + + + + + + + + + + + + + + + diff --git a/src/dom/css.h b/src/dom/css.h new file mode 100644 index 000000000..6ab92389e --- /dev/null +++ b/src/dom/css.h @@ -0,0 +1,4121 @@ +/* + * Copyright (c) 2000 World Wide Web Consortium, + * (Massachusetts Institute of Technology, Institut National de + * Recherche en Informatique et en Automatique, Keio University). All + * Rights Reserved. This program is distributed under the W3C's Software + * Intellectual Property License. This program is distributed 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 W3C License http://www.w3.org/Consortium/Legal/ for more details. + */ + +// File: http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.idl + +#ifndef __CSS_H__ +#define __CSS_H__ + +#include "dom.h" +#include "stylesheets.h" +#include "views.h" + +#include +#include + + +namespace org { +namespace w3c { +namespace dom { +namespace css { + + + + +//Make local definitions +typedef dom::DOMString DOMString; +typedef dom::Element Element; +typedef dom::DOMImplementation DOMImplementation; + +//forward declarations +class CSSRule; +class CSSStyleSheet; +class CSSStyleDeclaration; +class CSSValue; +class Counter; +class Rect; +class RGBColor; + + + + + +/*######################################################################### +## CSSRule +#########################################################################*/ + +/** + * + */ +class CSSRule +{ +public: + + typedef enum + { + UNKNOWN_RULE = 0, + STYLE_RULE = 1, + CHARSET_RULE = 2, + IMPORT_RULE = 3, + MEDIA_RULE = 4, + FONT_FACE_RULE = 5, + PAGE_RULE = 6 + } RuleType; + + + /** + * + */ + virtual unsigned short getType() + { + return type; + } + + /** + * + */ + virtual DOMString getCssText() + { + return cssText; + } + + /** + * + */ + virtual void setCssText(const DOMString &val) throw (dom::DOMException) + { + cssText = val; + } + + /** + * + */ + virtual CSSStyleSheet *getParentStyleSheet() + { + return parentStyleSheet; + } + + /** + * + */ + virtual CSSRule *getParentRule() + { + return parentRule; + } + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSRule() + { + type = UNKNOWN_RULE; + cssText = ""; + parentStyleSheet = NULL; + parentRule = NULL; + } + + /** + * + */ + CSSRule(const CSSRule &other) + { + type = other.type; + cssText = other.cssText; + parentStyleSheet = other.parentStyleSheet; + parentRule = other.parentRule; + } + + /** + * + */ + virtual ~CSSRule() {} + +protected: + + int type; + + DOMString cssText; + + CSSStyleSheet *parentStyleSheet; + + CSSRule *parentRule; +}; + + + +/*######################################################################### +## CSSRuleList +#########################################################################*/ + +/** + * + */ +class CSSRuleList +{ +public: + + /** + * + */ + virtual unsigned long getLength() + { + return rules.size(); + } + + /** + * + */ + virtual CSSRule item(unsigned long index) + { + if (index>=rules.size()) + { + CSSRule rule; + return rule; + } + return rules[index]; + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSRuleList() {} + + + /** + * + */ + CSSRuleList(const CSSRuleList &other) + { + rules = other.rules; + } + + /** + * + */ + virtual ~CSSRuleList() {} + +protected: + +friend class CSSMediaRule; +friend class CSSStyleSheet; + + /** + * + */ + virtual void addRule(const CSSRule &rule) + { + rules.push_back(rule); + } + + + /** + * + */ + virtual void deleteRule(unsigned long index) + { + if (index>=rules.size()) + return; + std::vector::iterator iter = rules.begin() + index; + rules.erase(iter); + } + + + /** + * + */ + virtual long insertRule(const CSSRule &rule, unsigned long index) + { + if (index>=rules.size()) + return -1; + std::vector::iterator iter = rules.begin() + index; + rules.insert(iter, rule); + return index; + } + + std::vectorrules; +}; + + +/*######################################################################### +## CSSStyleSheet +#########################################################################*/ + +/** + * + */ +class CSSStyleSheet : virtual public stylesheets::StyleSheet +{ +public: + + /** + * + */ + virtual CSSRule *getOwnerRule() + { + return ownerRule; + } + + /** + * + */ + virtual CSSRuleList getCssRules() + { + return rules; + } + + /** + * + */ + virtual unsigned long insertRule(const DOMString &ruleStr, + unsigned long index) + throw (dom::DOMException) + { + CSSRule rule; + return rules.insertRule(rule, index); + } + + /** + * + */ + virtual void deleteRule(unsigned long index) + throw (dom::DOMException) + { + rules.deleteRule(index); + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSStyleSheet() : stylesheets::StyleSheet() + { + } + + /** + * + */ + CSSStyleSheet(const CSSStyleSheet &other) : + stylesheets::StyleSheet(other) + { + ownerRule = other.ownerRule; + rules = other.rules; + } + + /** + * + */ + virtual ~CSSStyleSheet() {} + +protected: + + CSSRule *ownerRule; + + CSSRuleList rules; +}; + + +/*######################################################################### +## CSSValue +#########################################################################*/ + +/** + * + */ +class CSSValue +{ +public: + + /** + * UnitTypes + */ + enum + { + CSS_INHERIT = 0, + CSS_PRIMITIVE_VALUE = 1, + CSS_VALUE_LIST = 2, + CSS_CUSTOM = 3 + }; + + /** + * + */ + virtual DOMString getCssText() + { + return cssText; + } + + /** + * + */ + virtual void setCssText(const DOMString &val) + throw (dom::DOMException) + { + cssText = val; + } + + /** + * + */ + virtual unsigned short getCssValueType() + { + return valueType; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSValue() + { + valueType = CSS_INHERIT; + } + + /** + * + */ + CSSValue(const CSSValue &other) + { + cssText = other.cssText; + valueType = other.valueType; + } + + /** + * + */ + virtual ~CSSValue() {} + +protected: + + DOMString cssText; + int valueType; +}; + + + + + +/*######################################################################### +## CSSStyleDeclaration +#########################################################################*/ + +class CSSStyleDeclarationEntry +{ +public: + CSSStyleDeclarationEntry(const DOMString &nameArg, + const DOMString &valueArg, + const DOMString &prioArg) + { + name = nameArg; + value = valueArg; + prio = prioArg; + } + ~CSSStyleDeclarationEntry(){} + DOMString name; + DOMString value; + DOMString prio; +}; + + + +/** + * + */ +class CSSStyleDeclaration +{ +public: + + /** + * + */ + virtual DOMString getCssText() + { + return cssText; + } + + /** + * + */ + virtual void setCssText(const DOMString &val) + throw (dom::DOMException) + { + cssText = val; + } + + /** + * + */ + virtual DOMString getPropertyValue(const DOMString &propertyName) + { + std::vector::iterator iter; + for (iter=items.begin() ; iter!=items.end() ; iter++) + { + if (iter->name == propertyName) + return iter->value; + } + return ""; + } + + /** + * + */ + virtual CSSValue getPropertyCSSValue(const DOMString &propertyName) + { + CSSValue value; + return value; + } + + /** + * + */ + virtual DOMString removeProperty(const DOMString &propertyName) + throw (dom::DOMException) + { + std::vector::iterator iter; + for (iter=items.begin() ; iter!=items.end() ; iter++) + { + if (iter->name == propertyName) + items.erase(iter); + } + return propertyName; + } + + /** + * + */ + virtual DOMString getPropertyPriority(const DOMString &propertyName) + { + std::vector::iterator iter; + for (iter=items.begin() ; iter!=items.end() ; iter++) + { + if (iter->name == propertyName) + return iter->prio; + } + return ""; + } + + /** + * + */ + virtual void setProperty(const DOMString &propertyName, + const DOMString &value, + const DOMString &priority) + throw (dom::DOMException) + { + std::vector::iterator iter; + for (iter=items.begin() ; iter!=items.end() ; iter++) + { + if (iter->name == propertyName) + { + iter->name = propertyName; + iter->value = value; + iter->prio = priority; + return; + } + } + CSSStyleDeclarationEntry entry(propertyName, value, priority); + items.push_back(entry); + } + + /** + * + */ + virtual unsigned long getLength() + { + return items.size(); + } + + /** + * + */ + virtual DOMString item(unsigned long index) + { + if (index>=items.size()) + return ""; + DOMString ret = items[index].name; + ret.append(":"); + ret.append(items[index].value); + return ret; + } + + /** + * + */ + virtual CSSRule *getParentRule() + { + return parentRule; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSStyleDeclaration() + { + parentRule = NULL; + } + + /** + * + */ + CSSStyleDeclaration(const CSSStyleDeclaration &other) + { + } + + /** + * + */ + virtual ~CSSStyleDeclaration() {} + +protected: + + DOMString cssText; + + CSSRule *parentRule; + + std::vector items; +}; + + + + +/*######################################################################### +## CSSStyleRule +#########################################################################*/ + +/** + * + */ +class CSSStyleRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual DOMString getSelectorText() + { + return selectorText; + } + + /** + * + */ + virtual void setSelectorText(const DOMString &val) + throw (dom::DOMException) + { + selectorText = val; + } + + + /** + * + */ + virtual CSSStyleDeclaration &getStyle() + { + return style; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSStyleRule() : CSSRule() + { + type = STYLE_RULE; + selectorText = ""; + } + + + /** + * + */ + CSSStyleRule(const CSSStyleRule &other) : CSSRule(other) + { + selectorText = other.selectorText; + style = other.style; + } + + /** + * + */ + virtual ~CSSStyleRule() {} + +protected: + + DOMString selectorText; + + CSSStyleDeclaration style; + +}; + +/*######################################################################### +## CSSMediaRule +#########################################################################*/ + +/** + * + */ +class CSSMediaRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual stylesheets::MediaList getMedia() + { + return mediaList; + } + + /** + * + */ + virtual CSSRuleList getCssRules() + { + return cssRules; + } + + /** + * + */ + virtual unsigned long insertRule(const DOMString &ruleStr, + unsigned long index) + throw (dom::DOMException) + { + if (index>cssRules.getLength()) + return 0; + CSSRule rule; + cssRules.insertRule(rule, index); + return index; + } + + /** + * + */ + virtual void deleteRule(unsigned long index) + throw(dom::DOMException) + { + cssRules.deleteRule(index); + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSMediaRule() : CSSRule() + { + type = MEDIA_RULE; + } + + /** + * + */ + CSSMediaRule(const CSSMediaRule &other) : CSSRule(other) + { + mediaList = other.mediaList; + cssRules = other.cssRules; + } + + /** + * + */ + virtual ~CSSMediaRule() {} + +protected: + + stylesheets::MediaList mediaList; + + CSSRuleList cssRules; +}; + + + + +/*######################################################################### +## CSSFontFaceRule +#########################################################################*/ + +/** + * + */ +class CSSFontFaceRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual CSSStyleDeclaration getStyle() + { + return style; + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSFontFaceRule() : CSSRule() + { + type = FONT_FACE_RULE; + } + + /** + * + */ + CSSFontFaceRule(const CSSFontFaceRule &other) : CSSRule(other) + { + style = other.style; + } + + /** + * + */ + virtual ~CSSFontFaceRule() {} + +protected: + + CSSStyleDeclaration style; +}; + + + + +/*######################################################################### +## CSSPageRule +#########################################################################*/ + +/** + * + */ +class CSSPageRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual DOMString getSelectorText() + { + return selectorText; + } + + /** + * + */ + virtual void setSelectorText(const DOMString &val) + throw(dom::DOMException) + { + selectorText = val; + } + + + /** + * + */ + virtual CSSStyleDeclaration getStyle() + { + return style; + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSPageRule() : CSSRule() + { + type = PAGE_RULE; + } + + /** + * + */ + CSSPageRule(const CSSPageRule &other) : CSSRule(other) + { + selectorText = other.selectorText; + style = other.style; + } + + /** + * + */ + virtual ~CSSPageRule() {} + +protected: + + DOMString selectorText; + + CSSStyleDeclaration style; +}; + + + + + +/*######################################################################### +## CSSImportRule +#########################################################################*/ + +/** + * + */ +class CSSImportRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual DOMString getHref() + { + return href; + } + + /** + * + */ + virtual stylesheets::MediaList getMedia() + { + return mediaList; + } + + /** + * + */ + virtual CSSStyleSheet getStyleSheet() + { + return styleSheet; + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSImportRule() : CSSRule() + { + type = IMPORT_RULE; + } + + /** + * + */ + CSSImportRule(const CSSImportRule &other) : CSSRule(other) + { + mediaList = other.mediaList; + styleSheet = other.styleSheet; + } + + /** + * + */ + virtual ~CSSImportRule() {} + +protected: + + DOMString href; + + stylesheets::MediaList mediaList; + + CSSStyleSheet styleSheet; +}; + + + + + + +/*######################################################################### +## CSSCharsetRule +#########################################################################*/ + +/** + * + */ +class CSSCharsetRule : virtual public CSSRule +{ +public: + + /** + * + */ + virtual DOMString getEncoding() + { + return encoding; + } + + /** + * + */ + virtual void setEncoding(const DOMString &val) throw (dom::DOMException) + { + encoding = val; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSCharsetRule() : CSSRule() + { + type = CHARSET_RULE; + } + + /** + * + */ + CSSCharsetRule(const CSSCharsetRule &other) : CSSRule(other) + { + encoding = other.encoding; + } + + /** + * + */ + virtual ~CSSCharsetRule() {} + +protected: + + DOMString encoding; + +}; + + + + + +/*######################################################################### +## CSSUnknownRule +#########################################################################*/ + +/** + * + */ +class CSSUnknownRule : virtual public CSSRule +{ +public: + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSUnknownRule() : CSSRule() + { + type = UNKNOWN_RULE; + } + + /** + * + */ + CSSUnknownRule(const CSSUnknownRule &other) : CSSRule(other) + { + } + + /** + * + */ + virtual ~CSSUnknownRule() {} +}; + + + + + + + +/*######################################################################### +## CSSValueList +#########################################################################*/ + +/** + * + */ +class CSSValueList : virtual public CSSValue +{ +public: + + /** + * + */ + virtual unsigned long getLength() + { + return items.size(); + } + + /** + * + */ + virtual CSSValue item(unsigned long index) + { + if (index>=items.size()) + { + CSSValue dummy; + return dummy; + } + return items[index]; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSValueList() + { + } + + /** + * + */ + CSSValueList(const CSSValueList &other) : CSSValue(other) + { + items = other.items; + } + + /** + * + */ + virtual ~CSSValueList() {} + +protected: + + std::vector items; +}; + + + + +/*######################################################################### +## CSSPrimitiveValue +#########################################################################*/ + +/** + * + */ +class CSSPrimitiveValue : virtual public CSSValue +{ +public: + + /** + * UnitTypes + */ + enum + { + CSS_UNKNOWN = 0, + CSS_NUMBER = 1, + CSS_PERCENTAGE = 2, + CSS_EMS = 3, + CSS_EXS = 4, + CSS_PX = 5, + CSS_CM = 6, + CSS_MM = 7, + CSS_IN = 8, + CSS_PT = 9, + CSS_PC = 10, + CSS_DEG = 11, + CSS_RAD = 12, + CSS_GRAD = 13, + CSS_MS = 14, + CSS_S = 15, + CSS_HZ = 16, + CSS_KHZ = 17, + CSS_DIMENSION = 18, + CSS_STRING = 19, + CSS_URI = 20, + CSS_IDENT = 21, + CSS_ATTR = 22, + CSS_COUNTER = 23, + CSS_RECT = 24, + CSS_RGBCOLOR = 25 + }; + + + /** + * + */ + virtual unsigned short getPrimitiveType() + { + return primitiveType; + } + + /** + * + */ + virtual void setFloatValue(unsigned short unitType, + double doubleValueArg) + throw (dom::DOMException) + { + primitiveType = unitType; + doubleValue = doubleValueArg; + } + /** + * + */ + virtual double getFloatValue(unsigned short unitType) + throw (dom::DOMException) + { + return doubleValue; + } + + /** + * + */ + virtual void setStringValue(unsigned short stringType, + const DOMString &stringValueArg) + throw (dom::DOMException) + { + stringValue = stringValueArg; + } + + /** + * + */ + virtual DOMString getStringValue() throw (dom::DOMException) + { + return stringValue; + } + + /** + * + */ + virtual Counter *getCounterValue() throw (dom::DOMException) + { + return NULL; + } + + /** + * + */ + virtual Rect *getRectValue() throw (dom::DOMException) + { + return NULL; + } + + /** + * + */ + virtual RGBColor *getRGBColorValue() throw (dom::DOMException) + { + return NULL; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSSPrimitiveValue() : CSSValue() + { + } + + /** + * + */ + CSSPrimitiveValue(const CSSPrimitiveValue &other) : CSSValue(other) + { + } + + /** + * + */ + virtual ~CSSPrimitiveValue() {} + +protected: + + int primitiveType; + + double doubleValue; + + DOMString stringValue; + + +}; + + + +/*######################################################################### +## RGBColor +#########################################################################*/ + +/** + * + */ +class RGBColor +{ +public: + + /** + * + */ + virtual CSSPrimitiveValue getRed() + { + return red; + } + + /** + * + */ + virtual CSSPrimitiveValue getGreen() + { + return green; + } + + /** + * + */ + virtual CSSPrimitiveValue getBlue() + { + return blue; + } + + /** + * REPLACES: RGBColor CSSPrimitiveValue::getRGBColorValue() throw (dom::DOMException) + */ + static RGBColor getRGBColorValue(const CSSPrimitiveValue &val) + { + RGBColor col; + return col; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + RGBColor() {} + + /** + * + */ + RGBColor(const RGBColor &other) + { + red = other.red; + green = other.green; + blue = other.blue; + } + + /** + * + */ + virtual ~RGBColor() {} + +protected: + + CSSPrimitiveValue red; + CSSPrimitiveValue green; + CSSPrimitiveValue blue; +}; + + + + +/*######################################################################### +## Rect +#########################################################################*/ + +/** + * + */ +class Rect +{ +public: + + /** + * + */ + virtual CSSPrimitiveValue getTop() + { + return top; + } + + /** + * + */ + virtual CSSPrimitiveValue getRight() + { + return right; + } + + /** + * + */ + virtual CSSPrimitiveValue getBottom() + { + return bottom; + } + + /** + * + */ + virtual CSSPrimitiveValue getLeft() + { + return left; + } + + /** + * REPLACES: Rect CSSPrimitiveValue::getRectValue() throw (dom::DOMException) + */ + static Rect getRectValue(const CSSPrimitiveValue &val) + { + Rect rect; + return rect; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + Rect() {} + + /** + * + */ + Rect(const Rect &other) + { + top = other.top; + right = other.right; + bottom = other.bottom; + left = other.left; + } + + /** + * + */ + virtual ~Rect() {} + +protected: + + CSSPrimitiveValue top; + CSSPrimitiveValue right; + CSSPrimitiveValue bottom; + CSSPrimitiveValue left; +}; + + + + + + +/*######################################################################### +## Counter +#########################################################################*/ + +/** + * + */ +class Counter +{ +public: + + /** + * + */ + virtual DOMString getIdentifier() + { + return identifier; + } + + /** + * + */ + virtual DOMString getListStyle() + { + return listStyle; + } + + /** + * + */ + virtual DOMString getSeparator() + { + return separator; + } + + /** + * REPLACES: Counter CSSPrimitiveValue::getCounterValue() throw (dom::DOMException) + */ + static Counter getCounterValue(const CSSPrimitiveValue &val) + { + Counter counter; + return counter; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + Counter() {} + + /** + * + */ + Counter(const Counter &other) + { + identifier = other.identifier; + listStyle = other.listStyle; + separator = other.separator; + } + + /** + * + */ + virtual ~Counter() {} + +protected: + + DOMString identifier; + DOMString listStyle; + DOMString separator; + +}; + + + + +/*######################################################################### +## ElementCSSInlineStyle +#########################################################################*/ + +/** + * + */ +class ElementCSSInlineStyle +{ +public: + + /** + * + */ + virtual CSSStyleDeclaration getStyle() + { + return style; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + ElementCSSInlineStyle() {} + + /** + * + */ + ElementCSSInlineStyle(const ElementCSSInlineStyle &other) + { + style = other.style; + } + + /** + * + */ + virtual ~ElementCSSInlineStyle() {} + +protected: + + CSSStyleDeclaration style; +}; + + + + + + +/*######################################################################### +## CSS2Properties +#########################################################################*/ + +/** + * + */ +class CSS2Properties +{ +public: + + + /** + * return the 'azimuth' property + */ + virtual DOMString getAzimuth() + { + return azimuth; + } + + /** + * set the 'azimuth' property + */ + virtual void setAzimuth(const DOMString &val) + throw (dom::DOMException) + { + azimuth = val; + } + + /** + * return the 'background' property + */ + virtual DOMString getBackground() + { + return background; + } + + /** + * set the 'background' property + */ + virtual void setBackground(const DOMString &val) + throw (dom::DOMException) + { + background = val; + } + + /** + * return the 'backgroundAttachment' property + */ + virtual DOMString getBackgroundAttachment() + { + return backgroundAttachment; + } + + /** + * set the 'backgroundAttachment' property + */ + virtual void setBackgroundAttachment(const DOMString &val) + throw (dom::DOMException) + { + backgroundAttachment = val; + } + + /** + * return the 'backgroundColor' property + */ + virtual DOMString getBackgroundColor() + { + return backgroundColor; + } + + /** + * set the 'backgroundColor' property + */ + virtual void setBackgroundColor(const DOMString &val) + throw (dom::DOMException) + { + backgroundColor = val; + } + + /** + * return the 'backgroundImage' property + */ + virtual DOMString getBackgroundImage() + { + return backgroundImage; + } + + /** + * set the 'backgroundImage' property + */ + virtual void setBackgroundImage(const DOMString &val) + throw (dom::DOMException) + { + backgroundImage = val; + } + + /** + * return the 'backgroundPosition' property + */ + virtual DOMString getBackgroundPosition() + { + return backgroundPosition; + } + + /** + * set the 'backgroundPosition' property + */ + virtual void setBackgroundPosition(const DOMString &val) + throw (dom::DOMException) + { + backgroundPosition = val; + } + + /** + * return the 'backgroundRepeat' property + */ + virtual DOMString getBackgroundRepeat() + { + return backgroundRepeat; + } + + /** + * set the 'backgroundRepeat' property + */ + virtual void setBackgroundRepeat(const DOMString &val) + throw (dom::DOMException) + { + backgroundRepeat = val; + } + + /** + * return the 'border' property + */ + virtual DOMString getBorder() + { + return border; + } + + /** + * set the 'border' property + */ + virtual void setBorder(const DOMString &val) + throw (dom::DOMException) + { + border = val; + } + + /** + * return the 'borderCollapse' property + */ + virtual DOMString getBorderCollapse() + { + return borderCollapse; + } + + /** + * set the 'borderCollapse' property + */ + virtual void setBorderCollapse(const DOMString &val) + throw (dom::DOMException) + { + borderCollapse = val; + } + + /** + * return the 'borderColor' property + */ + virtual DOMString getBorderColor() + { + return borderColor; + } + + /** + * set the 'borderColor' property + */ + virtual void setBorderColor(const DOMString &val) + throw (dom::DOMException) + { + borderColor = val; + } + + /** + * return the 'borderSpacing' property + */ + virtual DOMString getBorderSpacing() + { + return borderSpacing; + } + + /** + * set the 'borderSpacing' property + */ + virtual void setBorderSpacing(const DOMString &val) + throw (dom::DOMException) + { + borderSpacing = val; + } + + /** + * return the 'borderStyle' property + */ + virtual DOMString getBorderStyle() + { + return borderStyle; + } + + /** + * set the 'borderStyle' property + */ + virtual void setBorderStyle(const DOMString &val) + throw (dom::DOMException) + { + borderStyle = val; + } + + /** + * return the 'borderTop' property + */ + virtual DOMString getBorderTop() + { + return borderTop; + } + + /** + * set the 'borderTop' property + */ + virtual void setBorderTop(const DOMString &val) + throw (dom::DOMException) + { + borderTop = val; + } + + /** + * return the 'borderRight' property + */ + virtual DOMString getBorderRight() + { + return borderRight; + } + + /** + * set the 'borderRight' property + */ + virtual void setBorderRight(const DOMString &val) + throw (dom::DOMException) + { + borderRight = val; + } + + /** + * return the 'borderBottom' property + */ + virtual DOMString getBorderBottom() + { + return borderBottom; + } + + /** + * set the 'borderBottom' property + */ + virtual void setBorderBottom(const DOMString &val) + throw (dom::DOMException) + { + borderBottom = val; + } + + /** + * return the 'borderLeft' property + */ + virtual DOMString getBorderLeft() + { + return borderLeft; + } + + /** + * set the 'borderLeft' property + */ + virtual void setBorderLeft(const DOMString &val) + throw (dom::DOMException) + { + borderLeft = val; + } + + /** + * return the 'borderTopColor' property + */ + virtual DOMString getBorderTopColor() + { + return borderTopColor; + } + + /** + * set the 'borderTopColor' property + */ + virtual void setBorderTopColor(const DOMString &val) + throw (dom::DOMException) + { + borderTopColor = val; + } + + /** + * return the 'borderRightColor' property + */ + virtual DOMString getBorderRightColor() + { + return borderRightColor; + } + + /** + * set the 'borderRightColor' property + */ + virtual void setBorderRightColor(const DOMString &val) + throw (dom::DOMException) + { + borderRightColor = val; + } + + /** + * return the 'borderBottomColor' property + */ + virtual DOMString getBorderBottomColor() + { + return borderBottomColor; + } + + /** + * set the 'borderBottomColor' property + */ + virtual void setBorderBottomColor(const DOMString &val) + throw (dom::DOMException) + { + borderBottomColor = val; + } + + /** + * return the 'borderLeftColor' property + */ + virtual DOMString getBorderLeftColor() + { + return borderLeftColor; + } + + /** + * set the 'borderLeftColor' property + */ + virtual void setBorderLeftColor(const DOMString &val) + throw (dom::DOMException) + { + borderLeftColor = val; + } + + /** + * return the 'borderTopStyle' property + */ + virtual DOMString getBorderTopStyle() + { + return borderTopStyle; + } + + /** + * set the 'borderTopStyle' property + */ + virtual void setBorderTopStyle(const DOMString &val) + throw (dom::DOMException) + { + borderTopStyle = val; + } + + /** + * return the 'borderRightStyle' property + */ + virtual DOMString getBorderRightStyle() + { + return borderRightStyle; + } + + /** + * set the 'borderRightStyle' property + */ + virtual void setBorderRightStyle(const DOMString &val) + throw (dom::DOMException) + { + borderRightStyle = val; + } + + /** + * return the 'borderBottomStyle' property + */ + virtual DOMString getBorderBottomStyle() + { + return borderBottomStyle; + } + + /** + * set the 'borderBottomStyle' property + */ + virtual void setBorderBottomStyle(const DOMString &val) + throw (dom::DOMException) + { + borderBottomStyle = val; + } + + /** + * return the 'borderLeftStyle' property + */ + virtual DOMString getBorderLeftStyle() + { + return borderLeftStyle; + } + + /** + * set the 'borderLeftStyle' property + */ + virtual void setBorderLeftStyle(const DOMString &val) + throw (dom::DOMException) + { + borderLeftStyle = val; + } + + /** + * return the 'borderTopWidth' property + */ + virtual DOMString getBorderTopWidth() + { + return borderTopWidth; + } + + /** + * set the 'borderTopWidth' property + */ + virtual void setBorderTopWidth(const DOMString &val) + throw (dom::DOMException) + { + borderTopWidth = val; + } + + /** + * return the 'borderRightWidth' property + */ + virtual DOMString getBorderRightWidth() + { + return borderRightWidth; + } + + /** + * set the 'borderRightWidth' property + */ + virtual void setBorderRightWidth(const DOMString &val) + throw (dom::DOMException) + { + borderRightWidth = val; + } + + /** + * return the 'borderBottomWidth' property + */ + virtual DOMString getBorderBottomWidth() + { + return borderBottomWidth; + } + + /** + * set the 'borderBottomWidth' property + */ + virtual void setBorderBottomWidth(const DOMString &val) + throw (dom::DOMException) + { + borderBottomWidth = val; + } + + /** + * return the 'borderLeftWidth' property + */ + virtual DOMString getBorderLeftWidth() + { + return borderLeftWidth; + } + + /** + * set the 'borderLeftWidth' property + */ + virtual void setBorderLeftWidth(const DOMString &val) + throw (dom::DOMException) + { + borderLeftWidth = val; + } + + /** + * return the 'borderWidth' property + */ + virtual DOMString getBorderWidth() + { + return borderWidth; + } + + /** + * set the 'borderWidth' property + */ + virtual void setBorderWidth(const DOMString &val) + throw (dom::DOMException) + { + borderWidth = val; + } + + /** + * return the 'bottom' property + */ + virtual DOMString getBottom() + { + return bottom; + } + + /** + * set the 'bottom' property + */ + virtual void setBottom(const DOMString &val) + throw (dom::DOMException) + { + bottom = val; + } + + /** + * return the 'captionSide' property + */ + virtual DOMString getCaptionSide() + { + return captionSide; + } + + /** + * set the 'captionSide' property + */ + virtual void setCaptionSide(const DOMString &val) + throw (dom::DOMException) + { + captionSide = val; + } + + /** + * return the 'clear' property + */ + virtual DOMString getClear() + { + return clear; + } + + /** + * set the 'clear' property + */ + virtual void setClear(const DOMString &val) + throw (dom::DOMException) + { + clear = val; + } + + /** + * return the 'clip' property + */ + virtual DOMString getClip() + { + return clip; + } + + /** + * set the 'clip' property + */ + virtual void setClip(const DOMString &val) + throw (dom::DOMException) + { + clip = val; + } + + /** + * return the 'color' property + */ + virtual DOMString getColor() + { + return color; + } + + /** + * set the 'color' property + */ + virtual void setColor(const DOMString &val) + throw (dom::DOMException) + { + color = val; + } + + /** + * return the 'content' property + */ + virtual DOMString getContent() + { + return content; + } + + /** + * set the 'content' property + */ + virtual void setContent(const DOMString &val) + throw (dom::DOMException) + { + content = val; + } + + /** + * return the 'counterIncrement' property + */ + virtual DOMString getCounterIncrement() + { + return counterIncrement; + } + + /** + * set the 'counterIncrement' property + */ + virtual void setCounterIncrement(const DOMString &val) + throw (dom::DOMException) + { + counterIncrement = val; + } + + /** + * return the 'counterReset' property + */ + virtual DOMString getCounterReset() + { + return counterReset; + } + + /** + * set the 'counterReset' property + */ + virtual void setCounterReset(const DOMString &val) + throw (dom::DOMException) + { + counterReset = val; + } + + /** + * return the 'cue' property + */ + virtual DOMString getCue() + { + return cue; + } + + /** + * set the 'cue' property + */ + virtual void setCue(const DOMString &val) + throw (dom::DOMException) + { + cue = val; + } + + /** + * return the 'cueAfter' property + */ + virtual DOMString getCueAfter() + { + return cueAfter; + } + + /** + * set the 'cueAfter' property + */ + virtual void setCueAfter(const DOMString &val) + throw (dom::DOMException) + { + cueAfter = val; + } + + /** + * return the 'cueBefore' property + */ + virtual DOMString getCueBefore() + { + return cueBefore; + } + + /** + * set the 'cueBefore' property + */ + virtual void setCueBefore(const DOMString &val) + throw (dom::DOMException) + { + cueBefore = val; + } + + /** + * return the 'cursor' property + */ + virtual DOMString getCursor() + { + return cursor; + } + + /** + * set the 'cursor' property + */ + virtual void setCursor(const DOMString &val) + throw (dom::DOMException) + { + cursor = val; + } + + /** + * return the 'direction' property + */ + virtual DOMString getDirection() + { + return direction; + } + + /** + * set the 'direction' property + */ + virtual void setDirection(const DOMString &val) + throw (dom::DOMException) + { + direction = val; + } + + /** + * return the 'display' property + */ + virtual DOMString getDisplay() + { + return display; + } + + /** + * set the 'display' property + */ + virtual void setDisplay(const DOMString &val) + throw (dom::DOMException) + { + display = val; + } + + /** + * return the 'elevation' property + */ + virtual DOMString getElevation() + { + return elevation; + } + + /** + * set the 'elevation' property + */ + virtual void setElevation(const DOMString &val) + throw (dom::DOMException) + { + elevation = val; + } + + /** + * return the 'emptyCells' property + */ + virtual DOMString getEmptyCells() + { + return emptyCells; + } + + /** + * set the 'emptyCells' property + */ + virtual void setEmptyCells(const DOMString &val) + throw (dom::DOMException) + { + emptyCells = val; + } + + /** + * return the 'cssFloat' property + */ + virtual DOMString getCssFloat() + { + return cssFloat; + } + + /** + * set the 'cssFloat' property + */ + virtual void setCssFloat(const DOMString &val) + throw (dom::DOMException) + { + cssFloat = val; + } + + /** + * return the 'font' property + */ + virtual DOMString getFont() + { + return font; + } + + /** + * set the 'font' property + */ + virtual void setFont(const DOMString &val) + throw (dom::DOMException) + { + font = val; + } + + /** + * return the 'fontFamily' property + */ + virtual DOMString getFontFamily() + { + return fontFamily; + } + + /** + * set the 'fontFamily' property + */ + virtual void setFontFamily(const DOMString &val) + throw (dom::DOMException) + { + fontFamily = val; + } + + /** + * return the 'fontSize' property + */ + virtual DOMString getFontSize() + { + return fontSize; + } + + /** + * set the 'fontSize' property + */ + virtual void setFontSize(const DOMString &val) + throw (dom::DOMException) + { + fontSize = val; + } + + /** + * return the 'fontSizeAdjust' property + */ + virtual DOMString getFontSizeAdjust() + { + return fontSizeAdjust; + } + + /** + * set the 'fontSizeAdjust' property + */ + virtual void setFontSizeAdjust(const DOMString &val) + throw (dom::DOMException) + { + fontSizeAdjust = val; + } + + /** + * return the 'fontStretch' property + */ + virtual DOMString getFontStretch() + { + return fontStretch; + } + + /** + * set the 'fontStretch' property + */ + virtual void setFontStretch(const DOMString &val) + throw (dom::DOMException) + { + fontStretch = val; + } + + /** + * return the 'fontStyle' property + */ + virtual DOMString getFontStyle() + { + return fontStyle; + } + + /** + * set the 'fontStyle' property + */ + virtual void setFontStyle(const DOMString &val) + throw (dom::DOMException) + { + fontStyle = val; + } + + /** + * return the 'fontVariant' property + */ + virtual DOMString getFontVariant() + { + return fontVariant; + } + + /** + * set the 'fontVariant' property + */ + virtual void setFontVariant(const DOMString &val) + throw (dom::DOMException) + { + fontVariant = val; + } + + /** + * return the 'fontWeight' property + */ + virtual DOMString getFontWeight() + { + return fontWeight; + } + + /** + * set the 'fontWeight' property + */ + virtual void setFontWeight(const DOMString &val) + throw (dom::DOMException) + { + fontWeight = val; + } + + /** + * return the 'height' property + */ + virtual DOMString getHeight() + { + return height; + } + + /** + * set the 'height' property + */ + virtual void setHeight(const DOMString &val) + throw (dom::DOMException) + { + height = val; + } + + /** + * return the 'left' property + */ + virtual DOMString getLeft() + { + return left; + } + + /** + * set the 'left' property + */ + virtual void setLeft(const DOMString &val) + throw (dom::DOMException) + { + left = val; + } + + /** + * return the 'letterSpacing' property + */ + virtual DOMString getLetterSpacing() + { + return letterSpacing; + } + + /** + * set the 'letterSpacing' property + */ + virtual void setLetterSpacing(const DOMString &val) + throw (dom::DOMException) + { + letterSpacing = val; + } + + /** + * return the 'lineHeight' property + */ + virtual DOMString getLineHeight() + { + return lineHeight; + } + + /** + * set the 'lineHeight' property + */ + virtual void setLineHeight(const DOMString &val) + throw (dom::DOMException) + { + lineHeight = val; + } + + /** + * return the 'listStyle' property + */ + virtual DOMString getListStyle() + { + return listStyle; + } + + /** + * set the 'listStyle' property + */ + virtual void setListStyle(const DOMString &val) + throw (dom::DOMException) + { + listStyle = val; + } + + /** + * return the 'listStyleImage' property + */ + virtual DOMString getListStyleImage() + { + return listStyleImage; + } + + /** + * set the 'listStyleImage' property + */ + virtual void setListStyleImage(const DOMString &val) + throw (dom::DOMException) + { + listStyleImage = val; + } + + /** + * return the 'listStylePosition' property + */ + virtual DOMString getListStylePosition() + { + return listStylePosition; + } + + /** + * set the 'listStylePosition' property + */ + virtual void setListStylePosition(const DOMString &val) + throw (dom::DOMException) + { + listStylePosition = val; + } + + /** + * return the 'listStyleType' property + */ + virtual DOMString getListStyleType() + { + return listStyleType; + } + + /** + * set the 'listStyleType' property + */ + virtual void setListStyleType(const DOMString &val) + throw (dom::DOMException) + { + listStyleType = val; + } + + /** + * return the 'margin' property + */ + virtual DOMString getMargin() + { + return margin; + } + + /** + * set the 'margin' property + */ + virtual void setMargin(const DOMString &val) + throw (dom::DOMException) + { + margin = val; + } + + /** + * return the 'marginTop' property + */ + virtual DOMString getMarginTop() + { + return marginTop; + } + + /** + * set the 'marginTop' property + */ + virtual void setMarginTop(const DOMString &val) + throw (dom::DOMException) + { + marginTop = val; + } + + /** + * return the 'marginRight' property + */ + virtual DOMString getMarginRight() + { + return marginRight; + } + + /** + * set the 'marginRight' property + */ + virtual void setMarginRight(const DOMString &val) + throw (dom::DOMException) + { + marginRight = val; + } + + /** + * return the 'marginBottom' property + */ + virtual DOMString getMarginBottom() + { + return marginBottom; + } + + /** + * set the 'marginBottom' property + */ + virtual void setMarginBottom(const DOMString &val) + throw (dom::DOMException) + { + marginBottom = val; + } + + /** + * return the 'marginLeft' property + */ + virtual DOMString getMarginLeft() + { + return marginLeft; + } + + /** + * set the 'marginLeft' property + */ + virtual void setMarginLeft(const DOMString &val) + throw (dom::DOMException) + { + marginLeft = val; + } + + /** + * return the 'markerOffset' property + */ + virtual DOMString getMarkerOffset() + { + return markerOffset; + } + + /** + * set the 'markerOffset' property + */ + virtual void setMarkerOffset(const DOMString &val) + throw (dom::DOMException) + { + markerOffset = val; + } + + /** + * return the 'marks' property + */ + virtual DOMString getMarks() + { + return marks; + } + + /** + * set the 'marks' property + */ + virtual void setMarks(const DOMString &val) + throw (dom::DOMException) + { + marks = val; + } + + /** + * return the 'maxHeight' property + */ + virtual DOMString getMaxHeight() + { + return maxHeight; + } + + /** + * set the 'maxHeight' property + */ + virtual void setMaxHeight(const DOMString &val) + throw (dom::DOMException) + { + maxHeight = val; + } + + /** + * return the 'maxWidth' property + */ + virtual DOMString getMaxWidth() + { + return maxWidth; + } + + /** + * set the 'maxWidth' property + */ + virtual void setMaxWidth(const DOMString &val) + throw (dom::DOMException) + { + maxWidth = val; + } + + /** + * return the 'minHeight' property + */ + virtual DOMString getMinHeight() + { + return minHeight; + } + + /** + * set the 'minHeight' property + */ + virtual void setMinHeight(const DOMString &val) + throw (dom::DOMException) + { + minHeight = val; + } + + /** + * return the 'minWidth' property + */ + virtual DOMString getMinWidth() + { + return minWidth; + } + + /** + * set the 'minWidth' property + */ + virtual void setMinWidth(const DOMString &val) + throw (dom::DOMException) + { + minWidth = val; + } + + /** + * return the 'orphans' property + */ + virtual DOMString getOrphans() + { + return orphans; + } + + /** + * set the 'orphans' property + */ + virtual void setOrphans(const DOMString &val) + throw (dom::DOMException) + { + orphans = val; + } + + /** + * return the 'outline' property + */ + virtual DOMString getOutline() + { + return outline; + } + + /** + * set the 'outline' property + */ + virtual void setOutline(const DOMString &val) + throw (dom::DOMException) + { + outline = val; + } + + /** + * return the 'outlineColor' property + */ + virtual DOMString getOutlineColor() + { + return outlineColor; + } + + /** + * set the 'outlineColor' property + */ + virtual void setOutlineColor(const DOMString &val) + throw (dom::DOMException) + { + outlineColor = val; + } + + /** + * return the 'outlineStyle' property + */ + virtual DOMString getOutlineStyle() + { + return outlineStyle; + } + + /** + * set the 'outlineStyle' property + */ + virtual void setOutlineStyle(const DOMString &val) + throw (dom::DOMException) + { + outlineStyle = val; + } + + /** + * return the 'outlineWidth' property + */ + virtual DOMString getOutlineWidth() + { + return outlineWidth; + } + + /** + * set the 'outlineWidth' property + */ + virtual void setOutlineWidth(const DOMString &val) + throw (dom::DOMException) + { + outlineWidth = val; + } + + /** + * return the 'overflow' property + */ + virtual DOMString getOverflow() + { + return overflow; + } + + /** + * set the 'overflow' property + */ + virtual void setOverflow(const DOMString &val) + throw (dom::DOMException) + { + overflow = val; + } + + /** + * return the 'padding' property + */ + virtual DOMString getPadding() + { + return padding; + } + + /** + * set the 'padding' property + */ + virtual void setPadding(const DOMString &val) + throw (dom::DOMException) + { + padding = val; + } + + /** + * return the 'paddingTop' property + */ + virtual DOMString getPaddingTop() + { + return paddingTop; + } + + /** + * set the 'paddingTop' property + */ + virtual void setPaddingTop(const DOMString &val) + throw (dom::DOMException) + { + paddingTop = val; + } + + /** + * return the 'paddingRight' property + */ + virtual DOMString getPaddingRight() + { + return paddingRight; + } + + /** + * set the 'paddingRight' property + */ + virtual void setPaddingRight(const DOMString &val) + throw (dom::DOMException) + { + paddingRight = val; + } + + /** + * return the 'paddingBottom' property + */ + virtual DOMString getPaddingBottom() + { + return paddingBottom; + } + + /** + * set the 'paddingBottom' property + */ + virtual void setPaddingBottom(const DOMString &val) + throw (dom::DOMException) + { + paddingBottom = val; + } + + /** + * return the 'paddingLeft' property + */ + virtual DOMString getPaddingLeft() + { + return paddingLeft; + } + + /** + * set the 'paddingLeft' property + */ + virtual void setPaddingLeft(const DOMString &val) + throw (dom::DOMException) + { + paddingLeft = val; + } + + /** + * return the 'page' property + */ + virtual DOMString getPage() + { + return page; + } + + /** + * set the 'page' property + */ + virtual void setPage(const DOMString &val) + throw (dom::DOMException) + { + page = val; + } + + /** + * return the 'pageBreakAfter' property + */ + virtual DOMString getPageBreakAfter() + { + return pageBreakAfter; + } + + /** + * set the 'pageBreakAfter' property + */ + virtual void setPageBreakAfter(const DOMString &val) + throw (dom::DOMException) + { + pageBreakAfter = val; + } + + /** + * return the 'pageBreakBefore' property + */ + virtual DOMString getPageBreakBefore() + { + return pageBreakBefore; + } + + /** + * set the 'pageBreakBefore' property + */ + virtual void setPageBreakBefore(const DOMString &val) + throw (dom::DOMException) + { + pageBreakBefore = val; + } + + /** + * return the 'pageBreakInside' property + */ + virtual DOMString getPageBreakInside() + { + return pageBreakInside; + } + + /** + * set the 'pageBreakInside' property + */ + virtual void setPageBreakInside(const DOMString &val) + throw (dom::DOMException) + { + pageBreakInside = val; + } + + /** + * return the 'pause' property + */ + virtual DOMString getPause() + { + return pause; + } + + /** + * set the 'pause' property + */ + virtual void setPause(const DOMString &val) + throw (dom::DOMException) + { + pause = val; + } + + /** + * return the 'pauseAfter' property + */ + virtual DOMString getPauseAfter() + { + return pauseAfter; + } + + /** + * set the 'pauseAfter' property + */ + virtual void setPauseAfter(const DOMString &val) + throw (dom::DOMException) + { + pauseAfter = val; + } + + /** + * return the 'pauseBefore' property + */ + virtual DOMString getPauseBefore() + { + return pauseBefore; + } + + /** + * set the 'pauseBefore' property + */ + virtual void setPauseBefore(const DOMString &val) + throw (dom::DOMException) + { + pauseBefore = val; + } + + /** + * return the 'pitch' property + */ + virtual DOMString getPitch() + { + return pitch; + } + + /** + * set the 'pitch' property + */ + virtual void setPitch(const DOMString &val) + throw (dom::DOMException) + { + pitch = val; + } + + /** + * return the 'pitchRange' property + */ + virtual DOMString getPitchRange() + { + return pitchRange; + } + + /** + * set the 'pitchRange' property + */ + virtual void setPitchRange(const DOMString &val) + throw (dom::DOMException) + { + pitchRange = val; + } + + /** + * return the 'playDuring' property + */ + virtual DOMString getPlayDuring() + { + return playDuring; + } + + /** + * set the 'playDuring' property + */ + virtual void setPlayDuring(const DOMString &val) + throw (dom::DOMException) + { + playDuring = val; + } + + /** + * return the 'position' property + */ + virtual DOMString getPosition() + { + return position; + } + + /** + * set the 'position' property + */ + virtual void setPosition(const DOMString &val) + throw (dom::DOMException) + { + position = val; + } + + /** + * return the 'quotes' property + */ + virtual DOMString getQuotes() + { + return quotes; + } + + /** + * set the 'quotes' property + */ + virtual void setQuotes(const DOMString &val) + throw (dom::DOMException) + { + quotes = val; + } + + /** + * return the 'richness' property + */ + virtual DOMString getRichness() + { + return richness; + } + + /** + * set the 'richness' property + */ + virtual void setRichness(const DOMString &val) + throw (dom::DOMException) + { + richness = val; + } + + /** + * return the 'right' property + */ + virtual DOMString getRight() + { + return right; + } + + /** + * set the 'right' property + */ + virtual void setRight(const DOMString &val) + throw (dom::DOMException) + { + right = val; + } + + /** + * return the 'size' property + */ + virtual DOMString getSize() + { + return size; + } + + /** + * set the 'size' property + */ + virtual void setSize(const DOMString &val) + throw (dom::DOMException) + { + size = val; + } + + /** + * return the 'speak' property + */ + virtual DOMString getSpeak() + { + return speak; + } + + /** + * set the 'speak' property + */ + virtual void setSpeak(const DOMString &val) + throw (dom::DOMException) + { + speak = val; + } + + /** + * return the 'speakHeader' property + */ + virtual DOMString getSpeakHeader() + { + return speakHeader; + } + + /** + * set the 'speakHeader' property + */ + virtual void setSpeakHeader(const DOMString &val) + throw (dom::DOMException) + { + speakHeader = val; + } + + /** + * return the 'speakNumeral' property + */ + virtual DOMString getSpeakNumeral() + { + return speakNumeral; + } + + /** + * set the 'speakNumeral' property + */ + virtual void setSpeakNumeral(const DOMString &val) + throw (dom::DOMException) + { + speakNumeral = val; + } + + /** + * return the 'speakPunctuation' property + */ + virtual DOMString getSpeakPunctuation() + { + return speakPunctuation; + } + + /** + * set the 'speakPunctuation' property + */ + virtual void setSpeakPunctuation(const DOMString &val) + throw (dom::DOMException) + { + speakPunctuation = val; + } + + /** + * return the 'speechRate' property + */ + virtual DOMString getSpeechRate() + { + return speechRate; + } + + /** + * set the 'speechRate' property + */ + virtual void setSpeechRate(const DOMString &val) + throw (dom::DOMException) + { + speechRate = val; + } + + /** + * return the 'stress' property + */ + virtual DOMString getStress() + { + return stress; + } + + /** + * set the 'stress' property + */ + virtual void setStress(const DOMString &val) + throw (dom::DOMException) + { + stress = val; + } + + /** + * return the 'tableLayout' property + */ + virtual DOMString getTableLayout() + { + return tableLayout; + } + + /** + * set the 'tableLayout' property + */ + virtual void setTableLayout(const DOMString &val) + throw (dom::DOMException) + { + tableLayout = val; + } + + /** + * return the 'textAlign' property + */ + virtual DOMString getTextAlign() + { + return textAlign; + } + + /** + * set the 'textAlign' property + */ + virtual void setTextAlign(const DOMString &val) + throw (dom::DOMException) + { + textAlign = val; + } + + /** + * return the 'textDecoration' property + */ + virtual DOMString getTextDecoration() + { + return textDecoration; + } + + /** + * set the 'textDecoration' property + */ + virtual void setTextDecoration(const DOMString &val) + throw (dom::DOMException) + { + textDecoration = val; + } + + /** + * return the 'textIndent' property + */ + virtual DOMString getTextIndent() + { + return textIndent; + } + + /** + * set the 'textIndent' property + */ + virtual void setTextIndent(const DOMString &val) + throw (dom::DOMException) + { + textIndent = val; + } + + /** + * return the 'textShadow' property + */ + virtual DOMString getTextShadow() + { + return textShadow; + } + + /** + * set the 'textShadow' property + */ + virtual void setTextShadow(const DOMString &val) + throw (dom::DOMException) + { + textShadow = val; + } + + /** + * return the 'textTransform' property + */ + virtual DOMString getTextTransform() + { + return textTransform; + } + + /** + * set the 'textTransform' property + */ + virtual void setTextTransform(const DOMString &val) + throw (dom::DOMException) + { + textTransform = val; + } + + /** + * return the 'top' property + */ + virtual DOMString getTop() + { + return top; + } + + /** + * set the 'top' property + */ + virtual void setTop(const DOMString &val) + throw (dom::DOMException) + { + top = val; + } + + /** + * return the 'unicodeBidi' property + */ + virtual DOMString getUnicodeBidi() + { + return unicodeBidi; + } + + /** + * set the 'unicodeBidi' property + */ + virtual void setUnicodeBidi(const DOMString &val) + throw (dom::DOMException) + { + unicodeBidi = val; + } + + /** + * return the 'verticalAlign' property + */ + virtual DOMString getVerticalAlign() + { + return verticalAlign; + } + + /** + * set the 'verticalAlign' property + */ + virtual void setVerticalAlign(const DOMString &val) + throw (dom::DOMException) + { + verticalAlign = val; + } + + /** + * return the 'visibility' property + */ + virtual DOMString getVisibility() + { + return visibility; + } + + /** + * set the 'visibility' property + */ + virtual void setVisibility(const DOMString &val) + throw (dom::DOMException) + { + visibility = val; + } + + /** + * return the 'voiceFamily' property + */ + virtual DOMString getVoiceFamily() + { + return voiceFamily; + } + + /** + * set the 'voiceFamily' property + */ + virtual void setVoiceFamily(const DOMString &val) + throw (dom::DOMException) + { + voiceFamily = val; + } + + /** + * return the 'volume' property + */ + virtual DOMString getVolume() + { + return volume; + } + + /** + * set the 'volume' property + */ + virtual void setVolume(const DOMString &val) + throw (dom::DOMException) + { + volume = val; + } + + /** + * return the 'whiteSpace' property + */ + virtual DOMString getWhiteSpace() + { + return whiteSpace; + } + + /** + * set the 'whiteSpace' property + */ + virtual void setWhiteSpace(const DOMString &val) + throw (dom::DOMException) + { + whiteSpace = val; + } + + /** + * return the 'widows' property + */ + virtual DOMString getWidows() + { + return widows; + } + + /** + * set the 'widows' property + */ + virtual void setWidows(const DOMString &val) + throw (dom::DOMException) + { + widows = val; + } + + /** + * return the 'width' property + */ + virtual DOMString getWidth() + { + return width; + } + + /** + * set the 'width' property + */ + virtual void setWidth(const DOMString &val) + throw (dom::DOMException) + { + width = val; + } + + /** + * return the 'wordSpacing' property + */ + virtual DOMString getWordSpacing() + { + return wordSpacing; + } + + /** + * set the 'wordSpacing' property + */ + virtual void setWordSpacing(const DOMString &val) + throw (dom::DOMException) + { + wordSpacing = val; + } + + /** + * return the 'zIndex' property + */ + virtual DOMString getZIndex() + { + return zIndex; + } + + /** + * set the 'zIndex' property + */ + virtual void setZIndex(const DOMString &val) + throw (dom::DOMException) + { + zIndex = val; + } + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CSS2Properties() + { + } + + /** + * + */ + CSS2Properties(const CSS2Properties &other) + { + azimuth = other.azimuth; + background = other.background; + backgroundAttachment = other.backgroundAttachment; + backgroundColor = other.backgroundColor; + backgroundImage = other.backgroundImage; + backgroundPosition = other.backgroundPosition; + backgroundRepeat = other.backgroundRepeat; + border = other.border; + borderCollapse = other.borderCollapse; + borderColor = other.borderColor; + borderSpacing = other.borderSpacing; + borderStyle = other.borderStyle; + borderTop = other.borderTop; + borderRight = other.borderRight; + borderBottom = other.borderBottom; + borderLeft = other.borderLeft; + borderTopColor = other.borderTopColor; + borderRightColor = other.borderRightColor; + borderBottomColor = other.borderBottomColor; + borderLeftColor = other.borderLeftColor; + borderTopStyle = other.borderTopStyle; + borderRightStyle = other.borderRightStyle; + borderBottomStyle = other.borderBottomStyle; + borderLeftStyle = other.borderLeftStyle; + borderTopWidth = other.borderTopWidth; + borderRightWidth = other.borderRightWidth; + borderBottomWidth = other.borderBottomWidth; + borderLeftWidth = other.borderLeftWidth; + borderWidth = other.borderWidth; + bottom = other.bottom; + captionSide = other.captionSide; + clear = other.clear; + clip = other.clip; + color = other.color; + content = other.content; + counterIncrement = other.counterIncrement; + counterReset = other.counterReset; + cue = other.cue; + cueAfter = other.cueAfter; + cueBefore = other.cueBefore; + cursor = other.cursor; + direction = other.direction; + display = other.display; + elevation = other.elevation; + emptyCells = other.emptyCells; + cssFloat = other.cssFloat; + font = other.font; + fontFamily = other.fontFamily; + fontSize = other.fontSize; + fontSizeAdjust = other.fontSizeAdjust; + fontStretch = other.fontStretch; + fontStyle = other.fontStyle; + fontVariant = other.fontVariant; + fontWeight = other.fontWeight; + height = other.height; + left = other.left; + letterSpacing = other.letterSpacing; + lineHeight = other.lineHeight; + listStyle = other.listStyle; + listStyleImage = other.listStyleImage; + listStylePosition = other.listStylePosition; + listStyleType = other.listStyleType; + margin = other.margin; + marginTop = other.marginTop; + marginRight = other.marginRight; + marginBottom = other.marginBottom; + marginLeft = other.marginLeft; + markerOffset = other.markerOffset; + marks = other.marks; + maxHeight = other.maxHeight; + maxWidth = other.maxWidth; + minHeight = other.minHeight; + minWidth = other.minWidth; + orphans = other.orphans; + outline = other.outline; + outlineColor = other.outlineColor; + outlineStyle = other.outlineStyle; + outlineWidth = other.outlineWidth; + overflow = other.overflow; + padding = other.padding; + paddingTop = other.paddingTop; + paddingRight = other.paddingRight; + paddingBottom = other.paddingBottom; + paddingLeft = other.paddingLeft; + page = other.page; + pageBreakAfter = other.pageBreakAfter; + pageBreakBefore = other.pageBreakBefore; + pageBreakInside = other.pageBreakInside; + pause = other.pause; + pauseAfter = other.pauseAfter; + pauseBefore = other.pauseBefore; + pitch = other.pitch; + pitchRange = other.pitchRange; + playDuring = other.playDuring; + position = other.position; + quotes = other.quotes; + richness = other.richness; + right = other.right; + size = other.size; + speak = other.speak; + speakHeader = other.speakHeader; + speakNumeral = other.speakNumeral; + speakPunctuation = other.speakPunctuation; + speechRate = other.speechRate; + stress = other.stress; + tableLayout = other.tableLayout; + textAlign = other.textAlign; + textDecoration = other.textDecoration; + textIndent = other.textIndent; + textShadow = other.textShadow; + textTransform = other.textTransform; + top = other.top; + unicodeBidi = other.unicodeBidi; + verticalAlign = other.verticalAlign; + visibility = other.visibility; + voiceFamily = other.voiceFamily; + volume = other.volume; + whiteSpace = other.whiteSpace; + widows = other.widows; + width = other.width; + wordSpacing = other.wordSpacing; + zIndex = other.zIndex; + } + + /** + * + */ + virtual ~CSS2Properties() {} + +protected: + + //###################### + //# P R O P E R T I E S + //###################### + DOMString azimuth; + DOMString background; + DOMString backgroundAttachment; + DOMString backgroundColor; + DOMString backgroundImage; + DOMString backgroundPosition; + DOMString backgroundRepeat; + DOMString border; + DOMString borderCollapse; + DOMString borderColor; + DOMString borderSpacing; + DOMString borderStyle; + DOMString borderTop; + DOMString borderRight; + DOMString borderBottom; + DOMString borderLeft; + DOMString borderTopColor; + DOMString borderRightColor; + DOMString borderBottomColor; + DOMString borderLeftColor; + DOMString borderTopStyle; + DOMString borderRightStyle; + DOMString borderBottomStyle; + DOMString borderLeftStyle; + DOMString borderTopWidth; + DOMString borderRightWidth; + DOMString borderBottomWidth; + DOMString borderLeftWidth; + DOMString borderWidth; + DOMString bottom; + DOMString captionSide; + DOMString clear; + DOMString clip; + DOMString color; + DOMString content; + DOMString counterIncrement; + DOMString counterReset; + DOMString cue; + DOMString cueAfter; + DOMString cueBefore; + DOMString cursor; + DOMString direction; + DOMString display; + DOMString elevation; + DOMString emptyCells; + DOMString cssFloat; + DOMString font; + DOMString fontFamily; + DOMString fontSize; + DOMString fontSizeAdjust; + DOMString fontStretch; + DOMString fontStyle; + DOMString fontVariant; + DOMString fontWeight; + DOMString height; + DOMString left; + DOMString letterSpacing; + DOMString lineHeight; + DOMString listStyle; + DOMString listStyleImage; + DOMString listStylePosition; + DOMString listStyleType; + DOMString margin; + DOMString marginTop; + DOMString marginRight; + DOMString marginBottom; + DOMString marginLeft; + DOMString markerOffset; + DOMString marks; + DOMString maxHeight; + DOMString maxWidth; + DOMString minHeight; + DOMString minWidth; + DOMString orphans; + DOMString outline; + DOMString outlineColor; + DOMString outlineStyle; + DOMString outlineWidth; + DOMString overflow; + DOMString padding; + DOMString paddingTop; + DOMString paddingRight; + DOMString paddingBottom; + DOMString paddingLeft; + DOMString page; + DOMString pageBreakAfter; + DOMString pageBreakBefore; + DOMString pageBreakInside; + DOMString pause; + DOMString pauseAfter; + DOMString pauseBefore; + DOMString pitch; + DOMString pitchRange; + DOMString playDuring; + DOMString position; + DOMString quotes; + DOMString richness; + DOMString right; + DOMString size; + DOMString speak; + DOMString speakHeader; + DOMString speakNumeral; + DOMString speakPunctuation; + DOMString speechRate; + DOMString stress; + DOMString tableLayout; + DOMString textAlign; + DOMString textDecoration; + DOMString textIndent; + DOMString textShadow; + DOMString textTransform; + DOMString top; + DOMString unicodeBidi; + DOMString verticalAlign; + DOMString visibility; + DOMString voiceFamily; + DOMString volume; + DOMString whiteSpace; + DOMString widows; + DOMString width; + DOMString wordSpacing; + DOMString zIndex; + + +}; + + + + + + + + +/*######################################################################### +## ViewCSS +#########################################################################*/ + +/** + * again, a mismatch with views::View and views::AbstractView + */ +class ViewCSS : virtual public views::View +{ +public: + + /** + * + */ + virtual CSSStyleDeclaration getComputedStyle(const Element &elt, + const DOMString &pseudoElt) + { + CSSStyleDeclaration style; + return style; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + ViewCSS() : views::View() + { + } + + /** + * + */ + ViewCSS(const ViewCSS &other) : views::View(other) + { + } + + /** + * + */ + virtual ~ViewCSS() {} +}; + + + + + +/*######################################################################### +## DocumentCSS +#########################################################################*/ + +/** + * + */ +class DocumentCSS : virtual public stylesheets::DocumentStyle +{ +public: + + /** + * + */ + virtual CSSStyleDeclaration getOverrideStyle(const Element *elt, + const DOMString &pseudoElt) + { + CSSStyleDeclaration style; + return style; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + DocumentCSS() : stylesheets::DocumentStyle() + { + } + + /** + * + */ + DocumentCSS(const DocumentCSS &other) : stylesheets::DocumentStyle(other) + { + } + + /** + * + */ + virtual ~DocumentCSS() {} +}; + + + + + + +/*######################################################################### +## DOMImplementationCSS +#########################################################################*/ + +/** + * + */ +class DOMImplementationCSS : virtual public DOMImplementation +{ +public: + + /** + * + */ + virtual CSSStyleSheet createCSSStyleSheet(const DOMString &title, + const DOMString &media) + throw (dom::DOMException) + { + CSSStyleSheet sheet; + return sheet; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + DOMImplementationCSS() {} + + /** + * + */ + DOMImplementationCSS(const DOMImplementationCSS &other) + : DOMImplementation(other) + { + } + + /** + * + */ + virtual ~DOMImplementationCSS() {} +}; + + + + + + + + +} //namespace css +} //namespace dom +} //namespace org +} //namespace w3c + + +#endif // __CSS_H__ + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ diff --git a/src/dom/cssparser.cpp b/src/dom/cssparser.cpp new file mode 100644 index 000000000..2dc342704 --- /dev/null +++ b/src/dom/cssparser.cpp @@ -0,0 +1,1670 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "cssparser.h" +#include "charclass.h" + +#include +#include + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace css +{ + +//######################################################################### +//# M E S S A G E S +//######################################################################### + +/** + * Get the column and row number of the given character position + */ +void CssParser::getColumnAndRow(int p, int &colResult, int &rowResult, int &lastNL) +{ + int col = 1; + int row = 1; + int lastnl = 0; + + for (int i=0 ; i

= parselen) + return 0; + XMLCh ch = parsebuf[p]; + //printf("%c", ch); + lastPosition = p; + return ch; +} + + + +/** + * Test if the given substring exists at the given position + * in parsebuf. Use get() in case of out-of-bounds + */ +bool CssParser::match(int pos, char *str) +{ + while (*str) + { + if (get(pos++) != *str++) + return false; + } + return true; +} + +/** + * + */ +int CssParser::skipwhite(int p) +{ + while (p < parselen) + { + //# XML COMMENT + if (match(p, "")) + { + p+=3; + done=true; + break; + } + p++; + } + lastPosition = p; + if (!done) + { + error("unterminated comment"); + return -1; + } + } + //# C comment + else if (match(p, "/*")) + { + p+=2; + bool done=false; + while (p'9') + break; + str.push_back(ch); + p++; + } + if (get(p) == '.' && get(p+1)>='0' && get(p+1)<='9') + { + p++; + str.push_back('.'); + while (p < parselen) + { + XMLCh ch = get(p); + if (ch<'0' || ch>'9') + break; + str.push_back(ch); + p++; + } + } + if (p>p0) + { + char *start = (char *)str.c_str(); + char *end = NULL; + double val = strtod(start, &end); + if (end > start) + { + result = val; + return p; + } + } + + //not a number + return p0; +} + + + +/** + * Assume that we are starting on a quote. Ends on the char + * after the final '"' + */ +int CssParser::getQuoted(int p0, DOMString &result) +{ + + int p = p0; + + XMLCh quoteChar = get(p); + if (quoteChar != '"' && quoteChar != '\'') + return p0; + + p++; + + DOMString buf; + + bool done = false; + while (pp) + { + p = p2; + continue; + } + + //Media + p2 = getMedia(p); + if (p2<0) + { + return -1; + } + if (p2>p) + { + p = p2; + continue; + } + + //Page + p2 = getPage(p); + if (p2<0) + { + return -1; + } + if (p2>p) + { + p = p2; + continue; + } + + //none of the above + break; + } + + return p; +} + +/** + * import + * : IMPORT_SYM S* + * [STRING|URI] S* [ medium [ COMMA S* medium]* ]? ';' S* + * ; + */ +int CssParser::getImport(int p0) +{ + int p = p0; + if (!match(p, "@import")) + return p0; + p+=7; + p = skipwhite(p); + + //# STRING | URI + DOMString str; + int p2 = getQuoted(p, str); + if (p2<0) + { + return -1; + } + if (p2<=p) + { + p2 = getUri(p, str); + if (p2<0) + { + return -1; + } + if (p2<=p) + { + error("quoted string or URI required after @import"); + return -1; + } + } + p = p2; + p2 = getMedium(p); + if (p2<0) + return -1; + + p = p2; + p = skipwhite(p); + XMLCh ch = get(p); + if (ch != ';') + { + error("@import must be terminated with ';'"); + return -1; + } + p++; + return p; +} + +/** + * media + * : MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* '}' S* + * ; + */ +int CssParser::getMedia(int p0) +{ + int p = p0; + XMLCh ch; + if (!match(p, "@media")) + return p0; + p+=6; + p = skipwhite(p); + + //# MEDIUM LIST + int p2 = getMedium(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("@media must be followed by medium"); + return -1; + } + p = p2; + while (true) + { + ch = get(p); + if (ch != ',') + break; + p2 = getMedium(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("',' in medium list must be followed by medium"); + return -1; + } + p = p2; + } + + p = skipwhite(p); + ch = get(p); + if (ch!='{') + { + error("@media requires '{' for ruleset"); + return -1; + } + p++; + p2 = getRuleSet(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("@media requires ruleset after '{'"); + return -1; + } + p = p2; + ch = get(p); + if (ch != '}') + { + error("@media requires '}' after ruleset"); + return -1; + } + p++; + return p0; +} + +/** + * medium + * : IDENT S* + * ; + */ +int CssParser::getMedium(int p0) +{ + int p = p0; + p = skipwhite(p); + + DOMString ident; + int p2 = getWord(p, ident); + if (p2<0) + return -1; + if (p2<=p) + return p0; + p = p2; + + return p; +} + +/** + * page + * : PAGE_SYM S* pseudo_page? S* + * LBRACE S* declaration [ ';' S* declaration ]* '}' S* + * ; + */ +int CssParser::getPage(int p0) +{ + int p = p0; + + //# @PAGE + p = skipwhite(p); + if (!match(p, "@page")) + return p0; + p+= 5; + + //#PSEUDO PAGE 0 or 1 + p = skipwhite(p); + int p2 = getPseudoPage(p); + if (p2<0) + return -1; + if (p2>p) + { + p = p2; + } + + //# { + p=skipwhite(p); + XMLCh ch = get(p); + if (p != '{') + { + error("@page requires '{' before declarations"); + } + p++; + + //# DECLARATION LIST + p = skipwhite(p); + CSSStyleDeclaration declarationList; + p2 = getDeclaration(p, declarationList); + if (p2<0) + return -1; + if (p2<=p) + { + error("@page requires declaration(s) after '{'"); + return -1; + } + while (true) + { + p = skipwhite(p2); + ch = get(p); + if (ch != ';') + break; + p++; + p = skipwhite(p); + p2 = getDeclaration(p, declarationList); + if (p2<0) + return -1; + if (p2<= p) + { + error("@page requires declaration after ';'"); + return -1; + } + } + + //# } + p=skipwhite(p); + ch = get(p); + if (p != '}') + { + error("@page requires '}' after declarations"); + } + p++; + + return p; +} + +/** + * pseudo_page + * : ':' IDENT + * ; + */ +int CssParser::getPseudoPage(int p0) +{ + int p = p0; + if (!match(p, ":")) + return p0; + p++; + DOMString str; + int p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("pseudo-page requires identifier after ':'"); + return -1; + } + p = p2; + return p; +} + +/** + * ruleset + * : selector [ COMMA S* selector ]* + * LBRACE S* declaration [ ';' S* declaration ]* '}' S* + * ; + */ +int CssParser::getRuleSet(int p0) +{ + int p = p0; + XMLCh ch; + + //## SELECTOR + p = skipwhite(p); + int p2 = getSelector(p); + if (p2<0) + return -1; + if (p2<=p) //no selector + { + if (get(p) != '{')//check for selector-less rule + return p0;//not me + } + p = p2; + while (true) + { + p = skipwhite(p); + ch = get(p); + if (ch != ',') + break; + p++; + p = skipwhite(p); + int p2 = getSelector(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("selector required after ',' in list"); + return -1; + } + p = p2; + } + + //## { + ch = get(p); + if (ch != '{') + { + error("'{' required before declarations of ruleset"); + return -1; + } + p++; + + //## DECLARATIONS ( 0 to many ) + CSSStyleDeclaration declarationList; + + p = skipwhite(p); + p2 = getDeclaration(p, declarationList); + if (p2<0) + return -1; + if (p2>p) + { + p = p2; + while (true) + { + p = skipwhite(p); + ch = get(p); + if (ch != ';') + break; + p++; + p = skipwhite(p); + p2 = getDeclaration(p, declarationList); + if (p2<0) + return -1; + if (p2<=p) + { + //apparently this is ok + //error("declaration required after ';' in ruleset"); + //return -1; + break; + } + p = p2; + } + } + //## } + ch = get(p); + if (ch != '}') + { + error("ruleset requires closing '}'"); + return -1; + } + p++; + p = skipwhite(p); + + return p; +} + +/** + * selector + * : simple_selector [ combinator simple_selector ]* + * ; + */ +int CssParser::getSelector(int p0) +{ + int p = p0; + + //## SIMPLE SELECTOR + p = skipwhite(p); + int p2 = getSimpleSelector(p); + if (p2<0) + return -1; + if (p2<=p) + return p0; //not me + p = p2; + + //## COMBINATORS + MORE SELECTORS + while (true) + { + XMLCh ch = get(p); + bool wasSpace = isspace(ch); + p = skipwhite(p); + ch = get(p); + //# Combinators + //easier to do here than have a getCombinator() + int visibleCombinator = false; + if (ch == '+') + { + visibleCombinator = true; + p++; + } + else if (ch == '>') + { + visibleCombinator = true; + p++; + } + else if (wasSpace) + { + } + else + { + break; + } + p = skipwhite(p); + p2 = getSimpleSelector(p); + if (p2<0) + return -1; + if (p2<=p) + { + if (visibleCombinator) + { + error("need simple selector after combinator"); + return -1; + } + else + { + break; + } + } + p = p2; + } + return p; +} + +/** + * simple_selector + * : element_name [ HASH | class | attrib | pseudo ]* + * | [ HASH | class | attrib | pseudo ]+ + * ; + */ +int CssParser::getSimpleSelector(int p0) +{ + int p = p0; + int p2; + + DOMString str; + + p = skipwhite(p); + + int selectorItems = 0; + + XMLCh ch = get(p); + + //###################### + //# Note: do NOT skipwhite between items. Only within the + //# pseudo function and attrib below + //###################### + + //#Element name 0 or 1 + if (isLetter(ch)) + { + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("null element name"); + return -1; + } + selectorItems++; + p = p2; + } + else if (ch == '*') + { + str = "*"; + p++; + selectorItems++; + } + + + + //## HASH, CLASS, ATTRIB, PSEUDO (0 to many with elem name, 1 to many without) + while (true) + { + XMLCh ch = get(p); + + //# HASH + if (ch == '#') + { + p++; + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("no name for hash"); + return -1; + } + p = p2; + selectorItems++; + } + + //# CLASS + else if (ch == '.') + { + p++; + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("no name for class"); + return -1; + } + p = p2; + selectorItems++; + } + + //# ATTRIB + else if (ch == '[') + { + p++; + p = skipwhite(p); + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("no name for class"); + return -1; + } + p = skipwhite(p2); + bool getRHS=false; + if (match(p, "=")) + { + p++; + getRHS=true; + } + else if (match(p, "~=")) + { + p+=2; + getRHS=true; + } + else if (match(p, "|=")) + { + p+=2; + getRHS=true; + } + if (getRHS) + { + p = skipwhite(p); + ch = get(p); + if (isLetter(ch)) + { + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("null ident on rhs of attrib"); + return -1; + } + p = p2; + } + else if (ch == '\'' || ch =='"') + { + p2 = getQuoted(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("null literal string on rhs of attrib"); + return -1; + } + p = p2; + } + }//getRHS + p = skipwhite(p); + ch = get(p); + if (ch != ']') + { + error("attrib needs closing ']'"); + //return -1; + p = skipBlock(p); + return p; + } + p++; + selectorItems++; + } + + //# PSEUDO + else if (ch == ':') + { + p++; + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("no name for pseudo"); + return -1; + } + p = p2; + selectorItems++; + ch = get(p); + if (ch == '(') + { + p++; + p = skipwhite(p); + ch = get(p); + if (isLetter(ch)) + { + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2<=p) + { + error("null function parameter in pseudo"); + return -1; + } + p = skipwhite(p2); + ch = get(p); + } + if (ch != ')') + { + error("function in pseudo needs ')'"); + return -1; + } + p++; + }// ch==( -function- + }//pseudo + + //# none of the above + else + { + break; + } + + }//while + + + if (selectorItems > 0) + return p; + return p0; +} + +/** + * declaration + * : property ':' S* expr prio? + * | {empty} + * ; + */ +int CssParser::getDeclaration(int p0, CSSStyleDeclaration &declarationList) +{ + int p = p0; + + //## PROPERTY + p = skipwhite(p); + XMLCh ch = get(p); + if (!isLetter(ch)) + return p0; //not me + DOMString propName; + int p2 = getWord(p, propName); + if (p2<0) + return -1; + + //## ':' + p = skipwhite(p2); + ch = get(p); + if (ch != ':') + { + error("declaration requires ':' between name and value"); + return -1; + } + p++; + + //## EXPR + p = skipwhite(p); + p2 = getExpr(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("declaration requires value after ':'"); + return -1; + } + DOMString propVal; + for (int i=p ; ip) + { + //do something + p = p2; + } + + return p; +} + +/** + * prio + * : IMPORTANT_SYM S* + * ; + */ +int CssParser::getPrio(int p0, DOMString &val) +{ + int p = p0; + + //## '!" + p = skipwhite(p); + XMLCh ch = get(p); + if (ch != '!') + return p0; + p++; + + //## "important" + p = skipwhite(p); + if (!match(p, "important")) + { + error("priority symbol is 'important'"); + return -1; + } + p += 9; + val = "important"; + return p; +} + +/** + * expr + * : term [ operator term ]* + * ; + */ +int CssParser::getExpr(int p0) +{ + int p = p0; + + //## TERM + p = skipwhite(p); + int p2 = getTerm(p); + if (p2<0) + return -1; + if (p2<=p) + return p0; //not me + p = p2; + while (p < parselen) + { + p = skipwhite(p); + //#Operator. do this instead of getOperator() + XMLCh ch = get(p); + int visibleTerm = false; + if (ch == '/') + { + visibleTerm = true; + p++; + } + else if (ch == ',') + { + visibleTerm = true; + p++; + } + else + { + //just space. this is allowable between terms, + // so we still need to check for another term + } + p = skipwhite(p); + p2 = getTerm(p); + if (p2<0) + return -1; + if (p2<=p) + { + if (visibleTerm) + { + error("expression requires term after operator"); + return -1; + } + else + { + break; + } + } + p = p2; + } + + return p; +} + +/** + * term + * : unary_operator? + * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | + * TIME S* | FREQ S* | function ] + * | STRING S* | IDENT S* | URI S* | hexcolor + * ; + */ +int CssParser::getTerm(int p0) +{ + int p = p0; + p = skipwhite(p); + int unitType = CSSPrimitiveValue::CSS_UNKNOWN; + //# Unary operator + XMLCh ch = get(p); + bool hasUnary = false; + if (ch == '-') + { + p++; + hasUnary = true; + } + else if (ch == '+') + { + p++; + hasUnary = true; + } + //# NUMERIC + double numVal; + int p2 = getNumber(p, numVal); + if (p2<0) + return -1; + if (p2>p) + { + p = p2; + if (match(p, "%")) + { + unitType = CSSPrimitiveValue::CSS_PERCENTAGE; + p++; + } + else if (match(p, "em")) + { + unitType = CSSPrimitiveValue::CSS_EMS; + p+=2; + } + else if (match(p, "ex")) + { + unitType = CSSPrimitiveValue::CSS_EXS; + p+=2; + } + else if (match(p, "px")) + { + unitType = CSSPrimitiveValue::CSS_PX; + p+=2; + } + else if (match(p, "cm")) + { + unitType = CSSPrimitiveValue::CSS_CM; + p+=2; + } + else if (match(p, "mm")) + { + unitType = CSSPrimitiveValue::CSS_MM; + p+=2; + } + else if (match(p, "in")) + { + unitType = CSSPrimitiveValue::CSS_IN; + p+=2; + } + else if (match(p, "pt")) + { + unitType = CSSPrimitiveValue::CSS_PT; + p+=2; + } + else if (match(p, "pc")) + { + unitType = CSSPrimitiveValue::CSS_PC; + p+=2; + } + else if (match(p, "deg")) + { + unitType = CSSPrimitiveValue::CSS_DEG; + p+=3; + } + else if (match(p, "rad")) + { + unitType = CSSPrimitiveValue::CSS_RAD; + p+=3; + } + else if (match(p, "grad")) + { + unitType = CSSPrimitiveValue::CSS_GRAD; + p+=4; + } + else if (match(p, "ms")) + { + unitType = CSSPrimitiveValue::CSS_MS; + p+=2; + } + else if (match(p, "s")) + { + unitType = CSSPrimitiveValue::CSS_S; + p+=1; + } + else if (match(p, "Hz")) + { + unitType = CSSPrimitiveValue::CSS_HZ; + p+=2; + } + else if (match(p, "kHz")) + { + unitType = CSSPrimitiveValue::CSS_KHZ; + p+=2; + } + else if (isLetter(get(p)))//some other string + { + DOMString suffix; + p2 = getWord(p, suffix); + if (p2<0) + return -1; + unitType = CSSPrimitiveValue::CSS_DIMENSION; + p = p2; + } + else //plain number + { + unitType = CSSPrimitiveValue::CSS_NUMBER; + } + return p; + } + + DOMString str; + + //## URI --do before function, as syntax is similar + p2 = getUri(p, str); + if (p2<0) + return -1; + if (p2>p) + { + if (hasUnary) + { + error("+ or - not allowed on URI"); + return -1; + } + p = p2; + unitType = CSSPrimitiveValue::CSS_URI; + return p; + } + + //## FUNCTION + p2 = getFunction(p); + if (p2<0) + return -1; + if (p2>p) + { + p = p2; + return p; + } + + //## STRING + ch = get(p); + if (ch == '"' || ch == '\'') + { + p2 = getQuoted(p, str); + if (p2<0) + return -1; + if (p2>p) + { + if (hasUnary) + { + error("+ or - not allowed on a string"); + return -1; + } + p = p2; + unitType = CSSPrimitiveValue::CSS_STRING; + return p; + } + } + + //## IDENT + ch = get(p); + if (isLetter(ch)) + { + p2 = getWord(p, str); + if (p2<0) + return -1; + if (p2>p) + { + if (hasUnary) + { + error("+ or - not allowed on an identifier"); + return -1; + } + p = p2; + unitType = CSSPrimitiveValue::CSS_IDENT; + return p; + } + } + + + //## HEXCOLOR + p2 = getHexColor(p); + if (p2<0) + return -1; + if (p2>p) + { + if (hasUnary) + { + error("+ or - not allowed on hex color"); + return -1; + } + p = p2; + unitType = CSSPrimitiveValue::CSS_RGBCOLOR; + return p; + } + + + return p0; +} + +/** + * function + * : FUNCTION S* expr ')' S* + * ; + */ +int CssParser::getFunction(int p0) +{ + int p = p0; + + //## IDENT + ( (both) + DOMString name; + p = skipwhite(p); + int p2 = getWord(p, name); + if (p2<0) + return -1; + if (p2<=p) + return p0; //not me + if (name == "uri" || name=="url") + return p0; //not me + p = skipwhite(p2); + XMLCh ch = get(p); + if (ch != '(') + return p0; //still not me + p++; + + //## EXPR + p = skipwhite(p); + p2 = getExpr(p); + if (p2<0) + return -1; + if (p2<=p) + { + error("function requires expression"); + return -1; + } + p = p2; + + //## ')' + p = skipwhite(p); + ch = get(p); + if (ch != ')') + { + error("function requires closing ')'"); + return -1; + } + p++; + p = skipwhite(p); + + return p; +} + +/** + * There is a constraint on the color that it must + * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) + * after the "#"; e.g., "#000" is OK, but "#abcd" is not. + * hexcolor + * : HASH S* + * ; + */ +int CssParser::getHexColor(int p0) +{ + int p = p0; + + //## '#' + p = skipwhite(p); + if (!match(p, "#")) + return p0; + p++; + + //## HEX + DOMString hex; + long hexVal = 0; + while (p < parselen) + { + XMLCh b = get(p); + if (b>='0' && b<='9') + { + hexVal = (hexVal << 4) + (b - '0'); + hex.push_back(b); + p++; + } + else if (b>='a' && b<='f') + { + hexVal = (hexVal << 4) + (b - 'a' + 10); + hex.push_back(b); + p++; + } + else if (b>='A' && b<='F') + { + hexVal = (hexVal << 4) + (b - 'A' + 10); + hex.push_back(b); + p++; + } + else + { + break; + } + } + + if (hex.size() != 3 && hex.size() != 6) + { + error("exactly 3 or 6 hex digits are required after '#'"); + return -1; + } + + return p; +} + + + +/** + * + */ +bool CssParser::parse(const DOMString &str) +{ + /* + int len = str.size(); + for (int i=0 ; i +#include + +#define OWN_STRING + +#ifdef OWN_STRING +#include "domstring.h" +#else +#include +typedef Glib::ustring DOMString +#endif + +#define XMLNSNAME "http://www.w3.org/2000/xmlns/" + +namespace org +{ +namespace w3c +{ +namespace dom +{ + + + +#ifndef OWN_STRING +typedef unsigned short XMLCh; +typedef std::string DOMString; +#endif + +/** + * + */ +typedef unsigned long long DOMTimeStamp; + +/** + * + */ +typedef void DOMUserData; + +/** + * + */ +typedef void DOMObject; + + +class DOMException; +class DOMStringList; +class NameList; +class DOMImplementationList; +class DOMImplementationSource; +class DOMImplementation; +class Node; +class NodeList; +class NamedNodeMap; +class CharacterData; +class Attr; +class Element; +class Text; +class Comment; +class TypeInfo; +class UserDataHandler; +class DOMError; +class DOMErrorHandler; +class DOMLocator; +class DOMConfiguration; +class CDATASection; +class DocumentType; +class Notation; +class Entity; +class EntityReference; +class ProcessingInstruction; +class DocumentFragment; +class Document; + + +/** + * NOTE: We were originally intending to split ALL specifications into + * interface and implementation. After consideration, though, it behooves + * us to simplify things by implementing the base exception and + * container classes directly: + * + * DOMException + * DOMStringList + * NameList + * DOMImplementationList + * DOMImplementationSource + * DOMImplementation + * NodeList + * NamedNodeMap + */ + + +/*######################################################################### +## DOMException +#########################################################################*/ +/** + * This is the only non-interface class + */ +class DOMException +{ + +public: + + DOMException(const DOMString &reasonMsg) + { msg = reasonMsg; } + + DOMException(short theCode) + { + code = theCode; + } + + virtual ~DOMException() throw() + {} + + /** + * + */ + unsigned short code; + + /** + * + */ + DOMString msg; + + /** + * Get a string, translated from the code. + * Like std::exception. Not in spec. + */ + const char *what() + { return (const char *)msg.c_str(); } + + + +}; + + + + +/** + * ExceptionCode + */ +typedef enum +{ + INDEX_SIZE_ERR = 1, + DOMSTRING_SIZE_ERR = 2, + HIERARCHY_REQUEST_ERR = 3, + WRONG_DOCUMENT_ERR = 4, + INVALID_CHARACTER_ERR = 5, + NO_DATA_ALLOWED_ERR = 6, + NO_MODIFICATION_ALLOWED_ERR = 7, + NOT_FOUND_ERR = 8, + NOT_SUPPORTED_ERR = 9, + INUSE_ATTRIBUTE_ERR = 10, + INVALID_STATE_ERR = 11, + SYNTAX_ERR = 12, + INVALID_MODIFICATION_ERR = 13, + NAMESPACE_ERR = 14, + INVALID_ACCESS_ERR = 15, + VALIDATION_ERR = 16, + TYPE_MISMATCH_ERR = 17 +} ExceptionCode; + + +/*######################################################################### +## DOMStringList +#########################################################################*/ + +class DOMStringList +{ +public: + + /** + * + */ + virtual DOMString item(unsigned long index) + { + if (index>=strings.size()) + return ""; + return strings[index]; + } + + /** + * + */ + virtual unsigned long getLength() + { + return (unsigned long) strings.size(); + } + + /** + * + */ + virtual bool contains(const DOMString &str) + { + for (unsigned int i=0; istrings; + +}; + + + +/*######################################################################### +## NameList +#########################################################################*/ +class NamePair +{ +public: + NamePair(const DOMString &theNamespaceURI, const DOMString &theName) + { + namespaceURI = theNamespaceURI; + name = theName; + } + NamePair(const NamePair &other) + { + namespaceURI = other.namespaceURI; + name = other.name; + } + virtual ~NamePair() {} + + DOMString namespaceURI; + DOMString name; +}; + + + +class NameList +{ +public: + + /** + * + */ + virtual DOMString getName(unsigned long index) + { + if (index>=namePairs.size()) + return ""; + return namePairs[index].name; + } + + /** + * + */ + virtual DOMString getNamespaceURI(unsigned long index) + { + if (index>=namePairs.size()) + return ""; + return namePairs[index].namespaceURI; + } + + /** + * + */ + virtual unsigned long getLength() + { + return (unsigned long)namePairs.size(); + } + + /** + * + */ + virtual bool contains(const DOMString &name) + { + for (unsigned int i=0; i namePairs; + +}; + +/*######################################################################### +## DOMImplementationList +#########################################################################*/ + +class DOMImplementationList +{ +public: + + /** + * + */ + virtual DOMImplementation *getDOMImplementation(unsigned long index) + { + if (index >implementations.size()) + return NULL; + return implementations[index]; + } + + /** + * + */ + virtual unsigned long getLength() + { + return (unsigned long) implementations.size(); + } + + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + DOMImplementationList() {} + + + /** + * + */ + DOMImplementationList(const DOMImplementationList &other) + { + implementations = other.implementations; + } + + /** + * + */ + virtual ~DOMImplementationList() {} + +protected: + + std::vectorimplementations; + +}; + + +/*######################################################################### +## DOMImplementationSource +#########################################################################*/ + +class DOMImplementationSource +{ +public: + + /** + * + */ + virtual DOMImplementation *getDOMImplementation(const DOMString &features) = 0; + + /** + * + */ + virtual DOMImplementationList getDOMImplementationList(const DOMString &features) = 0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DOMImplementationSource() {} + +}; + + + + + +/*######################################################################### +## DOMImplementation +#########################################################################*/ +/** + * + */ +class DOMImplementation +{ +public: + /** + * + */ + virtual bool hasFeature(const DOMString& feature, const DOMString& version) = 0; + + + /** + * + */ + virtual DocumentType *createDocumentType(const DOMString& qualifiedName, + const DOMString& publicId, + const DOMString& systemId) + throw(DOMException) = 0; + + /** + * + */ + virtual Document *createDocument(const DOMString& namespaceURI, + const DOMString& qualifiedName, + DocumentType *doctype) + throw(DOMException) = 0; + /** + * + */ + virtual DOMObject *getFeature(const DOMString& feature, + const DOMString& version) = 0; + + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DOMImplementation() {} + +}; + + + + +/*######################################################################### +## Node +#########################################################################*/ + +/** + * + */ +class Node +{ +public: + + typedef enum + { + ELEMENT_NODE = 1, + ATTRIBUTE_NODE = 2, + TEXT_NODE = 3, + CDATA_SECTION_NODE = 4, + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6, + PROCESSING_INSTRUCTION_NODE = 7, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, + NOTATION_NODE = 12 + } NodeType; + + /** + * + */ + virtual DOMString getNodeName() = 0; + + /** + * + */ + virtual DOMString getNodeValue() throw (DOMException) = 0; + + /** + * + */ + virtual void setNodeValue(const DOMString& val) throw (DOMException) = 0; + + /** + * + */ + virtual unsigned short getNodeType() = 0; + + /** + * + */ + virtual Node *getParentNode() = 0; + + /** + * + */ + virtual NodeList getChildNodes() = 0; + + /** + * + */ + virtual Node *getFirstChild() = 0; + + /** + * + */ + virtual Node *getLastChild() = 0; + + /** + * + */ + virtual Node *getPreviousSibling() = 0; + + /** + * + */ + virtual Node *getNextSibling() = 0; + + /** + * + */ + virtual NamedNodeMap &getAttributes() = 0; + + + /** + * + */ + virtual Document *getOwnerDocument() = 0; + + /** + * + */ + virtual Node *insertBefore(const Node *newChild, + const Node *refChild) + throw(DOMException) = 0; + + /** + * + */ + virtual Node *replaceChild(const Node *newChild, + const Node *oldChild) + throw(DOMException) = 0; + + /** + * + */ + virtual Node *removeChild(const Node *oldChild) + throw(DOMException) = 0; + + /** + * + */ + virtual Node *appendChild(const Node *newChild) + throw(DOMException) = 0; + + /** + * + */ + virtual bool hasChildNodes() = 0; + + /** + * + */ + virtual Node *cloneNode(bool deep) = 0; + + /** + * + */ + virtual void normalize() = 0; + + /** + * + */ + virtual bool isSupported(const DOMString& feature, + const DOMString& version) = 0; + + /** + * + */ + virtual DOMString getNamespaceURI() = 0; + + /** + * + */ + virtual DOMString getPrefix() = 0; + + /** + * + */ + virtual void setPrefix(const DOMString& val) throw(DOMException) = 0; + + /** + * + */ + virtual DOMString getLocalName() = 0; + + /** + * + */ + virtual bool hasAttributes() = 0; + + /** + * + */ + virtual DOMString getBaseURI() = 0; + + typedef enum + { + DOCUMENT_POSITION_DISCONNECTED = 0x01, + DOCUMENT_POSITION_PRECEDING = 0x02, + DOCUMENT_POSITION_FOLLOWING = 0x04, + DOCUMENT_POSITION_CONTAINS = 0x08, + DOCUMENT_POSITION_CONTAINED_BY = 0x10, + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20 + } DocumentPosition; + + + /** + * + */ + virtual unsigned short compareDocumentPosition(const Node *other) = 0; + + /** + * + */ + virtual DOMString getTextContext() throw(DOMException) = 0; + + + /** + * + */ + virtual void setTextContext(const DOMString &val) throw(DOMException) = 0; + + + /** + * + */ + virtual DOMString lookupPrefix(const DOMString &namespaceURI) =0; + + + /** + * + */ + virtual bool isDefaultNamespace(const DOMString &namespaceURI) =0; + + + /** + * + */ + virtual DOMString lookupNamespaceURI(const DOMString &prefix) =0; + + + /** + * + */ + virtual bool isEqualNode(const Node *node) =0; + + + + /** + * + */ + virtual DOMObject *getFeature(const DOMString &feature, + const DOMString &version) =0; + + /** + * + */ + virtual DOMUserData *setUserData(const DOMString &key, + const DOMUserData *data, + const UserDataHandler *handler) =0; + + + /** + * + */ + virtual DOMUserData *getUserData(const DOMString &namespaceURI) =0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Node() {} + + + + +}; + + + +/*######################################################################### +## NodeList +#########################################################################*/ + +/** + * + */ +class NodeList +{ +public: + /** + * + */ + virtual Node *item(unsigned long index) + { + if (index>=nodes.size()) + return NULL; + return nodes[index]; + } + + /** + * + */ + virtual unsigned long getLength() + { + return (unsigned long) nodes.size(); + } + + //################## + //# Non-API methods + //################## + + + /** + * + */ + NodeList() {} + + /** + * + */ + NodeList(const NodeList &other) + { + nodes = other.nodes; + } + + /** + * + */ + virtual ~NodeList() {} + +protected: + +friend class NodeImpl; +friend class ElementImpl; + + /* + * + */ + virtual void add(const Node *node) + { + nodes.push_back((Node *)node); + } + +protected: + + std::vector nodes; + +}; + + + + +/*######################################################################### +## NamedNodeMap +#########################################################################*/ + +class NamedNodeMapEntry +{ +public: + NamedNodeMapEntry(const DOMString &theNamespaceURI, + const DOMString &theName, + const Node *theNode) + { + namespaceURI = theNamespaceURI; + name = theName; + node = (Node *)theNode; + } + NamedNodeMapEntry(const NamedNodeMapEntry &other) + { + namespaceURI = other.namespaceURI; + name = other.name; + node = other.node; + } + virtual ~NamedNodeMapEntry() + { + } + DOMString namespaceURI; + DOMString name; + Node *node; +}; + +/** + * + */ +class NamedNodeMap +{ +public: + + /** + * + */ + virtual Node *getNamedItem(const DOMString& name) + { + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->name == name) + { + Node *node = iter->node; + return node; + } + } + return NULL; + } + + /** + * + */ + virtual Node *setNamedItem(Node *arg) throw(DOMException) + { + if (!arg) + return NULL; + DOMString namespaceURI = arg->getNamespaceURI(); + DOMString name = arg->getNodeName(); + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->name == name) + { + Node *node = iter->node; + iter->node = arg; + return node; + } + } + NamedNodeMapEntry entry(namespaceURI, name, arg); + entries.push_back(entry); + return arg; + } + + + /** + * + */ + virtual Node *removeNamedItem(const DOMString& name) throw(DOMException) + { + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->name == name) + { + Node *node = iter->node; + entries.erase(iter); + return node; + } + } + return NULL; + } + + /** + * + */ + virtual Node *item(unsigned long index) + { + if (index>=entries.size()) + return NULL; + return entries[index].node; + } + + /** + * + */ + virtual unsigned long getLength() + { + return (unsigned long)entries.size(); + } + + /** + * + */ + virtual Node *getNamedItemNS(const DOMString& namespaceURI, + const DOMString& localName) + { + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->namespaceURI == namespaceURI && iter->name == localName) + { + Node *node = iter->node; + return node; + } + } + return NULL; + } + + /** + * + */ + virtual Node *setNamedItemNS(Node *arg) throw(DOMException) + { + if (!arg) + return NULL; + DOMString namespaceURI = arg->getNamespaceURI(); + DOMString name = arg->getNodeName(); + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->namespaceURI == namespaceURI && iter->name == name) + { + Node *node = iter->node; + iter->node = arg; + return node; + } + } + NamedNodeMapEntry entry(namespaceURI, name, arg); + entries.push_back(entry); + return arg; + } + + /** + * + */ + virtual Node *removeNamedItemNS(const DOMString& namespaceURI, + const DOMString& localName) + throw(DOMException) + { + std::vector::iterator iter; + for (iter = entries.begin() ; iter!=entries.end() ; iter++) + { + if (iter->namespaceURI == namespaceURI && iter->name == localName) + { + Node *node = iter->node; + entries.erase(iter); + return node; + } + } + return NULL; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + NamedNodeMap() {} + + + /** + * + */ + NamedNodeMap(const NamedNodeMap &other) + { + entries = other.entries; + } + + + /** + * + */ + virtual ~NamedNodeMap() {} + +protected: + + std::vector entries; + +}; + + + + +/*######################################################################### +## CharacterData +#########################################################################*/ + +/** + * + */ +class CharacterData : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getData() throw(DOMException) = 0; + + /** + * + */ + virtual void setData(const DOMString& val) throw(DOMException) = 0; + + /** + * + */ + virtual unsigned long getLength() = 0; + + /** + * + */ + virtual DOMString substringData(unsigned long offset, + unsigned long count) + throw(DOMException) = 0; + + /** + * + */ + virtual void appendData(const DOMString& arg) throw(DOMException) = 0; + + /** + * + */ + virtual void insertData(unsigned long offset, + const DOMString& arg) + throw(DOMException) = 0; + + /** + * + */ + virtual void deleteData(unsigned long offset, + unsigned long count) + throw(DOMException) = 0; + + /** + * + */ + virtual void replaceData(unsigned long offset, + unsigned long count, + const DOMString& arg) + throw(DOMException) = 0; + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~CharacterData() {} + +}; + + + + + +/*######################################################################### +## Attr +#########################################################################*/ + +/** + * + */ +class Attr : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getName() = 0; + + /** + * + */ + virtual bool getSpecified() = 0; + + /** + * + */ + virtual DOMString getValue() = 0; + + /** + * + */ + virtual void setValue(const DOMString& val) throw(DOMException) = 0; + + /** + * + */ + virtual Element *getOwnerElement() = 0; + + + /** + * + */ + virtual TypeInfo *getSchemaTypeInfo() = 0; + + + /** + * + */ + virtual bool getIsId() = 0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Attr() {} + +}; + + + + + +/*######################################################################### +## Element +#########################################################################*/ + +/** + * + */ +class Element : virtual public Node +{ +public: + + + /** + * + */ + virtual DOMString getTagName() = 0; + + /** + * + */ + virtual DOMString getAttribute(const DOMString& name) = 0; + + /** + * + */ + virtual void setAttribute(const DOMString& name, + const DOMString& value) + throw(DOMException) = 0; + + /** + * + */ + virtual void removeAttribute(const DOMString& name) + throw(DOMException) = 0; + + /** + * + */ + virtual Attr *getAttributeNode(const DOMString& name) = 0; + + /** + * + */ + virtual Attr *setAttributeNode(Attr *newAttr) + throw(DOMException) = 0; + + /** + * + */ + virtual Attr *removeAttributeNode(Attr *oldAttr) + throw(DOMException) = 0; + + /** + * + */ + virtual NodeList getElementsByTagName(const DOMString& name) = 0; + + /** + * + */ + virtual DOMString getAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) = 0; + + /** + * + */ + virtual void setAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName, + const DOMString& value) + throw(DOMException) = 0; + + /** + * + */ + virtual void removeAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) + throw(DOMException) = 0; + + /** + * + */ + virtual Attr *getAttributeNodeNS(const DOMString& namespaceURI, + const DOMString& localName) = 0; + + /** + * + */ + virtual Attr *setAttributeNodeNS(Attr *newAttr) + throw(DOMException) = 0; + + /** + * + */ + virtual NodeList getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName) = 0; + + /** + * + */ + virtual bool hasAttribute(const DOMString& name) = 0; + + /** + * + */ + virtual bool hasAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) = 0; + + /** + * + */ + virtual TypeInfo *getSchemaTypeInto() = 0; + + + /** + * + */ + virtual void setIdAttribute(const DOMString &name, + bool isId) throw (DOMException) = 0; + + /** + * + */ + virtual void setIdAttributeNS(const DOMString &namespaceURI, + const DOMString &localName, + bool isId) throw (DOMException) = 0; + + /** + * + */ + virtual void setIdAttributeNode(const Attr *idAttr, + bool isId) throw (DOMException) = 0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~Element() {} + +}; + + + + + +/*######################################################################### +## Text +#########################################################################*/ + +/** + * + */ +class Text : virtual public CharacterData +{ +public: + + /** + * + */ + virtual Text *splitText(unsigned long offset) + throw(DOMException) = 0; + + /** + * + */ + virtual bool getIsElementContentWhitespace()= 0; + + /** + * + */ + virtual DOMString getWholeText() = 0; + + + /** + * + */ + virtual Text *replaceWholeText(const DOMString &content) + throw(DOMException) = 0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Text() {} + +}; + + + +/*######################################################################### +## Comment +#########################################################################*/ + +/** + * + */ +class Comment : virtual public CharacterData +{ +public: + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Comment() {} + + +}; + + + +/*######################################################################### +## TypeInfo +#########################################################################*/ + +/** + * + */ +class TypeInfo +{ +public: + + /** + * + */ + virtual DOMString getTypeName() =0; + + /** + * + */ + virtual DOMString getTypeNamespace() =0; + + typedef enum + { + DERIVATION_RESTRICTION = 0x00000001, + DERIVATION_EXTENSION = 0x00000002, + DERIVATION_UNION = 0x00000004, + DERIVATION_LIST = 0x00000008 + } DerivationMethod; + + + /** + * + */ + virtual bool isDerivedFrom(const DOMString &typeNamespaceArg, + const DOMString &typeNameArg, + DerivationMethod derivationMethod) =0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~TypeInfo() {} + +}; + + + + +/*######################################################################### +## UserDataHandler +#########################################################################*/ + +/** + * + */ +class UserDataHandler +{ +public: + + typedef enum + { + NODE_CLONED = 1, + NODE_IMPORTED = 2, + NODE_DELETED = 3, + NODE_RENAMED = 4, + NODE_ADOPTED = 5 + } OperationType; + + + /** + * + */ + virtual void handle(unsigned short operation, + const DOMString &key, + const DOMUserData *data, + const Node *src, + const Node *dst) =0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~UserDataHandler() {} + +}; + + +/*######################################################################### +## DOMError +#########################################################################*/ + +/** + * + */ +class DOMError +{ +public: + + typedef enum + { + SEVERITY_WARNING = 1, + SEVERITY_ERROR = 2, + SEVERITY_FATAL_ERROR = 3 + } ErrorSeverity; + + + /** + * + */ + virtual unsigned short getSeverity() =0; + + /** + * + */ + virtual DOMString getMessage() =0; + + /** + * + */ + virtual DOMString getType() =0; + + /** + * + */ + virtual DOMObject *getRelatedException() =0; + + /** + * + */ + virtual DOMObject *getRelatedData() =0; + + /** + * + */ + virtual DOMLocator *getLocation() =0; + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~DOMError() {} + +}; + + +/*######################################################################### +## DOMErrorHandler +#########################################################################*/ + +/** + * + */ +class DOMErrorHandler +{ +public: + /** + * + */ + virtual bool handleError(const DOMError *error) =0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~DOMErrorHandler() {} + +}; + + + +/*######################################################################### +## DOMLocator +#########################################################################*/ + +/** + * + */ +class DOMLocator +{ +public: + + /** + * + */ + virtual long getLineNumber() =0; + + /** + * + */ + virtual long getColumnNumber() =0; + + /** + * + */ + virtual long getByteOffset() =0; + + /** + * + */ + virtual long getUtf16Offset() =0; + + + /** + * + */ + virtual Node *getRelatedNode() =0; + + + /** + * + */ + virtual DOMString getUri() =0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DOMLocator() {} +}; + + +/*######################################################################### +## DOMConfiguration +#########################################################################*/ + +/** + * + */ +class DOMConfiguration +{ +public: + + /** + * + */ + virtual void setParameter(const DOMString &name, + const DOMUserData *value) throw (DOMException) =0; + + /** + * + */ + virtual DOMUserData *getParameter(const DOMString &name) + throw (DOMException) =0; + + /** + * + */ + virtual bool canSetParameter(const DOMString &name, + const DOMUserData *data) =0; + + /** + * + */ + virtual DOMStringList *getParameterNames() =0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~DOMConfiguration() {} + +}; + + + + + + +/*######################################################################### +## CDATASection +#########################################################################*/ +/** + * + */ +class CDATASection : virtual public Text +{ +public: + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~CDATASection() {} + +}; + + + + +/*######################################################################### +## DocumentType +#########################################################################*/ + +/** + * + */ +class DocumentType : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getName() = 0; + + /** + * + */ + virtual NamedNodeMap getEntities() = 0; + + /** + * + */ + virtual NamedNodeMap getNotations() = 0; + + /** + * + */ + virtual DOMString getPublicId() = 0; + + /** + * + */ + virtual DOMString getSystemId() = 0; + + /** + * + */ + virtual DOMString getInternalSubset() = 0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DocumentType() {} + +}; + + + + + +/*######################################################################### +## Notation +#########################################################################*/ + +/** + * + */ +class Notation : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getPublicId() = 0; + + /** + * + */ + virtual DOMString getSystemId() = 0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Notation() {} +}; + + + + + + +/*######################################################################### +## Entity +#########################################################################*/ + +/** + * + */ +class Entity : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getPublicId() = 0; + + /** + * + */ + virtual DOMString getSystemId() = 0; + + /** + * + */ + virtual DOMString getNotationName() = 0; + + /** + * + */ + virtual DOMString getInputEncoding() = 0; + + /** + * + */ + virtual DOMString getXmlEncoding() = 0; + + /** + * + */ + virtual DOMString getXmlVersion() = 0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~Entity() {} +}; + + + + + +/*######################################################################### +## EntityReference +#########################################################################*/ +/** + * + */ +class EntityReference : virtual public Node +{ +public: + + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~EntityReference() {} +}; + + + + + +/*######################################################################### +## ProcessingInstruction +#########################################################################*/ + +/** + * + */ +class ProcessingInstruction : virtual public Node +{ +public: + + /** + * + */ + virtual DOMString getTarget() = 0; + + /** + * + */ + virtual DOMString getData() = 0; + + /** + * + */ + virtual void setData(const DOMString& val) throw(DOMException) = 0; + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~ProcessingInstruction() {} + +}; + + + + + +/*######################################################################### +## DocumentFragment +#########################################################################*/ +/** + * + */ +class DocumentFragment : virtual public Node +{ +public: + + //################## + //# Non-API methods + //################## + + + /** + * + */ + virtual ~DocumentFragment() {} +}; + + + + + + +/*######################################################################### +## Document +#########################################################################*/ + +/** + * + */ +class Document : virtual public Node +{ +public: + + /** + * + */ + virtual DocumentType *getDoctype() = 0; + + /** + * + */ + virtual DOMImplementation *getImplementation() = 0; + + /** + * + */ + virtual Element *getDocumentElement() = 0; + + /** + * + */ + virtual Element *createElement(const DOMString& tagName) + throw(DOMException) = 0; + + /** + * + */ + virtual DocumentFragment *createDocumentFragment() = 0; + + /** + * + */ + virtual Text *createTextNode(const DOMString& data) = 0; + + /** + * + */ + virtual Comment *createComment(const DOMString& data) = 0; + + /** + * + */ + virtual CDATASection *createCDATASection(const DOMString& data) + throw(DOMException) = 0; + + /** + * + */ + virtual ProcessingInstruction *createProcessingInstruction(const DOMString& target, + const DOMString& data) + throw(DOMException) = 0; + + /** + * + */ + virtual Attr *createAttribute(const DOMString& name) + throw(DOMException) = 0; + + /** + * + */ + virtual EntityReference *createEntityReference(const DOMString& name) + throw(DOMException) = 0; + + /** + * + */ + virtual NodeList getElementsByTagName(const DOMString& tagname) = 0; + + + /** + * + */ + virtual Node *importNode(const Node *importedNode, + bool deep) + throw(DOMException) = 0; + + /** + * + */ + virtual Element *createElementNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException) = 0; + + /** + * + */ + virtual Attr *createAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException) = 0; + + /** + * + */ + virtual NodeList getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName) = 0; + + /** + * + */ + virtual Element *getElementById(const DOMString& elementId) = 0; + + + /** + * + */ + virtual DOMString getInputEncoding() = 0; + + + /** + * + */ + virtual DOMString getXmlEncoding() = 0; + + /** + * + */ + virtual bool getXmlStandalone() = 0; + + /** + * + */ + virtual void setXmlStandalone(bool val) throw (DOMException) = 0; + + /** + * + */ + virtual DOMString getXmlVersion() = 0; + + /** + * + */ + virtual void setXmlVersion(const DOMString &version) throw (DOMException) = 0; + + /** + * + */ + virtual bool getStrictErrorChecking() = 0; + + /** + * + */ + virtual void setStrictErrorChecking(bool val) = 0; + + + /** + * + */ + virtual DOMString getDocumentURI() = 0; + + /** + * + */ + virtual void setDocumentURI(const DOMString &uri) = 0; + + /** + * + */ + virtual Node *adoptNode(const Node *source) throw (DOMException) = 0; + + /** + * + */ + virtual DOMConfiguration *getDomConfig() = 0; + + /** + * + */ + virtual void normalizeDocument() = 0; + + /** + * + */ + virtual Node *renameNode(const Node *n, + const DOMString &namespaceURI, + const DOMString &qualifiedName) + throw (DOMException) = 0; + + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~Document() {} + +}; + + + + + + + + + + + +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif // __DOM_H__ + + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + + + + diff --git a/src/dom/domimpl.cpp b/src/dom/domimpl.cpp new file mode 100644 index 000000000..b3197c948 --- /dev/null +++ b/src/dom/domimpl.cpp @@ -0,0 +1,2984 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + +#include "domimpl.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ + + +/** + * Test if the given substring exists for the length of the string + * in a given buffer + */ +/* +static bool match(const DOMString &buf, char *str) +{ + int pos = 0; + while (*str) + { + if (buf[pos++] != *str++) + return false; + } + return true; +} +*/ + + + +/*######################################################################### +## DOMImplementationSourceImpl +#########################################################################*/ + + +/** + * + */ +DOMImplementationSourceImpl::DOMImplementationSourceImpl() +{ + domImpl = new DOMImplementationImpl(); +} + +/** + * + */ +DOMImplementationSourceImpl::~DOMImplementationSourceImpl() +{ + delete domImpl; +} + +/** + * + */ +DOMImplementation *DOMImplementationSourceImpl::getDOMImplementation( + const DOMString &features) +{ + return domImpl; +} + +/** + * + */ +DOMImplementationList DOMImplementationSourceImpl::getDOMImplementationList( + const DOMString &features) +{ + return domImplList; +} + + + + + + + +/*######################################################################### +## DOMImplementationImpl +#########################################################################*/ + + +/** + * + */ +DOMImplementationImpl::DOMImplementationImpl() +{ +} + +/** + * + */ +DOMImplementationImpl::~DOMImplementationImpl() +{ +} + +/** + * + */ +bool DOMImplementationImpl::hasFeature(const DOMString& feature, + const DOMString& version) +{ + return false; +} + + +/** + * + */ +DocumentType *DOMImplementationImpl::createDocumentType(const DOMString& qualifiedName, + const DOMString& publicId, + const DOMString& systemId) + throw(DOMException) +{ + DocumentTypeImpl *typeImpl = new DocumentTypeImpl(qualifiedName, + publicId, systemId); + return typeImpl; +} + +/** + * + */ +Document *DOMImplementationImpl::createDocument( + const DOMString& namespaceURI, + const DOMString& qualifiedName, + DocumentType *doctype) + throw(DOMException) +{ + DocumentImpl *doc = new DocumentImpl(this, + namespaceURI, + qualifiedName, + doctype); + return doc; +} + +/** + * + */ +DOMObject *DOMImplementationImpl::getFeature(const DOMString& feature, + const DOMString& version) + +{ + return NULL; +} + + + + + +/*######################################################################### +## NodeImpl +#########################################################################*/ + +/** + * Utility for finding the first Element above + * a given node. Used by several methods below + */ +static Node *getAncestorElement(Node *node) +{ + if (!node) + return NULL; + node = node->getParentNode(); + //Either quit because I am an element, or because I am null + while (node) + { + if (node->getNodeType() == Node::ELEMENT_NODE) + return node; + node = node->getParentNode(); + } + return node; +} + +/** + * + */ +DOMString NodeImpl::getNodeName() +{ + return nodeName; +} + +/** + * + */ +DOMString NodeImpl::getNodeValue() throw (DOMException) +{ + return nodeValue; +} + +/** + * + */ +void NodeImpl::setNodeValue(const DOMString& val) throw (DOMException) +{ + nodeValue = val; +} + +/** + * + */ +unsigned short NodeImpl::getNodeType() +{ + return nodeType; +} + +/** + * + */ +Node *NodeImpl::getParentNode() +{ + return parent; +} + +/** + * + */ +NodeList NodeImpl::getChildNodes() +{ + NodeList list; + for (NodeImpl *node = firstChild ; node ; node=node->next) + list.add(node); + return list; +} + +/** + * + */ +Node *NodeImpl::getFirstChild() +{ + return firstChild; +} + +/** + * + */ +Node *NodeImpl::getLastChild() +{ + return lastChild; +} + +/** + * + */ +Node *NodeImpl::getPreviousSibling() +{ + return prev; +} + +/** + * + */ +Node *NodeImpl::getNextSibling() +{ + return next; +} + +/** + * + */ +NamedNodeMap &NodeImpl::getAttributes() +{ + NamedNodeMap &attrs = attributes; + return attrs; +} + + +/** + * + */ +Document *NodeImpl::getOwnerDocument() +{ + return ownerDocument; +} + +/** + * + */ +Node *NodeImpl::insertBefore(const Node *newChild, + const Node *refChild) + throw(DOMException) +{ + Node *node = NULL; + return node; +} + +/** + * + */ +Node *NodeImpl::replaceChild(const Node *newChild, + const Node *oldChild) + throw(DOMException) +{ + Node *node = NULL; + return node; +} + +/** + * + */ +Node *NodeImpl::removeChild(const Node *oldChild) + throw(DOMException) +{ + Node *node = NULL; + return node; +} + +/** + * + */ +Node *NodeImpl::appendChild(const Node *newChild) + throw(DOMException) +{ + if (!newChild) + return NULL; + + Node *child = (Node *)newChild; + NodeImpl *childImpl = dynamic_cast (child); + + childImpl->parent = this; + childImpl->ownerDocument = ownerDocument; + + if (!firstChild || !lastChild) + { + //Set up our first member + firstChild = childImpl; + lastChild = childImpl; + } + else + { + //link at the last position + lastChild->next = childImpl; + childImpl->prev = lastChild; + lastChild = childImpl; + } + + return child; +} + +/** + * + */ +bool NodeImpl::hasChildNodes() +{ + return (firstChild != NULL); +} + +/** + * + */ +Node *NodeImpl::cloneNode(bool deep) +{ + NodeImpl *node = new NodeImpl(ownerDocument, nodeName); + node->parent = parent; + node->prev = prev; + node->next = next; + node->userData = userData; + node->nodeValue = nodeValue; + + if (deep) + { + node->firstChild = node->lastChild = NULL; + for (NodeImpl *child = firstChild ; child ; child=child->next) + { + node->appendChild(child->cloneNode(deep)); + } + } + else + { + node->firstChild = firstChild; + node->lastChild = lastChild; + } + + return node; +} + +/** + * Concatenate adjoining text subnodes, remove null-length nodes + */ +void NodeImpl::normalize() +{ + //First, concatenate adjoining text nodes + NodeImpl *next = NULL; + for (NodeImpl *child = firstChild ; child ; child=next) + { + if (child->getNodeType() != Node::TEXT_NODE) + continue; + next = NULL; + DOMString sval = child->getNodeValue(); + for (NodeImpl *sibling = child->next ; sibling ; sibling=next) + { + next = sibling->next; + if (sibling->getNodeType() != Node::TEXT_NODE) + break; + sval.append(sibling->getNodeValue()); + //unlink and delete + child->next = sibling->next; + if (sibling->next) + sibling->next->prev = child; + delete sibling; + } + child->setNodeValue(sval); + } + + //Next, we remove zero-length text subnodes + next = NULL; + for (NodeImpl *child = firstChild ; child ; child=next) + { + next = child->next; + if (child->getNodeType() != Node::TEXT_NODE) + continue; + if (child->getNodeValue().size() == 0) + { + //unlink and delete + if (child->prev) + child->prev->next = child->next; + if (child->next) + child->next->prev = child->prev; + delete child; + } + } + +} + +/** + * + */ +bool NodeImpl::isSupported(const DOMString& feature, + const DOMString& version) +{ + //again, no idea + return false; +} + +/** + * + */ +DOMString NodeImpl::getNamespaceURI() +{ + return namespaceURI; +} + +/** + * + */ +DOMString NodeImpl::getPrefix() +{ + return prefix; +} + +/** + * + */ +void NodeImpl::setPrefix(const DOMString& val) throw(DOMException) +{ + prefix = val; + if (prefix.size()>0) + nodeName = prefix + ":" + localName; + else + nodeName = localName; +} + +/** + * + */ +DOMString NodeImpl::getLocalName() +{ + return localName; +} + +/** + * + */ +bool NodeImpl::hasAttributes() +{ + return (attributes.getLength() > 0); +} + +/** + * + */ +DOMString NodeImpl::getBaseURI() +{ + return baseURI; +} + +/** + * + */ +unsigned short NodeImpl::compareDocumentPosition(const Node *otherArg) +{ + if (!otherArg || this == otherArg) + return 0;//no flags + + Node *node; + Node *other = (Node *)otherArg; + + //Look above me + for (node=getParentNode() ; node ; node=node->getParentNode()) + if (node == other) + return DOCUMENT_POSITION_CONTAINED_BY; + + //Look above the other guy. See me? + for (node=other->getParentNode() ; node ; node=node->getParentNode()) + if (node == this) + return DOCUMENT_POSITION_CONTAINS; + + + return DOCUMENT_POSITION_DISCONNECTED | + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; +} + +/** + * + */ +DOMString NodeImpl::getTextContext() throw(DOMException) +{ + //no idea + return DOMString(""); +} + + +/** + * + */ +void NodeImpl::setTextContext(const DOMString &val) throw(DOMException) +{ + //no idea +} + + +/** + * From DOM3 Namespace algorithms + */ +DOMString NodeImpl::lookupPrefix(const DOMString &theNamespaceURI) +{ + + if (theNamespaceURI.size()==0) + { + return DOMString(""); + } + + switch (nodeType) + { + case Node::ELEMENT_NODE: + { + ElementImpl *elem = (ElementImpl *)this; + return lookupNamespacePrefix(theNamespaceURI, elem); + } + case Node::DOCUMENT_NODE: + { + Document *doc = (Document *)this; + Element *elem = doc->getDocumentElement(); + return elem->lookupPrefix(theNamespaceURI); + } + case Node::ENTITY_NODE : + case Node::NOTATION_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + return DOMString(""); // type is unknown + case Node::ATTRIBUTE_NODE: + { + Attr *attr = (Attr *)this; + Element *elem = (Element *)attr->getOwnerElement(); + if ( elem ) + { + return elem->lookupPrefix(theNamespaceURI); + } + return DOMString(""); + } + default: + { + //Get ancestor element, if any + if ( Node *ancestor = getAncestorElement(this) ) + { + return ancestor->lookupPrefix(theNamespaceURI); + } + return DOMString(""); + } + }//switch + return DOMString(""); +} + + +/** + * + */ +bool NodeImpl::isDefaultNamespace(const DOMString &theNamespaceURI) +{ + switch (nodeType) + { + case ELEMENT_NODE: + if ( namespaceURI.size()>0 && prefix.size()==0 ) + { + return (namespaceURI == theNamespaceURI); + } + if ( Node *attr = attributes.getNamedItem("xmlns")) + { + return (attr->getNodeValue() == theNamespaceURI); + } + + + if ( Node *ancestor = getAncestorElement(this) ) + { + return ancestor->isDefaultNamespace(theNamespaceURI); + } + else + { + return false; + } + case DOCUMENT_NODE: + { //just use braces for local declaration + Document *doc = (Document *)this; + Element *elem = doc->getDocumentElement(); + return elem->isDefaultNamespace(theNamespaceURI); + } + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return false; + case ATTRIBUTE_NODE: + {//braces only for scope + Attr *attr = (Attr *)this; + Element *ownerElement = attr->getOwnerElement(); + if ( ownerElement ) + { + return ownerElement->isDefaultNamespace(theNamespaceURI); + } + else + { + return false; + } + } + default: + if ( Node *ancestor = getAncestorElement(this) ) + { + return ancestor->isDefaultNamespace(theNamespaceURI); + } + else + { + return false; + } + }//switch + + return false; +} + + +/** + * + */ +DOMString NodeImpl::lookupNamespaceURI(const DOMString &thePrefix) +{ + switch (nodeType) + { + case ELEMENT_NODE: + { + if ( namespaceURI.size()>0 && prefix == thePrefix ) + { + DOMString nsURI = namespaceURI; + return (nsURI); + } + if ( hasAttributes() ) + { + NamedNodeMap attributes = getAttributes(); + int nrAttrs = attributes.getLength(); + for (int i=0 ; igetPrefix() == "xmlns" && attr->getLocalName() == thePrefix ) + { // non default namespace + if (attr->getNodeValue().size()>0) + { + return (attr->getNodeValue()); + } + return DOMString(""); + } + else if (attr->getLocalName() == "xmlns" && thePrefix.size()==0) + { // default namespace + if (attr->getNodeValue().size()>0) + { + return (attr->getNodeValue()); + } + return DOMString(""); + } + } + } + + if ( Node *ancestor = getAncestorElement(this) ) + { + return ancestor->lookupNamespaceURI(thePrefix); + } + return DOMString(""); + } + case DOCUMENT_NODE: + { + Document *doc = (Document *)this; + Element *elem = doc->getDocumentElement(); + return elem->lookupNamespaceURI(thePrefix); + } + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return DOMString(""); + + case ATTRIBUTE_NODE: + { + if (Element *ownerElement = ((Attr *)this)->getOwnerElement()) + { + return ownerElement->lookupNamespaceURI(thePrefix); + } + else + { + return DOMString(""); + } + } + default: + if ( Node *ancestor = getAncestorElement(this) ) + { + return ancestor->lookupNamespaceURI(thePrefix); + } + else + { + return DOMString(""); + } + }//switch +} + + +/** + * + */ +bool NodeImpl::isEqualNode(const Node *nodeArg) +{ + if (!nodeArg) + return false; + + if (this == nodeArg) + return true; + + Node *node = (Node *)nodeArg; + + if (getNodeType() != node->getNodeType() || + getNodeName() != node->getNodeName() || + getLocalName() != node->getLocalName() || + getNamespaceURI() != node->getNamespaceURI() || + getPrefix() != node->getPrefix() || + getNodeValue() != node->getNodeValue() || + getBaseURI() != node->getBaseURI() ) + return false; + + return true; +} + + + +/** + * + */ +DOMObject *NodeImpl::getFeature(const DOMString &feature, + const DOMString &version) +{ + //dont know + return NULL; +} + +/** + * + */ +DOMUserData *NodeImpl::setUserData(const DOMString &key, + const DOMUserData *data, + const UserDataHandler *handler) +{ + UserDataEntry *entry = userDataEntries; + UserDataEntry *prev = NULL; + while (entry) + { + if (entry->key == key) + { + DOMUserData *oldData = entry->data; + entry->data = (DOMUserData *)data; + entry->handler = (UserDataHandler *)handler; + return oldData; + } + prev = entry; + entry = entry->next; + } + + //Make a new one + UserDataEntry *newEntry = new UserDataEntry(key, data, handler); + if (!prev) + userDataEntries = newEntry; + else + prev->next = newEntry; + + return NULL; +} + +/** + * + */ +DOMUserData *NodeImpl::getUserData(const DOMString &key) +{ + UserDataEntry *entry = userDataEntries; + while (entry) + { + if (entry->key == key) + return entry->data; + entry = entry->next; + } + return NULL; +} + + + +//################## +//# Non-API methods +//################## + +/** + * + */ +void NodeImpl::setNodeName(const DOMString &qualifiedName) +{ + nodeName = qualifiedName; + prefix = ""; + localName = ""; + for (unsigned int i=0 ; i0 && namespaceURI==theNamespaceURI && + prefix.size()>0 && + originalElement->lookupNamespaceURI(prefix) == theNamespaceURI) + { + return (prefix); + } + + if ( hasAttributes() ) + { + NamedNodeMap attributes = getAttributes(); + int nrAttrs = attributes.getLength(); + for (int i=0 ; igetLocalName(); + if (attr->getPrefix() == "xmlns" && + attr->getNodeValue() == theNamespaceURI && + originalElement->lookupNamespaceURI(attrLocalName) + == theNamespaceURI) + { + return (attrLocalName); + } + } + } + + //Get ancestor element, if any + NodeImpl *ancestor = parent; + while (ancestor && ancestor->getNodeType()!= Node::ELEMENT_NODE) + ancestor = ancestor->parent; + + if ( ancestor ) + { + return ancestor->lookupNamespacePrefix(theNamespaceURI, originalElement); + } + + return DOMString(""); +} + + +/** + * + */ +NodeImpl::NodeImpl() +{ + init(); +} + + + + +/** + * + */ +NodeImpl::NodeImpl(DocumentImpl *owner) +{ + init(); + ownerDocument = owner; +} + +/** + * + */ +NodeImpl::NodeImpl(DocumentImpl *owner, const DOMString &nodeName) +{ + init(); + ownerDocument = owner; + setNodeName(nodeName); +} + +/** + * + */ +NodeImpl::NodeImpl(DocumentImpl *owner, const DOMString &theNamespaceURI, + const DOMString &qualifiedName) +{ + init(); + ownerDocument = owner; + //if (owner) + // namespaceURI = owner->stringCache(theNamespaceURI); + setNodeName(qualifiedName); +} + + + +/** + * + */ +void NodeImpl::init() +{ + nodeType = 0; //none yet + nodeValue = ""; + setNodeName(""); + namespaceURI = ""; + parent = NULL; + prev = NULL; + next = NULL; + userData = NULL; + firstChild = NULL; + lastChild = NULL; + ownerDocument = NULL; + userDataEntries = NULL; +} + +/** + * + */ +void NodeImpl::assign(const NodeImpl &other) +{ + ownerDocument = other.ownerDocument; + prefix = other.prefix; + localName = other.localName; + nodeName = other.nodeName; + nodeValue = other.nodeValue; + namespaceURI = other.namespaceURI; + attributes = other.attributes; +} + + +/** + * + */ +NodeImpl::~NodeImpl() +{ + if (userDataEntries) + delete userDataEntries; +} + + + +/*######################################################################### +## CharacterDataImpl +#########################################################################*/ + + +/** + * + */ +CharacterDataImpl::CharacterDataImpl() : NodeImpl() +{ +} + +/** + * + */ +CharacterDataImpl::CharacterDataImpl(DocumentImpl *owner, + const DOMString &theValue) : NodeImpl() +{ + ownerDocument = owner; + nodeValue = theValue; +} + +/** + * + */ +CharacterDataImpl::~CharacterDataImpl() +{ +} + +/** + * + */ +DOMString CharacterDataImpl::getData() throw(DOMException) +{ + return nodeValue; +} + +/** + * + */ +void CharacterDataImpl::setData(const DOMString& val) throw(DOMException) +{ + nodeValue = val; +} + +/** + * + */ +unsigned long CharacterDataImpl::getLength() +{ + return nodeValue.size(); +} + +/** + * + */ +DOMString CharacterDataImpl::substringData(unsigned long offset, + unsigned long count) + throw(DOMException) +{ + return nodeValue.substr(offset, count); +} + +/** + * + */ +void CharacterDataImpl::appendData(const DOMString& arg) throw(DOMException) +{ + nodeValue += arg; +} + +/** + * + */ +void CharacterDataImpl::insertData(unsigned long offset, + const DOMString& arg) + throw(DOMException) +{ + nodeValue.insert(offset, arg); +} + +/** + * + */ +void CharacterDataImpl::deleteData(unsigned long offset, + unsigned long count) + throw(DOMException) +{ + nodeValue.erase(offset, count); +} + +/** + * + */ +void CharacterDataImpl::replaceData(unsigned long offset, + unsigned long count, + const DOMString& arg) + throw(DOMException) +{ + nodeValue.replace(offset, count, arg); +} + + + + + + +/*######################################################################### +## AttrImpl +#########################################################################*/ + +/** + * + */ +DOMString AttrImpl::getName() +{ + return nodeName; +} + +/** + * + */ +bool AttrImpl::getSpecified() +{ + return (nodeValue.size() > 0); +} + +/** + * + */ +DOMString AttrImpl::getValue() +{ + return nodeValue; +} + +/** + * + */ +void AttrImpl::setValue(const DOMString& val) throw(DOMException) +{ + nodeValue = val; +} + +/** + * + */ +Element *AttrImpl::getOwnerElement() +{ + return ownerElement; +} + + +/** + * + */ +TypeInfo *AttrImpl::getSchemaTypeInfo() +{ + return NULL; +} + + +/** + * + */ +bool AttrImpl::getIsId() +{ + return (nodeName == "id"); +} + + + +//################## +//# Non-API methods +//################## + + +void AttrImpl::setOwnerElement(const Element *elem) +{ + ownerElement = (Element *)elem; +} + +/** + * + */ +AttrImpl::AttrImpl(DocumentImpl *owner, const DOMString &theName) + : NodeImpl() +{ + nodeType = ATTRIBUTE_NODE; + ownerDocument = owner; + setNodeName(theName); +} + +/** + * + */ +AttrImpl::AttrImpl(DocumentImpl *owner, + const DOMString &theNamespaceURI, + const DOMString &theQualifiedName) + : NodeImpl() +{ + nodeType = ATTRIBUTE_NODE; + ownerDocument = owner; + //if (owner) + // namespaceURI = owner->stringCache(theNamespaceURI); + setNodeName(theQualifiedName); +} + +/** + * + */ +AttrImpl::~AttrImpl() +{ +} + + + + + +/*######################################################################### +## ElementImpl +#########################################################################*/ + + +/** + * + */ +DOMString ElementImpl::getTagName() +{ + if (prefix.size() > 0) + return prefix + ":" + nodeName; + else + return nodeName; +} + +/** + * + */ +DOMString ElementImpl::getAttribute(const DOMString& name) +{ + Node *node = attributes.getNamedItem(name); + if (!node || node->getNodeType() != ATTRIBUTE_NODE) + return DOMString(""); + Attr *attr = dynamic_cast(node); + return attr->getValue(); +} + +/** + * + */ +void ElementImpl::setAttribute(const DOMString& name, + const DOMString& value) + throw(DOMException) +{ + AttrImpl *attr = new AttrImpl(ownerDocument, name); + attr->setValue(value); + attr->setOwnerElement(this); + attributes.setNamedItem(attr); +} + +/** + * + */ +void ElementImpl::removeAttribute(const DOMString& name) + throw(DOMException) +{ + attributes.removeNamedItem(name); +} + +/** + * + */ +Attr *ElementImpl::getAttributeNode(const DOMString& name) +{ + Node *node = attributes.getNamedItem(name); + if (!node || node->getNodeType() != ATTRIBUTE_NODE) + return NULL; + Attr *attr = dynamic_cast(node); + return attr; +} + +/** + * + */ +Attr *ElementImpl::setAttributeNode(Attr *attr) + throw(DOMException) +{ + attributes.setNamedItem(attr); + return attr; +} + +/** + * + */ +Attr *ElementImpl::removeAttributeNode(Attr *attr) + throw(DOMException) +{ + if (!attr) + return NULL; + attributes.removeNamedItem(attr->getName()); + return attr; +} + + +/** + * + */ +void ElementImpl::getElementsByTagNameRecursive(NodeList &list, + const DOMString& name, Element *elem) +{ + if (!elem) + return; + + if (name == elem->getTagName()) + list.add(elem); + for (Node *node = elem->getFirstChild() ; node ; node=node->getNextSibling()) + { + if (node->getNodeType() != Node::ELEMENT_NODE) + continue; + Element *childElem = dynamic_cast(node); + getElementsByTagNameRecursive(list, name, childElem); + } +} + + +/** + * + */ +NodeList ElementImpl::getElementsByTagName(const DOMString& tagName) +{ + NodeList list; + getElementsByTagNameRecursive(list, tagName, this); + return list; +} + +/** + * + */ +DOMString ElementImpl::getAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) +{ + Node *node = attributes.getNamedItemNS(namespaceURI, localName); + if (!node || node->getNodeType()!=ATTRIBUTE_NODE) + return DOMString(""); + Attr *attr = dynamic_cast(node); + return attr->getValue(); +} + +/** + * + */ +void ElementImpl::setAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName, + const DOMString& value) + throw(DOMException) +{ + AttrImpl *attr = new AttrImpl(ownerDocument, namespaceURI, qualifiedName); + attr->setValue(value); + attr->setOwnerElement(this); + attributes.setNamedItemNS(attr); +} + +/** + * + */ +void ElementImpl::removeAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) + throw(DOMException) +{ + attributes.removeNamedItemNS(namespaceURI, localName); +} + +/** + * + */ + Attr *ElementImpl::getAttributeNodeNS(const DOMString& namespaceURI, + const DOMString& localName) +{ + Node *node = attributes.getNamedItemNS(namespaceURI, localName); + if (!node || node->getNodeType() != ATTRIBUTE_NODE) + return NULL; + Attr *attr = dynamic_cast(node); + return attr; +} + +/** + * + */ +Attr *ElementImpl::setAttributeNodeNS(Attr *attr) + throw(DOMException) +{ + attributes.setNamedItemNS(attr); + return attr; +} + + +/** + * + */ +void ElementImpl::getElementsByTagNameNSRecursive(NodeList &list, + const DOMString& namespaceURI, const DOMString& tagName, Element *elem) +{ + if (!elem) + return; + + if (namespaceURI == elem->getNamespaceURI() && tagName == elem->getTagName()) + list.add(elem); + for (Node *node = elem->getFirstChild() ; node ; node=node->getNextSibling()) + { + if (node->getNodeType() != Node::ELEMENT_NODE) + continue; + Element *childElem = dynamic_cast(node); + getElementsByTagNameNSRecursive(list, namespaceURI, tagName, childElem); + } +} + +/** + * + */ +NodeList ElementImpl::getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName) +{ + NodeList list; + getElementsByTagNameNSRecursive(list, namespaceURI, localName, this); + return list; +} + +/** + * + */ +bool ElementImpl::hasAttribute(const DOMString& attrName) +{ + Node *node = attributes.getNamedItem(attrName); + if (!node || node->getNodeType() != ATTRIBUTE_NODE) + return false; + return true; +} + +/** + * + */ +bool ElementImpl::hasAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) +{ + Node *node = attributes.getNamedItemNS(namespaceURI, localName); + if (!node || node->getNodeType() != ATTRIBUTE_NODE) + return false; + return true; +} + +/** + * + */ +TypeInfo *ElementImpl::getSchemaTypeInto() +{ + //fixme + return NULL; +} + + +/** + * + */ +void ElementImpl::setIdAttribute(const DOMString &name, + bool isId) throw (DOMException) +{ + //fixme +} + +/** + * + */ +void ElementImpl::setIdAttributeNS(const DOMString &namespaceURI, + const DOMString &localName, + bool isId) throw (DOMException) +{ + //fixme +} + +/** + * + */ +void ElementImpl::setIdAttributeNode(const Attr *idAttr, + bool isId) throw (DOMException) +{ + //fixme +} + + +//################## +//# Non-API methods +//################## + + +/** + * + */ +ElementImpl::ElementImpl() : NodeImpl() +{ + nodeType = ELEMENT_NODE; +} + +/** + * + */ +ElementImpl::ElementImpl(DocumentImpl *owner, const DOMString &tagName) + : NodeImpl() +{ + nodeType = ELEMENT_NODE; + ownerDocument = owner; + setNodeName(tagName); +} + +/** + * + */ +ElementImpl::ElementImpl(DocumentImpl *owner, + const DOMString &theNamespaceURI, + const DOMString &qualifiedName) : + NodeImpl() +{ + nodeType = ELEMENT_NODE; + ownerDocument = owner; + setNodeName(qualifiedName); +} + +/** + * + */ +ElementImpl::~ElementImpl() +{ +} + + +/** + * + */ +void ElementImpl::normalizeNamespaces() +{ + //printf("### NORMALIZE\n"); + + NamedNodeMap attrs = getAttributes(); + + //####################################### + //# Pick up local namespace declarations + //####################################### + bindingsClear(); //Reset bindings on this node + + int nrAttrs = attrs.getLength(); + for (int i=0; igetNodeType() != Node::ATTRIBUTE_NODE) + continue; + AttrImpl *attr = dynamic_cast(attrNode); + DOMString attrNS = attr->getNamespaceURI(); + DOMString attrName = attr->getLocalName(); + DOMString attrPrefix = attr->getPrefix(); + DOMString attrValue = attr->getNodeValue(); + if (attrName != "xmlns" && attrPrefix != "xmlns") + continue; + + //is the namespace declaration is invalid? + if (attrValue == XMLNSNAME || attrName == attrPrefix) + { + // Note: The prefix xmlns is used only to declare namespace bindings and + // is by definition bound to the namespace name http://www.w3.org/2000/xmlns/. + // It must not be declared. No other prefix may be bound to this namespace name. + + //==> Report an error. + printf("normalizeNamespaces() error: Namespace %s cannot be reassigned\n", + XMLNSNAME); + + } + else + { + //==> Record the namespace declaration + attr->setNamespaceURI(XMLNSNAME); + if (attrPrefix.size() > 0) + bindingsAdd(attrPrefix, attrValue); + else + bindingsAdd("*", attrValue);//default + + } + } + + + //####################################### + //# Fixup element's namespace + //####################################### + if ( namespaceURI.size() > 0 ) + { + DOMString key = prefix; + if (key.size() == 0) + key = "*"; + DOMString binding = bindingsFind(key); + //Element's prefix/namespace pair (or default namespace, if no prefix) + // are within the scope of a binding + if ( binding == namespaceURI ) + { + //==> do nothing, declaration in scope is inherited + + // See section "B.1.1: Scope of a binding" for an example + + } + else + { + + /* + ==> Create a local namespace declaration attr for this namespace, + with Element's current prefix (or a default namespace, if + no prefix). If there's a conflicting local declaration + already present, change its value to use this namespace. + + See section "B.1.2: Conflicting namespace declaration" for an example + */ + DOMString attrName = "xmlns"; + if (prefix.size() > 0) + { + attrName.append(":"); + attrName.append(prefix); + } + setAttribute(attrName, namespaceURI); + // NOTE that this may break other nodes within this Element's + // subtree, if they're already using this prefix. + // They will be repaired when we reach them. + } + } + else // Element has no namespace URI: + { + //############################################### + //# Bob -- alter this from the specs a bit. + //# Since the XmlReader does not set namespaces, + //# do it here + //############################################### + DOMString localName = getLocalName(); + if ( localName.size()==0 ) + { + // DOM Level 1 node + /* + ==> if in process of validation against a namespace aware schema + (i.e XML Schema) report a fatal error: the processor can not recover + in this situation. + Otherwise, report an error: no namespace fixup will be performed on this node. + */ + printf("normalizeNamespaces() error: no localName\n"); + } + else + { + // Element has no pseudo-prefix + //there's a conflicting local default namespace declaration already present + if ( prefix.size()==0 ) + { + //==> change its value to use this empty namespace. + namespaceURI = bindingsFind("*"); + //setAttribute("xmlns", ""); + } + else //#BOB . I added this. + { + namespaceURI = bindingsFind(prefix); + } + // NOTE that this may break other nodes within this Element's + // subtree, if they're already using the default namespaces. + // They will be repaired when we reach them. + } + } + + + //####################################### + //# Examine and polish the attributes + //####################################### + nrAttrs = attrs.getLength(); + for (int i=0; igetNodeType() != Node::ATTRIBUTE_NODE) + continue; + Attr *attr = dynamic_cast(attrNode); + DOMString attrNS = attr->getNamespaceURI(); + DOMString attrPrefix = attr->getPrefix(); + DOMString attrValue = attr->getNodeValue(); + if (attrNS == XMLNSNAME) + continue; + + if ( attrNS.size()>0 ) //Attr[i] has a namespace URI + { + DOMString attrBinding = bindingsFind(attrPrefix); + /* + if attribute has no prefix (default namespace decl does not apply to attributes) + OR + attribute prefix is not declared + OR + conflict: attribute has a prefix that conflicts with a binding + already active in scope + */ + if ( attrPrefix.size() == 0 || attrBinding.size() == 0) + { + //namespaceURI matches an in scope declaration of one or more prefixes) + DOMString prefixForNS = lookupNamespacePrefix(attrNS, this); + if ( prefixForNS.size() > 0 ) + { + // pick the most local binding available; + // if there is more than one pick one arbitrarily + + //==> change attribute's prefix. + attr->setPrefix(prefixForNS); + } + else + { + // the current prefix is not null and it has no in scope declaration) + if ( attrPrefix.size() > 0 || attrBinding.size() == 0 ) + { + //==> declare this prefix + DOMString newAttrName = "xmlns:"; + newAttrName.append(attrPrefix); + setAttribute(newAttrName, attrNS); + bindingsAdd(attrPrefix, attrNS); + } + else + { + // find a prefix following the pattern "NS" +index (starting at 1) + // make sure this prefix is not declared in the current scope. + // create a local namespace declaration attribute + + //==> declare this prefix + char buf[16]; + sprintf(buf, "%d" , ownerDocument->namespaceIndex++); + DOMString newPrefix = "NS"; + newPrefix.append(buf); + DOMString newAttrName = "xmlns:"; + newAttrName.append(newPrefix); + setAttribute(newAttrName, attrNS); + bindingsAdd(newPrefix, attrNS); + //==> change attribute's prefix. + } + } + } + } + else // Attr has no namespace URI + { + // Attr has no localName + if ( attr->getLocalName().size() == 0 ) + { + // DOM Level 1 node + /* + ==> if in process of validation against a namespace aware schema + (i.e XML Schema) report a fatal error: the processor can not recover + in this situation. + Otherwise, report an error: no namespace fixup will be performed on this node. + */ + printf("normalizeNamespaces: no local name for attribute\n"); + } + else + { + // attr has no namespace URI and no prefix + // no action is required, since attrs don't use default + //==> do nothing + } + } + } // end for-all-Attrs + + + //####################################### + //# Recursively normalize children + //####################################### + for (Node *child=getFirstChild() ; child ; child=child->getNextSibling()) + { + if (child->getNodeType() != Node::ELEMENT_NODE) + continue; + ElementImpl *childElement = dynamic_cast(child); + childElement->normalizeNamespaces(); + } + +} + + +/*######################################################################### +## TextImpl +#########################################################################*/ + + +/** + * + */ +TextImpl::TextImpl() : CharacterDataImpl() +{ + nodeType = TEXT_NODE; + nodeName = "#text"; +} + + +/** + * + */ +TextImpl::TextImpl(DocumentImpl *owner, const DOMString &value) + : CharacterDataImpl() +{ + nodeType = TEXT_NODE; + nodeName = "#text"; + ownerDocument = owner; + nodeValue = value; +} + + +/** + * + */ +TextImpl::~TextImpl() +{ +} + +/** + * + */ +Text *TextImpl::splitText(unsigned long offset) + throw(DOMException) +{ + return NULL; +} + +/** + * + */ +bool TextImpl::getIsElementContentWhitespace() +{ + return false; +} + +/** + * + */ +DOMString TextImpl::getWholeText() +{ + return nodeValue; +} + + +/** + * + */ +Text *TextImpl::replaceWholeText(const DOMString &content) + throw(DOMException) +{ + return NULL; +} + + +/*######################################################################### +## CommentImpl +#########################################################################*/ + +/** + * + */ +CommentImpl::CommentImpl() : CharacterDataImpl() +{ + nodeType = COMMENT_NODE; + nodeName = "#comment"; +} + + +/** + * + */ +CommentImpl::CommentImpl(DocumentImpl *owner, const DOMString &value) + : CharacterDataImpl() +{ + nodeType = COMMENT_NODE; + nodeName = "#comment"; + ownerDocument = owner; + nodeValue = value; +} + + +/** + * + */ +CommentImpl::~CommentImpl() +{ +} + + + + +/*######################################################################### +## TypeInfoImpl +#########################################################################*/ + + +/** + * + */ +TypeInfoImpl::TypeInfoImpl(const DOMString &typeNamespaceArg, + const DOMString &typeNameArg, + const DerivationMethod derivationMethodArg) +{ + typeNamespace = typeNamespaceArg; + typeName = typeNameArg; + derivationMethod = derivationMethodArg; +} + + +/** + * + */ +TypeInfoImpl::~TypeInfoImpl() +{ +} + + +/** + * + */ +DOMString TypeInfoImpl::getTypeName() +{ + return typeName; +} + +/** + * + */ +DOMString TypeInfoImpl::getTypeNamespace() +{ + return typeNamespace; +} + +/** + * + */ +bool TypeInfoImpl::isDerivedFrom(const DOMString &typeNamespaceArg, + const DOMString &typeNameArg, + const DerivationMethod derivationMethodArg) +{ + if (typeNamespaceArg == typeNamespace && + typeName == typeNameArg && + derivationMethod == derivationMethodArg) + return true; + return false; +} + + + +/*######################################################################### +## UserDataHandlerImpl +#########################################################################*/ + + + +/** + * + */ +UserDataHandlerImpl::UserDataHandlerImpl() +{ +} + + +/** + * + */ +UserDataHandlerImpl::~UserDataHandlerImpl() +{ +} + +/** + * + */ +void UserDataHandlerImpl::handle(unsigned short operation, + const DOMString &key, + const DOMUserData *data, + const Node *src, + const Node *dst) +{ + //do nothing. do we need anything here? +} + + + +/*######################################################################### +## DOMErrorImpl +#########################################################################*/ + + + +/** + * + */ +DOMErrorImpl::DOMErrorImpl() +{ +} + + +/** + * + */ +DOMErrorImpl::~DOMErrorImpl() +{ +} + +/** + * + */ +unsigned short DOMErrorImpl::getSeverity() +{ + return severity; +} + +/** + * + */ +DOMString DOMErrorImpl::getMessage() +{ + return message; +} + +/** + * + */ +DOMString DOMErrorImpl::getType() +{ + return type; +} + +/** + * + */ +DOMObject *DOMErrorImpl::getRelatedException() +{ + return NULL; +} + +/** + * + */ +DOMObject *DOMErrorImpl::getRelatedData() +{ + return NULL; +} + +/** + * + */ +DOMLocator *DOMErrorImpl::getLocation() +{ + //really should fill this in + return NULL; +} + + + + +/*######################################################################### +## DOMErrorHandlerImpl +#########################################################################*/ + + + +/** + * + */ +DOMErrorHandlerImpl::DOMErrorHandlerImpl() +{ +} + + +/** + * + */ +DOMErrorHandlerImpl::~DOMErrorHandlerImpl() +{ +} + +/** + * + */ +bool DOMErrorHandlerImpl::handleError(const DOMError *error) +{ + if (!error) + return false; + return true; +} + + + + +/*######################################################################### +## DOMLocatorImpl +#########################################################################*/ + + +/** + * + */ +DOMLocatorImpl::DOMLocatorImpl() +{ +} + + +/** + * + */ +DOMLocatorImpl::~DOMLocatorImpl() +{ +} + + +/** + * + */ +long DOMLocatorImpl::getLineNumber() +{ + return lineNumber; +} + +/** + * + */ +long DOMLocatorImpl::getColumnNumber() +{ + return columnNumber; +} + +/** + * + */ +long DOMLocatorImpl::getByteOffset() +{ + return byteOffset; +} + +/** + * + */ +long DOMLocatorImpl::getUtf16Offset() +{ + return utf16Offset; +} + + +/** + * + */ +Node *DOMLocatorImpl::getRelatedNode() +{ + return relatedNode; +} + + +/** + * + */ +DOMString DOMLocatorImpl::getUri() +{ + return uri; +} + + + +/*######################################################################### +## DOMConfigurationImpl +#########################################################################*/ + + +/** + * + */ +DOMConfigurationImpl::DOMConfigurationImpl() +{ +} + + +/** + * + */ +DOMConfigurationImpl::~DOMConfigurationImpl() +{ +} + +/** + * + */ +void DOMConfigurationImpl::setParameter(const DOMString &name, + const DOMUserData *value) throw (DOMException) +{ +} + +/** + * + */ +DOMUserData *DOMConfigurationImpl::getParameter(const DOMString &name) + throw (DOMException) +{ + return NULL; +} + +/** + * + */ +bool DOMConfigurationImpl::canSetParameter(const DOMString &name, + const DOMUserData *data) +{ + return false; +} + +/** + * + */ +DOMStringList *DOMConfigurationImpl::getParameterNames() +{ + return NULL; +} + + + +/*######################################################################### +## CDATASectionImpl +#########################################################################*/ + +/** + * + */ +CDATASectionImpl::CDATASectionImpl() : TextImpl() +{ + nodeType = CDATA_SECTION_NODE; + nodeName = "#cdata-section"; +} + +/** + * + */ +CDATASectionImpl::CDATASectionImpl(DocumentImpl *owner, const DOMString &theValue) + : TextImpl() +{ + nodeType = CDATA_SECTION_NODE; + nodeName = "#cdata-section"; + ownerDocument = owner; + nodeValue = theValue; +} + + +/** + * + */ +CDATASectionImpl::~CDATASectionImpl() +{ +} + + + + + +/*######################################################################### +## DocumentTypeImpl +#########################################################################*/ + +/** + * + */ +DocumentTypeImpl::DocumentTypeImpl(const DOMString& theName, + const DOMString& thePublicId, + const DOMString& theSystemId) + : NodeImpl() +{ + nodeType = DOCUMENT_TYPE_NODE; + nodeName = theName; + publicId = thePublicId; + systemId = theSystemId; +} + +/** + * + */ +DocumentTypeImpl::~DocumentTypeImpl() +{ +} + +/** + * + */ +DOMString DocumentTypeImpl::getName() +{ + return nodeName; +} + +/** + * + */ +NamedNodeMap DocumentTypeImpl::getEntities() +{ + return entities; +} + +/** + * + */ +NamedNodeMap DocumentTypeImpl::getNotations() +{ + return notations; +} + +/** + * + */ +DOMString DocumentTypeImpl::getPublicId() +{ + return publicId; +} + +/** + * + */ +DOMString DocumentTypeImpl::getSystemId() +{ + return systemId; +} + +/** + * + */ +DOMString DocumentTypeImpl::getInternalSubset() +{ + return DOMString(""); +} + + + + + + +/*######################################################################### +## NotationImpl +#########################################################################*/ + + + +/** + * + */ +NotationImpl::NotationImpl(DocumentImpl *owner) : NodeImpl() +{ + nodeType = NOTATION_NODE; + ownerDocument = owner; +} + + +/** + * + */ +NotationImpl::~NotationImpl() +{ +} + +/** + * + */ +DOMString NotationImpl::getPublicId() +{ + return publicId; +} + +/** + * + */ +DOMString NotationImpl::getSystemId() +{ + return systemId; +} + + + + + + + + +/*######################################################################### +## EntityImpl +#########################################################################*/ + + +/** + * + */ +EntityImpl::EntityImpl() : NodeImpl() +{ + nodeType = ENTITY_NODE; +} + + +/** + * + */ +EntityImpl::EntityImpl(DocumentImpl *owner) : NodeImpl() +{ + nodeType = ENTITY_NODE; + ownerDocument = owner; +} + + +/** + * + */ +EntityImpl::~EntityImpl() +{ +} + +/** + * + */ +DOMString EntityImpl::getPublicId() +{ + return publicId; +} + +/** + * + */ +DOMString EntityImpl::getSystemId() +{ + return systemId; +} + +/** + * + */ +DOMString EntityImpl::getNotationName() +{ + return notationName; +} + +/** + * + */ +DOMString EntityImpl::getInputEncoding() +{ + return inputEncoding; +} + +/** + * + */ +DOMString EntityImpl::getXmlEncoding() +{ + return xmlEncoding; +} + +/** + * + */ +DOMString EntityImpl::getXmlVersion() +{ + return xmlVersion; +} + + + + + + +/*######################################################################### +## EntityReferenceImpl +#########################################################################*/ + + + +/** + * + */ +EntityReferenceImpl::EntityReferenceImpl() : NodeImpl() +{ + nodeType = ENTITY_REFERENCE_NODE; +} + + +/** + * + */ +EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *owner, + const DOMString &theName) + : NodeImpl() +{ + nodeType = ENTITY_REFERENCE_NODE; + nodeName = theName; + ownerDocument = owner; +} + + +/** + * + */ +EntityReferenceImpl::~EntityReferenceImpl() +{ +} + + + +/*######################################################################### +## ProcessingInstructionImpl +#########################################################################*/ + + + + +/** + * + */ +ProcessingInstructionImpl::ProcessingInstructionImpl(): NodeImpl() +{ + nodeType = PROCESSING_INSTRUCTION_NODE; +} + + + +/** + * + */ +ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentImpl *owner, + const DOMString &target, + const DOMString &data) + : NodeImpl() +{ + nodeType = PROCESSING_INSTRUCTION_NODE; + ownerDocument = owner; + nodeName = target; + nodeValue = data; +} + + +/** + * + */ +ProcessingInstructionImpl::~ProcessingInstructionImpl() +{ +} + + + + +/** + * + */ +DOMString ProcessingInstructionImpl::getTarget() +{ + return nodeName; +} + +/** + * + */ +DOMString ProcessingInstructionImpl::getData() +{ + return nodeValue; +} + +/** + * + */ +void ProcessingInstructionImpl::setData(const DOMString& val) throw(DOMException) +{ + //do something here +} + + + + + + + +/*######################################################################### +## DocumentFragmentImpl +#########################################################################*/ + +/** + * + */ +DocumentFragmentImpl::DocumentFragmentImpl() : NodeImpl() +{ + nodeType = DOCUMENT_FRAGMENT_NODE; + nodeName = "#document-fragment"; +} + + +/** + * + */ +DocumentFragmentImpl::DocumentFragmentImpl(DocumentImpl *owner) : NodeImpl() +{ + nodeType = DOCUMENT_FRAGMENT_NODE; + nodeName = "#document-fragment"; + ownerDocument = owner; +} + + +/** + * + */ +DocumentFragmentImpl::~DocumentFragmentImpl() +{ +} + + + + + + +/*######################################################################### +## DocumentImpl +#########################################################################*/ + + + +/** + * + */ +DocumentType *DocumentImpl::getDoctype() +{ + return doctype; +} + +/** + * + */ +DOMImplementation *DocumentImpl::getImplementation() +{ + return parent; +} + +/** + * + */ +Element *DocumentImpl::getDocumentElement() +{ + return documentElement; +} + +/** + * + */ +Element *DocumentImpl::createElement(const DOMString& tagName) + throw(DOMException) +{ + ElementImpl *impl = new ElementImpl(this, tagName); + return impl; +} + +/** + * + */ +DocumentFragment *DocumentImpl::createDocumentFragment() +{ + DocumentFragmentImpl *frag = new DocumentFragmentImpl(this); + return frag; +} + +/** + * + */ +Text *DocumentImpl::createTextNode(const DOMString& data) +{ + TextImpl *text = new TextImpl(this, data); + return text; +} + +/** + * + */ +Comment *DocumentImpl::createComment(const DOMString& data) +{ + CommentImpl *comment = new CommentImpl(this, data); + return comment; +} + +/** + * + */ +CDATASection *DocumentImpl::createCDATASection(const DOMString& data) + throw(DOMException) +{ + CDATASectionImpl *cdata = new CDATASectionImpl(this, data); + return cdata; +} + +/** + * + */ +ProcessingInstruction *DocumentImpl::createProcessingInstruction(const DOMString& target, + const DOMString& data) + throw(DOMException) +{ + ProcessingInstructionImpl *cdata = + new ProcessingInstructionImpl(this, target, data); + return cdata; +} + +/** + * + */ +Attr *DocumentImpl::createAttribute(const DOMString& attrName) + throw(DOMException) +{ + AttrImpl *attr = new AttrImpl(this, attrName); + return attr; +} + +/** + * + */ +EntityReference *DocumentImpl::createEntityReference(const DOMString& erName) + throw(DOMException) +{ + EntityReferenceImpl *ref = new EntityReferenceImpl(this, erName); + return ref; +} + + +/** + * + */ +NodeList DocumentImpl::getElementsByTagName(const DOMString& tagname) +{ + NodeList list; + ElementImpl::getElementsByTagNameRecursive(list, + tagname, documentElement); + return list; +} + + +/** + * + */ +Node *DocumentImpl::importNode(const Node *importedNode, + bool deep) + throw(DOMException) +{ + return NULL; +} + +/** + * + */ +Element *DocumentImpl::createElementNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException) +{ + ElementImpl *elem = new ElementImpl(this, namespaceURI, qualifiedName); + return elem; +} + +/** + * + */ +Attr *DocumentImpl::createAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException) +{ + AttrImpl *attr = new AttrImpl(this, namespaceURI, qualifiedName); + return attr; +} + + +/** + * + */ +NodeList DocumentImpl::getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName) +{ + NodeList list; + ElementImpl::getElementsByTagNameNSRecursive(list, namespaceURI, + localName, documentElement); + return list; +} + +/** + * + */ +Element *DocumentImpl::getElementById(const DOMString& elementId) +{ + for (NamedElementItem *entry = elementsById.next; entry ; entry=entry->next) + if (entry->name == elementId) + return entry->elem; + return NULL; +} + + +/** + * + */ +DOMString DocumentImpl::getInputEncoding() +{ + return inputEncoding; +} + + +/** + * + */ +DOMString DocumentImpl::getXmlEncoding() +{ + return xmlEncoding; +} + +/** + * + */ +bool DocumentImpl::getXmlStandalone() +{ + return xmlStandalone; +} + +/** + * + */ +void DocumentImpl::setXmlStandalone(bool val) throw (DOMException) +{ + xmlStandalone = val; +} + +/** + * + */ +DOMString DocumentImpl::getXmlVersion() +{ + return xmlVersion; +} + +/** + * + */ +void DocumentImpl::setXmlVersion(const DOMString &version) throw (DOMException) +{ + xmlVersion = version; +} + +/** + * + */ +bool DocumentImpl::getStrictErrorChecking() +{ + return strictErrorChecking; +} + +/** + * + */ +void DocumentImpl::setStrictErrorChecking(bool val) +{ + strictErrorChecking = val; +} + + +/** + * + */ +DOMString DocumentImpl::getDocumentURI() +{ + if (!documentURI) + return DOMString(""); + DOMString docURI = *documentURI; + return docURI; +} + +/** + * + */ +void DocumentImpl::setDocumentURI(const DOMString &uri) +{ + //documentURI = stringCache(uri); +} + +/** + * + */ +Node *DocumentImpl::adoptNode(const Node *source) throw (DOMException) +{ + return (Node *)source; +} + +/** + * + */ +DOMConfiguration *DocumentImpl::getDomConfig() +{ + return domConfig; +} + +/** + * + */ +void DocumentImpl::normalizeDocument() +{ + //i assume that this means adjusting namespaces & prefixes + if (documentElement) + documentElement->normalizeNamespaces(); +} + +/** + * + */ +Node *DocumentImpl::renameNode(const Node *n, + const DOMString &namespaceURI, + const DOMString &qualifiedName) + throw (DOMException) +{ + Node *node = (Node *) n; + NodeImpl *nodeImpl = dynamic_cast (node); + //nodeImpl->namespaceURI = stringCache(namespaceURI); + nodeImpl->setNodeName(qualifiedName); + return node; +} + + + +//################## +//# Non-API methods +//################## + +/** + * + */ +DocumentImpl::DocumentImpl(const DOMImplementation *domImpl, + const DOMString &theNamespaceURI, + const DOMString &theQualifiedName, + const DocumentType *theDoctype) : NodeImpl() +{ + nodeType = DOCUMENT_NODE; + nodeName = "#document"; + parent = (DOMImplementation *)domImpl; + //documentURI = stringCache(theNamespaceURI); + qualifiedName = theQualifiedName; + if (theDoctype) //only assign if not null. + doctype = (DocumentType *)theDoctype; + else + doctype = new DocumentTypeImpl("", "", ""); + documentElement = new ElementImpl(this, "root"); + namespaceIndex = 0; +} + + +/** + * + */ +DocumentImpl::~DocumentImpl() +{ + delete documentElement; +} + + + + + + + + + + + + +} //namespace dom +} //namespace w3c +} //namespace org + + + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + + + + diff --git a/src/dom/domimpl.h b/src/dom/domimpl.h new file mode 100644 index 000000000..677f5eb8c --- /dev/null +++ b/src/dom/domimpl.h @@ -0,0 +1,2002 @@ +#ifndef __DOMIMPL_H__ +#define __DOMIMPL_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + +#include "dom.h" + +#include + +namespace org +{ +namespace w3c +{ +namespace dom +{ + + + +class DOMImplementationSourceImpl; +class DOMImplementationImpl; +class NodeImpl; +class CharacterDataImpl; +class AttrImpl; +class ElementImpl; +class TextImpl; +class CommentImpl; +class TypeInfoImpl; +class UserDataHandlerImpl; +class DOMErrorImpl; +class DOMErrorHandlerImpl; +class DOMLocatorImpl; +class DOMConfigurationImpl; +class CDATASectionImpl; +class DocumentTypeImpl; +class NotationImpl; +class EntityImpl; +class EntityReferenceImpl; +class ProcessingInstructionImpl; +class DocumentFragmentImpl; +class DocumentImpl; + + + +/*######################################################################### +## DOMImplementationSourceImpl +#########################################################################*/ + +class DOMImplementationSourceImpl : public DOMImplementationSource +{ +public: + + /** + * + */ + virtual DOMImplementation *getDOMImplementation(const DOMString &features); + + /** + * + */ + virtual DOMImplementationList getDOMImplementationList(const DOMString &features); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + DOMImplementationSourceImpl(); + + /** + * + */ + virtual ~DOMImplementationSourceImpl(); + +protected: + + + DOMImplementationImpl *domImpl; + DOMImplementationList domImplList; +}; + + + + + +/*######################################################################### +## DOMImplementationImpl +#########################################################################*/ +/** + * + */ +class DOMImplementationImpl : public DOMImplementation +{ +public: + + + /** + * + */ + DOMImplementationImpl(); + + /** + * + */ + virtual ~DOMImplementationImpl(); + + /** + * + */ + virtual bool hasFeature(const DOMString& feature, const DOMString& version); + + + /** + * + */ + virtual DocumentType *createDocumentType(const DOMString& qualifiedName, + const DOMString& publicId, + const DOMString& systemId) + throw(DOMException); + + /** + * + */ + virtual Document *createDocument(const DOMString& namespaceURI, + const DOMString& qualifiedName, + DocumentType *doctype) + throw(DOMException); + /** + * + */ + virtual DOMObject *getFeature(const DOMString& feature, + const DOMString& version); + + +protected: + +}; + + + + +/*######################################################################### +## NodeImpl +#########################################################################*/ + +/** + * + */ +class NodeImpl : virtual public Node +{ + + friend class DocumentImpl; + +public: + + /** + * + */ + virtual DOMString getNodeName(); + + /** + * + */ + virtual DOMString getNodeValue() throw (DOMException); + + /** + * + */ + virtual void setNodeValue(const DOMString& val) throw (DOMException); + + /** + * + */ + virtual unsigned short getNodeType(); + + /** + * + */ + virtual Node *getParentNode(); + + /** + * + */ + virtual NodeList getChildNodes(); + + /** + * + */ + virtual Node *getFirstChild(); + + /** + * + */ + virtual Node *getLastChild(); + + /** + * + */ + virtual Node *getPreviousSibling(); + + /** + * + */ + virtual Node *getNextSibling(); + + /** + * + */ + virtual NamedNodeMap &getAttributes(); + + + /** + * + */ + virtual Document *getOwnerDocument(); + + /** + * + */ + virtual Node *insertBefore(const Node *newChild, + const Node *refChild) + throw(DOMException); + + /** + * + */ + virtual Node *replaceChild(const Node *newChild, + const Node *oldChild) + throw(DOMException); + + /** + * + */ + virtual Node *removeChild(const Node *oldChild) + throw(DOMException); + + /** + * + */ + virtual Node *appendChild(const Node *newChild) + throw(DOMException); + + /** + * + */ + virtual bool hasChildNodes(); + + /** + * + */ + virtual Node *cloneNode(bool deep); + + /** + * + */ + virtual void normalize(); + + /** + * + */ + virtual bool isSupported(const DOMString& feature, + const DOMString& version); + + /** + * + */ + virtual DOMString getNamespaceURI(); + + /** + * + */ + virtual DOMString getPrefix(); + + /** + * + */ + virtual void setPrefix(const DOMString& val) throw(DOMException); + + /** + * + */ + virtual DOMString getLocalName(); + + /** + * + */ + virtual bool hasAttributes(); + + /** + * + */ + virtual DOMString getBaseURI(); + + /** + * + */ + virtual unsigned short compareDocumentPosition(const Node *other); + + /** + * + */ + virtual DOMString getTextContext() throw(DOMException); + + + /** + * + */ + virtual void setTextContext(const DOMString &val) throw(DOMException); + + + /** + * + */ + virtual DOMString lookupPrefix(const DOMString &namespaceURI); + + + /** + * + */ + virtual bool isDefaultNamespace(const DOMString &namespaceURI); + + + /** + * + */ + virtual DOMString lookupNamespaceURI(const DOMString &prefix); + + + /** + * + */ + virtual bool isEqualNode(const Node *node); + + + + /** + * + */ + virtual DOMObject *getFeature(const DOMString &feature, + const DOMString &version); + + /** + * + */ + virtual DOMUserData *setUserData(const DOMString &key, + const DOMUserData *data, + const UserDataHandler *handler); + + + /** + * + */ + virtual DOMUserData *getUserData(const DOMString &namespaceURI); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual void bindingsAdd(const DOMString &prefix, const DOMString &namespaceURI) + { + bindings[prefix] = namespaceURI; + } + + /** + * + */ + virtual void bindingsClear() + { + bindings.clear(); + } + + DOMString bindingsFind(const DOMString &prefix) + { + std::map::iterator iter = + bindings.find(prefix); + if (iter != bindings.end()) + { + DOMString ret = iter->second; + return ret; + } + if (parent) + { + DOMString ret = parent->bindingsFind(prefix); + if (ret.size() > 0) + return ret; + } + return ""; + } + + /** + * + */ + virtual void setNodeName(const DOMString &qualifiedName); + + /** + * + */ + virtual void setNamespaceURI(const DOMString &theNamespaceURI); + + /** + * + */ + DOMString lookupNamespacePrefix(const DOMString &namespaceURI, + Node *originalElement); + /** + * + */ + NodeImpl(); + + /** + * + */ + NodeImpl(DocumentImpl *owner); + + /** + * + */ + NodeImpl(DocumentImpl *owner, const DOMString &nodeName); + + /** + * + */ + NodeImpl(DocumentImpl *owner, const DOMString &namespaceURI, const DOMString &nodeName); + + /** + * + */ + virtual ~NodeImpl(); + + + /** + * + */ + void assign(const NodeImpl &other); + +protected: + + /** + * Set up the internal values + */ + void init(); + + unsigned short nodeType; + + NodeImpl *parent; + + NodeImpl *prev; + + NodeImpl *next; + + DOMUserData *userData; + + DOMString prefix; + + DOMString localName; + + DOMString nodeName; + + DOMString namespaceURI; + + DOMString baseURI; + + DOMString nodeValue; + + NodeImpl *firstChild; + NodeImpl *lastChild; + + DocumentImpl *ownerDocument; + + NamedNodeMap attributes; + + class UserDataEntry + { + public: + UserDataEntry(const DOMString &theKey, + const DOMUserData *theData, + const UserDataHandler *theHandler) + { + next = NULL; + key = theKey; + data = (DOMUserData *)theData; + handler = (UserDataHandler *)theHandler; + } + ~UserDataEntry() + { + //delete anything after me, too + if (next) + delete next; + } + + UserDataEntry *next; + DOMString key; + DOMUserData *data; + UserDataHandler *handler; + }; + + UserDataEntry *userDataEntries; + + //### Our prefix->namespaceURI bindings + + std::map bindings; + + +}; + + + +/*######################################################################### +## CharacterDataImpl +#########################################################################*/ + +/** + * + */ +class CharacterDataImpl : virtual public CharacterData, protected NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getData() throw(DOMException); + + /** + * + */ + virtual void setData(const DOMString& val) throw(DOMException); + + /** + * + */ + virtual unsigned long getLength(); + + /** + * + */ + virtual DOMString substringData(unsigned long offset, + unsigned long count) + throw(DOMException); + + /** + * + */ + virtual void appendData(const DOMString& arg) throw(DOMException); + + /** + * + */ + virtual void insertData(unsigned long offset, + const DOMString& arg) + throw(DOMException); + + /** + * + */ + virtual void deleteData(unsigned long offset, + unsigned long count) + throw(DOMException); + + /** + * + */ + virtual void replaceData(unsigned long offset, + unsigned long count, + const DOMString& arg) + throw(DOMException); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + CharacterDataImpl(); + + + /** + * + */ + CharacterDataImpl(DocumentImpl *owner, const DOMString &value); + + /** + * + */ + virtual ~CharacterDataImpl(); + +protected: + + //'data' is the nodeValue + +}; + + + + + +/*######################################################################### +## AttrImpl +#########################################################################*/ + +/** + * + */ +class AttrImpl : virtual public Attr, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getName(); + + /** + * + */ + virtual bool getSpecified(); + + /** + * + */ + virtual DOMString getValue(); + + /** + * + */ + virtual void setValue(const DOMString& val) throw(DOMException); + + /** + * + */ + virtual Element *getOwnerElement(); + + + /** + * + */ + virtual TypeInfo *getSchemaTypeInfo(); + + + /** + * + */ + virtual bool getIsId(); + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual void setOwnerElement(const Element *elem); + + /** + * + */ + AttrImpl(DocumentImpl *owner, const DOMString &name); + + /** + * + */ + AttrImpl(DocumentImpl *owner, const DOMString &namespaceURI, const DOMString &name); + + /** + * + */ + virtual ~AttrImpl(); + +protected: + + + Element *ownerElement; + + +}; + + + + + +/*######################################################################### +## ElementImpl +#########################################################################*/ + +/** + * + */ +class ElementImpl : virtual public Element, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getTagName(); + + /** + * + */ + virtual DOMString getAttribute(const DOMString& name); + + /** + * + */ + virtual void setAttribute(const DOMString& name, + const DOMString& value) + throw(DOMException); + + /** + * + */ + virtual void removeAttribute(const DOMString& name) + throw(DOMException); + + /** + * + */ + virtual Attr *getAttributeNode(const DOMString& name); + + /** + * + */ + virtual Attr *setAttributeNode(Attr *newAttr) + throw(DOMException); + + /** + * + */ + virtual Attr *removeAttributeNode(Attr *oldAttr) + throw(DOMException); + + /** + * + */ + virtual NodeList getElementsByTagName(const DOMString& name); + + /** + * + */ + virtual DOMString getAttributeNS(const DOMString& namespaceURI, + const DOMString& localName); + + /** + * + */ + virtual void setAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName, + const DOMString& value) + throw(DOMException); + + /** + * + */ + virtual void removeAttributeNS(const DOMString& namespaceURI, + const DOMString& localName) + throw(DOMException); + + /** + * + */ + virtual Attr *getAttributeNodeNS(const DOMString& namespaceURI, + const DOMString& localName); + + /** + * + */ + virtual Attr *setAttributeNodeNS(Attr *newAttr) + throw(DOMException); + + /** + * + */ + virtual NodeList getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName); + + /** + * + */ + virtual bool hasAttribute(const DOMString& name); + + /** + * + */ + virtual bool hasAttributeNS(const DOMString& namespaceURI, + const DOMString& localName); + + /** + * + */ + virtual TypeInfo *getSchemaTypeInto(); + + + /** + * + */ + virtual void setIdAttribute(const DOMString &name, + bool isId) throw (DOMException); + + /** + * + */ + virtual void setIdAttributeNS(const DOMString &namespaceURI, + const DOMString &localName, + bool isId) throw (DOMException); + + /** + * + */ + virtual void setIdAttributeNode(const Attr *idAttr, + bool isId) throw (DOMException); + + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + ElementImpl(); + + /** + * + */ + ElementImpl(DocumentImpl *owner, const DOMString &tagName); + + /** + * + */ + ElementImpl(DocumentImpl *owner, const DOMString &namespaceURI, const DOMString &tagName); + + /** + * + */ + virtual ~ElementImpl(); + + /** + * + */ + void normalizeNamespaces(); + +protected: + +friend class DocumentImpl; + + static void getElementsByTagNameRecursive(NodeList &list, + const DOMString& name, Element *elem); + static void getElementsByTagNameNSRecursive(NodeList &list, + const DOMString& namespaceURI, const DOMString& tagName, Element *elem); +}; + + + + + +/*######################################################################### +## TextImpl +#########################################################################*/ + +/** + * + */ +class TextImpl : virtual public Text, protected CharacterDataImpl +{ +public: + + /** + * + */ + virtual Text *splitText(unsigned long offset) + throw(DOMException); + + /** + * + */ + virtual bool getIsElementContentWhitespace(); + + /** + * + */ + virtual DOMString getWholeText(); + + + /** + * + */ + virtual Text *replaceWholeText(const DOMString &content) + throw(DOMException); + + //################## + //# Non-API methods + //################## + + /** + * + */ + TextImpl(); + + + /** + * + */ + TextImpl(DocumentImpl *owner, const DOMString &val); + + /** + * + */ + virtual ~TextImpl(); + +protected: + +}; + + + +/*######################################################################### +## CommentImpl +#########################################################################*/ + +/** + * + */ +class CommentImpl : virtual public Comment, protected CharacterDataImpl +{ +public: + + //################## + //# Non-API methods + //################## + + /** + * + */ + CommentImpl(); + + /** + * + */ + CommentImpl(DocumentImpl *owner, const DOMString &theValue); + + /** + * + */ + virtual ~CommentImpl(); +}; + + + +/*######################################################################### +## TypeInfoImpl +#########################################################################*/ + +/** + * + */ +class TypeInfoImpl : public TypeInfo +{ +public: + + /** + * + */ + virtual DOMString getTypeName(); + + /** + * + */ + virtual DOMString getTypeNamespace(); + + /** + * + */ + virtual bool isDerivedFrom(const DOMString &typeNamespaceArg, + const DOMString &typeNameArg, + const DerivationMethod derivationMethod); + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + TypeInfoImpl(const DOMString &typeNamespaceArg, + const DOMString &typeNameArg, + const DerivationMethod derivationMethod); + + /** + * + */ + virtual ~TypeInfoImpl(); + +protected: + + DOMString typeName; + + DOMString typeNamespace; + + unsigned short derivationMethod; + +}; + + + + +/*######################################################################### +## UserDataHandlerImpl +#########################################################################*/ + +/** + * + */ +class UserDataHandlerImpl : public UserDataHandler +{ +public: + + /** + * + */ + virtual void handle(unsigned short operation, + const DOMString &key, + const DOMUserData *data, + const Node *src, + const Node *dst); + + //################## + //# Non-API methods + //################## + + +protected: + + /** + * + */ + UserDataHandlerImpl(); + + /** + * + */ + virtual ~UserDataHandlerImpl(); +}; + + +/*######################################################################### +## DOMErrorImpl +#########################################################################*/ + +/** + * + */ +class DOMErrorImpl : public DOMError +{ +public: + + /** + * + */ + virtual unsigned short getSeverity(); + + /** + * + */ + virtual DOMString getMessage(); + + /** + * + */ + virtual DOMString getType(); + + /** + * + */ + virtual DOMObject *getRelatedException(); + + /** + * + */ + virtual DOMObject *getRelatedData(); + + /** + * + */ + virtual DOMLocator *getLocation(); + + + //################## + //# Non-API methods + //################## + + +protected: + + /** + * + */ + DOMErrorImpl(); + + /** + * + */ + virtual ~DOMErrorImpl(); + + unsigned short severity; + + DOMString message; + + DOMString type; + + +}; + + +/*######################################################################### +## DOMErrorHandlerImpl +#########################################################################*/ + +/** + * + */ +class DOMErrorHandlerImpl : public DOMErrorHandler +{ +public: + + /** + * + */ + virtual bool handleError(const DOMError *error); + + + + //################## + //# Non-API methods + //################## + + + +protected: + + /** + * + */ + DOMErrorHandlerImpl(); + + /** + * + */ + virtual ~DOMErrorHandlerImpl(); + + +}; + + + +/*######################################################################### +## DOMLocatorImpl +#########################################################################*/ + +/** + * + */ +class DOMLocatorImpl : public DOMLocator +{ +public: + + /** + * + */ + virtual long getLineNumber(); + + /** + * + */ + virtual long getColumnNumber(); + + /** + * + */ + virtual long getByteOffset(); + + /** + * + */ + virtual long getUtf16Offset(); + + + /** + * + */ + virtual Node *getRelatedNode(); + + + /** + * + */ + virtual DOMString getUri(); + + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + DOMLocatorImpl(); + + /** + * + */ + virtual ~DOMLocatorImpl(); + +protected: + + + long lineNumber; + + long columnNumber; + + long byteOffset; + + long utf16Offset; + + Node *relatedNode; + + DOMString uri; +}; + + +/*######################################################################### +## DOMConfigurationImpl +#########################################################################*/ + +/** + * + */ +class DOMConfigurationImpl : public DOMConfiguration +{ +public: + + /** + * + */ + virtual void setParameter(const DOMString &name, + const DOMUserData *value) throw (DOMException); + + /** + * + */ + virtual DOMUserData *getParameter(const DOMString &name) + throw (DOMException); + + /** + * + */ + virtual bool canSetParameter(const DOMString &name, + const DOMUserData *data); + + /** + * + */ + virtual DOMStringList *getParameterNames(); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + DOMConfigurationImpl(); + + /** + * + */ + virtual ~DOMConfigurationImpl(); + +protected: + +}; + + + + + + +/*######################################################################### +## CDATASectionImpl +#########################################################################*/ +/** + * + */ +class CDATASectionImpl : public CDATASection, public TextImpl +{ +public: + + //################## + //# Non-API methods + //################## + + /** + * + */ + CDATASectionImpl(); + + + /** + * + */ + CDATASectionImpl(DocumentImpl *owner, const DOMString &value); + + /** + * + */ + virtual ~CDATASectionImpl(); + +}; + + + + +/*######################################################################### +## DocumentTypeImpl +#########################################################################*/ + +/** + * + */ +class DocumentTypeImpl : public DocumentType, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getName(); + + /** + * + */ + virtual NamedNodeMap getEntities(); + + /** + * + */ + virtual NamedNodeMap getNotations(); + + /** + * + */ + virtual DOMString getPublicId(); + + /** + * + */ + virtual DOMString getSystemId(); + + /** + * + */ + virtual DOMString getInternalSubset(); + + //################## + //# Non-API methods + //################## + + /** + * + */ + DocumentTypeImpl(); + + /** + * + */ + DocumentTypeImpl(const DOMString& name, + const DOMString& publicId, + const DOMString& systemId); + /** + * + */ + virtual ~DocumentTypeImpl(); + + +protected: + DOMString name; + DOMString publicId; + DOMString systemId; + + NamedNodeMap entities; + NamedNodeMap notations; + +}; + + + + + +/*######################################################################### +## NotationImpl +#########################################################################*/ + +/** + * + */ +class NotationImpl : public Notation, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getPublicId(); + + /** + * + */ + virtual DOMString getSystemId(); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + NotationImpl(); + + /** + * + */ + NotationImpl(DocumentImpl *owner); + + /** + * + */ + virtual ~NotationImpl(); + + +protected: + + + + DOMString publicId; + + DOMString systemId; +}; + + + + + + +/*######################################################################### +## EntityImpl +#########################################################################*/ + +/** + * + */ +class EntityImpl : public Entity, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getPublicId(); + + /** + * + */ + virtual DOMString getSystemId(); + + /** + * + */ + virtual DOMString getNotationName(); + + /** + * + */ + virtual DOMString getInputEncoding(); + + /** + * + */ + virtual DOMString getXmlEncoding(); + + /** + * + */ + virtual DOMString getXmlVersion(); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + EntityImpl(); + + + /** + * + */ + EntityImpl(DocumentImpl *owner); + + /** + * + */ + virtual ~EntityImpl(); + +protected: + + + + DOMString publicId; + + DOMString systemId; + + DOMString notationName; + + DOMString inputEncoding; + + DOMString xmlEncoding; + + DOMString xmlVersion; + +}; + + + + + +/*######################################################################### +## EntityReferenceImpl +#########################################################################*/ +/** + * + */ +class EntityReferenceImpl : public EntityReference, public NodeImpl +{ +public: + + //################## + //# Non-API methods + //################## + + /** + * + */ + EntityReferenceImpl(); + + + /** + * + */ + EntityReferenceImpl(DocumentImpl *owner, const DOMString &theName); + + /** + * + */ + virtual ~EntityReferenceImpl(); + +}; + + + + + +/*######################################################################### +## ProcessingInstructionImpl +#########################################################################*/ + +/** + * + */ +class ProcessingInstructionImpl : public ProcessingInstruction, public NodeImpl +{ +public: + + /** + * + */ + virtual DOMString getTarget(); + + /** + * + */ + virtual DOMString getData(); + + /** + * + */ + virtual void setData(const DOMString& val) throw(DOMException); + + + //################## + //# Non-API methods + //################## + + + /** + * + */ + ProcessingInstructionImpl(); + + + /** + * + */ + ProcessingInstructionImpl(DocumentImpl *owner, + const DOMString &target, + const DOMString &data); + + /** + * + */ + virtual ~ProcessingInstructionImpl(); + + +protected: + + + //'target' is nodeName + + //'data' is nodeValue + + +}; + + + + + +/*######################################################################### +## DocumentFragmentImpl +#########################################################################*/ +/** + * + */ +class DocumentFragmentImpl : public DocumentFragment, public NodeImpl +{ + +public: + + //################## + //# Non-API methods + //################## + + /** + * + */ + DocumentFragmentImpl(); + + /** + * + */ + DocumentFragmentImpl(DocumentImpl *owner); + + /** + * + */ + virtual ~DocumentFragmentImpl(); + +}; + + + + + + +/*######################################################################### +## DocumentImpl +#########################################################################*/ + +/** + * + */ +class DocumentImpl : virtual public Document, public NodeImpl +{ +public: + + /** + * + */ + virtual DocumentType *getDoctype(); + + /** + * + */ + virtual DOMImplementation *getImplementation(); + + /** + * + */ + virtual Element *getDocumentElement(); + + /** + * + */ + virtual Element *createElement(const DOMString& tagName) + throw(DOMException); + + /** + * + */ + virtual DocumentFragment *createDocumentFragment(); + + /** + * + */ + virtual Text *createTextNode(const DOMString& data); + + /** + * + */ + virtual Comment *createComment(const DOMString& data); + + /** + * + */ + virtual CDATASection *createCDATASection(const DOMString& data) + throw(DOMException); + + /** + * + */ + virtual ProcessingInstruction *createProcessingInstruction(const DOMString& target, + const DOMString& data) + throw(DOMException); + + /** + * + */ + virtual Attr *createAttribute(const DOMString& name) + throw(DOMException); + + /** + * + */ + virtual EntityReference *createEntityReference(const DOMString& name) + throw(DOMException); + + /** + * + */ + virtual NodeList getElementsByTagName(const DOMString& tagname); + + + /** + * + */ + virtual Node *importNode(const Node *importedNode, + bool deep) + throw(DOMException); + + /** + * + */ + virtual Element *createElementNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException); + + /** + * + */ + virtual Attr *createAttributeNS(const DOMString& namespaceURI, + const DOMString& qualifiedName) + throw(DOMException); + + /** + * + */ + virtual NodeList getElementsByTagNameNS(const DOMString& namespaceURI, + const DOMString& localName); + + /** + * + */ + virtual Element *getElementById(const DOMString& elementId); + + + /** + * + */ + virtual DOMString getInputEncoding(); + + + /** + * + */ + virtual DOMString getXmlEncoding(); + + /** + * + */ + virtual bool getXmlStandalone(); + + /** + * + */ + virtual void setXmlStandalone(bool val) throw (DOMException); + + /** + * + */ + virtual DOMString getXmlVersion(); + + /** + * + */ + virtual void setXmlVersion(const DOMString &version) throw (DOMException); + + /** + * + */ + virtual bool getStrictErrorChecking(); + + /** + * + */ + virtual void setStrictErrorChecking(bool val); + + + /** + * + */ + virtual DOMString getDocumentURI(); + + /** + * + */ + virtual void setDocumentURI(const DOMString &uri); + + /** + * + */ + virtual Node *adoptNode(const Node *source) throw (DOMException); + + /** + * + */ + virtual DOMConfiguration *getDomConfig(); + + /** + * + */ + virtual void normalizeDocument(); + + /** + * + */ + virtual Node *renameNode(const Node *n, + const DOMString &name, + const DOMString &qualifiedName) + throw (DOMException); + + + //################## + //# Non-API methods + //################## + + DocumentImpl(const DOMImplementation *domImpl, + const DOMString &namespaceURI, + const DOMString &qualifiedName, + const DocumentType *doctype); + + virtual ~DocumentImpl(); + + + DOMString *stringCache(const DOMString &val); + + int namespaceIndex; + +protected: + + DOMImplementation *parent; + + DOMString *documentURI; + + DOMString qualifiedName; + + DocumentType *doctype; + + ElementImpl *documentElement; + + class NamedElementItem + { + public: + NamedElementItem() + { + next = NULL; + } + NamedElementItem(const DOMString &nameArg, Element *elemArg) + { + next = NULL; + name = nameArg; + elem = elemArg; + } + ~NamedElementItem() + { + if (next) + delete next; + } + NamedElementItem *next; + DOMString name; + Element *elem; + }; + + NamedElementItem elementsById; + + + DOMString xmlEncoding; + + DOMString inputEncoding; + + DOMString xmlVersion; + + bool xmlStandalone; + + bool strictErrorChecking; + + DOMConfiguration *domConfig; + + NamedNodeMap namespaceURIs; + + +}; + + + + + + + + + + + +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif // __DOMIMPL_H__ + + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + + + + diff --git a/src/dom/domstring.cpp b/src/dom/domstring.cpp new file mode 100644 index 000000000..34629edb6 --- /dev/null +++ b/src/dom/domstring.cpp @@ -0,0 +1,423 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include +#include + +#include "domstring.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ + + +//######################################################################### +//# C O N S T R U C T O R S +//######################################################################### + +DOMString::DOMString() +{ + init(); +} + +DOMString::DOMString(const DOMString &other) +{ + init(); + chars = other.chars; +} + +DOMString::DOMString(const char *str) +{ + init(); + append(str); +} + + +DOMString::~DOMString() +{ + if (cstring) + delete cstring; +} + + +/** + * Called only by Constructors + */ +void DOMString::init() +{ + cstring = NULL; + chars.clear(); +} + +//######################################################################### +//# M O D I F Y +//######################################################################### + +DOMString &DOMString::append(const DOMString &str) +{ + unsigned int len = str.size(); + for (unsigned int i=0 ; i::iterator iter = chars.begin(); + chars.erase(iter, iter + count); +} + +DOMString &DOMString::insert(unsigned long pos, const DOMString &str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + +DOMString &DOMString::insert(unsigned long pos, const char *str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + +DOMString &DOMString::insert(unsigned long pos, const std::string &str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + + +DOMString &DOMString::prepend(const DOMString &str) +{ + DOMString tmp = *this; + clear(); + append(str); + append(tmp); + return *this; +} + +DOMString &DOMString::prepend(const char *str) +{ + DOMString tmp = *this; + clear(); + append(str); + append(tmp); + return *this; +} + +DOMString &DOMString::prepend(const std::string &str) +{ + DOMString tmp = *this; + clear(); + append(str); + append(tmp); + return *this; +} + +DOMString &DOMString::replace(unsigned long pos, unsigned long count, + const DOMString &str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos+count, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + +DOMString &DOMString::replace(unsigned long pos, unsigned long count, + const char *str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos+count, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + +DOMString &DOMString::replace(unsigned long pos, unsigned long count, + const std::string &str) +{ + DOMString a = substr(0, pos); + DOMString b = substr(pos+count, size()); + clear(); + append(a); + append(str); + append(b); + return *this; +} + + +DOMString &DOMString::push_back(XMLCh ch) +{ + chars.push_back(ch); + return *this; +} + + + +void DOMString::clear() +{ + chars.clear(); + if (cstring) + { + delete cstring; + cstring = NULL; + } +} + +//######################################################################### +//# Q U E R Y +//######################################################################### + +XMLCh DOMString::charAt(unsigned long index) const +{ + return chars[index]; +} + +XMLCh DOMString::operator[](unsigned long index) const +{ + return chars[index]; +} + +DOMString DOMString::substr(unsigned long start, unsigned long end) const +{ + DOMString ret; + for (unsigned long i = start; i +#include + +namespace org +{ +namespace w3c +{ +namespace dom +{ + +/** + * + */ +typedef unsigned short XMLCh; + +class DOMString +{ +public: + + //############################### + //# C O N S T R U C T O R S + //############################### + + /** + * + */ + DOMString(); + + /** + * + */ + DOMString(const char *str); + + + /** + * + */ + DOMString(const DOMString &str); + + + /** + * + */ + DOMString(const std::string &str); + + /** + * + */ + virtual ~DOMString(); + + + //############################### + //# M O D I F Y + //############################### + + + + /** + * + */ + virtual DOMString &append(const DOMString &str); + virtual DOMString &operator +(const DOMString &str) + { return append(str); } + virtual DOMString &operator +=(const DOMString &str) + { return append(str); } + + /** + * + */ + virtual DOMString &append(const char *str); + + /** + * + */ + virtual DOMString &append(const std::string &str); + + + /** + * + */ + virtual DOMString &assign(const DOMString &str); + + /** + * + */ + DOMString &operator =(const DOMString &a); + + /** + * + */ + virtual DOMString &assign(const char *str); + + /** + * + */ + virtual DOMString &assign(const std::string &str); + + /** + * + */ + virtual void erase(unsigned long pos, unsigned long count); + + /** + * + */ + virtual DOMString &insert(unsigned long pos, const DOMString &str); + + /** + * + */ + virtual DOMString &insert(unsigned long pos, const char *str); + + /** + * + */ + virtual DOMString &insert(unsigned long pos, const std::string &str); + + + /** + * + */ + virtual DOMString &prepend(const DOMString &str); + + /** + * + */ + virtual DOMString &prepend(const char *str); + + /** + * + */ + virtual DOMString &prepend(const std::string &str); + + + /** + * + */ + virtual DOMString &replace(unsigned long pos, unsigned long count, + const DOMString &str); + + /** + * + */ + virtual DOMString &replace(unsigned long pos, unsigned long count, + const char *str); + + /** + * + */ + virtual DOMString &replace(unsigned long pos, unsigned long count, + const std::string &str); + + /** + * + */ + virtual DOMString &push_back(XMLCh ch); + + /** + * + */ + virtual void clear(); + + //############################### + //# Q U E R Y + //############################### + + /** + * + */ + virtual DOMString substr(unsigned long start, unsigned long end) const; + + /** + * + */ + virtual XMLCh charAt(unsigned long index) const; + + /** + * + */ + virtual XMLCh operator[](unsigned long index) const; + + /** + * + */ + virtual unsigned long size() const; + + /** + * + */ + virtual const char *c_str(); + + //############################### + //# C O M P A R I S O N + //############################### + + /** + * + */ + virtual int compare(const DOMString &str) const; + virtual bool operator <(const DOMString &str) const + { return (compare(str)<0) ; } + virtual bool operator <=(const DOMString &str) const + { return (compare(str)<=0) ; } + virtual bool operator >(const DOMString &str) const + { return (compare(str)>0) ; } + virtual bool operator >=(const DOMString &str) const + { return (compare(str)>=0) ; } + virtual bool operator !=(const DOMString &str) const + { return (compare(str)!=0) ; } + virtual bool operator ==(const DOMString &str) const + { return (compare(str)==0) ; } + + /** + * + */ + virtual int compare(const char *str) const; + virtual bool operator <(const char *str) const + { return (compare(str)<0) ; } + virtual bool operator <=(const char *str) const + { return (compare(str)<=0) ; } + virtual bool operator >(const char *str) const + { return (compare(str)>0) ; } + virtual bool operator >=(const char *str) const + { return (compare(str)>=0) ; } + virtual bool operator !=(const char *str) const + { return (compare(str)!=0) ; } + virtual bool operator ==(const char *str) const + { return (compare(str)==0) ; } + + /** + * + */ + virtual int compare(const std::string &str) const; + virtual bool operator <(const std::string &str) const + { return (compare(str)<0) ; } + virtual bool operator <=(const std::string &str) const + { return (compare(str)<=0) ; } + virtual bool operator >(const std::string &str) const + { return (compare(str)>0) ; } + virtual bool operator >=(const std::string &str) const + { return (compare(str)>=0) ; } + virtual bool operator !=(const std::string &str) const + { return (compare(str)!=0) ; } + virtual bool operator ==(const std::string &str) const + { return (compare(str)==0) ; } + + + + +protected: + + void init(); + + char *cstring; + + std::vector chars; + +}; // class DOMString + + + + +//############################### +//# O P E R A T O R S +//############################### + +DOMString &operator +(DOMString &a, const char *b); + +DOMString &operator +(const char *b, DOMString &a); + + + +} //namespace dom +} //namespace w3c +} //namespace org + +#endif // __DOMSTRING_H__ +//######################################################################### +//## E N D O F F I L E +//######################################################################### + + diff --git a/src/dom/domstringimpl.h b/src/dom/domstringimpl.h new file mode 100644 index 000000000..965f9702d --- /dev/null +++ b/src/dom/domstringimpl.h @@ -0,0 +1,107 @@ +#ifndef __DOMSTRINGIMPL_H__ +#define __DOMSTRINGIMPL_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +class DOMStringImpl +{ +public: + + /** + * + */ + DOMString(); + + /** + * + */ + DOMString(const char *str); + + /** + * + */ + DOMString(const DOMString &str); + + /** + * + */ + virtual ~DOMString(); + + /** + * + */ + virtual void append(const DOMString &str); + + /** + * + */ + virtual void append(const char *str); + + /** + * + */ + virtual void push_back(int ch); + + /** + * + */ + virtual DOMString &substring(int start, int end); + + /** + * + */ + virtual int charAt(unsigned long index); + + /** + * + */ + virtual unsigned long size(); + + /** + * + */ + virtual const char *c_str(); + + +protected: + + void init(); + + char *cstring; + + unsigned long length; + +}; + + + + + + +#endif /* __DOMSTRINGIMPL_H__ */ diff --git a/src/dom/events.h b/src/dom/events.h new file mode 100644 index 000000000..5a8fe6ccd --- /dev/null +++ b/src/dom/events.h @@ -0,0 +1,1351 @@ +#ifndef __EVENTS_H__ +#define __EVENTS_H__ + +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + +#include "dom.h" +#include "views.h" + + + + +namespace org { +namespace w3c { +namespace dom { +namespace events { + + + + +//Local definitions +typedef dom::DOMString DOMString; +typedef dom::DOMTimeStamp DOMTimeStamp; +typedef dom::Node Node; + + + + +//forward declarations +class Event; +class EventTarget; +class EventListener; +class DocumentEvent; +class CustomEvent; +class UIEvent; +class TextEvent; +class MouseEvent; +class KeyboardEvent; +class MutationEvent; +class MutationNameEvent; + + + +/*######################################################################### +## EventException +#########################################################################*/ + +/** + * + */ +class EventException +{ +public: + + EventException(short theCode) + { + code = theCode; + } + + ~EventException() throw() + {} + + unsigned short code; +}; + + /** + * EventExceptionCode + */ + enum + { + UNSPECIFIED_EVENT_TYPE_ERR = 0, + DISPATCH_REQUEST_ERR = 1 + }; + + + +/*######################################################################### +## Event +#########################################################################*/ + +/** + * + */ +class Event +{ +public: + + /** + * PhaseType + */ + typedef enum + { + CAPTURING_PHASE = 1, + AT_TARGET = 2, + BUBBLING_PHASE = 3 + } PhaseType; + + /** + * + */ + virtual DOMString getType() const + { return eventType; } + + /** + * + */ + virtual EventTarget *getTarget() + { return target; } + + /** + * + */ + virtual EventTarget *getCurrentTarget() + { return currentTarget; } + + /** + * + */ + virtual unsigned short getEventPhase() + { return eventPhase; } + + /** + * + */ + virtual bool getBubbles() + { return canBubble; } + + /** + * + */ + virtual bool getCancelable() + { return cancelable; } + + /** + * + */ + virtual DOMTimeStamp getTimeStamp() + { return timeStamp; } + + /** + * + */ + virtual void stopPropagation() + { + } + + /** + * + */ + virtual void preventDefault() + { + } + + /** + * + */ + virtual void initEvent(const DOMString &eventTypeArg, + bool canBubbleArg, + bool cancelableArg) + { + namespaceURI = ""; + eventType = eventTypeArg; + canBubble = canBubbleArg; + cancelable = cancelableArg; + } + + + /** + * + */ + virtual DOMString getNamespaceURI() const + { return namespaceURI; } + + /** + * + */ + virtual bool isCustom() + { return custom; } + + /** + * + */ + virtual void stopImmediatePropagation() + { + } + + /** + * + */ + virtual bool isDefaultPrevented() + { return defaultPrevented; } + + /** + * + */ + virtual void initEventNS(const DOMString &namespaceURIArg, + const DOMString &eventTypeArg, + bool canBubbleArg, + bool cancelableArg) + { + namespaceURI = namespaceURIArg; + eventType = eventTypeArg; + canBubble = canBubbleArg; + cancelable = cancelableArg; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + Event() + { + init(); + } + + /** + * + */ + Event(const DOMString &eventTypeArg) + { + init(); + eventType = eventTypeArg; + } + + + /** + * + */ + Event(const Event &other) + { + eventType = other.eventType; + target = other.target; + currentTarget = other.currentTarget; + eventPhase = other.eventPhase; + canBubble = other.canBubble; + cancelable = other.cancelable; + timeStamp = other.timeStamp; + namespaceURI = other.namespaceURI; + custom = other.custom; + defaultPrevented = other.defaultPrevented; + } + + /** + * + */ + virtual ~Event() {} + +protected: + + /** + * + */ + void init() + { + eventType = ""; + target = NULL; + currentTarget = NULL; + eventPhase = 0; + canBubble = false; + cancelable = false; + //timeStamp = other.timeStamp; + namespaceURI = ""; + custom = false; + defaultPrevented = false; + } + + DOMString eventType; + EventTarget *target; + EventTarget *currentTarget; + unsigned short eventPhase; + bool canBubble; + bool cancelable; + DOMTimeStamp timeStamp; + DOMString namespaceURI; + bool custom; + bool defaultPrevented; + +}; + + + + +/*######################################################################### +## EventListener +#########################################################################*/ + +/** + * + */ +class EventListener +{ +public: + + /** + * + */ + virtual void handleEvent(const Event &evt) + {} + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~EventListener() {} +}; + + + + + + +/*######################################################################### +## EventTarget +#########################################################################*/ + +class EventListenerEntry +{ +public: + EventListenerEntry(const DOMString &namespaceURIArg, + const DOMString &eventTypeArg, + const EventListener *listenerArg, + bool useCaptureArg) + { + namespaceURI = namespaceURIArg; + eventType = eventTypeArg; + listener = (EventListener *)listenerArg; + useCapture = useCaptureArg; + } + + EventListenerEntry(const EventListenerEntry &other) + { + namespaceURI = other.namespaceURI; + eventType = other.eventType; + listener = other.listener; + useCapture = other.useCapture; + } + + virtual ~EventListenerEntry() {} + + DOMString namespaceURI; + DOMString eventType; + EventListener *listener; + bool useCapture; +}; + + +/** + * + */ +class EventTarget +{ +public: + + /** + * + */ + virtual void addEventListener(const DOMString &type, + const EventListener *listener, + bool useCapture) + { + EventListenerEntry entry("", type, listener, useCapture); + listeners.push_back(entry); + } + + /** + * + */ + virtual void removeEventListener(const DOMString &type, + const EventListener *listener, + bool useCapture) + { + std::vector::iterator iter; + for (iter = listeners.begin() ; iter != listeners.end() ; iter++) + { + EventListenerEntry entry = *iter; + if (entry.eventType == type && + entry.listener == listener && + useCapture && entry.useCapture) + listeners.erase(iter); + } + } + + /** + * + */ + virtual bool dispatchEvent(const Event &evt) throw(EventException) + { + + for (unsigned int i=0 ; ihandleEvent(evt); + } + } + return true; + } + + + /** + * + */ + virtual void addEventListenerNS(const DOMString &namespaceURI, + const DOMString &type, + const EventListener *listener, + bool useCapture) + { + EventListenerEntry entry(namespaceURI, type, listener, useCapture); + listeners.push_back(entry); + } + + /** + * + */ + virtual void removeEventListenerNS(const DOMString &namespaceURI, + const DOMString &type, + const EventListener *listener, + bool useCapture) + { + std::vector::iterator iter; + for (iter = listeners.begin() ; iter != listeners.end() ; iter++) + { + EventListenerEntry entry = *iter; + if (entry.namespaceURI == namespaceURI && + entry.eventType == type && + entry.listener == listener && + useCapture && entry.useCapture) + listeners.erase(iter); + } + } + + /** + * + */ + virtual bool willTriggerNS(const DOMString &namespaceURI, + const DOMString &type) + { + std::vector::iterator iter; + for (iter = listeners.begin() ; iter != listeners.end() ; iter++) + { + EventListenerEntry entry = *iter; + if (entry.namespaceURI == namespaceURI && + entry.eventType == type) + return true; + } + return false; + } + + /** + * + */ + virtual bool hasEventListenerNS(const DOMString &namespaceURI, + const DOMString &type) + { + std::vector::iterator iter; + for (iter = listeners.begin() ; iter != listeners.end() ; iter++) + { + EventListenerEntry entry = *iter; + if (entry.namespaceURI == namespaceURI && + entry.eventType == type) + return true; + } + return false; + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + EventTarget() {} + + /** + * + */ + EventTarget(const EventTarget &other) + { + listeners = other.listeners; + } + + /** + * + */ + virtual ~EventTarget() {} + +protected: + + std::vector listeners; + +}; + + + + +/*######################################################################### +## DocumentEvent +#########################################################################*/ + +/* + * + */ +class DocumentEvent : virtual public Event +{ +public: + + /** + * + */ + virtual Event createEvent(const DOMString &eventType) + throw (dom::DOMException) + { + Event event; + return event; + } + + /** + * + */ + virtual bool canDispatch(const DOMString &namespaceURI, + const DOMString &type) + { + return dispatchable; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + DocumentEvent() {} + + /** + * + */ + DocumentEvent(const DocumentEvent &other) : Event(other) + { + dispatchable = other.dispatchable; + } + + /** + * + */ + virtual ~DocumentEvent() {} + +protected: + + bool dispatchable; + + +}; + + +/*######################################################################### +## CustomEvent +#########################################################################*/ + +/* + * + */ +class CustomEvent : virtual public Event +{ +public: + + /** + * + */ + virtual void setDispatchState(const EventTarget *target, + unsigned short phase) + { + } + + /** + * + */ + virtual bool isPropagationStopped() + { + return propagationStopped; + } + + /** + * + */ + virtual bool isImmediatePropagationStopped() + { + return immediatePropagationStopped; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + CustomEvent() {} + + /** + * + */ + CustomEvent(const CustomEvent &other) : Event(other) + { + propagationStopped = other.propagationStopped; + immediatePropagationStopped = other.immediatePropagationStopped; + } + + /** + * + */ + virtual ~CustomEvent() {} + +protected: + + bool propagationStopped; + bool immediatePropagationStopped; + + + +}; + + + + +/*######################################################################### +## UIEvent +#########################################################################*/ + +/** + * + */ +class UIEvent : virtual public Event +{ +public: + + /** + * Note that the return type as listed in level3 events.idl is + * AbstractView, while in level3 views.idl it is called View + */ + virtual views::View getView() + { return view; } + + /** + * + */ + virtual long getDetail() + { return detail; } + + /** + * Note that views.idl and events.idl disagree on the name of Views + */ + virtual void initUIEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg) + { + } + + /** + * Note that views.idl and events.idl disagree on the name of Views + */ + virtual void initUIEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg) + { + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + UIEvent() {} + + /** + * + */ + UIEvent(const UIEvent &other) : Event(other) + { + view = other.view; + detail = other.detail; + } + + /** + * + */ + virtual ~UIEvent() {} + +protected: + + views::View view; + long detail; +}; + + + + +/*######################################################################### +## TextEvent +#########################################################################*/ + +/** + * + */ +class TextEvent : virtual public UIEvent +{ +public: + + /** + * + */ + virtual DOMString getData() + { return data; } + + /** + * Note that views.idl and events.idl disagree on the name of Views + */ + virtual void initTextEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg) + { + } + + /** + * + */ + virtual void initTextEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg) + { + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + TextEvent() {} + + /** + * + */ + TextEvent(const TextEvent &other) : Event(other), UIEvent(other) + { + data = other.data; + } + + /** + * + */ + virtual ~TextEvent() {} + +protected: + + DOMString data; + +}; + + + + + + + + +/*######################################################################### +## MouseEvent +#########################################################################*/ + +/** + * + */ +class MouseEvent : virtual public UIEvent +{ +public: + + /** + * + */ + virtual long getScreenX() + { return screenX; } + + /** + * + */ + virtual long getScreenY() + { return screenY; } + + /** + * + */ + virtual long getClientX() + { return clientX; } + + /** + * + */ + virtual long getClientY() + { return clientY; } + + /** + * + */ + virtual bool getCtrlKey() + { return ctrlKey; } + + /** + * + */ + virtual bool getShiftKey() + { return shiftKey; } + + /** + * + */ + virtual bool getAltKey() + { return altKey; } + + /** + * + */ + virtual bool getMetaKey() + { return metaKey; } + + /** + * + */ + virtual unsigned short getButton() + { return button; } + + /** + * + */ + virtual EventTarget *getRelatedTarget() + { return relatedTarget; } + + + /** + * + */ + virtual bool getModifierState() + { return modifierState; } + + /** + * + */ + virtual void initMouseEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + bool ctrlKeyArg, + bool altKeyArg, + bool shiftKeyArg, + bool metaKeyArg, + unsigned short buttonArg, + const EventTarget *relatedTargetArg) + { + } + + + /** + * + */ + virtual void initMouseEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + unsigned short buttonArg, + const EventTarget *relatedTargetArg, + const DOMString &modifiersList) + { + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + MouseEvent() {} + + /** + * + */ + MouseEvent(const MouseEvent &other) : Event(other), UIEvent(other) + { + screenX = other.screenX; + screenY = other.screenY; + clientX = other.clientX; + clientY = other.clientY; + ctrlKey = other.ctrlKey; + shiftKey = other.shiftKey; + altKey = other.altKey; + metaKey = other.metaKey; + button = other.button; + relatedTarget = other.relatedTarget; + modifierState = other.modifierState; + } + + /** + * + */ + virtual ~MouseEvent() {} + +protected: + + long screenX; + long screenY; + long clientX; + long clientY; + bool ctrlKey; + bool shiftKey; + bool altKey; + bool metaKey; + unsigned short button; + EventTarget *relatedTarget; + bool modifierState; + + +}; + + + + +/*######################################################################### +## KeyboardEvent +#########################################################################*/ + +/** + * + */ +class KeyboardEvent : virtual public UIEvent +{ +public: + + typedef enum + { + DOM_KEY_LOCATION_STANDARD = 0x00, + DOM_KEY_LOCATION_LEFT = 0x01, + DOM_KEY_LOCATION_RIGHT = 0x02, + DOM_KEY_LOCATION_NUMPAD = 0x03 + } KeyLocationCode; + + /** + * + */ + virtual DOMString getKeyIdentifier() + { return keyIdentifier; } + + /** + * + */ + virtual unsigned long getKeyLocation() + { return keyLocation; } + + /** + * + */ + virtual bool getCtrlKey() + { return ctrlKey; } + + /** + * + */ + virtual bool getShiftKey() + { return shiftKey; } + + /** + * + */ + virtual bool getAltKey() + { return altKey; } + + /** + * + */ + virtual bool getMetaKey() + { return metaKey; } + + /** + * + */ + virtual bool getModifierState() + { return modifierState; } + + /** + * + */ + virtual void initKeyboardEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + const DOMString &keyIdentifier, + unsigned long keyLocation, + const DOMString modifiersList) + { + } + + + + /** + * + */ + virtual void initKeyboardEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const views::View *viewArg, + const DOMString &keyIdentifier, + unsigned long keyLocation, + const DOMString modifiersList) + { + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + KeyboardEvent() {} + + /** + * + */ + KeyboardEvent(const KeyboardEvent &other) : Event(other), UIEvent(other) + { + keyIdentifier = other.keyIdentifier; + keyLocation = other.keyLocation; + ctrlKey = other.ctrlKey; + shiftKey = other.shiftKey; + altKey = other.altKey; + metaKey = other.metaKey; + modifierState = other.modifierState; + } + + /** + * + */ + virtual ~KeyboardEvent() {} + +protected: + + DOMString keyIdentifier; + unsigned long keyLocation; + bool ctrlKey; + bool shiftKey; + bool altKey; + bool metaKey; + bool modifierState; +}; + + + + + + + + + +/*######################################################################### +## MutationEvent +#########################################################################*/ + +/** + * + */ +class MutationEvent : virtual public Event +{ +public: + + /** + * attrChangeType + */ + typedef enum + { + MODIFICATION = 1, + ADDITION = 2, + REMOVAL = 3 + } AttrChangeType; + + /** + * + */ + virtual Node *getRelatedNode() + { return relatedNode; } + + /** + * + */ + virtual DOMString getPrevValue() + { return prevValue; } + + /** + * + */ + virtual DOMString getNewValue() + { return newValue; } + + /** + * + */ + virtual DOMString getAttrName() + { return attrName; } + + /** + * + */ + virtual unsigned short getAttrChange() + { + return attrChange; + } + + /** + * + */ + virtual void initMutationEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node *relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg) + { + } + + /** + * + */ + virtual void initMutationEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node *relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg) + { + } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + MutationEvent() + { + relatedNode = NULL; + } + + /** + * + */ + MutationEvent(const MutationEvent &other) : Event(other) + { + relatedNode = other.relatedNode; + prevValue = other.prevValue; + newValue = other.newValue; + attrName = other.attrName; + attrChange = other.attrChange; + } + + /** + * + */ + virtual ~MutationEvent() {} + +protected: + + Node *relatedNode; + DOMString prevValue; + DOMString newValue; + DOMString attrName; + unsigned short attrChange; + +}; + + + + +/*######################################################################### +## MutationNameEvent +#########################################################################*/ + +/** + * + */ +class MutationNameEvent : virtual public MutationEvent +{ +public: + + /** + * + */ + virtual DOMString getPrevNamespaceURI() + { return prevNamespaceURI; } + + /** + * + */ + virtual DOMString getPrevNodeName() + { return prevNodeName; } + + /** + * + */ + virtual void initMutationNameEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node *relatedNodeArg, + const DOMString &prevNamespaceURIArg, + const DOMString &prevNodeNameArg) + { + } + + + /** + * + */ + virtual void initMutationNameEventNS(const DOMString &namespaceURI, + const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node *relatedNodeArg, + const DOMString &prevNamespaceURIArg, + const DOMString &prevNodeNameArg) + { + } + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + MutationNameEvent() {} + + + /** + * + */ + MutationNameEvent(const MutationNameEvent &other) + : Event(other), MutationEvent(other) + { + prevNamespaceURI = other.prevNamespaceURI; + prevNodeName = other.prevNodeName; + } + + + /** + * + */ + virtual ~MutationNameEvent() {} + +protected: + + DOMString prevNamespaceURI; + DOMString prevNodeName; + + +}; + + + + + + +} //namespace events +} //namespace dom +} //namespace w3c +} //namespace org + +#endif /* __EVENTS_H__ */ + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + diff --git a/src/dom/io/base64stream.cpp b/src/dom/io/base64stream.cpp new file mode 100644 index 000000000..880ad5a8e --- /dev/null +++ b/src/dom/io/base64stream.cpp @@ -0,0 +1,339 @@ +/** + * Phoebe DOM Implementation. + * + * Base64-enabled input and output streams + * + * This class allows easy encoding and decoding + * of Base64 data with a stream interface, hiding + * the implementation from the user. + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + +#include "base64stream.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# B A S E 6 4 I N P U T S T R E A M +//######################################################################### + +static int base64decode[] = +{ +/*00*/ -1, -1, -1, -1, -1, -1, -1, -1, +/*08*/ -1, -1, -1, -1, -1, -1, -1, -1, +/*10*/ -1, -1, -1, -1, -1, -1, -1, -1, +/*18*/ -1, -1, -1, -1, -1, -1, -1, -1, +/*20*/ -1, -1, -1, -1, -1, -1, -1, -1, +/*28*/ -1, -1, -1, 62, -1, -1, -1, 63, +/*30*/ 52, 53, 54, 55, 56, 57, 58, 59, +/*38*/ 60, 61, -1, -1, -1, -1, -1, -1, +/*40*/ -1, 0, 1, 2, 3, 4, 5, 6, +/*48*/ 7, 8, 9, 10, 11, 12, 13, 14, +/*50*/ 15, 16, 17, 18, 19, 20, 21, 22, +/*58*/ 23, 24, 25, -1, -1, -1, -1, -1, +/*60*/ -1, 26, 27, 28, 29, 30, 31, 32, +/*68*/ 33, 34, 35, 36, 37, 38, 39, 40, +/*70*/ 41, 42, 43, 44, 45, 46, 47, 48, +/*78*/ 49, 50, 51, -1, -1, -1, -1, -1 +}; + + +/** + * + */ +Base64InputStream::Base64InputStream(InputStream &sourceStream) + : BasicInputStream(sourceStream) +{ + outCount = 0; + padCount = 0; + closed = false; + done = false; +} + +/** + * + */ +Base64InputStream::~Base64InputStream() +{ + close(); +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int Base64InputStream::available() +{ + if (closed ) + return 0; + int len = source.available() * 2 / 3; + return len; +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void Base64InputStream::close() +{ + if (closed) + return; + source.close(); + closed = true; +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int Base64InputStream::get() +{ + if (closed) + return -1; + + if (outCount - padCount > 0) + { + return outBytes[3-(outCount--)]; + } + + if (done) + return -1; + + int inBytes[4]; + int inCount = 0; + while (inCount < 4) + { + int ch = source.get(); + if (ch < 0) + { + while (inCount < 4) //pad if needed + { + inBytes[inCount++] = 0; + padCount++; + } + done = true; + break; + } + if (isspace(ch)) //ascii whitespace + { + //nothing + } + else if (ch == '=') //padding + { + inBytes[inCount++] = 0; + padCount++; + } + else + { + int byteVal = base64decode[ch & 0x7f]; + //printf("char:%c %d\n", ch, byteVal); + if (byteVal < 0) + { + //Bad lookup value + } + inBytes[inCount++] = byteVal; + } + } + + outBytes[0] = ((inBytes[0]<<2) & 0xfc) | ((inBytes[1]>>4) & 0x03); + outBytes[1] = ((inBytes[1]<<4) & 0xf0) | ((inBytes[2]>>2) & 0x0f); + outBytes[2] = ((inBytes[2]<<6) & 0xc0) | ((inBytes[3] ) & 0x3f); + + outCount = 3; + + //try again + if (outCount - padCount > 0) + { + return outBytes[3-(outCount--)]; + } + + return -1; + +} + + +//######################################################################### +//# B A S E 6 4 O U T P U T S T R E A M +//######################################################################### + +static char *base64encode = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * + */ +Base64OutputStream::Base64OutputStream(OutputStream &destinationStream) + : BasicOutputStream(destinationStream) +{ + column = 0; + columnWidth = 72; + outBuf = 0L; + bitCount = 0; +} + +/** + * + */ +Base64OutputStream::~Base64OutputStream() +{ + close(); +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void Base64OutputStream::close() +{ + if (closed) + return; + + //get any last bytes (1 or 2) out of the buffer + if (bitCount == 16) + { + outBuf <<= 2; //pad to make 18 bits + + int indx = (int)((outBuf & 0x0003f000L) >> 12); + int obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x00000fc0L) >> 6); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x0000003fL) ); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + putCh('='); + } + else if (bitCount == 8) + { + outBuf <<= 4; //pad to make 12 bits + + int indx = (int)((outBuf & 0x00000fc0L) >> 6); + int obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x0000003fL) ); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + putCh('='); + putCh('='); + } + + if (columnWidth > 0) //if <=0, no newlines + destination.put('\n'); + + destination.close(); + closed = true; +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void Base64OutputStream::flush() +{ + if (closed) + return; + //dont flush here. do it on close() + destination.flush(); +} + +/** + * Private. Put a char to the output stream, checking for line length + */ +void Base64OutputStream::putCh(int ch) +{ + destination.put(ch); + column++; + if (columnWidth > 0 && column >= columnWidth) + { + destination.put('\n'); + column = 0; + } +} + + +/** + * Writes the specified byte to this output stream. + */ +void Base64OutputStream::put(int ch) +{ + if (closed) + { + //probably throw an exception here + return; + } + + outBuf <<= 8; + outBuf |= (ch & 0xff); + bitCount += 8; + if (bitCount >= 24) + { + int indx = (int)((outBuf & 0x00fc0000L) >> 18); + int obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x0003f000L) >> 12); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x00000fc0L) >> 6); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + indx = (int)((outBuf & 0x0000003fL) ); + obyte = (int)base64encode[indx & 63]; + putCh(obyte); + + bitCount = 0; + outBuf = 0L; + } +} + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/base64stream.h b/src/dom/io/base64stream.h new file mode 100644 index 000000000..6fc94d34f --- /dev/null +++ b/src/dom/io/base64stream.h @@ -0,0 +1,146 @@ +#ifndef __DOM_IO_BASE64STREAM_H__ +#define __DOM_IO_BASE64STREAM_H__ + +/** + * Phoebe DOM Implementation. + * + * Base64-enabled input and output streams + * + * This class allows easy encoding and decoding + * of Base64 data with a stream interface, hiding + * the implementation from the user. + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + +#include "domstream.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + +//######################################################################### +//# B A S E 6 4 I N P U T S T R E A M +//######################################################################### + +/** + * This class is for decoding a Base-64 encoded InputStream source + * + */ +class Base64InputStream : public BasicInputStream +{ + +public: + + Base64InputStream(InputStream &sourceStream); + + virtual ~Base64InputStream(); + + virtual int available(); + + virtual void close(); + + virtual int get(); + +private: + + int outBytes[3]; + + int outCount; + + int padCount; + + bool done; + +}; // class Base64InputStream + + + + +//######################################################################### +//# B A S E 6 4 O U T P U T S T R E A M +//######################################################################### + +/** + * This class is for Base-64 encoding data going to the + * destination OutputStream + * + */ +class Base64OutputStream : public BasicOutputStream +{ + +public: + + Base64OutputStream(OutputStream &destinationStream); + + virtual ~Base64OutputStream(); + + virtual void close(); + + virtual void flush(); + + virtual void put(int ch); + + /** + * Sets the maximum line length for base64 output. If + * set to <=0, then there will be no line breaks; + */ + virtual void setColumnWidth(int val) + { columnWidth = val; } + +private: + + void putCh(int ch); + + int column; + + int columnWidth; + + unsigned long outBuf; + + int bitCount; + +}; // class Base64OutputStream + + + + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif /* __INKSCAPE_IO_BASE64STREAM_H__ */ diff --git a/src/dom/io/bufferstream.cpp b/src/dom/io/bufferstream.cpp new file mode 100644 index 000000000..4664b07e9 --- /dev/null +++ b/src/dom/io/bufferstream.cpp @@ -0,0 +1,166 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + +/** + * This class provided buffered endpoints for input and output. + */ + +#include "bufferstream.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + +//######################################################################### +//# B U F F E R I N P U T S T R E A M +//######################################################################### + + +/** + * + */ +BufferInputStream::BufferInputStream( + const std::vector &sourceBuffer) + : buffer(sourceBuffer) +{ + position = 0; + closed = false; +} + +/** + * + */ +BufferInputStream::~BufferInputStream() +{ + +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int BufferInputStream::available() +{ + if (closed) + return -1; + return buffer.size() - position; +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void BufferInputStream::close() +{ + closed = true; +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int BufferInputStream::get() +{ + if (closed) + return -1; + if (position >= (int)buffer.size()) + return -1; + int ch = (int) buffer[position++]; + return ch; +} + + + + +//######################################################################### +//# B U F F E R O U T P U T S T R E A M +//######################################################################### + +/** + * + */ +BufferOutputStream::BufferOutputStream() +{ + closed = false; +} + +/** + * + */ +BufferOutputStream::~BufferOutputStream() +{ +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void BufferOutputStream::close() +{ + closed = true; +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void BufferOutputStream::flush() +{ + //nothing to do +} + +/** + * Writes the specified byte to this output stream. + */ +void BufferOutputStream::put(XMLCh ch) +{ + if (closed) + return; + buffer.push_back(ch); +} + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/bufferstream.h b/src/dom/io/bufferstream.h new file mode 100644 index 000000000..a8f7ea319 --- /dev/null +++ b/src/dom/io/bufferstream.h @@ -0,0 +1,133 @@ +#ifndef __BUFFERSTREAM_H__ +#define __BUFFERSTREAM_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + +#include "domstream.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# S T R I N G I N P U T S T R E A M +//######################################################################### + +/** + * This class is for reading character from a DOMString + * + */ +class BufferInputStream : public InputStream +{ + +public: + + BufferInputStream(const std::vector &sourceBuffer); + + virtual ~BufferInputStream(); + + virtual int available(); + + virtual void close(); + + virtual int get(); + +private: + + const std::vector &buffer; + + long position; + + bool closed; + +}; // class BufferInputStream + + + + +//######################################################################### +//# B U F F E R O U T P U T S T R E A M +//######################################################################### + +/** + * This class is for sending a stream to a character buffer + * + */ +class BufferOutputStream : public OutputStream +{ + +public: + + BufferOutputStream(); + + virtual ~BufferOutputStream(); + + virtual void close(); + + virtual void flush(); + + virtual void put(XMLCh ch); + + virtual std::vector &getBuffer() + { return buffer; } + + virtual void clear() + { buffer.clear(); } + +private: + + std::vector buffer; + + bool closed; + + +}; // class BufferOutputStream + + + + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + + +#endif /* __BUFFERSTREAM_H__ */ diff --git a/src/dom/io/domstream.cpp b/src/dom/io/domstream.cpp new file mode 100644 index 000000000..63257062f --- /dev/null +++ b/src/dom/io/domstream.cpp @@ -0,0 +1,878 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +/** + * Our base input/output stream classes. These are is directly + * inherited from iostreams, and includes any extra + * functionality that we might need. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Inkscape.org + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include "domstream.h" +#include "charclass.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# U T I L I T Y +//######################################################################### + +void pipeStream(InputStream &source, OutputStream &dest) +{ + for (;;) + { + int ch = source.get(); + if (ch<0) + break; + dest.put(ch); + } + dest.flush(); +} + +//######################################################################### +//# B A S I C I N P U T S T R E A M +//######################################################################### + + +/** + * + */ +BasicInputStream::BasicInputStream(const InputStream &sourceStream) + : source((InputStream &)sourceStream) +{ + closed = false; +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int BasicInputStream::available() +{ + if (closed) + return 0; + return source.available(); +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void BasicInputStream::close() +{ + if (closed) + return; + source.close(); + closed = true; +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int BasicInputStream::get() +{ + if (closed) + return -1; + return source.get(); +} + + + +//######################################################################### +//# B A S I C O U T P U T S T R E A M +//######################################################################### + +/** + * + */ +BasicOutputStream::BasicOutputStream(const OutputStream &destinationStream) + : destination((OutputStream &)destinationStream) +{ + closed = false; +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void BasicOutputStream::close() +{ + if (closed) + return; + destination.close(); + closed = true; +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void BasicOutputStream::flush() +{ + if (closed) + return; + destination.flush(); +} + +/** + * Writes the specified byte to this output stream. + */ +void BasicOutputStream::put(XMLCh ch) +{ + if (closed) + return; + destination.put(ch); +} + + + +//######################################################################### +//# B A S I C R E A D E R +//######################################################################### + + +/** + * + */ +BasicReader::BasicReader(Reader &sourceReader) +{ + source = &sourceReader; +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this reader without blocking by the next caller of a method for + * this reader. + */ +int BasicReader::available() +{ + if (source) + return source->available(); + else + return 0; +} + + +/** + * Closes this reader and releases any system resources + * associated with the reader. + */ +void BasicReader::close() +{ + if (source) + source->close(); +} + +/** + * Reads the next byte of data from the reader. + */ +int BasicReader::get() +{ + if (source) + return source->get(); + else + return -1; +} + + + + + + +/** + * Reads a line of data from the reader. + */ +DOMString BasicReader::readLine() +{ + DOMString str; + while (available() > 0) + { + XMLCh ch = get(); + if (ch == '\n') + break; + str.push_back(ch); + } + return str; +} + +/** + * Reads a line of data from the reader. + */ +DOMString BasicReader::readWord() +{ + DOMString str; + while (available() > 0) + { + XMLCh ch = get(); + if (isWhitespace(ch)) + break; + str.push_back(ch); + } + return str; +} + + +static bool getLong(DOMString &str, long *val) +{ + const char *begin = str.c_str(); + char *end; + long ival = strtol(begin, &end, 10); + if (str == end) + return false; + *val = ival; + return true; +} + +static bool getULong(const DOMString &str, unsigned long *val) +{ + DOMString tmp = str; + char *begin = (char *)tmp.c_str(); + char *end; + unsigned long ival = strtoul(begin, &end, 10); + if (begin == end) + return false; + *val = ival; + return true; +} + +static bool getDouble(const DOMString &str, double *val) +{ + DOMString tmp = str; + const char *begin = tmp.c_str(); + char *end; + double ival = strtod(begin, &end); + if (begin == end) + return false; + *val = ival; + return true; +} + + + + +/** + * + */ +Reader &BasicReader::readBool (bool& val ) +{ + DOMString buf = readWord(); + if (buf == "true") + val = true; + else + val = false; + return *this; +} + +/** + * + */ +Reader &BasicReader::readShort (short& val ) +{ + DOMString buf = readWord(); + long ival; + if (getLong(buf, &ival)) + val = (short) ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readUnsignedShort (unsigned short& val ) +{ + DOMString buf = readWord(); + unsigned long ival; + if (getULong(buf, &ival)) + val = (unsigned short) ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readInt (int& val ) +{ + DOMString buf = readWord(); + long ival; + if (getLong(buf, &ival)) + val = (int) ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readUnsignedInt (unsigned int& val ) +{ + DOMString buf = readWord(); + unsigned long ival; + if (getULong(buf, &ival)) + val = (unsigned int) ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readLong (long& val ) +{ + DOMString buf = readWord(); + long ival; + if (getLong(buf, &ival)) + val = ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readUnsignedLong (unsigned long& val ) +{ + DOMString buf = readWord(); + unsigned long ival; + if (getULong(buf, &ival)) + val = ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readFloat (float& val ) +{ + DOMString buf = readWord(); + double ival; + if (getDouble(buf, &ival)) + val = (float)ival; + return *this; +} + +/** + * + */ +Reader &BasicReader::readDouble (double& val ) +{ + DOMString buf = readWord(); + double ival; + if (getDouble(buf, &ival)) + val = ival; + return *this; +} + + + +//######################################################################### +//# I N P U T S T R E A M R E A D E R +//######################################################################### + + +InputStreamReader::InputStreamReader(const InputStream &inputStreamSource) + : inputStream((InputStream &)inputStreamSource) +{ +} + + + +/** + * Close the underlying OutputStream + */ +void InputStreamReader::close() +{ + inputStream.close(); +} + +/** + * Flush the underlying OutputStream + */ +int InputStreamReader::available() +{ + return inputStream.available(); +} + +/** + * Overloaded to receive its bytes from an InputStream + * rather than a Reader + */ +int InputStreamReader::get() +{ + //Do we need conversions here? + int ch = (XMLCh)inputStream.get(); + return ch; +} + + + +//######################################################################### +//# S T D R E A D E R +//######################################################################### + + +/** + * + */ +StdReader::StdReader() +{ + inputStream = new StdInputStream(); +} + +/** + * + */ +StdReader::~StdReader() +{ + delete inputStream; +} + + + +/** + * Close the underlying OutputStream + */ +void StdReader::close() +{ + inputStream->close(); +} + +/** + * Flush the underlying OutputStream + */ +int StdReader::available() +{ + return inputStream->available(); +} + +/** + * Overloaded to receive its bytes from an InputStream + * rather than a Reader + */ +int StdReader::get() +{ + //Do we need conversions here? + XMLCh ch = (XMLCh)inputStream->get(); + return ch; +} + + + + + +//######################################################################### +//# B A S I C W R I T E R +//######################################################################### + +/** + * + */ +BasicWriter::BasicWriter(const Writer &destinationWriter) +{ + destination = (Writer *)&destinationWriter; +} + +/** + * Closes this writer and releases any system resources + * associated with this writer. + */ +void BasicWriter::close() +{ + if (destination) + destination->close(); +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void BasicWriter::flush() +{ + if (destination) + destination->flush(); +} + +/** + * Writes the specified byte to this output writer. + */ +void BasicWriter::put(XMLCh ch) +{ + if (destination) + destination->put(ch); +} + +/** + * Provide printf()-like formatting + */ +Writer &BasicWriter::printf(char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + //replace this wish vsnprintf() + char buf[256]; + vsnprintf(buf, 255, fmt, args); + va_end(args); + if (buf) { + writeString(buf); + //free(buf); + } + return *this; +} +/** + * Writes the specified character to this output writer. + */ +Writer &BasicWriter::writeChar(char ch) +{ + XMLCh uch = ch; + put(uch); + return *this; +} + + +/** + * Writes the specified standard string to this output writer. + */ +Writer &BasicWriter::writeString(const DOMString &str) +{ + for (int i=0; i< (int)str.size(); i++) + put(str[i]); + return *this; +} + + +/** + * + */ +Writer &BasicWriter::writeBool (bool val ) +{ + if (val) + writeString("true"); + else + writeString("false"); + return *this; +} + + +/** + * + */ +Writer &BasicWriter::writeShort (short val ) +{ + char buf[32]; + snprintf(buf, 31, "%d", val); + writeString(buf); + return *this; +} + + + +/** + * + */ +Writer &BasicWriter::writeUnsignedShort (unsigned short val ) +{ + char buf[32]; + snprintf(buf, 31, "%u", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeInt (int val) +{ + char buf[32]; + snprintf(buf, 31, "%d", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeUnsignedInt (unsigned int val) +{ + char buf[32]; + snprintf(buf, 31, "%u", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeLong (long val) +{ + char buf[32]; + snprintf(buf, 31, "%ld", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeUnsignedLong(unsigned long val) +{ + char buf[32]; + snprintf(buf, 31, "%lu", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeFloat(float val) +{ + char buf[32]; + snprintf(buf, 31, "%8.3f", val); + writeString(buf); + return *this; +} + +/** + * + */ +Writer &BasicWriter::writeDouble(double val) +{ + char buf[32]; + snprintf(buf, 31, "%8.3f", val); + writeString(buf); + return *this; +} + + + + +//######################################################################### +//# O U T P U T S T R E A M W R I T E R +//######################################################################### + + +OutputStreamWriter::OutputStreamWriter(OutputStream &outputStreamDest) + : outputStream(outputStreamDest) +{ +} + + + +/** + * Close the underlying OutputStream + */ +void OutputStreamWriter::close() +{ + flush(); + outputStream.close(); +} + +/** + * Flush the underlying OutputStream + */ +void OutputStreamWriter::flush() +{ + outputStream.flush(); +} + +/** + * Overloaded to redirect the output chars from the next Writer + * in the chain to an OutputStream instead. + */ +void OutputStreamWriter::put(XMLCh ch) +{ + //Do we need conversions here? + int intCh = (int) ch; + outputStream.put(intCh); +} + +//######################################################################### +//# S T D W R I T E R +//######################################################################### + + +/** + * + */ +StdWriter::StdWriter() +{ + outputStream = new StdOutputStream(); +} + + +/** + * + */ +StdWriter::~StdWriter() +{ + delete outputStream; +} + + + +/** + * Close the underlying OutputStream + */ +void StdWriter::close() +{ + flush(); + outputStream->close(); +} + +/** + * Flush the underlying OutputStream + */ +void StdWriter::flush() +{ + outputStream->flush(); +} + +/** + * Overloaded to redirect the output chars from the next Writer + * in the chain to an OutputStream instead. + */ +void StdWriter::put(XMLCh ch) +{ + //Do we need conversions here? + int intCh = (int) ch; + outputStream->put(intCh); +} + + + + + + + + + + + + +//############################################### +//# O P E R A T O R S +//############################################### +//# Normally these would be in the .h, but we +//# just want to be absolutely certain that these +//# are never multiply defined. Easy to maintain, +//# though. Just occasionally copy/paste these +//# into the .h , and replace the {} with a ; +//############################################### + + + + +Reader& operator>> (Reader &reader, bool& val ) + { return reader.readBool(val); } + +Reader& operator>> (Reader &reader, short &val) + { return reader.readShort(val); } + +Reader& operator>> (Reader &reader, unsigned short &val) + { return reader.readUnsignedShort(val); } + +Reader& operator>> (Reader &reader, int &val) + { return reader.readInt(val); } + +Reader& operator>> (Reader &reader, unsigned int &val) + { return reader.readUnsignedInt(val); } + +Reader& operator>> (Reader &reader, long &val) + { return reader.readLong(val); } + +Reader& operator>> (Reader &reader, unsigned long &val) + { return reader.readUnsignedLong(val); } + +Reader& operator>> (Reader &reader, float &val) + { return reader.readFloat(val); } + +Reader& operator>> (Reader &reader, double &val) + { return reader.readDouble(val); } + + + + +Writer& operator<< (Writer &writer, char val) + { return writer.writeChar(val); } + +Writer& operator<< (Writer &writer, const DOMString &val) + { return writer.writeString(val); } + +Writer& operator<< (Writer &writer, bool val) + { return writer.writeBool(val); } + +Writer& operator<< (Writer &writer, short val) + { return writer.writeShort(val); } + +Writer& operator<< (Writer &writer, unsigned short val) + { return writer.writeUnsignedShort(val); } + +Writer& operator<< (Writer &writer, int val) + { return writer.writeInt(val); } + +Writer& operator<< (Writer &writer, unsigned int val) + { return writer.writeUnsignedInt(val); } + +Writer& operator<< (Writer &writer, long val) + { return writer.writeLong(val); } + +Writer& operator<< (Writer &writer, unsigned long val) + { return writer.writeUnsignedLong(val); } + +Writer& operator<< (Writer &writer, float val) + { return writer.writeFloat(val); } + +Writer& operator<< (Writer &writer, double val) + { return writer.writeDouble(val); } + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/domstream.h b/src/dom/io/domstream.h new file mode 100644 index 000000000..af43d0a51 --- /dev/null +++ b/src/dom/io/domstream.h @@ -0,0 +1,680 @@ +#ifndef __DOMSTREAM_H__ +#define __DOMSTREAM_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + + +#include "dom.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + +class StreamException +{ +public: + + StreamException(const DOMString &theReason) throw() + { reason = theReason; } + ~StreamException() throw() + { } + char const *what() + { return reason.c_str(); } + +private: + + DOMString reason; + +}; + +//######################################################################### +//# I N P U T S T R E A M +//######################################################################### + +/** + * This interface is the base of all input stream classes. Users who wish + * to make an InputStream that is part of a chain should inherit from + * BasicInputStream. Inherit from this class to make a source endpoint, + * such as a URI or buffer. + * + */ +class InputStream +{ + +public: + + /** + * Constructor. + */ + InputStream() {} + + /** + * Destructor + */ + virtual ~InputStream() {} + + /** + * Return the number of bytes that are currently available + * to be read + */ + virtual int available() = 0; + + /** + * Do whatever it takes to 'close' this input stream + * The most likely implementation of this method will be + * for endpoints that use a resource for their data. + */ + virtual void close() = 0; + + /** + * Read one byte from this input stream. This is a blocking + * call. If no data is currently available, this call will + * not return until it exists. If the user does not want + * their code to block, then the usual solution is: + * if (available() > 0) + * myChar = get(); + * This call returns -1 on end-of-file. + */ + virtual int get() = 0; + +}; // class InputStream + + + + +/** + * This is the class that most users should inherit, to provide + * their own streams. + * + */ +class BasicInputStream : public InputStream +{ + +public: + + BasicInputStream(const InputStream &sourceStream); + + virtual ~BasicInputStream() {} + + virtual int available(); + + virtual void close(); + + virtual int get(); + +protected: + + bool closed; + + InputStream &source; + +private: + + +}; // class BasicInputStream + + + +/** + * Convenience class for reading from standard input + */ +class StdInputStream : public InputStream +{ +public: + + int available() + { return 0; } + + void close() + { /* do nothing */ } + + int get() + { return getchar(); } + +}; + + + + + + +//######################################################################### +//# O U T P U T S T R E A M +//######################################################################### + +/** + * This interface is the base of all input stream classes. Users who wish + * to make an OutputStream that is part of a chain should inherit from + * BasicOutputStream. Inherit from this class to make a destination endpoint, + * such as a URI or buffer. + */ +class OutputStream +{ + +public: + + /** + * Constructor. + */ + OutputStream() {} + + /** + * Destructor + */ + virtual ~OutputStream() {} + + /** + * This call should + * 1. flush itself + * 2. close itself + * 3. close the destination stream + */ + virtual void close() = 0; + + /** + * This call should push any pending data it might have to + * the destination stream. It should NOT call flush() on + * the destination stream. + */ + virtual void flush() = 0; + + /** + * Send one byte to the destination stream. + */ + virtual void put(XMLCh ch) = 0; + + +}; // class OutputStream + + +/** + * This is the class that most users should inherit, to provide + * their own output streams. + */ +class BasicOutputStream : public OutputStream +{ + +public: + + BasicOutputStream(const OutputStream &destinationStream); + + virtual ~BasicOutputStream() {} + + virtual void close(); + + virtual void flush(); + + virtual void put(XMLCh ch); + +protected: + + bool closed; + + OutputStream &destination; + + +}; // class BasicOutputStream + + + +/** + * Convenience class for writing to standard output + */ +class StdOutputStream : public OutputStream +{ +public: + + void close() + { } + + void flush() + { } + + void put(XMLCh ch) + { putchar(ch); } + +}; + + + + +//######################################################################### +//# R E A D E R +//######################################################################### + + +/** + * This interface and its descendants are for unicode character-oriented input + * + */ +class Reader +{ + +public: + + /** + * Constructor. + */ + Reader() {} + + /** + * Destructor + */ + virtual ~Reader() {} + + + virtual int available() = 0; + + virtual void close() = 0; + + virtual int get() = 0; + + virtual DOMString readLine() = 0; + + virtual DOMString readWord() = 0; + + /* Input formatting */ + virtual Reader& readBool (bool& val ) = 0; + + virtual Reader& readShort (short &val) = 0; + + virtual Reader& readUnsignedShort (unsigned short &val) = 0; + + virtual Reader& readInt (int &val) = 0; + + virtual Reader& readUnsignedInt (unsigned int &val) = 0; + + virtual Reader& readLong (long &val) = 0; + + virtual Reader& readUnsignedLong (unsigned long &val) = 0; + + virtual Reader& readFloat (float &val) = 0; + + virtual Reader& readDouble (double &val) = 0; + +}; // interface Reader + + + +/** + * This class and its descendants are for unicode character-oriented input + * + */ +class BasicReader : public Reader +{ + +public: + + BasicReader(Reader &sourceStream); + + virtual ~BasicReader() {} + + virtual int available(); + + virtual void close(); + + virtual int get(); + + virtual DOMString readLine(); + + virtual DOMString readWord(); + + /* Input formatting */ + virtual Reader& readBool (bool& val ); + + virtual Reader& readShort (short &val) ; + + virtual Reader& readUnsignedShort (unsigned short &val) ; + + virtual Reader& readInt (int &val) ; + + virtual Reader& readUnsignedInt (unsigned int &val) ; + + virtual Reader& readLong (long &val) ; + + virtual Reader& readUnsignedLong (unsigned long &val) ; + + virtual Reader& readFloat (float &val) ; + + virtual Reader& readDouble (double &val) ; + +protected: + + Reader *source; + + BasicReader() + { source = NULL; } + +private: + +}; // class BasicReader + + + +Reader& operator>> (Reader &reader, bool& val ); + +Reader& operator>> (Reader &reader, short &val); + +Reader& operator>> (Reader &reader, unsigned short &val); + +Reader& operator>> (Reader &reader, int &val); + +Reader& operator>> (Reader &reader, unsigned int &val); + +Reader& operator>> (Reader &reader, long &val); + +Reader& operator>> (Reader &reader, unsigned long &val); + +Reader& operator>> (Reader &reader, float &val); + +Reader& operator>> (Reader &reader, double &val); + + + + +/** + * Class for placing a Reader on an open InputStream + * + */ +class InputStreamReader : public BasicReader +{ +public: + + InputStreamReader(const InputStream &inputStreamSource); + + /*Overload these 3 for your implementation*/ + virtual int available(); + + virtual void close(); + + virtual int get(); + + +private: + + InputStream &inputStream; + + +}; + +/** + * Convenience class for reading formatted from standard input + * + */ +class StdReader : public BasicReader +{ +public: + + StdReader(); + + ~StdReader(); + + /*Overload these 3 for your implementation*/ + virtual int available(); + + virtual void close(); + + virtual int get(); + + +private: + + InputStream *inputStream; + + +}; + + + + + +//######################################################################### +//# W R I T E R +//######################################################################### + +/** + * This interface and its descendants are for unicode character-oriented output + * + */ +class Writer +{ + +public: + + /** + * Constructor. + */ + Writer() {} + + /** + * Destructor + */ + virtual ~Writer() {} + + virtual void close() = 0; + + virtual void flush() = 0; + + virtual void put(XMLCh ch) = 0; + + /* Formatted output */ + virtual Writer& printf(char *fmt, ...) = 0; + + virtual Writer& writeChar(char val) = 0; + + virtual Writer& writeString(const DOMString &val) = 0; + + virtual Writer& writeBool (bool val ) = 0; + + virtual Writer& writeShort (short val ) = 0; + + virtual Writer& writeUnsignedShort (unsigned short val ) = 0; + + virtual Writer& writeInt (int val ) = 0; + + virtual Writer& writeUnsignedInt (unsigned int val ) = 0; + + virtual Writer& writeLong (long val ) = 0; + + virtual Writer& writeUnsignedLong (unsigned long val ) = 0; + + virtual Writer& writeFloat (float val ) = 0; + + virtual Writer& writeDouble (double val ) = 0; + + + +}; // interface Writer + + +/** + * This class and its descendants are for unicode character-oriented output + * + */ +class BasicWriter : public Writer +{ + +public: + + BasicWriter(const Writer &destinationWriter); + + virtual ~BasicWriter() {} + + /*Overload these 3 for your implementation*/ + virtual void close(); + + virtual void flush(); + + virtual void put(XMLCh ch); + + + + /* Formatted output */ + virtual Writer &printf(char *fmt, ...); + + virtual Writer& writeChar(char val); + + virtual Writer& writeString(const DOMString &val); + + virtual Writer& writeBool (bool val ); + + virtual Writer& writeShort (short val ); + + virtual Writer& writeUnsignedShort (unsigned short val ); + + virtual Writer& writeInt (int val ); + + virtual Writer& writeUnsignedInt (unsigned int val ); + + virtual Writer& writeLong (long val ); + + virtual Writer& writeUnsignedLong (unsigned long val ); + + virtual Writer& writeFloat (float val ); + + virtual Writer& writeDouble (double val ); + + +protected: + + Writer *destination; + + BasicWriter() + { destination = NULL; } + +private: + +}; // class BasicWriter + + + +Writer& operator<< (Writer &writer, char val); + +Writer& operator<< (Writer &writer, const DOMString &val); + +Writer& operator<< (Writer &writer, bool val); + +Writer& operator<< (Writer &writer, short val); + +Writer& operator<< (Writer &writer, unsigned short val); + +Writer& operator<< (Writer &writer, int val); + +Writer& operator<< (Writer &writer, unsigned int val); + +Writer& operator<< (Writer &writer, long val); + +Writer& operator<< (Writer &writer, unsigned long val); + +Writer& operator<< (Writer &writer, float val); + +Writer& operator<< (Writer &writer, double val); + + + + +/** + * Class for placing a Writer on an open OutputStream + * + */ +class OutputStreamWriter : public BasicWriter +{ +public: + + OutputStreamWriter(OutputStream &outputStreamDest); + + /*Overload these 3 for your implementation*/ + virtual void close(); + + virtual void flush(); + + virtual void put(XMLCh ch); + + +private: + + OutputStream &outputStream; + + +}; + + +/** + * Convenience class for writing to standard output + */ +class StdWriter : public BasicWriter +{ +public: + StdWriter(); + + ~StdWriter(); + + + virtual void close(); + + + virtual void flush(); + + + virtual void put(XMLCh ch); + + +private: + + OutputStream *outputStream; + +}; + +//######################################################################### +//# U T I L I T Y +//######################################################################### + +void pipeStream(InputStream &source, OutputStream &dest); + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif /* __DOMSTREAM_H__ */ + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/gzipstream.cpp b/src/dom/io/gzipstream.cpp new file mode 100644 index 000000000..628adaa7a --- /dev/null +++ b/src/dom/io/gzipstream.cpp @@ -0,0 +1,245 @@ +/** + * Zlib-enabled input and output streams + * + * This is a thin wrapper of libz calls, in order + * to provide a simple interface to our developers + * for gzip input and output. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + +#include "gzipstream.h" + +#include "util/ziptool.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# G Z I P I N P U T S T R E A M +//######################################################################### + +/** + * + */ +GzipInputStream::GzipInputStream(InputStream &sourceStream) + : BasicInputStream(sourceStream) +{ + loaded = false; + bufPos = 0; +} + +/** + * + */ +GzipInputStream::~GzipInputStream() +{ + close(); +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int GzipInputStream::available() +{ + if (closed) + return 0; + if (!loaded) + if (!load()) + return 0; + return (int) buffer.size(); +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void GzipInputStream::close() +{ + if (closed) + return; + + closed = true; +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int GzipInputStream::get() +{ + if (closed) + return -1; + + if (!loaded) + if (!load()) + return -1; + + if (bufPos >= buffer.size()) + return -1; + int ch = (int) buffer[bufPos++]; + + return ch; +} + +/** + * Processes input. Fills read buffer. + */ +bool GzipInputStream::load() +{ + if (closed) + return false; + + if (loaded) + return true; + + std::vector compBuf; + while (true) + { + int ch = source.get(); + if (ch < 0) + break; + compBuf.push_back(ch); + } + GzipFile gz; + if (!gz.readBuffer(compBuf)) + { + return -1; + } + buffer = gz.getData(); + bufPos = 0; + loaded = true; + return true; +} + + + + + + +//######################################################################### +//# G Z I P O U T P U T S T R E A M +//######################################################################### + +/** + * + */ +GzipOutputStream::GzipOutputStream(OutputStream &destinationStream) + : BasicOutputStream(destinationStream) +{ + + closed = false; +} + +/** + * + */ +GzipOutputStream::~GzipOutputStream() +{ + close(); +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void GzipOutputStream::close() +{ + if (closed) + return; + + flush(); + + closed = true; +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void GzipOutputStream::flush() +{ + if (closed || buffer.size()<1) + return; + + std::vector compBuf; + GzipFile gz; + + gz.writeBuffer(buffer); + + std::vector::iterator iter; + for (iter=compBuf.begin() ; iter!=compBuf.end() ; iter++) + { + int ch = (int) *iter; + destination.put(ch); + } + + buffer.clear(); + + //printf("done\n"); + +} + + + +/** + * Writes the specified byte to this output stream. + */ +void GzipOutputStream::put(int ch) +{ + if (closed) + { + //probably throw an exception here + return; + } + + //Add char to buffer + buffer.push_back(ch); + +} + + + +} // namespace io +} // namespace dom +} // namespace w3c +} // namespace org + + + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### + + + diff --git a/src/dom/io/gzipstream.h b/src/dom/io/gzipstream.h new file mode 100644 index 000000000..ed7462488 --- /dev/null +++ b/src/dom/io/gzipstream.h @@ -0,0 +1,125 @@ +#ifndef __GZIPSTREAM_H__ +#define __GZIPSTREAM_H__ +/** + * Zlib-enabled input and output streams + * + * This provides a simple mechanism for reading and + * writing Gzip files. We use our own 'ZipTool' class + * to accomplish this, avoiding a zlib dependency. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + + +#include "domstream.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + +//######################################################################### +//# G Z I P I N P U T S T R E A M +//######################################################################### + +/** + * This class is for deflating a gzip-compressed InputStream source + * + */ +class GzipInputStream : public BasicInputStream +{ + +public: + + GzipInputStream(InputStream &sourceStream); + + virtual ~GzipInputStream(); + + virtual int available(); + + virtual void close(); + + virtual int get(); + +private: + + bool load(); + + bool loaded; + + std::vector buffer; + unsigned int bufPos; + + +}; // class GzipInputStream + + + + +//######################################################################### +//# G Z I P O U T P U T S T R E A M +//######################################################################### + +/** + * This class is for gzip-compressing data going to the + * destination OutputStream + * + */ +class GzipOutputStream : public BasicOutputStream +{ + +public: + + GzipOutputStream(OutputStream &destinationStream); + + virtual ~GzipOutputStream(); + + virtual void close(); + + virtual void flush(); + + virtual void put(int ch); + +private: + + std::vector buffer; + + +}; // class GzipOutputStream + + + + + + + +} // namespace io +} // namespace dom +} // namespace w3c +} // namespace org + + +#endif /* __GZIPSTREAM_H__ */ diff --git a/src/dom/io/httpclient.cpp b/src/dom/io/httpclient.cpp new file mode 100644 index 000000000..b81e9aedf --- /dev/null +++ b/src/dom/io/httpclient.cpp @@ -0,0 +1,167 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + +#include "httpclient.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + + +/** + * + */ +HttpClient::HttpClient() +{ +} + + +/** + * + */ +HttpClient::~HttpClient() +{ +} + + +/** + * + */ +bool HttpClient::openGet(const URI &uri) +{ + + socket.disconnect(); + + if (uri.getScheme() == URI::SCHEME_HTTP) + socket.enableSSL(false); + else if (uri.getScheme() == URI::SCHEME_HTTPS) + socket.enableSSL(true); + else + { + printf("Bad proto scheme:%d\n", uri.getScheme()); + return false; + } + + DOMString host = uri.getHost(); + int port = uri.getPort(); + DOMString path = uri.getPath(); + if (path.size() == 0) + path = "/"; + + //printf("host:%s port:%d, path:%s\n", host.c_str(), port, path.c_str()); + + if (!socket.connect(host, port)) + { + return false; + } + + DOMString msg = "GET "; + msg.append(path); + msg.append(" HTTP/1.0\r\n\r\n"); + //printf("msg:'%s'\n", msg.c_str()); + + //# Make the request + if (!socket.write(msg)) + { + return false; + } + + //# Read the HTTP headers + while (true) + { + if (!socket.readLine(msg)) + return false; + printf("header:'%s'\n", msg.c_str()); + if (msg.size() < 1) + break; + } + + return true; +} + + +/** + * + */ +int HttpClient::read() +{ + int ret = socket.read(); + return ret; +} + +/** + * + */ +bool HttpClient::write(int ch) +{ + if (!socket.write(ch)) + return false; + return true; +} + +/** + * + */ +bool HttpClient::write(const DOMString &msg) +{ + if (!socket.write(msg)) + return false; + return true; +} + +/** + * + */ +bool HttpClient::close() +{ + socket.disconnect(); + return true; +} + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### + diff --git a/src/dom/io/httpclient.h b/src/dom/io/httpclient.h new file mode 100644 index 000000000..9f4d238f9 --- /dev/null +++ b/src/dom/io/httpclient.h @@ -0,0 +1,115 @@ +#ifndef __HTTPCLIENT_H__ +#define __HTTPCLIENT_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + + +#include "dom.h" +#include "uri.h" +#include "socket.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + + + + +class HttpClient +{ + +public: + + /** + * + */ + HttpClient(); + + /** + * + */ + virtual ~HttpClient(); + + /** + * + */ + bool openGet(const URI &uri); + + /** + * + */ + int read(); + + /** + * + */ + bool write(int ch); + + /** + * + */ + bool write(const DOMString &msg); + + /** + * + */ + bool close(); + + +private: + + + TcpSocket socket; + +}; + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif /* __HTTPCLIENT_H__ */ + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### + diff --git a/src/dom/io/socket.cpp b/src/dom/io/socket.cpp new file mode 100644 index 000000000..fbcc8e4ac --- /dev/null +++ b/src/dom/io/socket.cpp @@ -0,0 +1,618 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "socket.h" +#include "util/thread.h" + +#ifdef __WIN32__ +#include +#endif + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + +static void mybzero(void *s, size_t n) +{ + unsigned char *p = (unsigned char *)s; + while (n > 0) + { + *p++ = (unsigned char)0; + n--; + } +} + +static void mybcopy(void *src, void *dest, size_t n) +{ + unsigned char *p = (unsigned char *)dest; + unsigned char *q = (unsigned char *)src; + while (n > 0) + { + *p++ = *q++; + n--; + } +} + + + +//######################################################################### +//# T C P C O N N E C T I O N +//######################################################################### + +TcpSocket::TcpSocket() +{ + init(); +} + + +TcpSocket::TcpSocket(const DOMString &hostnameArg, int port) +{ + init(); + hostname = hostnameArg; + portno = port; +} + + +#ifdef HAVE_SSL + +static void cryptoLockCallback(int mode, int type, const char *file, int line) +{ + //printf("########### LOCK\n"); + static int modes[CRYPTO_NUM_LOCKS]; /* = {0, 0, ... } */ + const char *errstr = NULL; + + int rw = mode & (CRYPTO_READ|CRYPTO_WRITE); + if (!((rw == CRYPTO_READ) || (rw == CRYPTO_WRITE))) + { + errstr = "invalid mode"; + goto err; + } + + if (type < 0 || type >= CRYPTO_NUM_LOCKS) + { + errstr = "type out of bounds"; + goto err; + } + + if (mode & CRYPTO_LOCK) + { + if (modes[type]) + { + errstr = "already locked"; + /* must not happen in a single-threaded program + * (would deadlock) + */ + goto err; + } + + modes[type] = rw; + } + else if (mode & CRYPTO_UNLOCK) + { + if (!modes[type]) + { + errstr = "not locked"; + goto err; + } + + if (modes[type] != rw) + { + errstr = (rw == CRYPTO_READ) ? + "CRYPTO_r_unlock on write lock" : + "CRYPTO_w_unlock on read lock"; + } + + modes[type] = 0; + } + else + { + errstr = "invalid mode"; + goto err; + } + + err: + if (errstr) + { + /* we cannot use bio_err here */ + fprintf(stderr, "openssl (lock_dbg_cb): %s (mode=%d, type=%d) at %s:%d\n", + errstr, mode, type, file, line); + } +} + +static unsigned long cryptoIdCallback() +{ +#ifdef __WIN32__ + unsigned long ret = (unsigned long) GetCurrentThreadId(); +#else + unsigned long ret = (unsigned long) pthread_self(); +#endif + return ret; +} + +#endif + + +TcpSocket::TcpSocket(const TcpSocket &other) +{ + init(); + sock = other.sock; + hostname = other.hostname; + portno = other.portno; +} + +static bool tcp_socket_inited = false; + +void TcpSocket::init() +{ + if (!tcp_socket_inited) + { +#ifdef __WIN32__ + WORD wVersionRequested = MAKEWORD( 2, 2 ); + WSADATA wsaData; + WSAStartup( wVersionRequested, &wsaData ); +#endif +#ifdef HAVE_SSL + sslStream = NULL; + sslContext = NULL; + CRYPTO_set_locking_callback(cryptoLockCallback); + CRYPTO_set_id_callback(cryptoIdCallback); + SSL_library_init(); + SSL_load_error_strings(); +#endif + tcp_socket_inited = true; + } + sock = -1; + connected = false; + hostname = ""; + portno = -1; + sslEnabled = false; + receiveTimeout = 0; +} + +TcpSocket::~TcpSocket() +{ + disconnect(); +} + +bool TcpSocket::isConnected() +{ + if (!connected || sock < 0) + return false; + return true; +} + +void TcpSocket::enableSSL(bool val) +{ + sslEnabled = val; +} + + +bool TcpSocket::connect(const DOMString &hostnameArg, int portnoArg) +{ + hostname = hostnameArg; + portno = portnoArg; + return connect(); +} + + + +#ifdef HAVE_SSL +/* +static int password_cb(char *buf, int bufLen, int rwflag, void *userdata) +{ + char *password = "password"; + if (bufLen < (int)(strlen(password)+1)) + return 0; + + strcpy(buf,password); + int ret = strlen(password); + return ret; +} + +static void infoCallback(const SSL *ssl, int where, int ret) +{ + switch (where) + { + case SSL_CB_ALERT: + { + printf("## %d SSL ALERT: %s\n", where, SSL_alert_desc_string_long(ret)); + break; + } + default: + { + printf("## %d SSL: %s\n", where, SSL_state_string_long(ssl)); + break; + } + } +} +*/ +#endif + + +bool TcpSocket::startTls() +{ +#ifdef HAVE_SSL + sslStream = NULL; + sslContext = NULL; + + //SSL_METHOD *meth = SSLv23_method(); + //SSL_METHOD *meth = SSLv3_client_method(); + SSL_METHOD *meth = TLSv1_client_method(); + sslContext = SSL_CTX_new(meth); + //SSL_CTX_set_info_callback(sslContext, infoCallback); + +#if 0 + char *keyFile = "client.pem"; + char *caList = "root.pem"; + /* Load our keys and certificates*/ + if (!(SSL_CTX_use_certificate_chain_file(sslContext, keyFile))) + { + fprintf(stderr, "Can't read certificate file\n"); + disconnect(); + return false; + } + + SSL_CTX_set_default_passwd_cb(sslContext, password_cb); + + if (!(SSL_CTX_use_PrivateKey_file(sslContext, keyFile, SSL_FILETYPE_PEM))) + { + fprintf(stderr, "Can't read key file\n"); + disconnect(); + return false; + } + + /* Load the CAs we trust*/ + if (!(SSL_CTX_load_verify_locations(sslContext, caList, 0))) + { + fprintf(stderr, "Can't read CA list\n"); + disconnect(); + return false; + } +#endif + + /* Connect the SSL socket */ + sslStream = SSL_new(sslContext); + SSL_set_fd(sslStream, sock); + + if (SSL_connect(sslStream)<=0) + { + fprintf(stderr, "SSL connect error\n"); + disconnect(); + return false; + } + + sslEnabled = true; +#endif /*HAVE_SSL*/ + return true; +} + + +bool TcpSocket::connect() +{ + if (hostname.size()<1) + { + printf("open: null hostname\n"); + return false; + } + + if (portno<1) + { + printf("open: bad port number\n"); + return false; + } + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + printf("open: error creating socket\n"); + return false; + } + + char *c_hostname = (char *)hostname.c_str(); + struct hostent *server = gethostbyname(c_hostname); + if (!server) + { + printf("open: could not locate host '%s'\n", c_hostname); + return false; + } + + struct sockaddr_in serv_addr; + mybzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + mybcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(portno); + + int ret = ::connect(sock, (const sockaddr *)&serv_addr, sizeof(serv_addr)); + if (ret < 0) + { + printf("open: could not connect to host '%s'\n", c_hostname); + return false; + } + + if (sslEnabled) + { + if (!startTls()) + return false; + } + connected = true; + return true; +} + +bool TcpSocket::disconnect() +{ + bool ret = true; + connected = false; +#ifdef HAVE_SSL + if (sslEnabled) + { + if (sslStream) + { + int r = SSL_shutdown(sslStream); + switch(r) + { + case 1: + break; /* Success */ + case 0: + case -1: + default: + //printf("Shutdown failed"); + ret = false; + } + SSL_free(sslStream); + } + if (sslContext) + SSL_CTX_free(sslContext); + } + sslStream = NULL; + sslContext = NULL; +#endif /*HAVE_SSL*/ + +#ifdef __WIN32__ + closesocket(sock); +#else + ::close(sock); +#endif + sock = -1; + sslEnabled = false; + + return ret; +} + + + +bool TcpSocket::setReceiveTimeout(unsigned long millis) +{ + receiveTimeout = millis; + return true; +} + +/** + * For normal sockets, return the number of bytes waiting to be received. + * For SSL, just return >0 when something is ready to be read. + */ +long TcpSocket::available() +{ + if (!isConnected()) + return -1; + + long count = 0; +#ifdef __WIN32__ + if (ioctlsocket(sock, FIONREAD, (unsigned long *)&count) != 0) + return -1; +#else + if (ioctl(sock, FIONREAD, &count) != 0) + return -1; +#endif + if (count<=0 && sslEnabled) + { +#ifdef HAVE_SSL + return SSL_pending(sslStream); +#endif + } + return count; +} + + + +bool TcpSocket::write(int ch) +{ + if (!isConnected()) + { + printf("write: socket closed\n"); + return false; + } + unsigned char c = (unsigned char)ch; + + if (sslEnabled) + { +#ifdef HAVE_SSL + int r = SSL_write(sslStream, &c, 1); + if (r<=0) + { + switch(SSL_get_error(sslStream, r)) + { + default: + printf("SSL write problem"); + return -1; + } + } +#endif + } + else + { + if (send(sock, (const char *)&c, 1, 0) < 0) + //if (send(sock, &c, 1, 0) < 0) + { + printf("write: could not send data\n"); + return false; + } + } + return true; +} + +bool TcpSocket::write(const DOMString &strArg) +{ + DOMString str = strArg; + + if (!isConnected()) + { + printf("write(str): socket closed\n"); + return false; + } + int len = str.size(); + + if (sslEnabled) + { +#ifdef HAVE_SSL + int r = SSL_write(sslStream, (unsigned char *)str.c_str(), len); + if (r<=0) + { + switch(SSL_get_error(sslStream, r)) + { + default: + printf("SSL write problem"); + return -1; + } + } +#endif + } + else + { + if (send(sock, str.c_str(), len, 0) < 0) + //if (send(sock, &c, 1, 0) < 0) + { + printf("write: could not send data\n"); + return false; + } + } + return true; +} + +int TcpSocket::read() +{ + if (!isConnected()) + return -1; + + //We'll use this loop for timeouts, so that SSL and plain sockets + //will behave the same way + if (receiveTimeout > 0) + { + unsigned long tim = 0; + while (true) + { + int avail = available(); + if (avail > 0) + break; + if (tim >= receiveTimeout) + return -2; + org::w3c::dom::util::Thread::sleep(20); + tim += 20; + } + } + + //check again + if (!isConnected()) + return -1; + + unsigned char ch; + if (sslEnabled) + { +#ifdef HAVE_SSL + if (!sslStream) + return -1; + int r = SSL_read(sslStream, &ch, 1); + unsigned long err = SSL_get_error(sslStream, r); + switch (err) + { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_ZERO_RETURN: + return -1; + case SSL_ERROR_SYSCALL: + printf("SSL read problem(syscall) %s\n", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + default: + printf("SSL read problem %s\n", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } +#endif + } + else + { + int ret = recv(sock, (char *)&ch, 1, 0); + if (ret <= 0) + { + if (ret<0) + printf("read: could not receive data\n"); + disconnect(); + return -1; + } + } + return (int)ch; +} + +bool TcpSocket::readLine(DOMString &result) +{ + result = ""; + + while (isConnected()) + { + int ch = read(); + if (ch<0) + return true; + else if (ch=='\r') //we want canonical Net '\r\n' , so skip this + {} + else if (ch=='\n') + return true; + else + result.push_back((char)ch); + } + + return true; +} + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### + diff --git a/src/dom/io/socket.h b/src/dom/io/socket.h new file mode 100644 index 000000000..5383d9bab --- /dev/null +++ b/src/dom/io/socket.h @@ -0,0 +1,112 @@ +#ifndef __DOM_SOCKET_H__ +#define __DOM_SOCKET_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "dom.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + +class TcpSocket +{ +public: + + TcpSocket(); + + TcpSocket(const DOMString &hostname, int port); + + TcpSocket(const TcpSocket &other); + + virtual ~TcpSocket(); + + bool isConnected(); + + void enableSSL(bool val); + + bool connect(const DOMString &hostname, int portno); + + bool startTls(); + + bool connect(); + + bool disconnect(); + + bool setReceiveTimeout(unsigned long millis); + + long available(); + + bool write(int ch); + + bool write(const DOMString &str); + + int read(); + + bool readLine(DOMString &result); + +private: + + void init(); + + DOMString hostname; + int portno; + int sock; + bool connected; + + bool sslEnabled; + + unsigned long receiveTimeout; + +#ifdef HAVE_SSL + SSL_CTX *sslContext; + SSL *sslStream; +#endif + +}; + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + +#endif /* __DOM_SOCKET_H__ */ +//######################################################################### +//# E N D O F F I L E +//######################################################################### + diff --git a/src/dom/io/stringstream.cpp b/src/dom/io/stringstream.cpp new file mode 100644 index 000000000..93fb0c0b5 --- /dev/null +++ b/src/dom/io/stringstream.cpp @@ -0,0 +1,158 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +/** + * Our base String stream classes. We implement these to + * be based on DOMString + * + */ + + +#include "stringstream.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + +//######################################################################### +//# S T R I N G I N P U T S T R E A M +//######################################################################### + + +/** + * + */ +StringInputStream::StringInputStream(const DOMString &sourceString) + : buffer((DOMString &)sourceString) +{ + position = 0; +} + +/** + * + */ +StringInputStream::~StringInputStream() +{ + +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int StringInputStream::available() +{ + return buffer.size() - position; +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void StringInputStream::close() +{ +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int StringInputStream::get() +{ + if (position >= (int)buffer.size()) + return -1; + int ch = (int) buffer[position++]; + return ch; +} + + + + +//######################################################################### +//# S T R I N G O U T P U T S T R E A M +//######################################################################### + +/** + * + */ +StringOutputStream::StringOutputStream() +{ +} + +/** + * + */ +StringOutputStream::~StringOutputStream() +{ +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void StringOutputStream::close() +{ +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void StringOutputStream::flush() +{ + //nothing to do +} + +/** + * Writes the specified byte to this output stream. + */ +void StringOutputStream::put(XMLCh ch) +{ + buffer.push_back(ch); +} + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/stringstream.h b/src/dom/io/stringstream.h new file mode 100644 index 000000000..8bb4e76b1 --- /dev/null +++ b/src/dom/io/stringstream.h @@ -0,0 +1,129 @@ +#ifndef __STRINGSTREAM_H__ +#define __STRINGSTREAM_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * 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 + */ + + +#include "domstream.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# S T R I N G I N P U T S T R E A M +//######################################################################### + +/** + * This class is for reading character from a DOMString + * + */ +class StringInputStream : public InputStream +{ + +public: + + StringInputStream(const DOMString &sourceString); + + virtual ~StringInputStream(); + + virtual int available(); + + virtual void close(); + + virtual int get(); + +private: + + DOMString &buffer; + + long position; + +}; // class StringInputStream + + + + +//######################################################################### +//# S T R I N G O U T P U T S T R E A M +//######################################################################### + +/** + * This class is for sending a stream to a DOMString + * + */ +class StringOutputStream : public OutputStream +{ + +public: + + StringOutputStream(); + + virtual ~StringOutputStream(); + + virtual void close(); + + virtual void flush(); + + virtual void put(XMLCh ch); + + virtual DOMString &getString() + { return buffer; } + + virtual void clear() + { buffer = ""; } + +private: + + DOMString buffer; + + +}; // class StringOutputStream + + + + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + + +#endif /* __STRINGSTREAM_H__ */ diff --git a/src/dom/io/uristream.cpp b/src/dom/io/uristream.cpp new file mode 100644 index 000000000..a12fe9522 --- /dev/null +++ b/src/dom/io/uristream.cpp @@ -0,0 +1,502 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +/** + * Our base String stream classes. We implement these to + * be based on DOMString + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2004 Inkscape.org + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "uristream.h" + + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + + +//######################################################################### +//# U R I I N P U T S T R E A M / R E A D E R +//######################################################################### + + +/** + * + */ +UriInputStream::UriInputStream(const URI &source) + throw (StreamException): uri((URI &)source) +{ + init(); +} + +/** + * + */ +void UriInputStream::init() throw (StreamException) +{ + //get information from uri + scheme = uri.getScheme(); + + //printf("in scheme:'%d'\n", scheme); + DOMString path = uri.getPath(); + //printf("in path:'%s'\n", path.c_str()); + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + inf = fopen(path.c_str(), "rb"); + if (!inf) + { + DOMString err = "UriInputStream cannot open file "; + err.append(path); + throw StreamException(err); + } + break; + } + + case URI::SCHEME_DATA: + { + data = (unsigned char *) uri.getPath().c_str(); + //printf("in data:'%s'\n", data); + dataPos = 0; + dataLen = strlen((const char *)data); + break; + } + + case URI::SCHEME_HTTP: + case URI::SCHEME_HTTPS: + { + if (!httpClient.openGet(uri)) + { + DOMString err = "UriInputStream cannot open URL "; + err.append(uri.toString()); + throw StreamException(err); + } + break; + } + + } + + closed = false; +} + + + + + +/** + * + */ +UriInputStream::~UriInputStream() throw(StreamException) +{ + close(); +} + +/** + * Returns the number of bytes that can be read (or skipped over) from + * this input stream without blocking by the next caller of a method for + * this input stream. + */ +int UriInputStream::available() throw(StreamException) +{ + return 0; +} + + +/** + * Closes this input stream and releases any system resources + * associated with the stream. + */ +void UriInputStream::close() throw(StreamException) +{ + if (closed) + return; + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + if (!inf) + return; + fflush(inf); + fclose(inf); + inf=NULL; + break; + } + + case URI::SCHEME_DATA: + { + //do nothing + break; + } + + case URI::SCHEME_HTTP: + case URI::SCHEME_HTTPS: + { + httpClient.close(); + break; + } + + }//switch + + closed = true; +} + +/** + * Reads the next byte of data from the input stream. -1 if EOF + */ +int UriInputStream::get() throw(StreamException) +{ + int retVal = -1; + if (closed) + { + return -1; + } + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + if (!inf || feof(inf)) + { + retVal = -1; + } + else + { + retVal = fgetc(inf); + } + break; + } + + case URI::SCHEME_DATA: + { + if (dataPos >= dataLen) + { + retVal = -1; + } + else + { + retVal = data[dataPos++]; + } + break; + } + + case URI::SCHEME_HTTP: + case URI::SCHEME_HTTPS: + { + retVal = httpClient.read(); + break; + } + + }//switch + + return retVal; +} + + + + + + +/** + * + */ +UriReader::UriReader(const URI &uri) throw (StreamException) +{ + inputStream = new UriInputStream(uri); +} + +/** + * + */ +UriReader::~UriReader() throw (StreamException) +{ + delete inputStream; +} + +/** + * + */ +int UriReader::available() throw(StreamException) +{ + return inputStream->available(); +} + +/** + * + */ +void UriReader::close() throw(StreamException) +{ + inputStream->close(); +} + +/** + * + */ +int UriReader::get() throw(StreamException) +{ + int ch = (int)inputStream->get(); + return ch; +} + + +//######################################################################### +//# U R I O U T P U T S T R E A M / W R I T E R +//######################################################################### + +/** + * + */ +UriOutputStream::UriOutputStream(const URI &destination) + throw (StreamException): closed(false), + ownsFile(true), + outf(NULL), + uri((URI &)destination) +{ + init(); +} + + +/** + * + */ +void UriOutputStream::init() throw(StreamException) +{ + //get information from uri + scheme = uri.getScheme(); + + //printf("out schemestr:'%s' scheme:'%d'\n", schemestr, scheme); + char *cpath = NULL; + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + cpath = (char *) uri.getPath().c_str(); + //printf("out path:'%s'\n", cpath); + outf = fopen(cpath, "wb"); + if (!outf) + { + DOMString err = "UriOutputStream cannot open file "; + err += cpath; + throw StreamException(err); + } + break; + } + + case URI::SCHEME_DATA: + { + data = "data:"; + break; + } + + }//switch +} + +/** + * + */ +UriOutputStream::~UriOutputStream() throw(StreamException) +{ + close(); +} + +/** + * Closes this output stream and releases any system resources + * associated with this stream. + */ +void UriOutputStream::close() throw(StreamException) +{ + if (closed) + return; + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + if (!outf) + return; + fflush(outf); + if ( ownsFile ) + fclose(outf); + outf=NULL; + break; + } + + case URI::SCHEME_DATA: + { + uri = URI(data.c_str()); + break; + } + + }//switch + + closed = true; +} + +/** + * Flushes this output stream and forces any buffered output + * bytes to be written out. + */ +void UriOutputStream::flush() throw(StreamException) +{ + if (closed) + return; + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + if (!outf) + return; + fflush(outf); + break; + } + + case URI::SCHEME_DATA: + { + //nothing + break; + } + + }//switch + +} + +/** + * Writes the specified byte to this output stream. + */ +void UriOutputStream::put(XMLCh ch) throw(StreamException) +{ + if (closed) + return; + + switch (scheme) + { + + case URI::SCHEME_FILE: + { + if (!outf) + return; + unsigned char uch = (unsigned char)(ch & 0xff); + fputc(uch, outf); + //fwrite(uch, 1, 1, outf); + break; + } + + case URI::SCHEME_DATA: + { + data.push_back(ch); + break; + } + + }//switch + +} + + + + + +/** + * + */ +UriWriter::UriWriter(const URI &uri) + throw (StreamException) +{ + outputStream = new UriOutputStream(uri); +} + +/** + * + */ +UriWriter::~UriWriter() throw (StreamException) +{ + delete outputStream; +} + +/** + * + */ +void UriWriter::close() throw(StreamException) +{ + outputStream->close(); +} + +/** + * + */ +void UriWriter::flush() throw(StreamException) +{ + outputStream->flush(); +} + +/** + * + */ +void UriWriter::put(XMLCh ch) throw(StreamException) +{ + int ich = (int)ch; + outputStream->put(ich); +} + + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + + +//######################################################################### +//# E N D O F F I L E +//######################################################################### diff --git a/src/dom/io/uristream.h b/src/dom/io/uristream.h new file mode 100644 index 000000000..892ac5c68 --- /dev/null +++ b/src/dom/io/uristream.h @@ -0,0 +1,213 @@ +#ifndef __URISTREAM_H__ +#define __URISTREAM_H__ + +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +/** + * This should be the only way that we provide sources/sinks + * to any input/output stream. + * + */ + + +#include "uri.h" +#include "domstream.h" +#include "httpclient.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace io +{ + + +//######################################################################### +//# U R I I N P U T S T R E A M / R E A D E R +//######################################################################### + +/** + * This class is for receiving a stream of data from a resource + * defined in a URI + */ +class UriInputStream : public InputStream +{ + +public: + + UriInputStream(const URI &source) throw(StreamException); + + virtual ~UriInputStream() throw(StreamException); + + virtual int available() throw(StreamException); + + virtual void close() throw(StreamException); + + virtual int get() throw(StreamException); + +private: + + void init() throw(StreamException);//common code called by constructor + + bool closed; + + FILE *inf; //for file: uris + unsigned char *data; //for data: uris + int dataPos; // current read position in data field + int dataLen; // length of data buffer + + URI uri; + + int scheme; + + HttpClient httpClient; + +}; // class UriInputStream + + + + +/** + * This class is for receiving a stream of formatted data from a resource + * defined in a URI + */ +class UriReader : public Reader +{ + +public: + + UriReader(const URI &source) throw(StreamException); + + virtual ~UriReader() throw(StreamException); + + virtual int available() throw(StreamException); + + virtual void close() throw(StreamException); + + virtual int get() throw(StreamException); + +private: + + UriInputStream *inputStream; + +}; // class UriReader + + + +//######################################################################### +//# U R I O U T P U T S T R E A M / W R I T E R +//######################################################################### + +/** + * This class is for sending a stream to a destination resource + * defined in a URI + * + */ +class UriOutputStream : public OutputStream +{ + +public: + + UriOutputStream(const URI &destination) throw(StreamException); + + virtual ~UriOutputStream() throw(StreamException); + + virtual void close() throw(StreamException); + + virtual void flush() throw(StreamException); + + virtual void put(XMLCh ch) throw(StreamException); + +private: + + void init() throw(StreamException); //common code called by constructor + + bool closed; + bool ownsFile; + + FILE *outf; //for file: uris + DOMString data; //for data: uris + + URI uri; + + int scheme; + + HttpClient httpClient; + +}; // class UriOutputStream + + + + + +/** + * This class is for sending a stream of formatted data to a resource + * defined in a URI + */ +class UriWriter : public Writer +{ + +public: + + UriWriter(const URI &source) throw(StreamException); + + virtual ~UriWriter() throw(StreamException); + + virtual void close() throw(StreamException); + + virtual void flush() throw(StreamException); + + virtual void put(XMLCh ch) throw(StreamException); + +private: + + UriOutputStream *outputStream; + +}; // class UriReader + + + + + + +} //namespace io +} //namespace dom +} //namespace w3c +} //namespace org + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + + +#endif /* __URISTREAM_H__ */ diff --git a/src/dom/js/fdlibm/.cvsignore b/src/dom/js/fdlibm/.cvsignore new file mode 100644 index 000000000..bb5cc66ee --- /dev/null +++ b/src/dom/js/fdlibm/.cvsignore @@ -0,0 +1,7 @@ +*.pdb +*.ncb +*.opt +*.plg +Debug +Release +Makefile diff --git a/src/dom/js/fdlibm/CVS/Entries b/src/dom/js/fdlibm/CVS/Entries new file mode 100644 index 000000000..e2e8e9cb6 --- /dev/null +++ b/src/dom/js/fdlibm/CVS/Entries @@ -0,0 +1,87 @@ +/.cvsignore/1.3/Sat Dec 5 09:02:32 1998//TJS_150 +/Makefile.in/1.13/Sat Nov 15 00:11:04 2003//TJS_150 +/Makefile.ref/1.7/Sat Nov 15 00:11:04 2003//TJS_150 +/e_acos.c/1.9/Sat Nov 15 00:11:04 2003//TJS_150 +/e_acosh.c/1.8/Sat Nov 15 00:11:04 2003//TJS_150 +/e_asin.c/1.10/Sat Nov 15 00:11:04 2003//TJS_150 +/e_atan2.c/1.9/Sat Nov 15 00:11:04 2003//TJS_150 +/e_atanh.c/1.9/Sat Nov 15 00:11:04 2003//TJS_150 +/e_cosh.c/1.9/Sat Nov 15 00:11:04 2003//TJS_150 +/e_exp.c/1.10/Sat Nov 15 00:11:04 2003//TJS_150 +/e_fmod.c/1.8/Sat Nov 15 00:11:04 2003//TJS_150 +/e_gamma.c/1.7/Sat Nov 15 00:11:04 2003//TJS_150 +/e_gamma_r.c/1.7/Sat Nov 15 00:11:04 2003//TJS_150 +/e_hypot.c/1.8/Sat Nov 15 00:11:04 2003//TJS_150 +/e_j0.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/e_j1.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/e_jn.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_lgamma.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/e_lgamma_r.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_log.c/1.10/Sat Nov 15 00:11:05 2003//TJS_150 +/e_log10.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_pow.c/1.12/Sat Nov 15 00:11:05 2003//TJS_150 +/e_rem_pio2.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_remainder.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_scalb.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/e_sinh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/e_sqrt.c/1.10/Sat Nov 15 00:11:05 2003//TJS_150 +/fdlibm.h/1.14/Thu Sep 23 23:32:51 2004//TJS_150 +/fdlibm.mak/1.3/Sun Apr 4 19:46:38 2004//TJS_150 +/fdlibm.mdp/1.3/Wed May 26 01:34:31 1999/-kb/TJS_150 +/k_cos.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/k_rem_pio2.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/k_sin.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/k_standard.c/1.12/Sat Nov 15 00:11:05 2003//TJS_150 +/k_tan.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_asinh.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/s_atan.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/s_cbrt.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_ceil.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/s_copysign.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_cos.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_erf.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_expm1.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/s_fabs.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_finite.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_floor.c/1.9/Sat Nov 15 00:11:05 2003//TJS_150 +/s_frexp.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_ilogb.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_isnan.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_ldexp.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/s_lib_version.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/s_log1p.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_logb.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_matherr.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/s_modf.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_nextafter.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_rint.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_scalbn.c/1.10/Sat Nov 15 00:11:05 2003//TJS_150 +/s_signgam.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/s_significand.c/1.7/Sat Nov 15 00:11:05 2003//TJS_150 +/s_sin.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_tan.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/s_tanh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_acos.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_acosh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_asin.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_atan2.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_atanh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_cosh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_exp.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_fmod.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_gamma.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_gamma_r.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_hypot.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_j0.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_j1.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_jn.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_lgamma.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_lgamma_r.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_log.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_log10.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_pow.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_remainder.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_scalb.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_sinh.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +/w_sqrt.c/1.8/Sat Nov 15 00:11:05 2003//TJS_150 +D diff --git a/src/dom/js/fdlibm/CVS/Repository b/src/dom/js/fdlibm/CVS/Repository new file mode 100644 index 000000000..88fbd6589 --- /dev/null +++ b/src/dom/js/fdlibm/CVS/Repository @@ -0,0 +1 @@ +mozilla/js/src/fdlibm diff --git a/src/dom/js/fdlibm/CVS/Root b/src/dom/js/fdlibm/CVS/Root new file mode 100644 index 000000000..cdb6f4a07 --- /dev/null +++ b/src/dom/js/fdlibm/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot diff --git a/src/dom/js/fdlibm/CVS/Tag b/src/dom/js/fdlibm/CVS/Tag new file mode 100644 index 000000000..3483efebb --- /dev/null +++ b/src/dom/js/fdlibm/CVS/Tag @@ -0,0 +1 @@ +NJS_150 diff --git a/src/dom/js/fdlibm/Makefile.in b/src/dom/js/fdlibm/Makefile.in new file mode 100644 index 000000000..fdec7b7e8 --- /dev/null +++ b/src/dom/js/fdlibm/Makefile.in @@ -0,0 +1,127 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = js +LIBRARY_NAME = fdm + +CSRCS = \ + e_acos.c \ + e_asin.c \ + e_atan2.c \ + e_exp.c \ + e_fmod.c \ + e_log.c \ + e_pow.c \ + e_rem_pio2.c \ + s_scalbn.c \ + e_sqrt.c \ + k_cos.c \ + k_sin.c \ + k_rem_pio2.c \ + k_tan.c \ + s_atan.c \ + s_ceil.c \ + s_copysign.c \ + s_cos.c \ + s_fabs.c \ + s_finite.c \ + s_floor.c \ + s_isnan.c \ + s_lib_version.c \ + s_sin.c \ + s_tan.c \ + w_acos.c \ + w_asin.c \ + w_atan2.c \ + w_exp.c \ + w_fmod.c \ + w_log.c \ + w_pow.c \ + w_sqrt.c \ + $(NULL) + +EXPORTS = fdlibm.h + +# we need to force a static lib for the linking that js/src/Makefile.in wants +# to do, and we don't really need a shared library ever, so: +FORCE_STATIC_LIB = 1 +FORCE_USE_PIC = 1 + +include $(topsrcdir)/config/rules.mk + +# +# Default IEEE libm +# +CFLAGS += -D_IEEE_LIBM + +ifeq ($(OS_ARCH),Linux) +LDFLAGS += -ldl +endif + +ifeq ($(OS_ARCH),OSF1) +LDFLAGS += -lc_r +endif + +ifeq ($(OS_ARCH),SunOS) +LDFLAGS += -lposix4 -ldl -lnsl -lsocket +ifeq ($(CPU_ARCH),sparc) + +ifndef JS_NO_ULTRA +ULTRA_OPTIONS := -xarch=v8plus,-DULTRA_SPARC +ULTRA_OPTIONSCC := -DULTRA_SPARC +else +ULTRA_OPTIONS := -xarch=v8 +ULTRA_OPTIONSCC := +endif + +ifeq ($(shell uname -m),sun4u) +ASFLAGS += -Wa,$(ULTRA_OPTIONS),-P,-L,-D_ASM,-D__STDC__=0 $(ULTRA_OPTIONSCC) +else +ASFLAGS += -Wa,-xarch=v8,-P,-L,-D_ASM,-D__STDC__=0 +endif + +endif +endif + diff --git a/src/dom/js/fdlibm/Makefile.ref b/src/dom/js/fdlibm/Makefile.ref new file mode 100644 index 000000000..de378025c --- /dev/null +++ b/src/dom/js/fdlibm/Makefile.ref @@ -0,0 +1,192 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# 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 ***** + +# +# @(#)Makefile 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. +# ==================================================== +# +# + +# +# There are two options in making libm at fdlibm compile time: +# _IEEE_LIBM --- IEEE libm; smaller, and somewhat faster +# _MULTI_LIBM --- Support multi-standard at runtime by +# imposing wrapper functions defined in +# fdlibm.h: +# _IEEE_MODE -- IEEE +# _XOPEN_MODE -- X/OPEN +# _POSIX_MODE -- POSIX/ANSI +# _SVID3_MODE -- SVID +# +# Here is how to set up CFLAGS to create the desired libm at +# compile time: +# +# CFLAGS = -D_IEEE_LIBM ... IEEE libm (recommended) +# CFLAGS = -D_SVID3_MODE ... Multi-standard supported +# libm with SVID as the +# default standard +# CFLAGS = -D_XOPEN_MODE ... Multi-standard supported +# libm with XOPEN as the +# default standard +# CFLAGS = -D_POSIX_MODE ... Multi-standard supported +# libm with POSIX as the +# default standard +# CFLAGS = ... Multi-standard supported +# libm with IEEE as the +# default standard +# +# NOTE: if scalb's second arguement is an int, then one must +# define _SCALB_INT in CFLAGS. The default prototype of scalb +# is double scalb(double, double) +# + +DEPTH = .. + +include $(DEPTH)/config.mk + +# +# Default IEEE libm +# +CFLAGS += -DXP_UNIX $(OPTIMIZER) $(OS_CFLAGS) $(DEFINES) $(INCLUDES) \ + -DJSFILE $(XCFLAGS) -D_IEEE_LIBM + +# Need for jstypes.h and friends +INCLUDES += -I.. +INCLUDES += -I../$(OBJDIR) + +#CC = cc + +INCFILES = fdlibm.h +.INIT: $(INCFILES) +.KEEP_STATE: +FDLIBM_CFILES = \ + k_standard.c k_rem_pio2.c \ + k_cos.c k_sin.c k_tan.c \ + e_acos.c e_acosh.c e_asin.c e_atan2.c \ + e_atanh.c e_cosh.c e_exp.c e_fmod.c \ + e_gamma.c e_gamma_r.c e_hypot.c e_j0.c \ + e_j1.c e_jn.c e_lgamma.c e_lgamma_r.c \ + e_log.c e_log10.c e_pow.c e_rem_pio2.c e_remainder.c \ + e_scalb.c e_sinh.c e_sqrt.c \ + w_acos.c w_acosh.c w_asin.c w_atan2.c \ + w_atanh.c w_cosh.c w_exp.c w_fmod.c \ + w_gamma.c w_gamma_r.c w_hypot.c w_j0.c \ + w_j1.c w_jn.c w_lgamma.c w_lgamma_r.c \ + w_log.c w_log10.c w_pow.c w_remainder.c \ + w_scalb.c w_sinh.c w_sqrt.c \ + s_asinh.c s_atan.c s_cbrt.c s_ceil.c s_copysign.c \ + s_cos.c s_erf.c s_expm1.c s_fabs.c s_finite.c s_floor.c \ + s_frexp.c s_ilogb.c s_isnan.c s_ldexp.c s_lib_version.c \ + s_log1p.c s_logb.c s_matherr.c s_modf.c s_nextafter.c \ + s_rint.c s_scalbn.c s_signgam.c s_significand.c s_sin.c \ + s_tan.c s_tanh.c + +ifdef USE_MSVC +FDLIBM_OBJS = $(addprefix $(OBJDIR)/, $(FDLIBM_CFILES:.c=.obj)) +else +FDLIBM_OBJS = $(addprefix $(OBJDIR)/, $(FDLIBM_CFILES:.c=.o)) +endif + +ifdef USE_MSVC +LIBRARY = $(OBJDIR)/fdlibm.lib +else +LIBRARY = $(OBJDIR)/libfdm.a +endif + +define MAKE_OBJDIR +if test ! -d $(@D); then rm -rf $(@D); mkdir -p $(@D); fi +endef + +all: $(LIBRARY) + +export: + +$(OBJDIR)/%: %.c + @$(MAKE_OBJDIR) + $(CC) -o $@ $(CFLAGS) $*.c $(LDFLAGS) + +$(OBJDIR)/%.o: %.c + @$(MAKE_OBJDIR) + $(CC) -o $@ -c $(CFLAGS) $*.c + +$(OBJDIR)/%.o: %.s + @$(MAKE_OBJDIR) + $(AS) -o $@ $(ASFLAGS) $*.s + +# windows only +$(OBJDIR)/%.obj: %.c + @$(MAKE_OBJDIR) + $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $*.c + +ifeq ($(OS_ARCH),OS2) +$(LIBRARY): $(FDLIBM_OBJS) + $(AR) $@ $? $(AR_OS2_SUFFIX) + $(RANLIB) $@ +else +ifdef USE_MSVC +$(LIBRARY): $(FDLIBM_OBJS) + lib.exe /out:"$@" $? +else +$(LIBRARY): $(FDLIBM_OBJS) + $(AR) rv $@ $? + $(RANLIB) $@ +endif +endif + +libfdm.a : $(FDLIBM_OBJS) + $(AR) cru $(OBJDIR)/libfdm.a $(FDLIBM_OBJS) + $(RANLIB) $(OBJDIR)/libfdm.a + +clean: + rm -rf $(FDLIBM_OBJS) + +clobber: + rm -rf $(FDLIBM_OBJS) $(LIBRARY) $(DEPENDENCIES) + +SUFFIXES: .i +%.i: %.c + $(CC) -C -E $(CFLAGS) $< > $*.i diff --git a/src/dom/js/fdlibm/e_acos.c b/src/dom/js/fdlibm/e_acos.c new file mode 100644 index 000000000..a07c1eebc --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_acosh.c b/src/dom/js/fdlibm/e_acosh.c new file mode 100644 index 000000000..725cceefb --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_atan2.c b/src/dom/js/fdlibm/e_atan2.c new file mode 100644 index 000000000..9c9a2c01f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_atanh.c b/src/dom/js/fdlibm/e_atanh.c new file mode 100644 index 000000000..dc4a90c8e --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_cosh.c b/src/dom/js/fdlibm/e_cosh.c new file mode 100644 index 000000000..4f8d4f769 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_exp.c b/src/dom/js/fdlibm/e_exp.c new file mode 100644 index 000000000..ad9cec124 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_fmod.c b/src/dom/js/fdlibm/e_fmod.c new file mode 100644 index 000000000..7b5ce780f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_gamma.c b/src/dom/js/fdlibm/e_gamma.c new file mode 100644 index 000000000..a34faa32c --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_gamma_r.c b/src/dom/js/fdlibm/e_gamma_r.c new file mode 100644 index 000000000..f10e32e36 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_hypot.c b/src/dom/js/fdlibm/e_hypot.c new file mode 100644 index 000000000..390023087 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_j0.c b/src/dom/js/fdlibm/e_j0.c new file mode 100644 index 000000000..078e09641 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_j1.c b/src/dom/js/fdlibm/e_j1.c new file mode 100644 index 000000000..8982ac86a --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_jn.c b/src/dom/js/fdlibm/e_jn.c new file mode 100644 index 000000000..2b61b4439 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_lgamma.c b/src/dom/js/fdlibm/e_lgamma.c new file mode 100644 index 000000000..beb3bd932 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_lgamma_r.c b/src/dom/js/fdlibm/e_lgamma_r.c new file mode 100644 index 000000000..df92e7a26 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_log.c b/src/dom/js/fdlibm/e_log.c new file mode 100644 index 000000000..8645d6efd --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_log10.c b/src/dom/js/fdlibm/e_log10.c new file mode 100644 index 000000000..5f88f4b4c --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_pow.c b/src/dom/js/fdlibm/e_pow.c new file mode 100644 index 000000000..18c8d0695 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_rem_pio2.c b/src/dom/js/fdlibm/e_rem_pio2.c new file mode 100644 index 000000000..6f9423551 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_remainder.c b/src/dom/js/fdlibm/e_remainder.c new file mode 100644 index 000000000..de40f0c2a --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_scalb.c b/src/dom/js/fdlibm/e_scalb.c new file mode 100644 index 000000000..621704ea0 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_sinh.c b/src/dom/js/fdlibm/e_sinh.c new file mode 100644 index 000000000..98ab9b5a3 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/e_sqrt.c b/src/dom/js/fdlibm/e_sqrt.c new file mode 100644 index 000000000..91802839b --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/fdlibm.h b/src/dom/js/fdlibm/fdlibm.h new file mode 100644 index 000000000..8e25214f0 --- /dev/null +++ b/src/dom/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__) || defined(__x86_64__)) +#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/dom/js/fdlibm/fdlibm.mak b/src/dom/js/fdlibm/fdlibm.mak new file mode 100644 index 000000000..436c1c450 --- /dev/null +++ b/src/dom/js/fdlibm/fdlibm.mak @@ -0,0 +1,1453 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +!IF "$(CFG)" == "" +CFG=fdlibm - Win32 Debug +!MESSAGE No configuration specified. Defaulting to fdlibm - Win32 Debug. +!ENDIF + +!IF "$(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 "fdlibm.mak" CFG="fdlibm - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!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 +CPP=cl.exe + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "fdlibm__" +# PROP BASE Intermediate_Dir "fdlibm__" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "fdlibm__" +# PROP Intermediate_Dir "fdlibm__" +# PROP Target_Dir "" +OUTDIR=.\fdlibm__ +INTDIR=.\fdlibm__ + +ALL : "$(OUTDIR)\fdlibm.lib" + +CLEAN : + -@erase "$(INTDIR)\e_acos.obj" + -@erase "$(INTDIR)\e_acosh.obj" + -@erase "$(INTDIR)\e_asin.obj" + -@erase "$(INTDIR)\e_atan2.obj" + -@erase "$(INTDIR)\e_atanh.obj" + -@erase "$(INTDIR)\e_cosh.obj" + -@erase "$(INTDIR)\e_exp.obj" + -@erase "$(INTDIR)\e_fmod.obj" + -@erase "$(INTDIR)\e_gamma.obj" + -@erase "$(INTDIR)\e_gamma_r.obj" + -@erase "$(INTDIR)\e_hypot.obj" + -@erase "$(INTDIR)\e_j0.obj" + -@erase "$(INTDIR)\e_j1.obj" + -@erase "$(INTDIR)\e_jn.obj" + -@erase "$(INTDIR)\e_lgamma.obj" + -@erase "$(INTDIR)\e_lgamma_r.obj" + -@erase "$(INTDIR)\e_log.obj" + -@erase "$(INTDIR)\e_log10.obj" + -@erase "$(INTDIR)\e_pow.obj" + -@erase "$(INTDIR)\e_rem_pio2.obj" + -@erase "$(INTDIR)\e_remainder.obj" + -@erase "$(INTDIR)\e_scalb.obj" + -@erase "$(INTDIR)\e_sinh.obj" + -@erase "$(INTDIR)\e_sqrt.obj" + -@erase "$(INTDIR)\k_cos.obj" + -@erase "$(INTDIR)\k_rem_pio2.obj" + -@erase "$(INTDIR)\k_sin.obj" + -@erase "$(INTDIR)\k_standard.obj" + -@erase "$(INTDIR)\k_tan.obj" + -@erase "$(INTDIR)\s_asinh.obj" + -@erase "$(INTDIR)\s_atan.obj" + -@erase "$(INTDIR)\s_cbrt.obj" + -@erase "$(INTDIR)\s_ceil.obj" + -@erase "$(INTDIR)\s_copysign.obj" + -@erase "$(INTDIR)\s_cos.obj" + -@erase "$(INTDIR)\s_erf.obj" + -@erase "$(INTDIR)\s_expm1.obj" + -@erase "$(INTDIR)\s_fabs.obj" + -@erase "$(INTDIR)\s_finite.obj" + -@erase "$(INTDIR)\s_floor.obj" + -@erase "$(INTDIR)\s_frexp.obj" + -@erase "$(INTDIR)\s_ilogb.obj" + -@erase "$(INTDIR)\s_isnan.obj" + -@erase "$(INTDIR)\s_ldexp.obj" + -@erase "$(INTDIR)\s_lib_version.obj" + -@erase "$(INTDIR)\s_log1p.obj" + -@erase "$(INTDIR)\s_logb.obj" + -@erase "$(INTDIR)\s_matherr.obj" + -@erase "$(INTDIR)\s_modf.obj" + -@erase "$(INTDIR)\s_nextafter.obj" + -@erase "$(INTDIR)\s_rint.obj" + -@erase "$(INTDIR)\s_scalbn.obj" + -@erase "$(INTDIR)\s_signgam.obj" + -@erase "$(INTDIR)\s_significand.obj" + -@erase "$(INTDIR)\s_sin.obj" + -@erase "$(INTDIR)\s_tan.obj" + -@erase "$(INTDIR)\s_tanh.obj" + -@erase "$(INTDIR)\w_acos.obj" + -@erase "$(INTDIR)\w_acosh.obj" + -@erase "$(INTDIR)\w_asin.obj" + -@erase "$(INTDIR)\w_atan2.obj" + -@erase "$(INTDIR)\w_atanh.obj" + -@erase "$(INTDIR)\w_cosh.obj" + -@erase "$(INTDIR)\w_exp.obj" + -@erase "$(INTDIR)\w_fmod.obj" + -@erase "$(INTDIR)\w_gamma.obj" + -@erase "$(INTDIR)\w_gamma_r.obj" + -@erase "$(INTDIR)\w_hypot.obj" + -@erase "$(INTDIR)\w_j0.obj" + -@erase "$(INTDIR)\w_j1.obj" + -@erase "$(INTDIR)\w_jn.obj" + -@erase "$(INTDIR)\w_lgamma.obj" + -@erase "$(INTDIR)\w_lgamma_r.obj" + -@erase "$(INTDIR)\w_log.obj" + -@erase "$(INTDIR)\w_log10.obj" + -@erase "$(INTDIR)\w_pow.obj" + -@erase "$(INTDIR)\w_remainder.obj" + -@erase "$(INTDIR)\w_scalb.obj" + -@erase "$(INTDIR)\w_sinh.obj" + -@erase "$(INTDIR)\w_sqrt.obj" + -@erase "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\ + /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\fdlibm__/ +CPP_SBRS=.\. +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_acos.obj" \ + "$(INTDIR)\e_acosh.obj" \ + "$(INTDIR)\e_asin.obj" \ + "$(INTDIR)\e_atan2.obj" \ + "$(INTDIR)\e_atanh.obj" \ + "$(INTDIR)\e_cosh.obj" \ + "$(INTDIR)\e_exp.obj" \ + "$(INTDIR)\e_fmod.obj" \ + "$(INTDIR)\e_gamma.obj" \ + "$(INTDIR)\e_gamma_r.obj" \ + "$(INTDIR)\e_hypot.obj" \ + "$(INTDIR)\e_j0.obj" \ + "$(INTDIR)\e_j1.obj" \ + "$(INTDIR)\e_jn.obj" \ + "$(INTDIR)\e_lgamma.obj" \ + "$(INTDIR)\e_lgamma_r.obj" \ + "$(INTDIR)\e_log.obj" \ + "$(INTDIR)\e_log10.obj" \ + "$(INTDIR)\e_pow.obj" \ + "$(INTDIR)\e_rem_pio2.obj" \ + "$(INTDIR)\e_remainder.obj" \ + "$(INTDIR)\e_scalb.obj" \ + "$(INTDIR)\e_sinh.obj" \ + "$(INTDIR)\e_sqrt.obj" \ + "$(INTDIR)\k_cos.obj" \ + "$(INTDIR)\k_rem_pio2.obj" \ + "$(INTDIR)\k_sin.obj" \ + "$(INTDIR)\k_standard.obj" \ + "$(INTDIR)\k_tan.obj" \ + "$(INTDIR)\s_asinh.obj" \ + "$(INTDIR)\s_atan.obj" \ + "$(INTDIR)\s_cbrt.obj" \ + "$(INTDIR)\s_ceil.obj" \ + "$(INTDIR)\s_copysign.obj" \ + "$(INTDIR)\s_cos.obj" \ + "$(INTDIR)\s_erf.obj" \ + "$(INTDIR)\s_expm1.obj" \ + "$(INTDIR)\s_fabs.obj" \ + "$(INTDIR)\s_finite.obj" \ + "$(INTDIR)\s_floor.obj" \ + "$(INTDIR)\s_frexp.obj" \ + "$(INTDIR)\s_ilogb.obj" \ + "$(INTDIR)\s_isnan.obj" \ + "$(INTDIR)\s_ldexp.obj" \ + "$(INTDIR)\s_lib_version.obj" \ + "$(INTDIR)\s_log1p.obj" \ + "$(INTDIR)\s_logb.obj" \ + "$(INTDIR)\s_matherr.obj" \ + "$(INTDIR)\s_modf.obj" \ + "$(INTDIR)\s_nextafter.obj" \ + "$(INTDIR)\s_rint.obj" \ + "$(INTDIR)\s_scalbn.obj" \ + "$(INTDIR)\s_signgam.obj" \ + "$(INTDIR)\s_significand.obj" \ + "$(INTDIR)\s_sin.obj" \ + "$(INTDIR)\s_tan.obj" \ + "$(INTDIR)\s_tanh.obj" \ + "$(INTDIR)\w_acos.obj" \ + "$(INTDIR)\w_acosh.obj" \ + "$(INTDIR)\w_asin.obj" \ + "$(INTDIR)\w_atan2.obj" \ + "$(INTDIR)\w_atanh.obj" \ + "$(INTDIR)\w_cosh.obj" \ + "$(INTDIR)\w_exp.obj" \ + "$(INTDIR)\w_fmod.obj" \ + "$(INTDIR)\w_gamma.obj" \ + "$(INTDIR)\w_gamma_r.obj" \ + "$(INTDIR)\w_hypot.obj" \ + "$(INTDIR)\w_j0.obj" \ + "$(INTDIR)\w_j1.obj" \ + "$(INTDIR)\w_jn.obj" \ + "$(INTDIR)\w_lgamma.obj" \ + "$(INTDIR)\w_lgamma_r.obj" \ + "$(INTDIR)\w_log.obj" \ + "$(INTDIR)\w_log10.obj" \ + "$(INTDIR)\w_pow.obj" \ + "$(INTDIR)\w_remainder.obj" \ + "$(INTDIR)\w_scalb.obj" \ + "$(INTDIR)\w_sinh.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_0" +# PROP BASE Intermediate_Dir "fdlibm_0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "fdlibm_0" +# PROP Intermediate_Dir "fdlibm_0" +# PROP Target_Dir "" +OUTDIR=.\fdlibm_0 +INTDIR=.\fdlibm_0 + +ALL : "$(OUTDIR)\fdlibm.lib" + +CLEAN : + -@erase "$(INTDIR)\e_acos.obj" + -@erase "$(INTDIR)\e_acosh.obj" + -@erase "$(INTDIR)\e_asin.obj" + -@erase "$(INTDIR)\e_atan2.obj" + -@erase "$(INTDIR)\e_atanh.obj" + -@erase "$(INTDIR)\e_cosh.obj" + -@erase "$(INTDIR)\e_exp.obj" + -@erase "$(INTDIR)\e_fmod.obj" + -@erase "$(INTDIR)\e_gamma.obj" + -@erase "$(INTDIR)\e_gamma_r.obj" + -@erase "$(INTDIR)\e_hypot.obj" + -@erase "$(INTDIR)\e_j0.obj" + -@erase "$(INTDIR)\e_j1.obj" + -@erase "$(INTDIR)\e_jn.obj" + -@erase "$(INTDIR)\e_lgamma.obj" + -@erase "$(INTDIR)\e_lgamma_r.obj" + -@erase "$(INTDIR)\e_log.obj" + -@erase "$(INTDIR)\e_log10.obj" + -@erase "$(INTDIR)\e_pow.obj" + -@erase "$(INTDIR)\e_rem_pio2.obj" + -@erase "$(INTDIR)\e_remainder.obj" + -@erase "$(INTDIR)\e_scalb.obj" + -@erase "$(INTDIR)\e_sinh.obj" + -@erase "$(INTDIR)\e_sqrt.obj" + -@erase "$(INTDIR)\k_cos.obj" + -@erase "$(INTDIR)\k_rem_pio2.obj" + -@erase "$(INTDIR)\k_sin.obj" + -@erase "$(INTDIR)\k_standard.obj" + -@erase "$(INTDIR)\k_tan.obj" + -@erase "$(INTDIR)\s_asinh.obj" + -@erase "$(INTDIR)\s_atan.obj" + -@erase "$(INTDIR)\s_cbrt.obj" + -@erase "$(INTDIR)\s_ceil.obj" + -@erase "$(INTDIR)\s_copysign.obj" + -@erase "$(INTDIR)\s_cos.obj" + -@erase "$(INTDIR)\s_erf.obj" + -@erase "$(INTDIR)\s_expm1.obj" + -@erase "$(INTDIR)\s_fabs.obj" + -@erase "$(INTDIR)\s_finite.obj" + -@erase "$(INTDIR)\s_floor.obj" + -@erase "$(INTDIR)\s_frexp.obj" + -@erase "$(INTDIR)\s_ilogb.obj" + -@erase "$(INTDIR)\s_isnan.obj" + -@erase "$(INTDIR)\s_ldexp.obj" + -@erase "$(INTDIR)\s_lib_version.obj" + -@erase "$(INTDIR)\s_log1p.obj" + -@erase "$(INTDIR)\s_logb.obj" + -@erase "$(INTDIR)\s_matherr.obj" + -@erase "$(INTDIR)\s_modf.obj" + -@erase "$(INTDIR)\s_nextafter.obj" + -@erase "$(INTDIR)\s_rint.obj" + -@erase "$(INTDIR)\s_scalbn.obj" + -@erase "$(INTDIR)\s_signgam.obj" + -@erase "$(INTDIR)\s_significand.obj" + -@erase "$(INTDIR)\s_sin.obj" + -@erase "$(INTDIR)\s_tan.obj" + -@erase "$(INTDIR)\s_tanh.obj" + -@erase "$(INTDIR)\w_acos.obj" + -@erase "$(INTDIR)\w_acosh.obj" + -@erase "$(INTDIR)\w_asin.obj" + -@erase "$(INTDIR)\w_atan2.obj" + -@erase "$(INTDIR)\w_atanh.obj" + -@erase "$(INTDIR)\w_cosh.obj" + -@erase "$(INTDIR)\w_exp.obj" + -@erase "$(INTDIR)\w_fmod.obj" + -@erase "$(INTDIR)\w_gamma.obj" + -@erase "$(INTDIR)\w_gamma_r.obj" + -@erase "$(INTDIR)\w_hypot.obj" + -@erase "$(INTDIR)\w_j0.obj" + -@erase "$(INTDIR)\w_j1.obj" + -@erase "$(INTDIR)\w_jn.obj" + -@erase "$(INTDIR)\w_lgamma.obj" + -@erase "$(INTDIR)\w_lgamma_r.obj" + -@erase "$(INTDIR)\w_log.obj" + -@erase "$(INTDIR)\w_log10.obj" + -@erase "$(INTDIR)\w_pow.obj" + -@erase "$(INTDIR)\w_remainder.obj" + -@erase "$(INTDIR)\w_scalb.obj" + -@erase "$(INTDIR)\w_sinh.obj" + -@erase "$(INTDIR)\w_sqrt.obj" + -@erase "$(OUTDIR)\fdlibm.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /MLd /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\ + /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\fdlibm_0/ +CPP_SBRS=.\. +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_acos.obj" \ + "$(INTDIR)\e_acosh.obj" \ + "$(INTDIR)\e_asin.obj" \ + "$(INTDIR)\e_atan2.obj" \ + "$(INTDIR)\e_atanh.obj" \ + "$(INTDIR)\e_cosh.obj" \ + "$(INTDIR)\e_exp.obj" \ + "$(INTDIR)\e_fmod.obj" \ + "$(INTDIR)\e_gamma.obj" \ + "$(INTDIR)\e_gamma_r.obj" \ + "$(INTDIR)\e_hypot.obj" \ + "$(INTDIR)\e_j0.obj" \ + "$(INTDIR)\e_j1.obj" \ + "$(INTDIR)\e_jn.obj" \ + "$(INTDIR)\e_lgamma.obj" \ + "$(INTDIR)\e_lgamma_r.obj" \ + "$(INTDIR)\e_log.obj" \ + "$(INTDIR)\e_log10.obj" \ + "$(INTDIR)\e_pow.obj" \ + "$(INTDIR)\e_rem_pio2.obj" \ + "$(INTDIR)\e_remainder.obj" \ + "$(INTDIR)\e_scalb.obj" \ + "$(INTDIR)\e_sinh.obj" \ + "$(INTDIR)\e_sqrt.obj" \ + "$(INTDIR)\k_cos.obj" \ + "$(INTDIR)\k_rem_pio2.obj" \ + "$(INTDIR)\k_sin.obj" \ + "$(INTDIR)\k_standard.obj" \ + "$(INTDIR)\k_tan.obj" \ + "$(INTDIR)\s_asinh.obj" \ + "$(INTDIR)\s_atan.obj" \ + "$(INTDIR)\s_cbrt.obj" \ + "$(INTDIR)\s_ceil.obj" \ + "$(INTDIR)\s_copysign.obj" \ + "$(INTDIR)\s_cos.obj" \ + "$(INTDIR)\s_erf.obj" \ + "$(INTDIR)\s_expm1.obj" \ + "$(INTDIR)\s_fabs.obj" \ + "$(INTDIR)\s_finite.obj" \ + "$(INTDIR)\s_floor.obj" \ + "$(INTDIR)\s_frexp.obj" \ + "$(INTDIR)\s_ilogb.obj" \ + "$(INTDIR)\s_isnan.obj" \ + "$(INTDIR)\s_ldexp.obj" \ + "$(INTDIR)\s_lib_version.obj" \ + "$(INTDIR)\s_log1p.obj" \ + "$(INTDIR)\s_logb.obj" \ + "$(INTDIR)\s_matherr.obj" \ + "$(INTDIR)\s_modf.obj" \ + "$(INTDIR)\s_nextafter.obj" \ + "$(INTDIR)\s_rint.obj" \ + "$(INTDIR)\s_scalbn.obj" \ + "$(INTDIR)\s_signgam.obj" \ + "$(INTDIR)\s_significand.obj" \ + "$(INTDIR)\s_sin.obj" \ + "$(INTDIR)\s_tan.obj" \ + "$(INTDIR)\s_tanh.obj" \ + "$(INTDIR)\w_acos.obj" \ + "$(INTDIR)\w_acosh.obj" \ + "$(INTDIR)\w_asin.obj" \ + "$(INTDIR)\w_atan2.obj" \ + "$(INTDIR)\w_atanh.obj" \ + "$(INTDIR)\w_cosh.obj" \ + "$(INTDIR)\w_exp.obj" \ + "$(INTDIR)\w_fmod.obj" \ + "$(INTDIR)\w_gamma.obj" \ + "$(INTDIR)\w_gamma_r.obj" \ + "$(INTDIR)\w_hypot.obj" \ + "$(INTDIR)\w_j0.obj" \ + "$(INTDIR)\w_j1.obj" \ + "$(INTDIR)\w_jn.obj" \ + "$(INTDIR)\w_lgamma.obj" \ + "$(INTDIR)\w_lgamma_r.obj" \ + "$(INTDIR)\w_log.obj" \ + "$(INTDIR)\w_log10.obj" \ + "$(INTDIR)\w_pow.obj" \ + "$(INTDIR)\w_remainder.obj" \ + "$(INTDIR)\w_scalb.obj" \ + "$(INTDIR)\w_sinh.obj" \ + "$(INTDIR)\w_sqrt.obj" + +"$(OUTDIR)\fdlibm.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +.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) $< + +################################################################################ +# 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=.\w_sqrt.c +DEP_CPP_W_SQR=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_sqrt.obj" : $(SOURCE) $(DEP_CPP_W_SQR) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_acosh.c +DEP_CPP_E_ACO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_acosh.obj" : $(SOURCE) $(DEP_CPP_E_ACO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_asin.c +DEP_CPP_E_ASI=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_asin.obj" : $(SOURCE) $(DEP_CPP_E_ASI) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_atan2.c +DEP_CPP_E_ATA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_atan2.obj" : $(SOURCE) $(DEP_CPP_E_ATA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_atanh.c +DEP_CPP_E_ATAN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_atanh.obj" : $(SOURCE) $(DEP_CPP_E_ATAN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_cosh.c +DEP_CPP_E_COS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_cosh.obj" : $(SOURCE) $(DEP_CPP_E_COS) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_exp.c +DEP_CPP_E_EXP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_exp.obj" : $(SOURCE) $(DEP_CPP_E_EXP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_fmod.c +DEP_CPP_E_FMO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_fmod.obj" : $(SOURCE) $(DEP_CPP_E_FMO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_gamma.c +DEP_CPP_E_GAM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_gamma.obj" : $(SOURCE) $(DEP_CPP_E_GAM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_gamma_r.c +DEP_CPP_E_GAMM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_gamma_r.obj" : $(SOURCE) $(DEP_CPP_E_GAMM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_hypot.c +DEP_CPP_E_HYP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_hypot.obj" : $(SOURCE) $(DEP_CPP_E_HYP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_j0.c +DEP_CPP_E_J0_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_j0.obj" : $(SOURCE) $(DEP_CPP_E_J0_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_j1.c +DEP_CPP_E_J1_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_j1.obj" : $(SOURCE) $(DEP_CPP_E_J1_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_jn.c +DEP_CPP_E_JN_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_jn.obj" : $(SOURCE) $(DEP_CPP_E_JN_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_lgamma.c +DEP_CPP_E_LGA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_lgamma.obj" : $(SOURCE) $(DEP_CPP_E_LGA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_lgamma_r.c +DEP_CPP_E_LGAM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_lgamma_r.obj" : $(SOURCE) $(DEP_CPP_E_LGAM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_log.c +DEP_CPP_E_LOG=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_log.obj" : $(SOURCE) $(DEP_CPP_E_LOG) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_log10.c +DEP_CPP_E_LOG1=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_log10.obj" : $(SOURCE) $(DEP_CPP_E_LOG1) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_pow.c +DEP_CPP_E_POW=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_pow.obj" : $(SOURCE) $(DEP_CPP_E_POW) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_rem_pio2.c +DEP_CPP_E_REM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_rem_pio2.obj" : $(SOURCE) $(DEP_CPP_E_REM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_remainder.c +DEP_CPP_E_REMA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_remainder.obj" : $(SOURCE) $(DEP_CPP_E_REMA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_scalb.c +DEP_CPP_E_SCA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_scalb.obj" : $(SOURCE) $(DEP_CPP_E_SCA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_sinh.c +DEP_CPP_E_SIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_sinh.obj" : $(SOURCE) $(DEP_CPP_E_SIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_sqrt.c +DEP_CPP_E_SQR=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_sqrt.obj" : $(SOURCE) $(DEP_CPP_E_SQR) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\fdlibm.h + +!IF "$(CFG)" == "fdlibm - Win32 Release" + +!ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\k_cos.c +DEP_CPP_K_COS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\k_cos.obj" : $(SOURCE) $(DEP_CPP_K_COS) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\k_rem_pio2.c +DEP_CPP_K_REM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\k_rem_pio2.obj" : $(SOURCE) $(DEP_CPP_K_REM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\k_sin.c +DEP_CPP_K_SIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\k_sin.obj" : $(SOURCE) $(DEP_CPP_K_SIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\k_standard.c +DEP_CPP_K_STA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\k_standard.obj" : $(SOURCE) $(DEP_CPP_K_STA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\k_tan.c +DEP_CPP_K_TAN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\k_tan.obj" : $(SOURCE) $(DEP_CPP_K_TAN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_asinh.c +DEP_CPP_S_ASI=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_asinh.obj" : $(SOURCE) $(DEP_CPP_S_ASI) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_atan.c +DEP_CPP_S_ATA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_atan.obj" : $(SOURCE) $(DEP_CPP_S_ATA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_cbrt.c +DEP_CPP_S_CBR=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_cbrt.obj" : $(SOURCE) $(DEP_CPP_S_CBR) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_ceil.c +DEP_CPP_S_CEI=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_ceil.obj" : $(SOURCE) $(DEP_CPP_S_CEI) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_copysign.c +DEP_CPP_S_COP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_copysign.obj" : $(SOURCE) $(DEP_CPP_S_COP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_cos.c +DEP_CPP_S_COS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_cos.obj" : $(SOURCE) $(DEP_CPP_S_COS) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_erf.c +DEP_CPP_S_ERF=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_erf.obj" : $(SOURCE) $(DEP_CPP_S_ERF) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_expm1.c +DEP_CPP_S_EXP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_expm1.obj" : $(SOURCE) $(DEP_CPP_S_EXP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_fabs.c +DEP_CPP_S_FAB=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_fabs.obj" : $(SOURCE) $(DEP_CPP_S_FAB) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_finite.c +DEP_CPP_S_FIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_finite.obj" : $(SOURCE) $(DEP_CPP_S_FIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_floor.c +DEP_CPP_S_FLO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_floor.obj" : $(SOURCE) $(DEP_CPP_S_FLO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_frexp.c +DEP_CPP_S_FRE=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_frexp.obj" : $(SOURCE) $(DEP_CPP_S_FRE) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_ilogb.c +DEP_CPP_S_ILO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_ilogb.obj" : $(SOURCE) $(DEP_CPP_S_ILO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_isnan.c +DEP_CPP_S_ISN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_isnan.obj" : $(SOURCE) $(DEP_CPP_S_ISN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_ldexp.c +DEP_CPP_S_LDE=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_ldexp.obj" : $(SOURCE) $(DEP_CPP_S_LDE) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_lib_version.c +DEP_CPP_S_LIB=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_lib_version.obj" : $(SOURCE) $(DEP_CPP_S_LIB) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_log1p.c +DEP_CPP_S_LOG=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_log1p.obj" : $(SOURCE) $(DEP_CPP_S_LOG) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_logb.c +DEP_CPP_S_LOGB=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_logb.obj" : $(SOURCE) $(DEP_CPP_S_LOGB) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_matherr.c +DEP_CPP_S_MAT=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_matherr.obj" : $(SOURCE) $(DEP_CPP_S_MAT) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_modf.c +DEP_CPP_S_MOD=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_modf.obj" : $(SOURCE) $(DEP_CPP_S_MOD) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_nextafter.c +DEP_CPP_S_NEX=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_nextafter.obj" : $(SOURCE) $(DEP_CPP_S_NEX) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_rint.c +DEP_CPP_S_RIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_rint.obj" : $(SOURCE) $(DEP_CPP_S_RIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_scalbn.c +DEP_CPP_S_SCA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_scalbn.obj" : $(SOURCE) $(DEP_CPP_S_SCA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_signgam.c +DEP_CPP_S_SIG=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_signgam.obj" : $(SOURCE) $(DEP_CPP_S_SIG) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_significand.c +DEP_CPP_S_SIGN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_significand.obj" : $(SOURCE) $(DEP_CPP_S_SIGN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_sin.c +DEP_CPP_S_SIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_sin.obj" : $(SOURCE) $(DEP_CPP_S_SIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_tan.c +DEP_CPP_S_TAN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_tan.obj" : $(SOURCE) $(DEP_CPP_S_TAN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\s_tanh.c +DEP_CPP_S_TANH=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\s_tanh.obj" : $(SOURCE) $(DEP_CPP_S_TANH) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_acos.c +DEP_CPP_W_ACO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_acos.obj" : $(SOURCE) $(DEP_CPP_W_ACO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_acosh.c +DEP_CPP_W_ACOS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_acosh.obj" : $(SOURCE) $(DEP_CPP_W_ACOS) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_asin.c +DEP_CPP_W_ASI=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_asin.obj" : $(SOURCE) $(DEP_CPP_W_ASI) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_atan2.c +DEP_CPP_W_ATA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_atan2.obj" : $(SOURCE) $(DEP_CPP_W_ATA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_atanh.c +DEP_CPP_W_ATAN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_atanh.obj" : $(SOURCE) $(DEP_CPP_W_ATAN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_cosh.c +DEP_CPP_W_COS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_cosh.obj" : $(SOURCE) $(DEP_CPP_W_COS) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_exp.c +DEP_CPP_W_EXP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_exp.obj" : $(SOURCE) $(DEP_CPP_W_EXP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_fmod.c +DEP_CPP_W_FMO=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_fmod.obj" : $(SOURCE) $(DEP_CPP_W_FMO) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_gamma.c +DEP_CPP_W_GAM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_gamma.obj" : $(SOURCE) $(DEP_CPP_W_GAM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_gamma_r.c +DEP_CPP_W_GAMM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_gamma_r.obj" : $(SOURCE) $(DEP_CPP_W_GAMM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_hypot.c +DEP_CPP_W_HYP=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_hypot.obj" : $(SOURCE) $(DEP_CPP_W_HYP) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_j0.c +DEP_CPP_W_J0_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_j0.obj" : $(SOURCE) $(DEP_CPP_W_J0_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_j1.c +DEP_CPP_W_J1_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_j1.obj" : $(SOURCE) $(DEP_CPP_W_J1_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_jn.c +DEP_CPP_W_JN_=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_jn.obj" : $(SOURCE) $(DEP_CPP_W_JN_) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_lgamma.c +DEP_CPP_W_LGA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_lgamma.obj" : $(SOURCE) $(DEP_CPP_W_LGA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_lgamma_r.c +DEP_CPP_W_LGAM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_lgamma_r.obj" : $(SOURCE) $(DEP_CPP_W_LGAM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_log.c +DEP_CPP_W_LOG=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_log.obj" : $(SOURCE) $(DEP_CPP_W_LOG) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_log10.c +DEP_CPP_W_LOG1=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_log10.obj" : $(SOURCE) $(DEP_CPP_W_LOG1) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_pow.c +DEP_CPP_W_POW=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_pow.obj" : $(SOURCE) $(DEP_CPP_W_POW) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_remainder.c +DEP_CPP_W_REM=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_remainder.obj" : $(SOURCE) $(DEP_CPP_W_REM) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_scalb.c +DEP_CPP_W_SCA=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_scalb.obj" : $(SOURCE) $(DEP_CPP_W_SCA) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\w_sinh.c +DEP_CPP_W_SIN=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\w_sinh.obj" : $(SOURCE) $(DEP_CPP_W_SIN) "$(INTDIR)" + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\e_acos.c +DEP_CPP_E_ACOS=\ + ".\fdlibm.h"\ + + +"$(INTDIR)\e_acos.obj" : $(SOURCE) $(DEP_CPP_E_ACOS) "$(INTDIR)" + + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/src/dom/js/fdlibm/fdlibm.mdp b/src/dom/js/fdlibm/fdlibm.mdp new file mode 100644 index 000000000..5904c4940 Binary files /dev/null and b/src/dom/js/fdlibm/fdlibm.mdp differ diff --git a/src/dom/js/fdlibm/k_cos.c b/src/dom/js/fdlibm/k_cos.c new file mode 100644 index 000000000..17b505d58 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/k_rem_pio2.c b/src/dom/js/fdlibm/k_rem_pio2.c new file mode 100644 index 000000000..d261e190a --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/k_sin.c b/src/dom/js/fdlibm/k_sin.c new file mode 100644 index 000000000..d2bdabd6d --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/k_standard.c b/src/dom/js/fdlibm/k_standard.c new file mode 100644 index 000000000..720109c9d --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/k_tan.c b/src/dom/js/fdlibm/k_tan.c new file mode 100644 index 000000000..1e7681b88 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_asinh.c b/src/dom/js/fdlibm/s_asinh.c new file mode 100644 index 000000000..fdf70a91c --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_atan.c b/src/dom/js/fdlibm/s_atan.c new file mode 100644 index 000000000..99a00c686 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_cbrt.c b/src/dom/js/fdlibm/s_cbrt.c new file mode 100644 index 000000000..4aed19b1b --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_ceil.c b/src/dom/js/fdlibm/s_ceil.c new file mode 100644 index 000000000..826bcac6c --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_erf.c b/src/dom/js/fdlibm/s_erf.c new file mode 100644 index 000000000..6eae8de3b --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_expm1.c b/src/dom/js/fdlibm/s_expm1.c new file mode 100644 index 000000000..578d2e144 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_fabs.c b/src/dom/js/fdlibm/s_fabs.c new file mode 100644 index 000000000..6b029da10 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_finite.c b/src/dom/js/fdlibm/s_finite.c new file mode 100644 index 000000000..4a0a4d3c6 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_floor.c b/src/dom/js/fdlibm/s_floor.c new file mode 100644 index 000000000..6c23495f0 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_ilogb.c b/src/dom/js/fdlibm/s_ilogb.c new file mode 100644 index 000000000..f769781a6 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_isnan.c b/src/dom/js/fdlibm/s_isnan.c new file mode 100644 index 000000000..52f8759c7 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_ldexp.c b/src/dom/js/fdlibm/s_ldexp.c new file mode 100644 index 000000000..9475520d4 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_lib_version.c b/src/dom/js/fdlibm/s_lib_version.c new file mode 100644 index 000000000..2ccf67d51 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_log1p.c b/src/dom/js/fdlibm/s_log1p.c new file mode 100644 index 000000000..1840156b1 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_logb.c b/src/dom/js/fdlibm/s_logb.c new file mode 100644 index 000000000..f885c4dc3 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_matherr.c b/src/dom/js/fdlibm/s_matherr.c new file mode 100644 index 000000000..cd99ca88f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_modf.c b/src/dom/js/fdlibm/s_modf.c new file mode 100644 index 000000000..3b182bd3b --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_nextafter.c b/src/dom/js/fdlibm/s_nextafter.c new file mode 100644 index 000000000..f71c5c835 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_rint.c b/src/dom/js/fdlibm/s_rint.c new file mode 100644 index 000000000..3c4fab6d9 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_scalbn.c b/src/dom/js/fdlibm/s_scalbn.c new file mode 100644 index 000000000..3deeaa305 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_signgam.c b/src/dom/js/fdlibm/s_signgam.c new file mode 100644 index 000000000..4eb8ce72f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_significand.c b/src/dom/js/fdlibm/s_significand.c new file mode 100644 index 000000000..2e1c0b28f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_sin.c b/src/dom/js/fdlibm/s_sin.c new file mode 100644 index 000000000..8bbc5c62d --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_tan.c b/src/dom/js/fdlibm/s_tan.c new file mode 100644 index 000000000..ded36c1d7 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/s_tanh.c b/src/dom/js/fdlibm/s_tanh.c new file mode 100644 index 000000000..aa6809f85 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_acos.c b/src/dom/js/fdlibm/w_acos.c new file mode 100644 index 000000000..872c81d20 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_acosh.c b/src/dom/js/fdlibm/w_acosh.c new file mode 100644 index 000000000..745d402ea --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_asin.c b/src/dom/js/fdlibm/w_asin.c new file mode 100644 index 000000000..18aaefde9 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_atan2.c b/src/dom/js/fdlibm/w_atan2.c new file mode 100644 index 000000000..8cfa4bbbd --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_atanh.c b/src/dom/js/fdlibm/w_atanh.c new file mode 100644 index 000000000..6ba52d1e2 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_cosh.c b/src/dom/js/fdlibm/w_cosh.c new file mode 100644 index 000000000..146449e02 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_exp.c b/src/dom/js/fdlibm/w_exp.c new file mode 100644 index 000000000..f5dea0b01 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_j1.c b/src/dom/js/fdlibm/w_j1.c new file mode 100644 index 000000000..86a506bc2 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_jn.c b/src/dom/js/fdlibm/w_jn.c new file mode 100644 index 000000000..6926b0da8 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_lgamma.c b/src/dom/js/fdlibm/w_lgamma.c new file mode 100644 index 000000000..f7576e892 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_lgamma_r.c b/src/dom/js/fdlibm/w_lgamma_r.c new file mode 100644 index 000000000..ba2ad5933 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_log.c b/src/dom/js/fdlibm/w_log.c new file mode 100644 index 000000000..7e358fcf1 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_log10.c b/src/dom/js/fdlibm/w_log10.c new file mode 100644 index 000000000..6b298b236 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_pow.c b/src/dom/js/fdlibm/w_pow.c new file mode 100644 index 000000000..3d2c15ad3 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_remainder.c b/src/dom/js/fdlibm/w_remainder.c new file mode 100644 index 000000000..25d1ba171 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_scalb.c b/src/dom/js/fdlibm/w_scalb.c new file mode 100644 index 000000000..35c16a500 --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_sinh.c b/src/dom/js/fdlibm/w_sinh.c new file mode 100644 index 000000000..8b04ecb7f --- /dev/null +++ b/src/dom/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/dom/js/fdlibm/w_sqrt.c b/src/dom/js/fdlibm/w_sqrt.c new file mode 100644 index 000000000..462d776f8 --- /dev/null +++ b/src/dom/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/dom/js/js.c b/src/dom/js/js.c new file mode 100644 index 000000000..77c778b92 --- /dev/null +++ b/src/dom/js/js.c @@ -0,0 +1,2447 @@ +/* -*- 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 +#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; + } + if ((gBranchCount & 0x3fff) == 1) + JS_MaybeGC(cx); + 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}, + {"atline", JSOPTION_ATLINE}, + {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; + uint32 oldopts; + + 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); + oldopts = JS_GetOptions(cx); + JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); + script = JS_CompileFile(cx, obj, filename); + if (!script) { + ok = JS_FALSE; + } else { + ok = JS_ExecuteScript(cx, obj, script, &result); + JS_DestroyScript(cx, script); + } + JS_SetOptions(cx, oldopts); + 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(fun); + } + 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, uintN flags, + JSObject **objp) +{ + if (its_noisy) { + fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n", + JS_GetStringBytes(JS_ValueToString(cx, id)), + (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "", + (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "", + (flags & JSRESOLVE_DETECTING) ? "detecting" : ""); + } + 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", JSCLASS_NEW_RESOLVE, + its_addProperty, its_delProperty, its_getProperty, its_setProperty, + its_enumerate, (JSResolveOp)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 +}; + +#ifdef NARCISSUS + +static JSBool +defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsval value; + JSBool dontDelete, readOnly, dontEnum; + const jschar *chars; + size_t length; + uintN attrs; + + dontDelete = readOnly = dontEnum = JS_FALSE; + if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb", + &str, &value, &dontDelete, &readOnly, &dontEnum)) { + return JS_FALSE; + } + chars = JS_GetStringChars(str); + length = JS_GetStringLength(str); + attrs = dontEnum ? 0 : JSPROP_ENUMERATE; + if (dontDelete) + attrs |= JSPROP_PERMANENT; + if (readOnly) + attrs |= JSPROP_READONLY; + return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL, + attrs); +} + +#include +#include + +static JSBool +snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + const char *filename; + int fd, cc; + JSBool ok; + size_t len; + char *buf; + struct stat sb; + + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + filename = JS_GetStringBytes(str); + fd = open(filename, O_RDONLY); + ok = JS_TRUE; + len = 0; + buf = NULL; + if (fd < 0) { + JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno)); + ok = JS_FALSE; + } else if (fstat(fd, &sb) < 0) { + JS_ReportError(cx, "can't stat %s", filename); + ok = JS_FALSE; + } else { + len = sb.st_size; + buf = JS_malloc(cx, len + 1); + if (!buf) { + ok = JS_FALSE; + } else if ((cc = read(fd, buf, len)) != len) { + JS_free(cx, buf); + JS_ReportError(cx, "can't read %s: %s", filename, + (cc < 0) ? strerror(errno) : "short read"); + ok = JS_FALSE; + } + } + close(fd); + if (!ok) + return ok; + buf[len] = '\0'; + str = JS_NewString(cx, buf, len); + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* NARCISSUS */ + +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(64L * 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; + +#ifdef NARCISSUS + { + jsval v; + static const char Object_prototype[] = "Object.prototype"; + + if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0)) + return 1; + if (!JS_EvaluateScript(cx, glob, + Object_prototype, sizeof Object_prototype - 1, + NULL, 0, &v)) { + return 1; + } + if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__", + defineProperty, 5, 0)) { + return 1; + } + } +#endif + + 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/dom/js/js.mak b/src/dom/js/js.mak new file mode 100644 index 000000000..a155abfa2 --- /dev/null +++ b/src/dom/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/dom/js/js.msg b/src/dom/js/js.msg new file mode 100644 index 000000000..dc6c534f7 --- /dev/null +++ b/src/dom/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_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") +MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") +MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") +MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") +MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_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_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") +MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") +MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") +MSG_DEF(JSMSG_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/dom/js/jsapi.c b/src/dom/js/jsapi.c new file mode 100644 index 000000000..5b7200a49 --- /dev/null +++ b/src/dom/js/jsapi.c @@ -0,0 +1,4330 @@ +/* -*- 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': + /* + * Don't convert a cloned function object to its shared private + * data, then follow fun->object back to the clone-parent. + */ + if (JSVAL_IS_FUNCTION(cx, *sp)) { + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*sp)); + } else { + 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: + /* + * Don't convert a cloned function object to its shared private data, + * then follow fun->object back to the clone-parent. + */ + if (JSVAL_IS_FUNCTION(cx, v)) { + ok = JS_TRUE; + *vp = v; + } else { + 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 != NULL)) { + type = JSTYPE_FUNCTION; + } else { +#ifdef NARCISSUS + if (obj) { + /* XXX suppress errors/exceptions */ + OBJ_GET_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.callAtom, + &v); + if (JSVAL_IS_FUNCTION(cx, v)) + return JSTYPE_FUNCTION; + } +#endif + 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_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_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 2004-09-24"; +} + + +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; +} + +JS_PUBLIC_API(JSBool) +JS_EnterLocalRootScope(JSContext *cx) +{ + CHECK_REQUEST(cx); + return js_EnterLocalRootScope(cx); +} + +JS_PUBLIC_API(void) +JS_LeaveLocalRootScope(JSContext *cx) +{ + CHECK_REQUEST(cx); + js_LeaveLocalRootScope(cx); +} + +JS_PUBLIC_API(void) +JS_ForgetLocalRoot(JSContext *cx, void *thing) +{ + CHECK_REQUEST(cx); + js_ForgetLocalRoot(cx, (jsval) thing); +} + +#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 +/* FIXME: 242518 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) +{ + JS_ASSERT(((jsid)obj & JSVAL_TAGMASK) == 0); + *idp = (jsid) obj | JSVAL_INT; + 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_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} + +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_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp) +{ + JSAtom *atom; + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + ok = OBJ_IS_NATIVE(obj) + ? js_LookupPropertyWithFlags(cx, obj, (jsid)atom, flags, &obj2, &prop +#if defined JS_THREADSAFE && defined DEBUG + , __FILE__, __LINE__ +#endif + ) + : OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &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_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); + if (ok) { + *vp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} + +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_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, 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; +} + +static JSBool +ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, + uint32 index, uint32 limit) +{ + /* Check the computed, possibly per-instance, upper bound. */ + if (clasp->reserveSlots) + JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj)); + if (index >= limit) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_RESERVED_SLOT_RANGE); + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +{ + JSClass *clasp; + uint32 limit, slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + limit = JSCLASS_RESERVED_SLOTS(clasp); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + 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 limit, slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + limit = JSCLASS_RESERVED_SLOTS(clasp); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + return JS_FALSE; + slot = JSSLOT_START(clasp) + index; + return OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(jsrefcount) +JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) +{ + return JS_ATOMIC_INCREMENT(&principals->refcount); +} + +JS_PUBLIC_API(jsrefcount) +JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) +{ + jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); + if (rc == 0) + principals->destroy(cx, principals); + return rc; +} +#endif + +JS_PUBLIC_API(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); +} + +JS_PUBLIC_API(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); +} + +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; + + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + 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) +{ + uint32 options; + JSScript *script; + JSBool ok; + + CHECK_REQUEST(cx); + options = cx->options; + cx->options = options | JSOPTION_COMPILE_N_GO; + script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, + filename, lineno); + cx->options = options; + 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 +} + +JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + return js_ReportUncaughtException(cx); +#else + return JS_TRUE; +#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) +{ + jsword old = cx->thread; + cx->thread = js_CurrentThreadId(); + return old; +} + +JS_PUBLIC_API(jsword) +JS_ClearContextThread(JSContext *cx) +{ + jsword 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/dom/js/jsapi.h b/src/dom/js/jsapi.h new file mode 100644 index 000000000..8f491ffec --- /dev/null +++ b/src/dom/js/jsapi.h @@ -0,0 +1,1842 @@ +/* -*- 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 */ +#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script + promises to execute compiled + script once only; enables + compile-time scope chain + resolution of consts. */ +#define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] + option supported for the + XUL preprocessor and kindred + beasts. */ + +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. + * + * See the scoped local root API immediately below for a better way to manage + * newborns in cases where native hooks (functions, getters, setters, etc.) + * create many GC-things, potentially without connecting them to predefined + * local roots such as *rval or argv[i] in an active native function. Using + * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type + * newborn roots, until control flow unwinds and leaves the outermost nesting + * local root scope. + */ +extern JS_PUBLIC_API(void) +JS_ClearNewbornRoots(JSContext *cx); + +/* + * Scoped local root management allows native functions, getter/setters, etc. + * to avoid worrying about the newborn root pigeon-holes, overloading local + * roots allocated in argv and *rval, or ending up having to call JS_Add*Root + * and JS_RemoveRoot to manage global roots temporarily. + * + * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around + * the body of the native hook causes the engine to allocate a local root for + * each newborn created in between the two API calls, using a local root stack + * associated with cx. For example: + * + * JSBool + * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) + * { + * JSBool ok; + * + * if (!JS_EnterLocalRootScope(cx)) + * return JS_FALSE; + * ok = my_GetPropertyBody(cx, obj, id, vp); + * JS_LeaveLocalRootScope(cx); + * return ok; + * } + * + * NB: JS_LeaveLocalRootScope must be called once for every prior successful + * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must + * not make the matching JS_LeaveLocalRootScope call. + * + * In case a native hook allocates many objects or other GC-things, but the + * native protects some of those GC-things by storing them as property values + * in an object that is itself protected, the hook can call JS_ForgetLocalRoot + * to free the local root automatically pushed for the now-protected GC-thing. + * + * JS_ForgetLocalRoot works on any GC-thing allocated in the current local + * root scope, but it's more time-efficient when called on references to more + * recently created GC-things. Calling it successively on other than the most + * recently allocated GC-thing will tend to average the time inefficiency, and + * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too + * many local roots if you can root as you go (build a tree of objects from + * the top down, forgetting each latest-allocated GC-thing immediately upon + * linking it to its parent). + */ +extern JS_PUBLIC_API(JSBool) +JS_EnterLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_LeaveLocalRootScope(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_ForgetLocalRoot(JSContext *cx, void *thing); + +#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; + JSReserveSlotsOp reserveSlots; +}; + +#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 */ +#define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ +#define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ +#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ + +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_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_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_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval); + +extern JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); + +extern JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); + +extern JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); + +extern JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs); + +extern JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); + +extern JS_PUBLIC_API(JSBool) +JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); + +extern JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); + +extern JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); + +extern JS_PUBLIC_API(void) +JS_ClearScope(JSContext *cx, JSObject *obj); + +extern JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj); + +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(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs); + +extern JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); + +/* + * Given a buffer, return JS_FALSE if the buffer might become a valid + * javascript statement with the addition of more lines. Otherwise return + * JS_TRUE. The intent is to support interactive compilation - accumulate + * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to + * the compiler. + */ +extern JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length); + +/* + * The JSScript objects returned by the following functions refer to string and + * other kinds of literals, including doubles and RegExp objects. These + * literals are vulnerable to garbage collection; to root script objects and + * prevent literals from being collected, create a rootable object using + * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. + */ +extern JS_PUBLIC_API(JSScript *) +JS_CompileScript(JSContext *cx, JSObject *obj, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *fh); + +extern JS_PUBLIC_API(JSScript *) +JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *fh, + JSPrincipals *principals); + +/* + * NB: you must use JS_NewScriptObject and root a pointer to its return value + * in order to keep a JSScript and its atoms safe from garbage collection after + * creating the script via JS_Compile* and before a JS_ExecuteScript* call. + * E.g., and without error checks: + * + * JSScript *script = JS_CompileFile(cx, global, filename); + * JSObject *scrobj = JS_NewScriptObject(cx, script); + * JS_AddNamedRoot(cx, &scrobj, "scrobj"); + * do { + * jsval result; + * JS_ExecuteScript(cx, global, script, &result); + * JS_GC(); + * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); + * JS_RemoveRoot(cx, &scrobj); + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script); + +/* + * Infallible getter for a script's object. If JS_NewScriptObject has not been + * called on script yet, the return value will be null. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script); + +extern JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, + uintN indent); + +/* + * API extension: OR this into indent to avoid pretty-printing the decompiled + * source resulting from JS_DecompileFunction{,Body}. + */ +#define JS_DONT_PRETTY_PRINT ((uintN)0x8000) + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); + +extern JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); + +/* + * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* + * quadruplets all use the obj parameter as the initial scope chain header, + * the 'this' keyword value, and the variables object (ECMA parlance for where + * 'var' and 'function' bind names) of the execution context for script. + * + * Using obj as the variables object is problematic if obj's parent (which is + * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in + * this case, variables created by 'var x = 0', e.g., go in obj, but variables + * created by assignment to an unbound id, 'x = 0', go in the last object on + * the scope chain linked by parent. + * + * ECMA calls that last scoping object the "global object", but note that many + * embeddings have several such objects. ECMA requires that "global code" be + * executed with the variables object equal to this global object. But these + * JS API entry points provide freedom to execute code against a "sub-global", + * i.e., a parented or scoped object, in which case the variables object will + * differ from the last object on the scope chain, resulting in confusing and + * non-ECMA explicit vs. implicit variable creation. + * + * Caveat embedders: unless you already depend on this buggy variables object + * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or + * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if + * someone may have set other options on cx already -- for each context in the + * application, if you pass parented objects as the obj parameter, or may ever + * pass such objects in the future. + * + * Why a runtime option? The alternative is to add six or so new API entry + * points with signatures matching the following six, and that doesn't seem + * worth the code bloat cost. Such new entry points would probably have less + * obvious names, too, so would not tend to be used. The JS_SetOption call, + * OTOH, can be more easily hacked into existing code that does not depend on + * the bug; such code can continue to use the familiar JS_EvaluateScript, + * etc., entry points. + */ +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); + +/* + * Execute either the function-defining prolog of a script, or the script's + * main body, but not both. + */ +typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; + +extern JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval); + +extern JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval); + +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; + JSLocaleToUnicode localeToUnicode; +}; + +/* + * 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); + +extern JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx); + +/* + * Save the current exception state. This takes a snapshot of cx's current + * exception state without making any change to that state. + * + * The returned state pointer MUST be passed later to JS_RestoreExceptionState + * (to restore that saved state, overriding any more recent state) or else to + * JS_DropExceptionState (to free the state struct in case it is not correct + * or desirable to restore it). Both Restore and Drop free the state struct, + * so callers must stop using the pointer returned from Save after calling the + * Release or Drop API. + */ +extern JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx); + +extern JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); + +extern JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state); + +/* + * If the given value is an exception object that originated from an error, + * the exception will contain an error report struct, and this API will return + * the address of that struct. Otherwise, it returns NULL. The lifetime of + * the error report struct that might be returned is the same as the lifetime + * of the exception object. + */ +extern JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v); + +#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(jsword) +JS_ClearContextThread(JSContext *cx); + +#endif /* JS_THREADSAFE */ + +/************************************************************************/ + +JS_END_EXTERN_C + +#endif /* jsapi_h___ */ diff --git a/src/dom/js/jsarena.c b/src/dom/js/jsarena.c new file mode 100644 index 000000000..2abcacd2b --- /dev/null +++ b/src/dom/js/jsarena.c @@ -0,0 +1,567 @@ +/* -*- 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. We must preserve LIFO order of mark/release cursors, so we use + * the oversized-allocation arena's back pointer (or if not oversized, we + * use the result of searching the entire pool) to compute the address of + * the arena that precedes a. + */ + if (pool->current == a) + pool->current = (JSArena *) ((char *)ap - offsetof(JSArena, next)); + + /* + * 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/dom/js/jsarena.h b/src/dom/js/jsarena.h new file mode 100644 index 000000000..e52398a1a --- /dev/null +++ b/src/dom/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/dom/js/jsarray.c b/src/dom/js/jsarray.c new file mode 100644 index 000000000..a05a6ee3c --- /dev/null +++ b/src/dom/js/jsarray.c @@ -0,0 +1,1428 @@ +/* -*- 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; + + 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 (!literalize && (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/dom/js/jsarray.h b/src/dom/js/jsarray.h new file mode 100644 index 000000000..cbb2aedf1 --- /dev/null +++ b/src/dom/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/dom/js/jsatom.c b/src/dom/js/jsatom.c new file mode 100644 index 000000000..ede2350b6 --- /dev/null +++ b/src/dom/js/jsatom.c @@ -0,0 +1,927 @@ +/* -*- 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"; + +#ifdef NARCISSUS +const char js_call_str[] = "__call__"; +const char js_construct_str[] = "__construct__"; +const char js_hasInstance_str[] = "__hasInstance__"; +const char js_ExecutionContext_str[] = "ExecutionContext"; +const char js_current_str[] = "current"; +#endif + +#define HASH_OBJECT(o) ((JSHashNumber)(o) >> JSVAL_TAGBITS) +#define HASH_INT(i) ((JSHashNumber)(i)) +#define HASH_DOUBLE(dp) ((JSHashNumber)(JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) +#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); + +#ifdef NARCISSUS + FROB(callAtom, js_call_str); + FROB(constructAtom, js_construct_str); + FROB(hasInstanceAtom, js_hasInstance_str); + FROB(ExecutionContextAtom, js_ExecutionContext_str); + FROB(currentAtom, js_current_str); +#endif + +#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 < 10) { + /* 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/dom/js/jsatom.h b/src/dom/js/jsatom.h new file mode 100644 index 000000000..6f486c337 --- /dev/null +++ b/src/dom/js/jsatom.h @@ -0,0 +1,426 @@ +/* -*- 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_VALUE(ale) ((jsval) (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_VALUE(ale,val) ((ale)->entry.value = (JSHashEntry *)(val)) +#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 +#ifdef NARCISSUS + JSAtom *callAtom; + JSAtom *constructAtom; + JSAtom *hasInstanceAtom; + JSAtom *ExecutionContextAtom; + JSAtom *currentAtom; +#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[]; + +#ifdef NARCISSUS +extern const char js_call_str[]; +extern const char js_construct_str[]; +extern const char js_hasInstance_str[]; +extern const char js_ExecutionContext_str[]; +extern const char js_current_str[]; +#endif + +/* + * Initialize atom state. Return true on success, false 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/dom/js/jsbit.h b/src/dom/js/jsbit.h new file mode 100644 index 000000000..db41acf9f --- /dev/null +++ b/src/dom/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/dom/js/jsbool.c b/src/dom/js/jsbool.c new file mode 100644 index 000000000..75d4ee05a --- /dev/null +++ b/src/dom/js/jsbool.c @@ -0,0 +1,220 @@ +/* -*- 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 (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + b = JS_FALSE; + } else 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 { + JS_ASSERT(JSVAL_IS_BOOLEAN(v)); + b = JSVAL_TO_BOOLEAN(v); + } + + *bp = b; + return JS_TRUE; +} diff --git a/src/dom/js/jsbool.h b/src/dom/js/jsbool.h new file mode 100644 index 000000000..c07910f59 --- /dev/null +++ b/src/dom/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/dom/js/jsclist.h b/src/dom/js/jsclist.h new file mode 100644 index 000000000..2eafe8e40 --- /dev/null +++ b/src/dom/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/dom/js/jscntxt.c b/src/dom/js/jscntxt.c new file mode 100644 index 000000000..de254ca9c --- /dev/null +++ b/src/dom/js/jscntxt.c @@ -0,0 +1,1031 @@ +/* -*- 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_InitRuntimeScriptState(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; + JSLocalRootStack *lrs; + JSLocalRootChunk *lrc; + + 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); + + /* Now after the last GC can we free the script filename table. */ + js_FinishRuntimeScriptState(cx); + + /* Take the runtime down, now that it has no contexts or atoms. */ + JS_LOCK_GC(rt); + rt->state = JSRTS_DOWN; + JS_NOTIFY_ALL_CONDVAR(rt->stateChange); + 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; + } + + lrs = cx->localRootStack; + if (lrs) { + while ((lrc = lrs->topChunk) != &lrs->firstChunk) { + lrs->topChunk = lrc->down; + JS_free(cx, lrc); + } + JS_free(cx, lrs); + } + + /* 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; +} + +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 +}; + +JSBool +js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry **entryp) +{ + JSDHashTable *table; + JSResolvingEntry *entry; + + table = cx->resolvingTable; + if (!table) { + table = JS_NewDHashTable(&resolving_dhash_ops, NULL, + sizeof(JSResolvingEntry), + JS_DHASH_MIN_SIZE); + if (!table) + goto outofmem; + cx->resolvingTable = table; + } + + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, key, JS_DHASH_ADD); + if (!entry) + goto outofmem; + + if (entry->flags & flag) { + /* An entry for (key, flag) exists already -- dampen recursion. */ + entry = NULL; + } else { + /* Fill in key if we were the first to add entry, then set flag. */ + if (!entry->key.obj) + entry->key = *key; + entry->flags |= flag; + } + *entryp = entry; + return JS_TRUE; + +outofmem: + JS_ReportOutOfMemory(cx); + return JS_FALSE; +} + +void +js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry *entry, uint32 generation) +{ + JSDHashTable *table; + + /* + * Clear flag from entry->flags and return early if other flags remain. + * We must take care to re-lookup entry if the table has changed since + * it was found by js_StartResolving. + */ + table = cx->resolvingTable; + if (!entry || table->generation != generation) { + entry = (JSResolvingEntry *) + JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); + } + JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); + entry->flags &= ~flag; + if (entry->flags) + return; + + /* + * Do a raw remove only if fewer entries were removed than would cause + * alpha to be less than .5 (alpha is at most .75). Otherwise, we just + * call JS_DHashTableOperate to re-lookup the key and remove its entry, + * compressing or shrinking the table as needed. + */ + if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) + JS_DHashTableRawRemove(table, &entry->hdr); + else + JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); +} + +JSBool +js_EnterLocalRootScope(JSContext *cx) +{ + JSLocalRootStack *lrs; + int mark; + + lrs = cx->localRootStack; + if (!lrs) { + lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs); + if (!lrs) + return JS_FALSE; + lrs->scopeMark = JSLRS_NULL_MARK; + lrs->rootCount = 0; + lrs->topChunk = &lrs->firstChunk; + lrs->firstChunk.down = NULL; + cx->localRootStack = lrs; + } + + /* Push lrs->scopeMark to save it for restore when leaving. */ + mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); + if (mark < 0) + return JS_FALSE; + lrs->scopeMark = (uint16) mark; + return JS_TRUE; +} + +void +js_LeaveLocalRootScope(JSContext *cx) +{ + JSLocalRootStack *lrs; + unsigned mark, m, n; + JSLocalRootChunk *lrc; + + /* Defend against buggy native callers. */ + lrs = cx->localRootStack; + JS_ASSERT(lrs && lrs->rootCount != 0); + if (!lrs || lrs->rootCount == 0) + return; + + mark = lrs->scopeMark; + JS_ASSERT(mark != JSLRS_NULL_MARK); + if (mark == JSLRS_NULL_MARK) + return; + + /* Free any chunks being popped by this leave operation. */ + m = mark >> JSLRS_CHUNK_SHIFT; + n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; + while (n > m) { + lrc = lrs->topChunk; + JS_ASSERT(lrc != &lrs->firstChunk); + lrs->topChunk = lrc->down; + JS_free(cx, lrc); + --n; + } + + /* Pop the scope, restoring lrs->scopeMark. */ + lrc = lrs->topChunk; + m = mark & JSLRS_CHUNK_MASK; + lrs->scopeMark = JSVAL_TO_INT(lrc->roots[m]); + lrc->roots[m] = JSVAL_NULL; + lrs->rootCount = (uint16) mark; + + /* + * Free the stack eagerly, risking malloc churn. The alternative would + * require an lrs->entryCount member, maintained by Enter and Leave, and + * tested by the GC in addition to the cx->localRootStack non-null test. + * + * That approach would risk hoarding 264 bytes (net) per context. Right + * now it seems better to give fresh (dirty in CPU write-back cache, and + * the data is no longer needed) memory back to the malloc heap. + */ + if (mark == 0) { + cx->localRootStack = NULL; + JS_free(cx, lrs); + } else if (m == 0) { + lrs->topChunk = lrc->down; + JS_free(cx, lrc); + } +} + +void +js_ForgetLocalRoot(JSContext *cx, jsval v) +{ + JSLocalRootStack *lrs; + unsigned i, j, m, n, mark; + JSLocalRootChunk *lrc, *lrc2; + jsval top; + + lrs = cx->localRootStack; + JS_ASSERT(lrs && lrs->rootCount); + if (!lrs || lrs->rootCount == 0) + return; + + /* Prepare to pop the top-most value from the stack. */ + n = lrs->rootCount - 1; + m = n & JSLRS_CHUNK_MASK; + lrc = lrs->topChunk; + top = lrc->roots[m]; + + /* Be paranoid about calls on an empty scope. */ + mark = lrs->scopeMark; + JS_ASSERT(mark < n); + if (mark >= n) + return; + + /* If v was not the last root pushed in the top scope, find it. */ + if (top != v) { + /* Search downward in case v was recently pushed. */ + i = n; + j = m; + lrc2 = lrc; + while (--i > mark) { + if (j == 0) + lrc2 = lrc2->down; + j = i & JSLRS_CHUNK_MASK; + if (lrc2->roots[j] == v) + break; + } + + /* If we didn't find v in this scope, assert and bail out. */ + JS_ASSERT(i != mark); + if (i == mark) + return; + + /* Swap top and v so common tail code can pop v. */ + lrc2->roots[j] = top; + } + + /* Pop the last value from the stack. */ + lrc->roots[m] = JSVAL_NULL; + lrs->rootCount = n; + if (m == 0) { + JS_ASSERT(n != 0); + JS_ASSERT(lrc != &lrs->firstChunk); + lrs->topChunk = lrc->down; + JS_free(cx, lrc); + } +} + +int +js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) +{ + unsigned n, m; + JSLocalRootChunk *lrc; + + n = lrs->rootCount; + m = n & JSLRS_CHUNK_MASK; + if (n == 0 || m != 0) { + /* + * At start of first chunk, or not at start of a non-first top chunk. + * Check for lrs->rootCount overflow. + */ + if ((uint16)(n + 1) == 0) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_LOCAL_ROOTS); + return -1; + } + lrc = lrs->topChunk; + JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); + } else { + /* + * After lrs->firstChunk, trying to index at a power-of-two chunk + * boundary: need a new chunk. + */ + lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc); + if (!lrc) + return -1; + lrc->down = lrs->topChunk; + lrs->topChunk = lrc; + } + lrs->rootCount = n + 1; + lrc->roots[m] = v; + return (int) m; +} + +void +js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) +{ + unsigned n, m, mark; + JSLocalRootChunk *lrc; + + n = lrs->rootCount; + if (n == 0) + return; + + mark = lrs->scopeMark; + lrc = lrs->topChunk; + while (--n > mark) { +#ifdef GC_MARK_DEBUG + char name[22]; + JS_snprintf(name, sizeof name, "", n); +#else + const char *name = NULL; +#endif + m = n & JSLRS_CHUNK_MASK; + JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); + JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL); + if (m == 0) + lrc = lrc->down; + } +} + +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/dom/js/jscntxt.h b/src/dom/js/jscntxt.h new file mode 100644 index 000000000..630d6a63f --- /dev/null +++ b/src/dom/js/jscntxt.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 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; + + /* Script filename table. */ + struct JSHashTable *scriptFilenameTable; +#ifdef JS_THREADSAFE + PRLock *scriptFilenameTableLock; +#endif + + /* Number localization, used by jsnum.c */ + const char *thousandsSeparator; + const char *decimalSeparator; + const char *numGrouping; + +#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 */ + +typedef struct JSLocalRootChunk JSLocalRootChunk; + +#define JSLRS_CHUNK_SHIFT 6 +#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) +#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) + +struct JSLocalRootChunk { + jsval roots[JSLRS_CHUNK_SIZE]; + JSLocalRootChunk *down; +}; + +typedef struct JSLocalRootStack { + uint16 scopeMark; + uint16 rootCount; + JSLocalRootChunk *topChunk; + JSLocalRootChunk firstChunk; +} JSLocalRootStack; + +#define JSLRS_NULL_MARK ((uint16) -1) + +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; + + /* Optional stack of scoped local GC roots. */ + JSLocalRootStack *localRootStack; +}; + +/* + * Slightly more readable macros, also to hide bitset implementation detail. + * XXX beware non-boolean truth values, which belie the bitset hiding claim! + */ +#define JS_HAS_STRICT_OPTION(cx) ((cx)->options & JSOPTION_STRICT) +#define JS_HAS_WERROR_OPTION(cx) ((cx)->options & JSOPTION_WERROR) +#define JS_HAS_COMPILE_N_GO_OPTION(cx) ((cx)->options & JSOPTION_COMPILE_N_GO) +#define JS_HAS_ATLINE_OPTION(cx) ((cx)->options & JSOPTION_ATLINE) + +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); + +/* + * JSClass.resolve and watchpoint recursion damping machinery. + */ +extern JSBool +js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry **entryp); + +extern void +js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, + JSResolvingEntry *entry, uint32 generation); + +/* + * Local root set management. + */ +extern JSBool +js_EnterLocalRootScope(JSContext *cx); + +extern void +js_LeaveLocalRootScope(JSContext *cx); + +extern void +js_ForgetLocalRoot(JSContext *cx, jsval v); + +extern int +js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); + +extern void +js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs); + +/* + * 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/dom/js/jscompat.h b/src/dom/js/jscompat.h new file mode 100644 index 000000000..6ba036a5e --- /dev/null +++ b/src/dom/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/dom/js/jsconfig.h b/src/dom/js/jsconfig.h new file mode 100644 index 000000000..44a64d870 --- /dev/null +++ b/src/dom/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 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 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/dom/js/jsconfig.mk b/src/dom/js/jsconfig.mk new file mode 100644 index 000000000..a3b886732 --- /dev/null +++ b/src/dom/js/jsconfig.mk @@ -0,0 +1,181 @@ +# -*- Mode: makefile -*- +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Communicator client code, released +# March 31, 1998. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998-1999 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifndef OBJDIR + ifdef OBJDIR_NAME + OBJDIR = $(OBJDIR_NAME) + endif +endif + +NSPR_VERSION = v4.0 +NSPR_LIBSUFFIX = 4 + +NSPR_LOCAL = $(MOZ_DEPTH)/dist/$(OBJDIR)/nspr +NSPR_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) +NSPR_OBJDIR = $(OBJDIR) +ifeq ($(OS_ARCH), SunOS) + NSPR_OBJDIR := $(subst _sparc,,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_ARCH), Linux) + LINUX_REL := $(shell uname -r) + ifneq (,$(findstring 2.0,$(LINUX_REL))) + NSPR_OBJDIR := $(subst _All,2.0_x86_glibc_PTH,$(NSPR_OBJDIR)) + else + NSPR_OBJDIR := $(subst _All,2.2_x86_glibc_PTH,$(NSPR_OBJDIR)) + endif +endif +ifeq ($(OS_ARCH), AIX) + NSPR_OBJDIR := $(subst 4.1,4.2,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_CONFIG), IRIX6.2) + NSPR_OBJDIR := $(subst 6.2,6.2_n32_PTH,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_CONFIG), IRIX6.5) + NSPR_OBJDIR := $(subst 6.5,6.5_n32_PTH,$(NSPR_OBJDIR)) +endif +ifeq ($(OS_ARCH), WINNT) + ifeq ($(OBJDIR), WIN32_D.OBJ) + NSPR_OBJDIR = WINNT4.0_DBG.OBJ + endif + ifeq ($(OBJDIR), WIN32_O.OBJ) + NSPR_OBJDIR = WINNT4.0_OPT.OBJ + endif +endif +NSPR_SHARED = /share/builds/components/nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) +ifeq ($(OS_ARCH), WINNT) + NSPR_SHARED = nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) +endif +NSPR_VERSIONFILE = $(NSPR_LOCAL)/Version +NSPR_CURVERSION := $(shell cat $(NSPR_VERSIONFILE)) + +get_nspr: + @echo "Grabbing NSPR component..." +ifeq ($(NSPR_VERSION), $(NSPR_CURVERSION)) + @echo "No need, NSPR is up to date in this tree (ver=$(NSPR_VERSION))." +else + mkdir -p $(NSPR_LOCAL) + mkdir -p $(NSPR_DIST) + ifneq ($(OS_ARCH), WINNT) + cp $(NSPR_SHARED)/*.jar $(NSPR_LOCAL) + else + sh $(MOZ_DEPTH)/../reltools/compftp.sh $(NSPR_SHARED) $(NSPR_LOCAL) *.jar + endif + unzip -o $(NSPR_LOCAL)/mdbinary.jar -d $(NSPR_DIST) + mkdir -p $(NSPR_DIST)/include + unzip -o $(NSPR_LOCAL)/mdheader.jar -d $(NSPR_DIST)/include + rm -rf $(NSPR_DIST)/META-INF + rm -rf $(NSPR_DIST)/include/META-INF + echo $(NSPR_VERSION) > $(NSPR_VERSIONFILE) +endif + +SHIP_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) +SHIP_DIR = $(SHIP_DIST)/SHIP + +SHIP_LIBS = libjs.$(SO_SUFFIX) libjs.a +ifdef JS_LIVECONNECT + SHIP_LIBS += libjsj.$(SO_SUFFIX) libjsj.a +endif +ifeq ($(OS_ARCH), WINNT) + SHIP_LIBS = js32.dll js32.lib + ifdef JS_LIVECONNECT + SHIP_LIBS += jsj.dll jsj.lib + endif +endif +SHIP_LIBS += $(LCJAR) +SHIP_LIBS := $(addprefix $(SHIP_DIST)/lib/, $(SHIP_LIBS)) + +SHIP_INCS = js*.h prmjtime.h resource.h *.msg *.tbl +ifdef JS_LIVECONNECT + SHIP_INCS += netscape*.h nsC*.h nsI*.h +endif +SHIP_INCS := $(addprefix $(SHIP_DIST)/include/, $(SHIP_INCS)) + +SHIP_BINS = js +ifdef JS_LIVECONNECT + SHIP_BINS += lcshell +endif +ifeq ($(OS_ARCH), WINNT) + SHIP_BINS := $(addsuffix .exe, $(SHIP_BINS)) +endif +SHIP_BINS := $(addprefix $(SHIP_DIST)/bin/, $(SHIP_BINS)) + +ifdef BUILD_OPT + JSREFJAR = jsref_opt.jar +else +ifdef BUILD_IDG + JSREFJAR = jsref_idg.jar +else + JSREFJAR = jsref_dbg.jar +endif +endif + +ship: + mkdir -p $(SHIP_DIR)/$(LIBDIR) + mkdir -p $(SHIP_DIR)/include + mkdir -p $(SHIP_DIR)/bin + cp $(SHIP_LIBS) $(SHIP_DIR)/$(LIBDIR) + cp $(SHIP_INCS) $(SHIP_DIR)/include + cp $(SHIP_BINS) $(SHIP_DIR)/bin + cd $(SHIP_DIR); \ + zip -r $(JSREFJAR) bin lib include +ifdef BUILD_SHIP + cp $(SHIP_DIR)/$(JSREFJAR) $(BUILD_SHIP) +endif + +CWD = $(shell pwd) +shipSource: $(SHIP_DIR)/jsref_src.lst .FORCE + mkdir -p $(SHIP_DIR) + cd $(MOZ_DEPTH)/.. ; \ + zip $(CWD)/$(SHIP_DIR)/jsref_src.jar -@ < $(CWD)/$(SHIP_DIR)/jsref_src.lst +ifdef BUILD_SHIP + cp $(SHIP_DIR)/jsref_src.jar $(BUILD_SHIP) +endif + +JSREFSRCDIRS := $(shell cat $(DEPTH)/SpiderMonkey.rsp) +$(SHIP_DIR)/jsref_src.lst: .FORCE + mkdir -p $(SHIP_DIR) + rm -f $@ + touch $@ + for d in $(JSREFSRCDIRS); do \ + cd $(MOZ_DEPTH)/..; \ + ls -1 -d $$d | grep -v CVS | grep -v \.OBJ >> $(CWD)/$@; \ + cd $(CWD); \ + done + +.FORCE: diff --git a/src/dom/js/jscpucfg.c b/src/dom/js/jscpucfg.c new file mode 100644 index 000000000..aa6c04c4d --- /dev/null +++ b/src/dom/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/dom/js/jscpucfg.h b/src/dom/js/jscpucfg.h new file mode 100644 index 000000000..897ee577c --- /dev/null +++ b/src/dom/js/jscpucfg.h @@ -0,0 +1,204 @@ +/* -*- 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) + +#ifdef __WATCOMC__ +#define HAVE_VA_LIST_AS_ARRAY +#endif + +#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/dom/js/jsdate.c b/src/dom/js/jsdate.c new file mode 100644 index 000000000..4d85b1ef7 --- /dev/null +++ b/src/dom/js/jsdate.c @@ -0,0 +1,2238 @@ +/* -*- 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 +#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)); + } + + } + + if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) + return cx->localeCallbacks->localeToUnicode(cx, buf, rval); + + 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/dom/js/jsdate.h b/src/dom/js/jsdate.h new file mode 100644 index 000000000..790b4daf6 --- /dev/null +++ b/src/dom/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/dom/js/jsdbgapi.c b/src/dom/js/jsdbgapi.c new file mode 100644 index 000000000..8713a86b9 --- /dev/null +++ b/src/dom/js/jsdbgapi.c @@ -0,0 +1,1260 @@ +/* -*- 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(fun); + 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; + JSProperty *prop; + JSScopeProperty *sprop; + JSRuntime *rt; + JSBool ok; + 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, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + rt = cx->runtime; + if (!sprop) { + /* Check for a deleted symbol watchpoint, which holds its property. */ + sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); + if (!sprop) { + /* Make a new property in obj so we can watch for the first set. */ + if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, + NULL, NULL, JSPROP_ENUMERATE, + &prop)) { + return JS_FALSE; + } + sprop = (JSScopeProperty *) prop; + } + } else if (pobj != obj) { + /* Clone the prototype property so we can watch the right object. */ + jsval value; + JSPropertyOp getter, setter; + uintN attrs; + + 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, prop); + return JS_FALSE; + } + getter = setter = JS_PropertyStub; + attrs = JSPROP_ENUMERATE; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + + if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs, + &prop)) { + return JS_FALSE; + } + sprop = (JSScopeProperty *) prop; + } + + /* + * At this point, prop/sprop exists in obj, obj is locked, and we must + * OBJ_DROP_PROPERTY(cx, obj, prop) before returning. + */ + ok = JS_TRUE; + wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); + if (!wp) { + watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); + if (!watcher) { + ok = JS_FALSE; + goto out; + } + + wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); + if (!wp) { + ok = JS_FALSE; + goto out; + } + wp->handler = NULL; + wp->closure = NULL; + ok = js_AddRoot(cx, &wp->closure, "wp->closure"); + if (!ok) { + JS_free(cx, wp); + goto out; + } + 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; + + /* XXXbe nest in obj lock here */ + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, + sprop->getter, watcher); + if (!sprop) { + DropWatchPoint(cx, wp); + ok = JS_FALSE; + goto out; + } + } + wp->handler = handler; + wp->closure = closure; + +out: + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, + JSWatchPointHandler *handlerp, void **closurep) +{ + JSRuntime *rt; + JSWatchPoint *wp; + + rt = cx->runtime; + 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(fun); +} + +JS_PUBLIC_API(JSPrincipals *) +JS_GetScriptPrincipals(JSContext *cx, JSScript *script) +{ + return script->principals; +} + +/************************************************************************/ + +/* + * Stack Frame Iterator + */ +JS_PUBLIC_API(JSStackFrame *) +JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) +{ + *iteratorp = (*iteratorp == NULL) ? 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->interpreted) + nbytes += JS_GetScriptTotalSize(cx, fun->u.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/dom/js/jsdbgapi.h b/src/dom/js/jsdbgapi.h new file mode 100644 index 000000000..90215a275 --- /dev/null +++ b/src/dom/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/dom/js/jsdhash.c b/src/dom/js/jsdhash.c new file mode 100644 index 000000000..abcc36d05 --- /dev/null +++ b/src/dom/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 * JS_DHASH_FASTCALL +SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, + JSDHashOperator op) +{ + JSDHashNumber hash1, hash2; + int hashShift, sizeLog2; + JSDHashEntryHdr *entry, *firstRemoved; + JSDHashMatchEntry matchEntry; + uint32 sizeMask; + + METER(table->stats.searches++); + JS_ASSERT(!(keyHash & COLLISION_FLAG)); + + /* Compute the primary hash address. */ + hashShift = table->hashShift; + hash1 = HASH1(keyHash, hashShift); + entry = ADDRESS_ENTRY(table, hash1); + + /* Miss: return space for a new entry. */ + if (JS_DHASH_ENTRY_IS_FREE(entry)) { + METER(table->stats.misses++); + return entry; + } + + /* Hit: return entry. */ + matchEntry = table->ops->matchEntry; + if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { + METER(table->stats.hits++); + return entry; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - table->hashShift; + hash2 = HASH2(keyHash, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */ + 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_DHASH_FASTCALL +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/dom/js/jsdhash.h b/src/dom/js/jsdhash.h new file mode 100644 index 000000000..68f593b1b --- /dev/null +++ b/src/dom/js/jsdhash.h @@ -0,0 +1,579 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla JavaScript code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999-2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brendan Eich (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsdhash_h___ +#define jsdhash_h___ +/* + * Double hashing, a la Knuth 6. + */ +#include "jstypes.h" + +JS_BEGIN_EXTERN_C + +#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) +#define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) +#else +#define JS_DHASH_FASTCALL +#endif + +#ifdef DEBUG_XXXbrendan +#define JS_DHASHMETER 1 +#endif + +/* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ +#undef JS_DHASH_SIZE_LIMIT +#define JS_DHASH_SIZE_LIMIT JS_BIT(24) + +/* Minimum table size, or gross entry count (net is at most .75 loaded). */ +#ifndef JS_DHASH_MIN_SIZE +#define JS_DHASH_MIN_SIZE 16 +#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 +#error "JS_DHASH_MIN_SIZE must be a power of two!" +#endif + +/* + * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, + * expressed as a fixed-point 32-bit fraction. + */ +#define JS_DHASH_BITS 32 +#define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U + +/* Primitive and forward-struct typedefs. */ +typedef uint32 JSDHashNumber; +typedef struct JSDHashEntryHdr JSDHashEntryHdr; +typedef struct JSDHashEntryStub JSDHashEntryStub; +typedef struct JSDHashTable JSDHashTable; +typedef struct JSDHashTableOps JSDHashTableOps; + +/* + * Table entry header structure. + * + * In order to allow in-line allocation of key and value, we do not declare + * either here. Instead, the API uses const void *key as a formal parameter, + * 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_DHASH_FASTCALL +JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); + +/* + * Remove an entry already accessed via LOOKUP or ADD. + * + * NB: this is a "raw" or low-level routine, intended to be used only where + * the inefficiency of a full JS_DHashTableOperate (which rehashes in order + * to find the entry given its key) is not tolerable. This function does not + * shrink the table if it is underloaded. It does not update stats #ifdef + * JS_DHASHMETER, either. + */ +extern JS_PUBLIC_API(void) +JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); + +/* + * Enumerate entries in table using etor: + * + * count = JS_DHashTableEnumerate(table, etor, arg); + * + * JS_DHashTableEnumerate calls etor like so: + * + * op = etor(table, entry, number, arg); + * + * where number is a zero-based ordinal assigned to live entries according to + * their order in table->entryStore. + * + * The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, + * then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via + * table->ops->clearEntry) and free entry. Then we check whether op contains + * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries + * that were enumerated so far. Return the total number of live entries when + * enumeration completes normally. + * + * If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it + * must return JS_DHASH_STOP; otherwise undefined behavior results. + * + * If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk + * or compressed after enumeration, but before JS_DHashTableEnumerate returns. + * Such an enumerator therefore can't safely set aside entry pointers, but an + * enumerator that never returns JS_DHASH_REMOVE can set pointers to entries + * aside, e.g., to avoid copying live entries into an array of the entry type. + * Copying entry pointers is cheaper, and safe so long as the caller of such a + * "stable" Enumerate doesn't use the set-aside pointers after any call either + * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might + * grow or shrink entryStore. + * + * If your enumerator wants to remove certain entries, but set aside pointers + * to other entries that it retains, it can use JS_DHashTableRawRemove on the + * entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if + * you want to remove entries, but for some reason you do not want entryStore + * to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on + * the entry being enumerated, rather than returning JS_DHASH_REMOVE. + */ +typedef JSDHashOperator +(* 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/dom/js/jsdtoa.c b/src/dom/js/jsdtoa.c new file mode 100644 index 000000000..9f729fa8a --- /dev/null +++ b/src/dom/js/jsdtoa.c @@ -0,0 +1,3135 @@ +/* -*- 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; + + *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; + return sign ? -rv : rv; + +nomem: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + *err = JS_DTOA_ENOMEM; + return 0; +} + + +/* 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; + + 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); */ + return JS_FALSE; + } + strcpy(buf, s); + if (rve) { + *rve = buf[3] ? buf + 8 : buf + 3; + JS_ASSERT(**rve == '\0'); + } + return JS_TRUE; + } + + 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); */ + return JS_FALSE; + } + 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); + return JS_TRUE; + } + + 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); + return JS_FALSE; + } + 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++ = (char)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; + return JS_TRUE; + +nomem: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + Bfree(b); + return JS_FALSE; +} + + +/* 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/dom/js/jsdtoa.h b/src/dom/js/jsdtoa.h new file mode 100644 index 000000000..409f45454 --- /dev/null +++ b/src/dom/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/dom/js/jsemit.c b/src/dom/js/jsemit.c new file mode 100644 index 000000000..ece00fae1 --- /dev/null +++ b/src/dom/js/jsemit.c @@ -0,0 +1,4851 @@ +/* -*- 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 "jsregexp.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; + ATOM_LIST_INIT(&cg->constList); + 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"; +const char js_script_str[] = "script"; + +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 js_script_str; + 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; +} + +JSBool +js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + JSParseNode *pn) +{ + jsdouble dval; + jsint ival; + JSAtom *valueAtom; + JSAtomListElement *ale; + + /* XXX just do numbers for now */ + if (pn->pn_type == TOK_NUMBER) { + dval = pn->pn_dval; + valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) + ? js_AtomizeInt(cx, ival, 0) + : js_AtomizeDouble(cx, dval, 0); + if (!valueAtom) + return JS_FALSE; + ale = js_IndexAtom(cx, atom, &cg->constList); + if (!ale) + return JS_FALSE; + ALE_SET_VALUE(ale, ATOM_KEY(valueAtom)); + } + return JS_TRUE; +} + +JSBool +js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + jsval *vp) +{ + JSBool ok; + JSStackFrame *fp; + JSAtomListElement *ale; + JSObject *obj, *pobj; + JSProperty *prop; + uintN attrs; + + /* + * fp chases cg down the stack, but only until we reach the outermost cg. + * This enables propagating consts from top-level into switch cases in a + * function compiled along with the top-level script. All stack frames + * with matching code generators should be flagged with JSFRAME_COMPILING; + * we check sanity here. + */ + *vp = JSVAL_VOID; + ok = JS_TRUE; + fp = cx->fp; + do { + JS_ASSERT(fp->flags & JSFRAME_COMPILING); + + obj = fp->varobj; + if (obj == fp->scopeChain && + !js_InWithStatement(&cg->treeContext) && + !js_InCatchBlock(&cg->treeContext, atom)) { + ATOM_LIST_SEARCH(ale, &cg->constList, atom); + if (ale) { + *vp = ALE_VALUE(ale); + return JS_TRUE; + } + + /* + * Try looking in the variable object for a direct property that + * is readonly and permanent. We know such a property can't be + * shadowed by another property on obj's prototype chain, or a + * with object or catch variable; nor can prop's value be changed, + * nor can prop be deleted. + */ + prop = NULL; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop); + if (ok) { + if (pobj == obj && + (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { + /* + * We're compiling code that will be executed immediately, + * not re-executed against a different scope chain and/or + * variable object. Therefore we can get constant values + * from our variable object here. + */ + ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) + ok = OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + if (!ok || prop) + break; + } + fp = fp->down; + } while ((cg = cg->parent) != NULL); + return ok; +} + +/* + * Allocate an index invariant for all activations of the code being compiled + * in cg, that can be used to store and fetch a reference to a cloned RegExp + * object that shares the same JSRegExp private data created for the object + * literal in pn->pn_atom. We need clones to hold lastIndex and other direct + * properties that should not be shared among threads sharing a precompiled + * function or script. + * + * If the code being compiled is function code, allocate a reserved slot in + * the cloned function object that shares its precompiled script with other + * cloned function objects and with the compiler-created clone-parent. There + * are fun->nregexps such reserved slots in each function object cloned from + * fun->object. NB: during compilation, funobj slots must never be allocated, + * because js_AllocSlot could hand out one of the slots that should be given + * to a regexp clone. + * + * If the code being compiled is global code, reserve the fp->vars slot at + * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least + * one more than this index. For global code, fp->vars is parallel to the + * global script->atomMap.vector array, but possibly shorter for the common + * case (where var declarations and regexp literals cluster toward the front + * of the script or function body). + * + * Global variable name literals in script->atomMap have fast-global slot + * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array + * element. The atomIndex for a regexp object literal thus also addresses an + * fp->vars element that is not used by any optimized global variable, so we + * use that GC-scanned element to keep the regexp object clone alive, as well + * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode. + * + * In no case can cx->fp->varobj be a Call object here, because that implies + * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) + * is true, and js_GetToken will have already selected JSOP_OBJECT instead of + * JSOP_REGEXP, to avoid all this RegExp object cloning business. + * + * Why clone regexp objects? ECMA specifies that when a regular expression + * literal is scanned, a RegExp object is created. In the spec, compilation + * and execution happen indivisibly, but in this implementation and many of + * its embeddings, code is precompiled early and re-executed in multiple + * threads, or using multiple global objects, or both, for efficiency. + * + * In such cases, naively following ECMA leads to wrongful sharing of RegExp + * objects, which makes for collisions on the lastIndex property (especially + * for global regexps) and on any ad-hoc properties. Also, __proto__ and + * __parent__ refer to the pre-compilation prototype and global objects, a + * pigeon-hole problem for instanceof tests. + */ +static JSBool +IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, + JSCodeGenerator *cg) +{ + JSObject *varobj, *reobj; + JSClass *clasp; + JSFunction *fun; + JSRegExp *re; + uint16 *countPtr; + uintN cloneIndex; + + JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))); + + varobj = cx->fp->varobj; + clasp = OBJ_GET_CLASS(cx, varobj); + if (clasp == &js_FunctionClass) { + fun = (JSFunction *) JS_GetPrivate(cx, varobj); + countPtr = &fun->nregexps; + cloneIndex = *countPtr; + } else { + JS_ASSERT(clasp != &js_CallClass); + countPtr = &cg->treeContext.numGlobalVars; + cloneIndex = ALE_INDEX(ale); + } + + if ((cloneIndex + 1) >> 16) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NEED_DIET, js_script_str); + return JS_FALSE; + } + if (cloneIndex >= *countPtr) + *countPtr = cloneIndex + 1; + + reobj = ATOM_TO_OBJECT(pn->pn_atom); + JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass); + re = (JSRegExp *) JS_GetPrivate(cx, reobj); + re->cloneIndex = cloneIndex; + 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; + if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg)) + 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) +{ + JSStackFrame *fp; + JSObject *obj, *pobj; + JSClass *clasp; + JSBool optimizeGlobals; + JSAtom *atom; + JSProperty *prop; + JSScopeProperty *sprop; + JSOp op; + JSPropertyOp getter; + uintN attrs; + jsint slot; + JSAtomListElement *ale; + + 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. + */ + fp = cx->fp; + obj = fp->varobj; + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp != &js_FunctionClass && clasp != &js_CallClass) { + /* + * Optimize global variable accesses if there are at least 100 uses + * in unambiguous contexts, or failing that, if least half of all the + * uses of global vars/consts/functions are in loops. + */ + optimizeGlobals = (tc->globalUses >= 100 || + (tc->loopyGlobalUses && + tc->loopyGlobalUses >= tc->globalUses / 2)); + if (!optimizeGlobals) + return JS_TRUE; + } else { + optimizeGlobals = JS_FALSE; + } + + /* + * 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 (fp->scopeChain != obj || + js_InWithStatement(tc) || + js_InCatchBlock(tc, atom)) { + return JS_TRUE; + } + + op = pn->pn_op; + getter = NULL; +#ifdef __GNUC__ + attrs = slot = 0; /* quell GCC overwarning */ +#endif + if (optimizeGlobals) { + /* + * We are optimizing global variables, and there is no pre-existing + * global property named atom. If atom was declared via const or var, + * optimize pn to access fp->vars using the appropriate JOF_QVAR op. + */ + ATOM_LIST_SEARCH(ale, &tc->decls, atom); + if (!ale) { + /* Use precedes declaration, or name is never declared. */ + return JS_TRUE; + } + + attrs = (ALE_JSOP(ale) == JSOP_DEFCONST) + ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT + : JSPROP_ENUMERATE | JSPROP_PERMANENT; + + /* Index atom so we can map fast global number to name. */ + JS_ASSERT(tc->flags & TCF_COMPILING); + ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList); + if (!ale) + return JS_FALSE; + + /* Defend against tc->numGlobalVars 16-bit overflow. */ + slot = ALE_INDEX(ale); + if ((slot + 1) >> 16) + return JS_TRUE; + + if ((uint16)(slot + 1) > tc->numGlobalVars) + tc->numGlobalVars = (uint16)(slot + 1); + } else { + /* + * 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 evaluates + * to false, due to JSPROP_PERMANENT. + */ + if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + if (sprop) { + if (pobj == obj) { + getter = sprop->getter; + attrs = sprop->attrs; + slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + } + + if (optimizeGlobals || getter) { + if (optimizeGlobals) { + switch (op) { + case JSOP_NAME: op = JSOP_GETGVAR; break; + case JSOP_SETNAME: op = JSOP_SETGVAR; break; + case JSOP_SETCONST: /* NB: no change */ break; + case JSOP_INCNAME: op = JSOP_INCGVAR; break; + case JSOP_NAMEINC: op = JSOP_GVARINC; break; + case JSOP_DECNAME: op = JSOP_DECGVAR; break; + case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; + case JSOP_FORNAME: /* NB: no change */ break; + case JSOP_DELNAME: /* NB: no change */ break; + default: JS_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); + } + } else if (getter == js_GetArgument || + (getter == js_CallClass.getProperty && + fp->fun && (uintN) slot < fp->fun->nargs)) { + 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); + } + } + if (op != pn->pn_op) { + pn->pn_op = op; + pn->pn_slot = slot; + } + pn->pn_attrs = attrs; + } + + if (pn->pn_slot < 0) { + /* + * We couldn't optimize pn, so it's not a global or local slot 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, constPropagated; + ptrdiff_t top, off, defaultOffset; + JSParseNode *pn2, *pn3, *pn4; + uint32 caseCount, tableLength; + JSParseNode **table; + jsdouble d; + jsint i, low, high; + jsval v; + JSAtom *atom; + JSAtomListElement *ale; + intN noteIndex; + size_t switchSize, tableSize; + jsbytecode *pc, *savepc; + + /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ + switchOp = JSOP_TABLESWITCH; + ok = JS_TRUE; + hasDefault = constPropagated = JS_FALSE; + defaultOffset = -1; + + /* 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; + caseCount = pn2->pn_count; + tableLength = 0; + table = NULL; + + if (caseCount == 0 || + (caseCount == 1 && + (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { + caseCount = 0; + low = 0; + high = -1; + } else { +#define INTMAP_LENGTH 256 + jsbitmap intmap_space[INTMAP_LENGTH]; + jsbitmap *intmap = NULL; + int32 intmap_bitlen = 0; + + low = JSVAL_INT_MAX; + high = JSVAL_INT_MIN; + + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) { + hasDefault = JS_TRUE; + caseCount--; /* one of the "cases" was the default */ + continue; + } + + JS_ASSERT(pn3->pn_type == TOK_CASE); + if (switchOp == JSOP_CONDSWITCH) + continue; + + pn4 = pn3->pn_left; + 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_NAME: + if (!pn4->pn_expr) { + ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); + if (!ok) + goto release; + if (!JSVAL_IS_VOID(v)) { + pn3->pn_val = v; + constPropagated = JS_TRUE; + break; + } + } + /* FALL THROUGH */ + case TOK_PRIMARY: + if (pn4->pn_op == JSOP_TRUE) { + pn3->pn_val = JSVAL_TRUE; + break; + } + if (pn4->pn_op == JSOP_FALSE) { + pn3->pn_val = JSVAL_FALSE; + break; + } + /* FALL THROUGH */ + default: + switchOp = JSOP_CONDSWITCH; + continue; + } + + JS_ASSERT(JSVAL_IS_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) { + tableLength = (uint32)(high - low + 1); + if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) + 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 + tableLength)); + } else { + /* + * JSOP_LOOKUPSWITCH: + * 1 offset (len) and 1 atom index (npairs) before the table, + * 1 atom index and 1 jump offset per entry. + */ + switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN + + (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); + } + + /* + * Emit switchOp followed by switchSize bytes of jump or lookup table. + * + * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial + * to emit the immediate operand(s) by which bytecode readers such as + * BuildSpanDepTable discover the length of the switch opcode *before* + * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's + * also important to zero all unknown jump offset immediate operands, + * so they can be converted to span dependencies with null targets to + * be computed later (js_EmitN zeros switchSize bytes after switchOp). + */ + if (js_EmitN(cx, cg, switchOp, switchSize) < 0) + return JS_FALSE; + + off = -1; + if (switchOp == JSOP_CONDSWITCH) { + intN caseNoteIndex = -1; + + /* 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 { + pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); + + if (switchOp == JSOP_TABLESWITCH) { + /* Fill in switch bounds, which we know fit in 16-bit offsets. */ + SET_JUMP_OFFSET(pc, low); + pc += JUMP_OFFSET_LEN; + SET_JUMP_OFFSET(pc, high); + pc += JUMP_OFFSET_LEN; + + /* + * Use malloc to avoid arena bloat for programs with many switches. + * We free table if non-null at label out, so all control flow must + * exit this function through goto out or goto bad. + */ + if (tableLength != 0) { + tableSize = (size_t)tableLength * sizeof *table; + table = (JSParseNode **) 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 < tableLength); + table[i] = pn3; + } + } + } else { + JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); + + /* Fill in the number of cases. */ + SET_ATOM_INDEX(pc, caseCount); + pc += ATOM_INDEX_LEN; + } + + /* + * After this point, all control flow involving JSOP_TABLESWITCH + * must set ok and goto out to exit this function. To keep things + * simple, all switchOp cases exit that way. + */ + if (constPropagated) { + /* + * Skip switchOp, as we are not setting jump offsets in the two + * for loops below. We'll restore CG_NEXT(cg) from savepc after, + * unless there was an error. + */ + savepc = CG_NEXT(cg); + CG_NEXT(cg) = pc + 1; + if (switchOp == JSOP_TABLESWITCH) { + for (i = 0; i < (jsint)tableLength; i++) { + pn3 = table[i]; + if (pn3 && + (pn4 = pn3->pn_left) != NULL && + pn4->pn_type == TOK_NAME) { + /* Note a propagated constant with the const's name. */ + JS_ASSERT(!pn4->pn_expr); + ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); + if (!ale) + goto bad; + CG_NEXT(cg) = pc; + if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) + ALE_INDEX(ale)) < 0) { + goto bad; + } + } + pc += JUMP_OFFSET_LEN; + } + } else { + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + pn4 = pn3->pn_left; + if (pn4 && pn4->pn_type == TOK_NAME) { + /* Note a propagated constant with the const's name. */ + JS_ASSERT(!pn4->pn_expr); + ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); + if (!ale) + goto bad; + CG_NEXT(cg) = pc; + if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) + ALE_INDEX(ale)) < 0) { + goto bad; + } + } + pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; + } + } + CG_NEXT(cg) = savepc; + } + } + + /* Emit code for each case's statements, copying pn_offset up to pn3. */ + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset); + pn4 = pn3->pn_right; + ok = js_EmitTree(cx, cg, pn4); + if (!ok) + goto out; + pn3->pn_offset = pn4->pn_offset; + if (pn3->pn_type == TOK_DEFAULT) + off = pn3->pn_offset - top; + } + + if (!hasDefault) { + /* If no default case, offset for default is to end of switch. */ + off = CG_OFFSET(cg) - top; + } + + /* We better have set "off" by now. */ + JS_ASSERT(off != -1); + + /* Set the default offset (to end of switch if no default). */ + if (switchOp == JSOP_CONDSWITCH) { + pc = NULL; + JS_ASSERT(defaultOffset != -1); + ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), + off - (defaultOffset - top)); + if (!ok) + goto out; + } else { + pc = CG_CODE(cg, top); + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + + /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ + off = CG_OFFSET(cg) - top; + ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); + if (!ok) + goto out; + + if (switchOp == JSOP_TABLESWITCH) { + /* Skip over the already-initialized switch bounds. */ + pc += 2 * JUMP_OFFSET_LEN; + + /* Fill in the jump table, if there is one. */ + for (i = 0; i < (jsint)tableLength; i++) { + pn3 = table[i]; + off = pn3 ? pn3->pn_offset - top : 0; + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + } else if (switchOp == JSOP_LOOKUPSWITCH) { + /* Skip over the already-initialized number of cases. */ + pc += 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) + goto bad; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + goto bad; + SET_ATOM_INDEX(pc, ALE_INDEX(ale)); + pc += ATOM_INDEX_LEN; + + off = pn3->pn_offset - top; + ok = js_SetJumpOffset(cx, cg, pc, off); + if (!ok) + goto out; + pc += JUMP_OFFSET_LEN; + } + } + +out: + if (table) + JS_free(cx, table); + return ok && js_PopStatementCG(cx, cg); + +bad: + ok = JS_FALSE; + goto out; +} +#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; + 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; + frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) + ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO + : JSFRAME_COMPILING; + cx->fp = &frame; + ok = js_EmitTree(cx, cg, body); + cx->fp = fp; + if (!ok) + return JS_FALSE; + + fun->u.script = js_NewScriptFromCG(cx, cg, fun); + if (!fun->u.script) + return JS_FALSE; + fun->interpreted = JS_TRUE; + 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; + cg2->parent = cg; + 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; + JSProperty *prop; + uintN slot; + + obj = OBJ_GET_PARENT(cx, fun->object); + if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj, + &prop)) { + return JS_FALSE; + } + JS_ASSERT(prop && pobj == obj); + slot = ((JSScopeProperty *) prop)->shortid; + OBJ_DROP_PROPERTY(cx, pobj, prop); + + /* 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', emit code to define x if necessary + * using a prolog opcode, but do not emit a pop. If the left part + * is 'var x = i', emit prolog code to define x if necessary; then + * emit code to evaluate i, assign the result to x, and pop the + * result off the stack. + * + * All the logic to do this is implemented in the outer switch's + * TOK_VAR case, conditioned on pn_extra flags set by the parser. + * + * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) + * called here will generate the SRC_VAR note for the assignment + * op that sets x = i, hoisting the initialized var declaration + * out of the loop: 'var x = i; for (x in o) ...'. + * + * In the 'for (var x in o) ...' case, nothing but the prolog op + * (if needed) should be generated here, we must emit the SRC_VAR + * just before the JSOP_FOR* opcode in the switch on pn3->pn_type + * a bit below, so nothing is hoisted: 'for (var x in o) ...'. + */ + pn3 = pn2->pn_left; + if (pn3->pn_type == TOK_VAR && !js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + + /* 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; + JS_ASSERT(pn3->pn_type == TOK_NAME); + if (!pn3->pn_expr && js_NewSrcNote(cx, cg, SRC_VAR) < 0) + return JS_FALSE; + /* FALL THROUGH */ + case TOK_NAME: + if (pn3->pn_slot >= 0) { + op = pn3->pn_op; + switch (op) { + case JSOP_GETARG: /* FALL THROUGH */ + case JSOP_SETARG: op = JSOP_FORARG; break; + case JSOP_GETVAR: /* FALL THROUGH */ + case JSOP_SETVAR: op = JSOP_FORVAR; break; + case JSOP_GETGVAR: + case JSOP_SETGVAR: op = JSOP_FORNAME; break; + default: JS_ASSERT(0); + } + } else { + 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) { + if (js_Emit1(cx, cg, JSOP_POP2) < 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. + */ + + /* leavewith, annotated so the decompiler knows to pop */ + off = cg->stackDepth - 1; + if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 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 [setsp],[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); + + 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 ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST && + (!(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); + pn3 = pn2->pn_expr; + if (pn->pn_op == JSOP_DEFCONST && + !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, + pn3)) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + } + } + + /* + * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the + * TOK_VAR case, but only the initialized case (a strange one that + * falls out of ECMA-262's grammar) wants to run past this point. + * Both cases must conditionally emit a JSOP_DEFVAR, above. Note + * that the parser error-checks to ensure that pn->pn_count is 1. + * + * XXX Narcissus keeps track of variable declarations in the node + * for the script being compiled, so there's no need to share any + * conditional prolog code generation there. We could do likewise, + * but it's a big change, requiring extra allocation, so probably + * not worth the trouble for SpiderMonkey. + */ + if ((pn->pn_extra & PNX_FORINVAR) && !pn2->pn_expr) + break; + + 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 & PNX_POPVAR) { + 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->interpreted || + (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_SETGVAR) + ? JSOP_GETGVAR + : (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) { + /* Incrementing a declared const: just get its value. */ + op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST) + ? JSOP_GETGVAR + : 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 & PNX_ENDCOMMA) { + /* Emit a source note so we know to decompile an extra comma. */ + if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) + return JS_FALSE; + } + + /* 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_NewSrcNote2(cx, cg, SRC_LABEL, 0) < 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/dom/js/jsemit.h b/src/dom/js/jsemit.h new file mode 100644 index 000000000..3c8c9c6ae --- /dev/null +++ b/src/dom/js/jsemit.h @@ -0,0 +1,574 @@ +/* -*- 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 */ + uint16 flags; /* statement state flags, see below */ + uint16 numGlobalVars; /* max. no. of global variables/regexps */ + uint32 tryCount; /* total count of try statements parsed */ + uint32 globalUses; /* optimizable global var uses in total */ + uint32 loopyGlobalUses;/* optimizable global var uses in loops */ + 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 = (tc)->numGlobalVars = 0, \ + (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 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 */ + JSAtomList constList; /* compile time constants */ + JSCodeGenerator *parent; /* Enclosing function or global context */ +}; + +#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); + +/* + * Define and lookup a primitive jsval associated with the const named by atom. + * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn + * and saves the const's value in cg->constList, if it can be used at compile + * time. It returns true unless an error occurred. + * + * If the initializer's value could not be saved, js_LookupCompileTimeConstant + * calls will return the undefined value. js_LookupCompileTimeConstant tries + * to find a const value memorized for atom, returning true with *vp set to a + * value other than undefined if the constant was found, true with *vp set to + * JSVAL_VOID if not found, and false on error. + */ +extern JSBool +js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + JSParseNode *pn); + +extern JSBool +js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, + jsval *vp); + +/* + * 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/dom/js/jsexn.c b/src/dom/js/jsexn.c new file mode 100644 index 000000000..6d3a182db --- /dev/null +++ b/src/dom/js/jsexn.c @@ -0,0 +1,1084 @@ +/* -*- 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; + } + argv[0] = STRING_TO_JSVAL(message); + } else { + message = cx->runtime->emptyString; + } + + /* Set the 'fileName' property. */ + if (argc > 1) { + filename = js_ValueToString(cx, argv[1]); + if (!filename) { + ok = JS_FALSE; + goto out; + } + argv[1] = STRING_TO_JSVAL(filename); + } 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 = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; + + if (!JS_GetProperty(cx, obj, js_message_str, &v)) + return JS_FALSE; + message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) + : cx->runtime->emptyString; + + if (JSSTRING_LENGTH(message) != 0) { + name_length = JSSTRING_LENGTH(name); + message_length = JSSTRING_LENGTH(message); + length = (name_length ? name_length + 2 : 0) + message_length; + cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + + if (name_length) { + 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_TRUE; + + if (!JS_GetPendingException(cx, &exn)) + return JS_FALSE; + + /* + * Because js_ValueToString below could error and an exception object + * could become unrooted, we must root exnObject. + */ + if (JSVAL_IS_PRIMITIVE(exn)) { + exnObject = NULL; + } else { + exnObject = JSVAL_TO_OBJECT(exn); + if (!js_AddRoot(cx, &exnObject, "exn.report.root")) + return JS_FALSE; + } + +#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); + JS_ClearPendingException(cx); + return JS_TRUE; +} + +#endif /* JS_HAS_EXCEPTIONS */ diff --git a/src/dom/js/jsexn.h b/src/dom/js/jsexn.h new file mode 100644 index 000000000..aeaab0a58 --- /dev/null +++ b/src/dom/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/dom/js/jsfile.c b/src/dom/js/jsfile.c new file mode 100644 index 000000000..ef5e93b29 --- /dev/null +++ b/src/dom/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/dom/js/jsfile.h b/src/dom/js/jsfile.h new file mode 100644 index 000000000..47a8692d8 --- /dev/null +++ b/src/dom/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/dom/js/jsfile.msg b/src/dom/js/jsfile.msg new file mode 100644 index 000000000..f03c51d42 --- /dev/null +++ b/src/dom/js/jsfile.msg @@ -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 + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + Error messages for jsfile.c. See js.msg for format specification. +*/ + +MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") +MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") +MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") +MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") +MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") +MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") +MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") +MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") +MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") +MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") +MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") +MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") +MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") +MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") +MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") +MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") +MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") +MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") +MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") +MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") +MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") +MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") +MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") +MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") +MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") +MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") +MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") +MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") +MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") +MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") +MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") +MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") +MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") +MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") +MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") +MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") +MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") + + diff --git a/src/dom/js/jsfun.c b/src/dom/js/jsfun.c new file mode 100644 index 000000000..bbb1e1b61 --- /dev/null +++ b/src/dom/js/jsfun.c @@ -0,0 +1,2015 @@ +/* -*- 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; +} + +#define MAXARGS(fp) ((fp)->fun ? JS_MAX((fp)->argc, (fp)->fun->nargs) \ + : (fp)->argc) + +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 = MAXARGS(fp); + 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 (MAXARGS(fp) <= 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 < MAXARGS(fp)) { + 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. */ + 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 (MAXARGS(fp) > 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); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case ARGS_CALLEE: + case ARGS_LENGTH: + SET_OVERRIDE_BIT(fp, slot); + break; + + default: + if ((uintN)slot < MAXARGS(fp) && !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); + + 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 < MAXARGS(fp) && !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); + + slot = JSVAL_TO_INT(id); + switch (slot) { + case ARGS_CALLEE: + case ARGS_LENGTH: + SET_OVERRIDE_BIT(fp, slot); + break; + + default: + if ((uintN)slot < MAXARGS(fp) && !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); + + if (JSVAL_IS_INT(id)) { + slot = JSVAL_TO_INT(id); + if (slot < MAXARGS(fp) && !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); + + /* + * 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 = MAXARGS(fp); + 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; + JSProperty *prop; + 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, &prop)) + return JS_FALSE; + + sprop = (JSScopeProperty *) prop; + 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, prop); + 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 (!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) + *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 (15.3.5.2) says that constructor.prototype is DontDelete for + * user-defined functions, but DontEnum | ReadOnly | DontDelete for + * native "system" constructors such as Object or Function. So lazily + * set the former here in fun_resolve, but eagerly define the latter + * in JS_InitClass, with the right attributes. + */ + if (!js_SetClassPrototype(cx, obj, proto, + JSPROP_ENUMERATE | 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->interpreted) + js_DestroyScript(cx, fun->u.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; + uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ + 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; + if (!fun->interpreted) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NOT_SCRIPTED_FUNCTION, + JS_GetFunctionName(fun)); + return JS_FALSE; + } + atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + flagsword = ((uint32)fun->nregexps << 16) | fun->flags; + } 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_XDRUint32(xdr, &flagsword)) { + 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->u.script, NULL)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + fun->interpreted = JS_TRUE; + fun->flags = (uint8) flagsword; + fun->nregexps = (uint16) (flagsword >> 16); + + *objp = fun->object; + if (atomstr) { + /* XXX only if this was a top-level function! */ + fun->atom = js_AtomizeString(cx, atomstr, 0); + if (!fun->atom) + return JS_FALSE; + } + + js_CallNewScriptHook(cx, fun->u.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; + JSString *str; + + 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; + } + + return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); +} + +#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->interpreted) + js_MarkScript(cx, fun->u.script, arg); + } + return 0; +} + +static uint32 +fun_reserveSlots(JSContext *cx, JSObject *obj) +{ + JSFunction *fun; + + fun = (JSFunction *) JS_GetPrivate(cx, obj); + return fun ? fun->nregexps : 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, fun_reserveSlots +}; + +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 arguments. */ + 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; + JSProperty *prop; + 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->u.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, &prop)) + goto bad_formal; + sprop = (JSScopeProperty *) prop; + 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, prop); + 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->u.script = js_NewScript(cx, 0, 0, 0); + if (!fun->u.script) + goto bad; + fun->interpreted = JS_TRUE; + 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->u.native = native; + fun->nargs = nargs; + fun->extra = 0; + fun->nvars = 0; + fun->flags = flags & JSFUN_FLAGS_MASK; + fun->interpreted = JS_FALSE; + fun->nregexps = 0; + 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/dom/js/jsfun.h b/src/dom/js/jsfun.h new file mode 100644 index 000000000..eb79161a9 --- /dev/null +++ b/src/dom/js/jsfun.h @@ -0,0 +1,158 @@ +/* -*- 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 */ + union { + JSNative native; /* native method pointer or null */ + JSScript *script; /* interpreted bytecode descriptor or null */ + } u; + 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 */ + JSPackedBool interpreted; /* use u.script if true, u.native if false */ + uint16 nregexps; /* number of regular expressions literals */ + uint16 spare; /* reserved for future use */ + JSAtom *atom; /* name for diagnostics and decompiling */ + JSClass *clasp; /* if non-null, constructor for this class */ +}; + +#define FUN_NATIVE(fun) ((fun)->interpreted ? NULL : (fun)->u.native) +#define FUN_SCRIPT(fun) ((fun)->interpreted ? (fun)->u.script : NULL) + +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/dom/js/jsgc.c b/src/dom/js/jsgc.c new file mode 100644 index 000000000..754f4ae6d --- /dev/null +++ b/src/dom/js/jsgc.c @@ -0,0 +1,1441 @@ +/* -*- 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; + JSLocalRootStack *lrs; + +#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; + } + goto fail; + } + + /* Find the flags pointer given thing's address. */ + flagp = js_GetGCThingFlags(thing); + } + + lrs = cx->localRootStack; + if (lrs) { + /* + * If we're in a local root scope, don't set cx->newborn[type] at all, + * to avoid entraining garbage from it for an unbounded amount of time + * on this context. A caller will leave the local root scope and pop + * this reference, allowing thing to be GC'd if it has no other refs. + * See JS_EnterLocalRootScope and related APIs. + */ + if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) + goto fail; + } else { + /* + * No local root scope, so we're stuck with the old, fragile model of + * depending on a pigeon-hole newborn per type per context. + */ + cx->newborn[flags & GCF_TYPEMASK] = thing; + } + + /* We can't fail now, so update flags and rt->gcBytes. */ + *flagp = (uint8)flags; + rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8); + + /* + * 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; + +fail: + METER(rt->gcStats.fail++); + JS_UNLOCK_GC(rt); + JS_ReportOutOfMemory(cx); + return NULL; +} + +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); + } 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 (acx->localRootStack) + js_MarkLocalRoots(cx, acx->localRootStack); + } +#ifdef DUMP_CALL_TABLE + js_DumpCallTable(cx); +#endif + + 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/dom/js/jsgc.h b/src/dom/js/jsgc.h new file mode 100644 index 000000000..a4813d16d --- /dev/null +++ b/src/dom/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 the 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/dom/js/jshash.c b/src/dom/js/jshash.c new file mode 100644 index 000000000..954368450 --- /dev/null +++ b/src/dom/js/jshash.c @@ -0,0 +1,471 @@ +/* -*- 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); + 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; + 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/dom/js/jshash.h b/src/dom/js/jshash.h new file mode 100644 index 000000000..85e3cf811 --- /dev/null +++ b/src/dom/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/dom/js/jsinterp.c b/src/dom/js/jsinterp.c new file mode 100644 index 000000000..6881e07dd --- /dev/null +++ b/src/dom/js/jsinterp.c @@ -0,0 +1,4797 @@ +/* -*- 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; +} + +#ifdef DUMP_CALL_TABLE + +#include "jsclist.h" +#include "jshash.h" +#include "jsdtoa.h" + +typedef struct CallKey { + jsval callee; /* callee value */ + const char *filename; /* function filename or null */ + uintN lineno; /* function lineno or 0 */ +} CallKey; + +/* Compensate for typeof null == "object" brain damage. */ +#define JSTYPE_NULL JSTYPE_LIMIT +#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) +#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) +#define NTYPEHIST (JSTYPE_LIMIT + 1) + +typedef struct CallValue { + uint32 total; /* total call count */ + uint32 recycled; /* LRU-recycled calls lost */ + uint16 minargc; /* minimum argument count */ + uint16 maxargc; /* maximum argument count */ + struct ArgInfo { + uint32 typeHist[NTYPEHIST]; /* histogram by type */ + JSCList lruList; /* top 10 values LRU list */ + struct ArgValCount { + JSCList lruLink; /* LRU list linkage */ + jsval value; /* recently passed value */ + uint32 count; /* number of times passed */ + char strbuf[112]; /* string conversion buffer */ + } topValCounts[10]; /* top 10 value storage */ + } argInfo[8]; +} CallValue; + +typedef struct CallEntry { + JSHashEntry entry; + CallKey key; + CallValue value; + char name[32]; /* function name copy */ +} CallEntry; + +static void * +AllocCallTable(void *pool, size_t size) +{ + return malloc(size); +} + +static void +FreeCallTable(void *pool, void *item) +{ + free(item); +} + +static JSHashEntry * +AllocCallEntry(void *pool, const void *key) +{ + return (JSHashEntry*) calloc(1, sizeof(CallEntry)); +} + +static void +FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) +{ + JS_ASSERT(flag == HT_FREE_ENTRY); + free(he); +} + +static JSHashAllocOps callTableAllocOps = { + AllocCallTable, FreeCallTable, + AllocCallEntry, FreeCallEntry +}; + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_call_key(const void *key) +{ + CallKey *ck = (CallKey *) key; + JSHashNumber hash = (jsuword)ck->callee >> 3; + + if (ck->filename) { + hash = (hash << 4) ^ JS_HashString(ck->filename); + hash = (hash << 4) ^ ck->lineno; + } + return hash; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_compare_call_keys(const void *k1, const void *k2) +{ + CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; + + return ck1->callee == ck2->callee && + ((ck1->filename && ck2->filename) + ? strcmp(ck1->filename, ck2->filename) == 0 + : ck1->filename == ck2->filename) && + ck1->lineno == ck2->lineno; +} + +JSHashTable *js_CallTable; +size_t js_LogCallToSourceLimit; + +JS_STATIC_DLL_CALLBACK(intN) +CallTableDumper(JSHashEntry *he, intN k, void *arg) +{ + CallEntry *ce = (CallEntry *)he; + FILE *fp = (FILE *)arg; + uintN argc, i, n; + struct ArgInfo *ai; + JSType save, type; + JSCList *cl; + struct ArgValCount *avc; + jsval argval; + + if (ce->key.filename) { + /* We're called at the end of the mark phase, so mark our filenames. */ + js_MarkScriptFilename(ce->key.filename); + fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); + } else { + fprintf(fp, "@%p ", (void *) ce->key.callee); + } + + if (ce->name[0]) + fprintf(fp, "name %s ", ce->name); + fprintf(fp, "calls %lu (%lu) argc %u/%u\n", + (unsigned long) ce->value.total, + (unsigned long) ce->value.recycled, + ce->value.minargc, ce->value.maxargc); + + argc = JS_MIN(ce->value.maxargc, 8); + for (i = 0; i < argc; i++) { + ai = &ce->value.argInfo[i]; + + n = 0; + save = -1; + for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { + if (ai->typeHist[type]) { + save = type; + ++n; + } + } + if (n == 1) { + fprintf(fp, " arg %u type %s: %lu\n", + i, TYPENAME(save), (unsigned long) ai->typeHist[save]); + } else { + fprintf(fp, " arg %u type histogram:\n", i); + for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { + fprintf(fp, " %9s: %8lu ", + TYPENAME(type), (unsigned long) ai->typeHist[type]); + for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) + fputc('*', fp); + fputc('\n', fp); + } + } + + fprintf(fp, " arg %u top 10 values:\n", i); + n = 1; + for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { + avc = (struct ArgValCount *)cl; + if (!avc->count) + break; + argval = avc->value; + fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", + n, (unsigned long) avc->count, + sizeof avc->strbuf, avc->strbuf, argval); + ++n; + } + } + + return HT_ENUMERATE_NEXT; +} + +void +js_DumpCallTable(JSContext *cx) +{ + char name[24]; + FILE *fp; + static uintN dumpCount; + + if (!js_CallTable) + return; + + JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); + dumpCount++; + fp = fopen(name, "w"); + if (!fp) + return; + + JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); + fclose(fp); +} + +static void +LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) +{ + CallKey key; + const char *name, *cstr; + JSFunction *fun; + JSHashNumber keyHash; + JSHashEntry **hep, *he; + CallEntry *ce; + uintN i, j; + jsval argval; + JSType type; + struct ArgInfo *ai; + struct ArgValCount *avc; + JSString *str; + + if (!js_CallTable) { + js_CallTable = JS_NewHashTable(1024, js_hash_call_key, + js_compare_call_keys, NULL, + &callTableAllocOps, NULL); + if (!js_CallTable) + return; + } + + key.callee = callee; + key.filename = NULL; + key.lineno = 0; + name = ""; + if (JSVAL_IS_FUNCTION(cx, callee)) { + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); + if (fun->atom) + name = js_AtomToPrintableString(cx, fun->atom); + if (fun->interpreted) { + key.filename = fun->u.script->filename; + key.lineno = fun->u.script->lineno; + } + } + keyHash = js_hash_call_key(&key); + + hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); + he = *hep; + if (he) { + ce = (CallEntry *) he; + JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); + } else { + he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); + if (!he) + return; + ce = (CallEntry *) he; + ce->entry.key = &ce->key; + ce->entry.value = &ce->value; + ce->key = key; + for (i = 0; i < 8; i++) { + ai = &ce->value.argInfo[i]; + JS_INIT_CLIST(&ai->lruList); + for (j = 0; j < 10; j++) + JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); + } + strncpy(ce->name, name, sizeof ce->name); + } + + ++ce->value.total; + if (ce->value.minargc < argc) + ce->value.minargc = argc; + if (ce->value.maxargc < argc) + ce->value.maxargc = argc; + if (argc > 8) + argc = 8; + for (i = 0; i < argc; i++) { + ai = &ce->value.argInfo[i]; + argval = argv[i]; + type = TYPEOF(cx, argval); + ++ai->typeHist[type]; + + for (j = 0; ; j++) { + if (j == 10) { + avc = (struct ArgValCount *) ai->lruList.next; + ce->value.recycled += avc->count; + avc->value = argval; + avc->count = 1; + break; + } + avc = &ai->topValCounts[j]; + if (avc->value == argval) { + ++avc->count; + break; + } + } + + /* Move avc to the back of the LRU list. */ + JS_REMOVE_LINK(&avc->lruLink); + JS_APPEND_LINK(&avc->lruLink, &ai->lruList); + + str = NULL; + cstr = ""; + switch (TYPEOF(cx, argval)) { + case JSTYPE_VOID: + cstr = js_type_str[JSTYPE_VOID]; + break; + case JSTYPE_NULL: + cstr = js_null_str; + break; + case JSTYPE_BOOLEAN: + cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; + break; + case JSTYPE_NUMBER: + if (JSVAL_IS_INT(argval)) { + JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", + JSVAL_TO_INT(argval)); + } else { + JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, + *JSVAL_TO_DOUBLE(argval)); + } + continue; + case JSTYPE_STRING: + str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); + break; + case JSTYPE_FUNCTION: + if (JSVAL_IS_FUNCTION(cx, argval)) { + fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); + if (fun && fun->atom) { + str = ATOM_TO_STRING(fun->atom); + break; + } + } + /* FALL THROUGH */ + case JSTYPE_OBJECT: + js_LogCallToSourceLimit = sizeof avc->strbuf; + cx->options |= JSOPTION_LOGCALL_TOSOURCE; + str = js_ValueToSource(cx, argval); + cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; + break; + } + if (str) + cstr = JS_GetStringBytes(str); + strncpy(avc->strbuf, cstr, sizeof avc->strbuf); + } +} + +#endif /* DUMP_CALL_TABLE */ + +/* + * 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(&cx->stackPool, + (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 this makes no sense -- why convert to function if clasp->call? + * XXX better to call that hook without converting + * XXX the only thing that needs fixing is liveconnect + * + * 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); + if (fun->interpreted) { + native = NULL; + script = fun->u.script; + } else { + native = fun->u.native; + script = NULL; + } + 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; + + /* 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) { +#ifdef DUMP_CALL_TABLE + LogCall(cx, *vp, argc, frame.argv); +#endif + /* 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)))->interpreted && + !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 flags, jsval *result) +{ + JSInterpreterHook hook; + void *hookData, *mark; + JSStackFrame *oldfp, frame; + JSObject *obj, *tmp; + JSBool ok; + + hook = cx->runtime->executeHook; + hookData = mark = 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 = 0; + frame.argv = NULL; + frame.nvars = script->numGlobalVars; + if (frame.nvars) { + frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); + if (!frame.vars) + return JS_FALSE; + memset(frame.vars, 0, frame.nvars * sizeof(jsval)); + } else { + 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 = flags; + frame.dormantNext = 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); + + /* + * Use frame.rval, not result, so the last result stays rooted across any + * GC activations nested within this js_Interpret. + */ + ok = js_Interpret(cx, &frame.rval); + *result = frame.rval; + + if (hookData) { + hook = cx->runtime->executeHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } + if (mark) + js_FreeRawStack(cx, mark); + cx->fp = oldfp; + + if (oldfp && oldfp != down) { + JS_ASSERT(cx->dormantFrameChain == oldfp); + cx->dormantFrameChain = oldfp->dormantNext; + oldfp->dormantNext = NULL; + } + + 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, + JSObject **objp, JSProperty **propp) +{ + JSObject *obj2; + JSProperty *prop; + uintN oldAttrs, report; + JSBool isFunction; + jsval value; + const char *type, *name; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (propp) { + *objp = obj2; + *propp = prop; + } + if (!prop) + return JS_TRUE; + + /* From here, return true, or goto bad on failure to drop prop. */ + if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) + goto bad; + + /* 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)) + goto bad; + 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) + goto bad; + return JS_ReportErrorFlagsAndNumber(cx, report, + js_GetErrorMessage, NULL, + JSMSG_REDECLARED_VAR, + type, name); + +bad: + if (propp) { + *objp = NULL; + *propp = NULL; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_FALSE; +} + +#ifndef MAX_INTERP_LEVEL +#if defined(XP_OS2) +#define MAX_INTERP_LEVEL 250 +#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. We reload onbranch after calling out to native + * functions (but not to getters, setters, or other native hooks). + */ +#define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) + + LOAD_BRANCH_CALLBACK(cx); + 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); + JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class); + 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)) { + SAVE_SP(fp); + if (cond) { + str = JSVAL_TO_STRING(ltmp); + ok = (str2 = js_ValueToString(cx, rtmp)) != NULL; + } else { + str2 = JSVAL_TO_STRING(rtmp); + 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_BRANCH_CALLBACK(cx); + 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_MIDDLE() \ + 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_MIDDLE(); + } + + 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 = 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_MIDDLE(); + *vp = rval; + PUSH_OPND(rtmp); + break; + +#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \ + slot = GET_VARNO(pc); \ + JS_ASSERT(slot < fp->nvars); \ + lval = fp->vars[slot]; \ + if (JSVAL_IS_NULL(lval)) { \ + op = SLOWOP; \ + goto do_op; \ + } \ + slot = JSVAL_TO_INT(lval); \ + obj = fp->varobj; \ + rval = OBJ_GET_SLOT(cx, obj, slot); \ + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + OBJ_SET_SLOT(cx, obj, slot, rval); \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_global_incop; + + case JSOP_INCGVAR: + FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); + case JSOP_DECGVAR: + FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); + case JSOP_GVARINC: + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); + case JSOP_GVARDEC: + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); + +#undef FAST_GLOBAL_INCREMENT_OP + + do_nonint_fast_global_incop: + NONINT_INCREMENT_OP_MIDDLE(); + OBJ_SET_SLOT(cx, obj, slot, 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->interpreted && + !(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->u.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; + } +#ifdef DUMP_CALL_TABLE + LogCall(cx, *vp, argc, vp + 2); +#endif + + /* 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_BRANCH_CALLBACK(cx); + 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_BRANCH_CALLBACK(cx); + 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: + case JSOP_OBJECT: + atom = GET_ATOM(cx, script, pc); + PUSH_OPND(ATOM_KEY(atom)); + obj = NULL; + break; + + case JSOP_REGEXP: + { + JSRegExp *re; + JSObject *funobj; + + /* + * Push a regexp object for the atom mapped by the bytecode at pc, + * cloning the literal's regexp object if necessary, to simulate in + * the pre-compile/execute-later case what ECMA specifies for the + * compile-and-go case: that scanning each regexp literal creates + * a single corresponding RegExp object. + * + * To support pre-compilation transparently, we must handle the + * case where a regexp object literal is used in a different global + * at execution time from the global with which it was scanned at + * compile time. We do this by re-wrapping the JSRegExp private + * data struct with a cloned object having the right prototype and + * parent, and having its own lastIndex property value storage. + * + * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone + * literal objects, we don't want to pay a script prolog execution + * price for all regexp literals in a script (many may not be used + * by a particular execution of that script, depending on control + * flow), so we initialize lazily here. + * + * XXX This code is specific to regular expression objects. If we + * need a similar op for other kinds of object literals, we should + * push cloning down under JSObjectOps and reuse code here. + */ + atom = GET_ATOM(cx, script, pc); + JS_ASSERT(ATOM_IS_OBJECT(atom)); + obj = ATOM_TO_OBJECT(atom); + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); + + re = (JSRegExp *) JS_GetPrivate(cx, obj); + slot = re->cloneIndex; + if (fp->fun) { + /* + * We're in function code, not global or eval code (in eval + * code, JSOP_REGEXP is never emitted). The code generator + * recorded in fp->fun->nregexps the number of re->cloneIndex + * slots that it reserved in the cloned funobj. + */ + funobj = JSVAL_TO_OBJECT(fp->argv[-2]); + slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); + if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) + return JS_FALSE; + if (JSVAL_IS_VOID(rval)) + rval = JSVAL_NULL; + } else { + /* + * We're in global code. The code generator already arranged + * via script->numGlobalVars to reserve a global variable slot + * at cloneIndex. All global variable slots are initialized + * to null, not void, for faster testing in JSOP_*GVAR cases. + */ + rval = fp->vars[slot]; +#ifdef __GNUC__ + funobj = NULL; /* suppress bogus gcc warnings */ +#endif + } + + if (JSVAL_IS_NULL(rval)) { + /* Compute the current global object in obj2. */ + obj2 = fp->scopeChain; + while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) + obj2 = parent; + + /* + * If obj's parent is not obj2, we must clone obj so that it + * has the right parent, and therefore, the right prototype. + * + * Yes, this means we assume that the correct RegExp.prototype + * to which regexp instances (including literals) delegate can + * be distinguished solely by the instance's parent, which was + * set to the parent of the RegExp constructor function object + * when the instance was created. In other words, + * + * (/x/.__parent__ == RegExp.__parent__) implies + * (/x/.__proto__ == RegExp.prototype) + * + * (unless you assign a different object to RegExp.prototype + * at runtime, in which case, ECMA doesn't specify operation, + * and you get what you deserve). + * + * This same coupling between instance parent and constructor + * parent turns up everywhere (see jsobj.c's FindConstructor, + * js_ConstructObject, and js_NewObject). It's fundamental to + * the design of the language when you consider multiple global + * objects and separate compilation and execution, even though + * it is not specified fully in ECMA. + */ + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneRegExpObject(cx, obj, obj2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + rval = OBJECT_TO_JSVAL(obj); + + /* Store the regexp object value in its cloneIndex slot. */ + if (fp->fun) { + if (!JS_SetReservedSlot(cx, funobj, slot, rval)) + return JS_FALSE; + } else { + fp->vars[slot] = rval; + } + } + + 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_GETGVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->nvars); + lval = fp->vars[slot]; + if (JSVAL_IS_NULL(lval)) { + op = JSOP_NAME; + goto do_op; + } + slot = JSVAL_TO_INT(lval); + obj = fp->varobj; + rval = OBJ_GET_SLOT(cx, obj, slot); + PUSH_OPND(rval); + break; + + case JSOP_SETGVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->nvars); + rval = FETCH_OPND(-1); + lval = fp->vars[slot]; + obj = fp->varobj; + if (JSVAL_IS_NULL(lval)) { + /* + * Inline-clone and specialize JSOP_SETNAME code here because + * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] + * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. + */ + atom = GET_ATOM(cx, script, pc); + id = (jsid)atom; + SAVE_SP(fp); + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + STORE_OPND(-1, rval); + } else { + slot = JSVAL_TO_INT(lval); + GC_POKE(cx, obj->slots[slot]); + OBJ_SET_SLOT(cx, obj, slot, rval); + } + break; + + case JSOP_DEFCONST: + case JSOP_DEFVAR: + { + jsatomid atomIndex; + + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + 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, &obj2, &prop); + if (!ok) + goto out; + + /* Bind a variable only if it's not yet defined. */ + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + attrs, &prop); + if (!ok) + goto out; + JS_ASSERT(prop); + obj2 = obj; + } + + /* + * Try to optimize a property we either just created, or found + * directly in the global object, that is permanent, has a slot, + * and has stub getter and setter, into a "fast global" accessed + * by the JSOP_*GVAR opcodes. + */ + if (script->numGlobalVars && + (attrs & JSPROP_PERMANENT) && + obj2 == obj && + OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *) prop; + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && + SPROP_HAS_STUB_GETTER(sprop) && + SPROP_HAS_STUB_SETTER(sprop)) { + /* + * Fast globals use fp->vars to map the global name's + * atomIndex to the permanent fp->varobj slot number, + * tagged as a jsval. The atomIndex for the global's + * name literal is identical to its fp->vars index. + */ + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } + } + + OBJ_DROP_PROPERTY(cx, obj2, prop); + break; + } + + case JSOP_DEFFUN: + { + jsatomid atomIndex; + uintN flags; + + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + 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, NULL, NULL); + 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, + &prop); + if (!ok) + goto out; + if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && + script->numGlobalVars) { + /* + * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals + * use fp->vars to map the global function name's atomIndex to + * its permanent fp->varobj slot number, tagged as a jsval. + */ + sprop = (JSScopeProperty *) prop; + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } + OBJ_DROP_PROPERTY(cx, parent, prop); + 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: + { + jsatomid atomIndex; + + /* + * 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. + */ + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + 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; + parent = fp->varobj; + 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, + &prop); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + if (attrs == 0 && script->numGlobalVars) { + /* + * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals + * use fp->vars to map the global function name's atomIndex to + * its permanent fp->varobj slot number, tagged as a jsval. + */ + sprop = (JSScopeProperty *) prop; + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } + OBJ_DROP_PROPERTY(cx, parent, prop); + 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, NULL, NULL); + 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/dom/js/jsinterp.h b/src/dom/js/jsinterp.h new file mode 100644 index 000000000..81f16d760 --- /dev/null +++ b/src/dom/js/jsinterp.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 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 */ +}; + +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_COMPILING 0x40 /* frame is being used by compiler */ +#define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name + references based on scope chain */ + +#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); + +#ifdef DUMP_CALL_TABLE +# define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15) + +extern JSHashTable *js_CallTable; +extern size_t js_LogCallToSourceLimit; + +extern void js_DumpCallTable(JSContext *cx); +#endif + +/* + * 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, + JSObject **objp, JSProperty **propp); + +extern JSBool +js_Interpret(JSContext *cx, jsval *result); + +JS_END_EXTERN_C + +#endif /* jsinterp_h___ */ diff --git a/src/dom/js/jslibmath.h b/src/dom/js/jslibmath.h new file mode 100644 index 000000000..7cbcf767a --- /dev/null +++ b/src/dom/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/dom/js/jslock.c b/src/dom/js/jslock.c new file mode 100644 index 000000000..e0c87b1fc --- /dev/null +++ b/src/dom/js/jslock.c @@ -0,0 +1,1261 @@ +/* -*- 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; + } + + /* + * If scope->ownercx is not null, it's likely that two contexts not using + * requests nested locks for scope. The first context, cx here, claimed + * scope; the second, scope->ownercx here, re-claimed it because the first + * was not in a request, or was on the same thread. We don't want to keep + * track of such nesting, because it penalizes the common non-nested case. + * Instead of asserting here and silently coping, we simply re-claim scope + * for cx and return. + * + * See http://bugzilla.mozilla.org/show_bug.cgi?id=229200 for a real world + * case where an asymmetric thread model (Mozilla's main thread is known + * to be the only thread that runs the GC) combined with multiple contexts + * per thread has led to such request-less nesting. + */ + if (scope->ownercx) { + JS_ASSERT(scope->u.count == 0); + JS_ASSERT(scope->lock.owner == 0); + scope->ownercx = cx; + return; + } + + 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/dom/js/jslock.h b/src/dom/js/jslock.h new file mode 100644 index 000000000..9ece59c9b --- /dev/null +++ b/src/dom/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/dom/js/jslocko.asm b/src/dom/js/jslocko.asm new file mode 100644 index 000000000..2589bb7f9 --- /dev/null +++ b/src/dom/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/dom/js/jslog2.c b/src/dom/js/jslog2.c new file mode 100644 index 000000000..113c276d0 --- /dev/null +++ b/src/dom/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/dom/js/jslong.c b/src/dom/js/jslong.c new file mode 100644 index 000000000..76259e502 --- /dev/null +++ b/src/dom/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/dom/js/jslong.h b/src/dom/js/jslong.h new file mode 100644 index 000000000..bde8bfbbf --- /dev/null +++ b/src/dom/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/dom/js/jsmath.c b/src/dom/js/jsmath.c new file mode 100644 index 000000000..9c6fcec0e --- /dev/null +++ b/src/dom/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/dom/js/jsmath.h b/src/dom/js/jsmath.h new file mode 100644 index 000000000..7a6b21657 --- /dev/null +++ b/src/dom/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/dom/js/jsnum.c b/src/dom/js/jsnum.c new file mode 100644 index 000000000..9de1d72db --- /dev/null +++ b/src/dom/js/jsnum.c @@ -0,0 +1,1148 @@ +/* -*- 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 +#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) +{ + char thousandsLength, decimalLength; + const char *numGrouping, *tmpGroup; + JSRuntime *rt; + JSString *numStr, *str; + char *num, *buf, *dec, *end, *tmpSrc, *tmpDest; + int digits, size, remainder, nrepeat; + + /* + * Create the string, move back to bytes to make string twiddling + * a bit easier and so we can insert platform charset seperators. + */ + if (!num_toString(cx, obj, 0, argv, rval)) + return JS_FALSE; + JS_ASSERT(JSVAL_IS_STRING(*rval)); + numStr = JSVAL_TO_STRING(*rval); + num = js_GetStringBytes(numStr); + + /* Find bit before the decimal. */ + dec = strchr(num, '.'); + digits = dec ? dec - num : (int)strlen(num); + end = num + digits; + + rt = cx->runtime; + thousandsLength = strlen(rt->thousandsSeparator); + decimalLength = strlen(rt->decimalSeparator); + + /* Figure out how long resulting string will be. */ + size = digits + (dec ? decimalLength + strlen(dec + 1) : 0); + + numGrouping = tmpGroup = rt->numGrouping; + remainder = digits; + if (*num == '-') + remainder--; + + while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { + if (*tmpGroup >= remainder) + break; + size += thousandsLength; + remainder -= *tmpGroup; + tmpGroup++; + } + if (*tmpGroup == '\0' && *numGrouping != '\0') { + nrepeat = (remainder - 1) / tmpGroup[-1]; + size += thousandsLength * nrepeat; + remainder -= nrepeat * tmpGroup[-1]; + } else { + nrepeat = 0; + } + tmpGroup--; + + buf = (char *)JS_malloc(cx, size + 1); + if (!buf) + return JS_FALSE; + + tmpDest = buf; + tmpSrc = num; + + while (*tmpSrc == '-' || remainder--) + *tmpDest++ = *tmpSrc++; + while (tmpSrc < end) { + strcpy(tmpDest, rt->thousandsSeparator); + tmpDest += thousandsLength; + memcpy(tmpDest, tmpSrc, *tmpGroup); + tmpDest += *tmpGroup; + tmpSrc += *tmpGroup; + if (--nrepeat < 0) + tmpGroup--; + } + + if (dec) { + strcpy(tmpDest, rt->decimalSeparator); + tmpDest += decimalLength; + strcpy(tmpDest, dec + 1); + } else { + *tmpDest++ = '\0'; + } + + str = JS_NewString(cx, buf, size); + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + + return JS_TRUE; +} + +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 XP_OS2) && \ + !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; + struct lconv *locale; + + 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; + + locale = localeconv(); + rt->thousandsSeparator = + JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); + rt->decimalSeparator = + JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); + rt->numGrouping = + JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); + + return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; +} + +void +js_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; + + JS_free(cx, (void *)rt->thousandsSeparator); + JS_free(cx, (void *)rt->decimalSeparator); + JS_free(cx, (void *)rt->numGrouping); + rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; +} + +JSObject * +js_InitNumberClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *ctor; + JSRuntime *rt; + + /* XXX must do at least once per new thread, so do it per JSContext... */ + FIX_FPU(); + + if (!JS_DefineFunctions(cx, obj, number_functions)) + return NULL; + + proto = JS_InitClass(cx, obj, NULL, &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; + + if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { + *rval = INT_TO_JSVAL(i); + } else { + if (!js_NewDoubleValue(cx, d, rval)) + return JS_FALSE; + } + return JS_TRUE; +} + +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/dom/js/jsnum.h b/src/dom/js/jsnum.h new file mode 100644 index 000000000..28f40e6ec --- /dev/null +++ b/src/dom/js/jsnum.h @@ -0,0 +1,257 @@ +/* -*- 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); + +JS_END_EXTERN_C + +#endif /* jsnum_h___ */ diff --git a/src/dom/js/jsobj.c b/src/dom/js/jsobj.c new file mode 100644 index 000000000..847040c61 --- /dev/null +++ b/src/dom/js/jsobj.c @@ -0,0 +1,4066 @@ +/* -*- 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 (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; + } + } + +#ifdef DUMP_CALL_TABLE + if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { + const char *classname = OBJ_GET_CLASS(cx, obj)->name; + size_t classnchars = strlen(classname); + static const char classpropid[] = "C"; + const char *cp; + size_t onchars = nchars; + + /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ + classnchars += sizeof classpropid - 1 + 2 + 2; + if (ida->length) + classnchars += 2; + + /* 2 for the braces, 1 for the terminator */ + chars = (jschar *) + realloc((ochars = chars), + (nchars + classnchars + 2 + 1) * sizeof(jschar)); + if (!chars) { + free(ochars); + goto error; + } + + chars[nchars++] = '{'; /* 1 from the 2 braces */ + for (cp = classpropid; *cp; cp++) + chars[nchars++] = (jschar) *cp; + chars[nchars++] = ':'; + chars[nchars++] = ' '; /* 2 for ': ' */ + chars[nchars++] = '"'; + for (cp = classname; *cp; cp++) + chars[nchars++] = (jschar) *cp; + chars[nchars++] = '"'; /* 2 quotes */ + if (ida->length) { + chars[nchars++] = ','; + chars[nchars++] = ' '; /* 2 for ', ' */ + } + + JS_ASSERT(nchars - onchars == 1 + classnchars); + } else +#endif + 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. Also, negative + * integer ids must be quoted. + */ + if (atom + ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr)) + : JSVAL_TO_INT(id) < 0) { + 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); +#ifdef DUMP_CALL_TABLE + if (outermost && nchars >= js_LogCallToSourceLimit) + break; +#endif + } + } + + 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; + } + + 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; +} + +#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 (!js_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); + js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); + return ok; +} + +static JSBool +obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *funobj; + JSFunction *fun; + jsval userid, value; + jsid propid; + uintN attrs; + + if (JSVAL_IS_FUNCTION(cx, argv[1])) { + funobj = JSVAL_TO_OBJECT(argv[1]); + } else { + fun = js_ValueToFunction(cx, &argv[1], 0); + if (!fun) + return JS_FALSE; + funobj = fun->object; + } + argv[1] = OBJECT_TO_JSVAL(funobj); + + /* 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, funobj); +} + +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; + 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, NULL, NULL)) + 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_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, + NULL); +} + +static JSBool +obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval fval, junk; + jsid id; + 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, NULL, NULL)) + 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_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, + NULL); +} + +static JSBool +obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + if (sprop->attrs & JSPROP_GETTER) + *rval = OBJECT_TO_JSVAL(sprop->getter); + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + return JS_TRUE; +} + +static JSBool +obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsid id; + JSObject *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + + if (!JS_ValueToId(cx, argv[0], &id)) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + if (OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *) prop; + if (sprop->attrs & JSPROP_SETTER) + *rval = OBJECT_TO_JSVAL(sprop->setter); + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + 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->interpreted); + 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; + JSClass *protoclasp; + 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. We assume that if prototype and object are of the + * same class, they always have the same number of computed reserved + * slots (returned via clasp->reserveSlots); otherwise, prototype and + * object classes must have the same (null or not) reserveSlots hook. + */ + if (proto && + (map = proto->map)->ops == ops && + ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || + (!((protoclasp->flags ^ clasp->flags) & + (JSCLASS_HAS_PRIVATE | + (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && + protoclasp->reserveSlots == clasp->reserveSlots))) + { + /* 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) { + JS_KEEP_ATOMS(cx->runtime); + cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData); + JS_UNKEEP_ATOMS(cx->runtime); + } + + return obj; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +static JSBool +FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) +{ + JSAtom *atom; + JSObject *obj, *pobj; + JSProperty *prop; + JSScopeProperty *sprop; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + + if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { + /* Find the topmost object in the scope chain. */ + do { + obj = start; + start = OBJ_GET_PARENT(cx, obj); + } while (start); + } else { + obj = cx->globalObject; + if (!obj) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + } + + JS_ASSERT(OBJ_IS_NATIVE(obj)); + if (!js_LookupPropertyWithFlags(cx, obj, (jsid)atom, JSRESOLVE_CLASSNAME, + &pobj, &prop +#if defined JS_THREADSAFE && defined DEBUG + , __FILE__, __LINE__ +#endif + )) { + return JS_FALSE; + } + if (!prop) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JS_ASSERT(OBJ_IS_NATIVE(pobj)); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, prop); + 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; + JSClass *clasp; + uint32 nslots, i; + jsval *newslots; + + map = obj->map; + JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); + clasp = LOCKED_OBJ_GET_CLASS(obj); + if (map->freeslot == JSSLOT_FREE(clasp)) { + /* Adjust map->freeslot to include computed reserved slots, if any. */ + if (clasp->reserveSlots) + map->freeslot += clasp->reserveSlots(cx, obj); + } + nslots = map->nslots; + if (map->freeslot >= nslots) { + nslots = map->freeslot; + JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); + nslots += (nslots + 1) / 2; + + newslots = (jsval *) + JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + 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; + 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; + + newslots = (jsval *) + JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + 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; + JSProperty *prop; + 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, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + 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 (prop) { + /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = 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; +} + +/* + * Given pc pointing after a property accessing bytecode, return true if the + * access is a "object-detecting" in the sense used by web pages, e.g., when + * checking whether document.all is defined. + */ +static JSBool +Detecting(JSContext *cx, jsbytecode *pc) +{ + JSScript *script; + jsbytecode *endpc; + JSOp op; + JSAtom *atom; + + script = cx->fp->script; + for (endpc = script->code + script->length; pc < endpc; pc++) { + /* General case: a branch or equality op follows the access. */ + op = (JSOp) *pc; + if (js_CodeSpec[op].format & JOF_DETECTING) + return JS_TRUE; + + /* + * Special case #1: handle (document.all == null). Don't sweat about + * JS1.2's revision of the equality operators here. + */ + if (op == JSOP_NULL) { + if (++pc < endpc) + return *pc == JSOP_EQ || *pc == JSOP_NE; + break; + } + + /* + * Special case #2: handle (document.all == undefined). Don't worry + * about someone redefining undefined, which was added by Edition 3, + * so was read/write for backward compatibility. + */ + if (op == JSOP_NAME) { + atom = GET_ATOM(cx, script, pc); + if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && + (pc += js_CodeSpec[op].length) < endpc) { + op = (JSOp) *pc; + return op == JSOP_EQ || op == JSOP_NE || + op == JSOP_NEW_EQ || op == JSOP_NEW_NE; + } + break; + } + + /* At this point, anything but grouping means we're not detecting. */ + if (op != JSOP_GROUP) + break; + } + return JS_FALSE; +} + +JSBool +js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#endif + ) +{ + JSObject *start, *obj2, *proto; + JSScope *scope; + JSScopeProperty *sprop; + JSClass *clasp; + JSResolveOp resolve; + JSResolvingKey key; + JSResolvingEntry *entry; + uint32 generation; + JSNewResolveOp newresolve; + jsbytecode *pc; + const JSCodeSpec *cs; + 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 (!js_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; + if (cx->fp && (pc = cx->fp->pc)) { + cs = &js_CodeSpec[*pc]; + format = cs->format; + if ((format & JOF_MODEMASK) != JOF_NAME) + flags |= JSRESOLVE_QUALIFIED; + if ((format & JOF_ASSIGNING) || + (cx->fp->flags & JSFRAME_ASSIGNING)) { + flags |= JSRESOLVE_ASSIGNING; + } else { + pc += cs->length; + if (Detecting(cx, pc)) + flags |= JSRESOLVE_DETECTING; + } + if (format & JOF_DECLARING) + flags |= JSRESOLVE_DECLARING; + } + 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: + js_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; +} + +#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) +{ + return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp, file, line); +} +#else +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); +} +#endif + +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; + JSProperty *prop; + JSScope *scope; + JSScopeProperty *sprop; + 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, &prop)) + return JS_FALSE; + if (!prop) { + 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; + JSString *str; + + /* Kludge to allow (typeof foo == "undefined") tests. */ + JS_ASSERT(cx->fp->script); + pc = cx->fp->pc; + pc += js_CodeSpec[*pc].length; + if (Detecting(cx, pc)) + return JS_TRUE; + + /* Ok, bad undefined property reference: whine about it. */ + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + 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, prop); + return OBJ_GET_PROPERTY(cx, obj2, id, vp); + } + + /* Unlock obj2 before calling getter, relock after to avoid deadlock. */ + scope = OBJ_SCOPE(obj2); + sprop = (JSScopeProperty *) prop; + 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; + JSProperty *prop; + 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, &prop)) + return JS_FALSE; + + if (prop && !OBJ_IS_NATIVE(pobj)) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = NULL; + } + sprop = (JSScopeProperty *) prop; + + /* + * Now either sprop is null, meaning id was not found in obj or one of its + * prototypes; or sprop is non-null, meaning id was found in pobj's scope. + * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop + * is held: we must 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 (( +#ifdef DUMP_CALL_TABLE + (cx->options & JSOPTION_LOGCALL_TOSOURCE) || +#endif + (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 (( +#ifdef DUMP_CALL_TABLE + (cx->options & JSOPTION_LOGCALL_TOSOURCE) || +#endif + (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; + } +} + +#ifdef NARCISSUS +static JSBool +GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) +{ + JSObject *tmp; + jsval xcval; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!OBJ_GET_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.ExecutionContextAtom, + &xcval)) { + return JS_FALSE; + } + if (JSVAL_IS_PRIMITIVE(xcval)) { + JS_ReportError(cx, "invalid ExecutionContext in global object"); + return JS_FALSE; + } + if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), + (jsid)cx->runtime->atomState.currentAtom, + rval)) { + return JS_FALSE; + } + return JS_TRUE; +} +#endif + +JSBool +js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); + if (!clasp->call) { +#ifdef NARCISSUS + JSObject *callee, *args; + jsval fval, nargv[3]; + JSBool ok; + + callee = JSVAL_TO_OBJECT(argv[-2]); + if (!OBJ_GET_PROPERTY(cx, callee, + (jsid)cx->runtime->atomState.callAtom, + &fval)) { + return JS_FALSE; + } + if (JSVAL_IS_FUNCTION(cx, fval)) { + if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) + return JS_FALSE; + args = js_GetArgsObject(cx, cx->fp); + if (!args) + return JS_FALSE; + nargv[0] = OBJECT_TO_JSVAL(obj); + nargv[1] = OBJECT_TO_JSVAL(args); + return js_InternalCall(cx, callee, fval, 3, nargv, rval); + } + if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) { + argv[-2] = fval; + ok = js_Call(cx, obj, argc, argv, rval); + argv[-2] = OBJECT_TO_JSVAL(callee); + return ok; + } +#endif + 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) { +#ifdef NARCISSUS + JSObject *callee, *args; + jsval cval, nargv[2]; + JSBool ok; + + callee = JSVAL_TO_OBJECT(argv[-2]); + if (!OBJ_GET_PROPERTY(cx, callee, + (jsid)cx->runtime->atomState.constructAtom, + &cval)) { + return JS_FALSE; + } + if (JSVAL_IS_FUNCTION(cx, cval)) { + if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) + return JS_FALSE; + args = js_GetArgsObject(cx, cx->fp); + if (!args) + return JS_FALSE; + nargv[0] = OBJECT_TO_JSVAL(args); + return js_InternalCall(cx, callee, cval, 2, nargv, rval); + } + if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) { + argv[-2] = cval; + ok = js_Call(cx, obj, argc, argv, rval); + argv[-2] = OBJECT_TO_JSVAL(callee); + return ok; + } +#endif + ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT); + return JS_FALSE; + } + return clasp->construct(cx, obj, argc, argv, rval); +} + +JSBool +js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSClass *clasp; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->hasInstance) + return clasp->hasInstance(cx, obj, v, bp); +#ifdef NARCISSUS + { + jsval fval, rval; + + if (!OBJ_GET_PROPERTY(cx, obj, + (jsid)cx->runtime->atomState.hasInstanceAtom, + &fval)) { + return JS_FALSE; + } + if (JSVAL_IS_FUNCTION(cx, fval)) { + return js_InternalCall(cx, obj, fval, 1, &v, &rval) && + js_ValueToBoolean(cx, rval, bp); + } + } +#endif + *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 DontDelete prototype (which may be + * reset), while native or "system" constructors have 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)) { + JS_ClearPendingException(cx); + ok = JS_TRUE; + } else if (!JSVAL_IS_PRIMITIVE(fval)) { + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + if (!ok) + JS_ClearPendingException(cx); + } 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(scope->map.freeslot, scope->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; +} + +JSBool +js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) +{ + JSScope *scope; + uint32 nslots, i; + JSClass *clasp; + jsval *newslots; + + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + nslots = (uint32) obj->slots[-1]; + if (slot >= nslots) { + /* + * At this point, obj may or may not own scope. If some path calls + * js_GetMutableScope but does not add a slot-owning property, then + * scope->object == obj but nslots will be nominal. If obj shares a + * prototype's scope, then we cannot update scope->map here, but we + * must update obj->slots[-1] when we grow obj->slots. + * + * See js_Mark, before the last return, where we make a special case + * for unmutated (scope->object != obj) objects. + */ + JS_ASSERT(nslots == JS_INITIAL_NSLOTS); + clasp = LOCKED_OBJ_GET_CLASS(obj); + nslots = JSSLOT_FREE(clasp); + if (clasp->reserveSlots) + nslots += clasp->reserveSlots(cx, obj); + JS_ASSERT(slot < nslots); + + newslots = (jsval *) + JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + if (!newslots) { + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE; + } + for (i = 1 + newslots[0]; i <= nslots; i++) + newslots[i] = JSVAL_VOID; + if (scope->object == obj) + scope->map.nslots = nslots; + newslots[0] = nslots; + obj->slots = newslots + 1; + } + + /* Whether or not we grew nslots, we may need to advance freeslot. */ + if (scope->object == obj && slot >= scope->map.freeslot) + scope->map.freeslot = slot + 1; + + obj->slots[slot] = v; + JS_UNLOCK_SCOPE(cx, scope); + return JS_TRUE; +} + +#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/dom/js/jsobj.h b/src/dom/js/jsobj.h new file mode 100644 index 000000000..9ac5857e0 --- /dev/null +++ b/src/dom/js/jsobj.h @@ -0,0 +1,475 @@ +/* -*- 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) \ + : JS_TRUE) + +/* + * 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 + +/* + * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. + */ +extern JSBool +js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, + JSObject **objp, JSProperty **propp +#if defined JS_THREADSAFE && defined DEBUG + , const char *file, uintN line +#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 JSBool +js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); + +JS_END_EXTERN_C + +#endif /* jsobj_h___ */ diff --git a/src/dom/js/jsopcode.c b/src/dom/js/jsopcode.c new file mode 100644 index 000000000..e751103a0 --- /dev/null +++ b/src/dom/js/jsopcode.c @@ -0,0 +1,2730 @@ +/* -*- 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 "jsregexp.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; + ptrdiff_t len, off, jmplen; + uint32 type; + 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 = (ptrdiff_t) cs->length; + fprintf(fp, "%05u:", loc); + if (lines) + fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); + fprintf(fp, " %s", cs->name); + type = cs->format & JOF_TYPEMASK; + switch (type) { + case JOF_BYTE: + if (op == JSOP_TRAP) { + op = JS_GetTrapOpcode(cx, script, pc); + if (op == JSOP_LIMIT) + return 0; + len = (ptrdiff_t) 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: + case JOF_TABLESWITCHX: + { + jsbytecode *pc2; + jsint i, low, high; + + jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); + for (i = low; i <= high; i++) { + off = GetJumpOffset(pc, pc2); + fprintf(fp, "\n\t%d: %d", i, off); + pc2 += jmplen; + } + len = 1 + pc2 - pc; + break; + } + + case JOF_LOOKUPSWITCH: + case JOF_LOOKUPSWITCHX: + { + jsbytecode *pc2; + jsatomid npairs; + + jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + npairs = GET_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 += jmplen; + + 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 */ + JSPackedBool pretty; /* pretty-print: indent, use newlines */ + JSPackedBool grouped; /* in parenthesized expression context */ + JSScript *script; /* script being printed */ + JSScope *scope; /* script function scope */ +}; + +/* + * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters + * to functions such as js_DecompileFunction and js_NewPrinter. This time, as + * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a + * uintN is at least 32 bits. + */ +#define JS_IN_GROUP_CONTEXT 0x10000 + +JSPrinter * +js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) +{ + JSPrinter *jp; + + 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 & ~JS_IN_GROUP_CONTEXT; + jp->pretty = pretty; + jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; + jp->script = NULL; + jp->scope = NULL; + 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; + JSAtom *label; + jsint order; /* source order for stable tableswitch sort */ +} TableEntry; + +static int +CompareOffsets(const void *v1, const void *v2, void *arg) +{ + const TableEntry *te1 = (const TableEntry *) v1, + *te2 = (const TableEntry *) v2; + + if (te1->offset == te2->offset) + return (int) (te1->order - te2->order); + return (int) (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; + off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength; + + key = table[i].key; + if (isCondSwitch) { + ptrdiff_t nextCaseExprOff; + + /* + * key encodes the JSOP_CASE bytecode's offset from switchtop. + * The next case expression follows immediately, unless we are + * at the last case. + */ + nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); + nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; + jp->indent += 2; + if (!Decompile(ss, pc + caseExprOff, + nextCaseExprOff - caseExprOff)) { + 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. But if table[i].label + * is non-null, key was constant-propagated and label is the + * name of the const we should show as the case label. We set + * key to undefined so this identifier is escaped, if required + * by non-ASCII characters, but not quoted, by QuoteString. + */ + if (table[i].label) { + str = ATOM_TO_STRING(table[i].label); + key = JSVAL_VOID; + } else { + str = js_ValueToString(cx, key); + if (!str) + return JS_FALSE; + } + rval = QuoteString(&ss->sprinter, str, + JSVAL_IS_STRING(key) ? (jschar)'"' : 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + jp->indent += 2; + js_printf(jp, "\tcase %s:\n", rval); + } + + jp->indent += 2; + if (off <= defaultOffset && defaultOffset < off2) { + diff = defaultOffset - off; + if (diff != 0) { + if (!Decompile(ss, pc + off, diff)) + 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 tail, todo, len, oplen, cond, next; + 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; + static const char catch_cookie[] = "/*CATCH*/"; + static const char with_cookie[] = "/*WITH*/"; + +/* + * 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 + +/* + * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to + * common ATOM_TO_STRING(atom) here and near the call sites. + */ +#define ATOM_IS_IDENTIFIER(atom) \ + (!ATOM_KEYWORD(atom) && js_IsIdentifier(ATOM_TO_STRING(atom))) + +/* + * 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_IS_IDENTIFIER(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; + tail = -1; + todo = -2; /* NB: different from Sprint() error return. */ + 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_printf(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; + todo = Sprint(&ss->sprinter, catch_cookie); + 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*/"; + + 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. + */ + LOCAL_ASSERT(ss->top >= (uintN) GET_ATOM_INDEX(pc)); + 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; + + case JSOP_ENTERWITH: + JS_ASSERT(!js_GetSrcNote(jp->script, pc)); + rval = POP_STR(); + js_printf(jp, "\twith (%s) {\n", rval); + jp->indent += 4; + todo = Sprint(&ss->sprinter, with_cookie); + break; + + case JSOP_LEAVEWITH: + sn = js_GetSrcNote(jp->script, pc); + todo = -2; + if (sn && SN_TYPE(sn) == SRC_HIDDEN) + break; + rval = POP_STR(); + if (sn && SN_TYPE(sn) == SRC_CATCH) { + LOCAL_ASSERT(strcmp(rval, catch_cookie) == 0); + LOCAL_ASSERT((uintN) js_GetSrcNoteOffset(sn, 0) == ss->top); + break; + } + LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); + jp->indent -= 4; + js_printf(jp, "\t}\n"); + break; + + case JSOP_SETRVAL: + case JSOP_RETURN: + lval = js_CodeSpec[JSOP_RETURN].name; + rval = POP_STR(); + if (*rval != '\0') + js_printf(jp, "\t%s %s;\n", lval, rval); + else + js_printf(jp, "\t%s;\n", lval); + 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_IS_IDENTIFIER(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: + case JSOP_SETGVAR: + 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: + case JSOP_INCGVAR: + case JSOP_DECGVAR: + 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: + case JSOP_GVARINC: + case JSOP_GVARDEC: + 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: + case JSOP_GETGVAR: + 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_REGEXP: + case JSOP_ANONFUNOBJ: + case JSOP_NAMEDFUNOBJ: + atom = GET_ATOM(cx, jp->script, pc); + if (op == JSOP_OBJECT || op == JSOP_REGEXP) { + if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, + &val)) { + return JS_FALSE; + } + } else { + if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), + (pc + len < endpc && + pc[len] == JSOP_GROUP) + ? JS_IN_GROUP_CONTEXT | + JS_DONT_PRETTY_PRINT + : 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 jmplen, 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); + jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + + n = high - low + 1; + if (n == 0) { + table = NULL; + j = 0; + } else { + table = (TableEntry *) + JS_malloc(cx, (size_t)n * sizeof *table); + if (!table) + return JS_FALSE; + for (i = j = 0; i < n; i++) { + table[j].label = NULL; + off2 = GetJumpOffset(pc, pc2); + if (off2) { + sn = js_GetSrcNote(jp->script, pc2); + if (sn) { + JS_ASSERT(SN_TYPE(sn) == SRC_LABEL); + table[j].label = + js_GetAtom(cx, &jp->script->atomMap, + (jsatomid) + js_GetSrcNoteOffset(sn, 0)); + } + table[j].key = INT_TO_JSVAL(low + i); + table[j].offset = off2; + table[j].order = j; + j++; + } + pc2 += jmplen; + } + js_HeapSort(table, (size_t) j, sizeof(TableEntry), + 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 jmplen, off, off2; + jsatomid npairs, k; + TableEntry *table; + + sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + len = js_GetSrcNoteOffset(sn, 0); + jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN + : JUMPX_OFFSET_LEN; + pc2 = pc; + off = GetJumpOffset(pc, pc2); + pc2 += jmplen; + npairs = GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + + table = (TableEntry *) + JS_malloc(cx, (size_t)npairs * sizeof *table); + if (!table) + return JS_FALSE; + for (k = 0; k < npairs; k++) { + sn = js_GetSrcNote(jp->script, pc2); + if (sn) { + JS_ASSERT(SN_TYPE(sn) == SRC_LABEL); + table[k].label = + js_GetAtom(cx, &jp->script->atomMap, (jsatomid) + js_GetSrcNoteOffset(sn, 0)); + } else { + table[k].label = NULL; + } + atom = GET_ATOM(cx, jp->script, pc2); + pc2 += ATOM_INDEX_LEN; + off2 = GetJumpOffset(pc, pc2); + pc2 += jmplen; + table[k].key = ATOM_KEY(atom); + table[k].offset = off2; + } + + ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, + JS_FALSE); + 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_IS_IDENTIFIER(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 ATOM_IS_IDENTIFIER +#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; + + if (!fun->interpreted) { + js_printf(jp, native_code_str); + return JS_TRUE; + } + script = fun->u.script; + 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 (!jp->grouped && (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->interpreted && 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->interpreted && fun->object) { + oldscope = jp->scope; + jp->scope = scope; + ok = js_DecompileScript(jp, fun->u.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 (!jp->grouped && (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->interpreted)); + 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; + } + + name = NULL; + jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); + if (jp) { + if (fp->fun && fp->fun->object) { + JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); + jp->scope = OBJ_SCOPE(fp->fun->object); + } + if (js_DecompileCode(jp, script, begin, len)) + name = js_GetPrinterOutput(jp); + } + js_DestroyPrinter(jp); + if (tmp) + JS_free(cx, tmp); + return name; + + do_fallback: + return fallback ? fallback : js_ValueToString(cx, v); +} diff --git a/src/dom/js/jsopcode.h b/src/dom/js/jsopcode.h new file mode 100644 index 000000000..01a6d46ac --- /dev/null +++ b/src/dom/js/jsopcode.h @@ -0,0 +1,275 @@ +/* -*- 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_DETECTING 0x2000 /* object detection flag for JSNewResolveOp */ +#define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ +#define JOF_LEFTASSOC 0x8000 /* left-associative operator */ +#define JOF_DECLARING 0x10000 /* var, const, or function declaration op */ + +#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/dom/js/jsopcode.tbl b/src/dom/js/jsopcode.tbl new file mode 100644 index 000000000..c4996f349 --- /dev/null +++ b/src/dom/js/jsopcode.tbl @@ -0,0 +1,344 @@ +/* -*- 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|JOF_DETECTING) +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|JOF_DETECTING) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +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|JOF_DETECTING) +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|JOF_DETECTING) +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|JOF_DETECTING) +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|JOF_DETECTING) +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|JOF_DETECTING) +OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_DETECTING) + +/* 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|JOF_DETECTING) +OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) + +/* 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|JOF_DETECTING) + +/* 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|JOF_DETECTING) + +/* 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|JOF_DECLARING) +OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME|JOF_DECLARING) +OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME|JOF_DECLARING) + +/* 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|JOF_DECLARING) + +/* Extended jumps. */ +OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) +OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) +OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING) +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) + +/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ +OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME) +OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) +OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC) +OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) + +/* Regular expression literal requiring special fork-on-exec handling. */ +OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 12, JOF_CONST) diff --git a/src/dom/js/jsosdep.h b/src/dom/js/jsosdep.h new file mode 100644 index 000000000..c93eee2f1 --- /dev/null +++ b/src/dom/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/dom/js/jsotypes.h b/src/dom/js/jsotypes.h new file mode 100644 index 000000000..ad8b5203e --- /dev/null +++ b/src/dom/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/dom/js/jsparse.c b/src/dom/js/jsparse.c new file mode 100644 index 000000000..6b3f7600c --- /dev/null +++ b/src/dom/js/jsparse.c @@ -0,0 +1,3635 @@ +/* -*- 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; + left->pn_pos.end = right->pn_pos.end; + 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; + uint32 flags; + 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; + } + flags = cx->fp->flags; + cx->fp->flags = flags | + (JS_HAS_COMPILE_N_GO_OPTION(cx) + ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO + : JSFRAME_COMPILING); + + /* 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->flags = flags; + 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). + */ +#define ENDS_IN_OTHER 0 +#define ENDS_IN_RETURN 1 +#define ENDS_IN_BREAK 2 + +static int +HasFinalReturn(JSParseNode *pn) +{ + uintN rv, rv2, hasDefault; + JSParseNode *pn2, *pn3; + + switch (pn->pn_type) { + case TOK_LC: + if (!pn->pn_head) + return ENDS_IN_OTHER; + return HasFinalReturn(PN_LAST(pn)); + + case TOK_IF: + rv = HasFinalReturn(pn->pn_kid2); + if (pn->pn_kid3) + rv &= HasFinalReturn(pn->pn_kid3); + return rv; + +#if JS_HAS_SWITCH_STATEMENT + case TOK_SWITCH: + rv = ENDS_IN_RETURN; + hasDefault = ENDS_IN_OTHER; + for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_DEFAULT) + hasDefault = ENDS_IN_RETURN; + pn3 = pn2->pn_right; + JS_ASSERT(pn3->pn_type == TOK_LC); + if (pn3->pn_head) { + rv2 = HasFinalReturn(PN_LAST(pn3)); + if (rv2 == ENDS_IN_OTHER && pn2->pn_next) + /* Falling through to next case or default. */; + else + rv &= rv2; + } + } + /* If a final switch has no default case, we judge it harshly. */ + rv &= hasDefault; + return rv; +#endif /* JS_HAS_SWITCH_STATEMENT */ + + case TOK_BREAK: + return ENDS_IN_BREAK; + + case TOK_WITH: + return HasFinalReturn(pn->pn_right); + + case TOK_RETURN: + return ENDS_IN_RETURN; + +#if JS_HAS_EXCEPTIONS + case TOK_THROW: + return ENDS_IN_RETURN; + + case TOK_TRY: + /* If we have a finally block that returns, we are done. */ + if (pn->pn_kid3) { + rv = HasFinalReturn(pn->pn_kid3); + if (rv == ENDS_IN_RETURN) + return rv; + } + + /* Else check the try block and any and all catch statements. */ + rv = HasFinalReturn(pn->pn_kid1); + if (pn->pn_kid2) + rv &= HasFinalReturn(pn->pn_kid2); + return rv; + + case TOK_CATCH: + /* Check this block's code and iterate over further catch blocks. */ + rv = HasFinalReturn(pn->pn_kid3); + for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2) + rv &= HasFinalReturn(pn2->pn_kid3); + return rv; +#endif + + default: + return ENDS_IN_OTHER; + } +} + +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) == ENDS_IN_RETURN || 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; + frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO); + 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. */ + 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; + frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) + ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO + : JSFRAME_COMPILING; + 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->u.script = js_NewScriptFromCG(cx, &funcg, fun); + if (!fun->u.script) { + ok = JS_FALSE; + } else { + fun->interpreted = JS_TRUE; + 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) +{ + JSOp op, prevop; + JSParseNode *pn, *body; + JSAtom *funAtom, *argAtom; + JSStackFrame *fp; + JSObject *varobj, *pobj; + JSAtomListElement *ale; + JSProperty *prop; + JSFunction *fun; + uintN dupflag; + JSBool ok; + JSTreeContext funtc; + + /* Make a TOK_FUNCTION node. */ +#if JS_HAS_GETTER_SETTER + op = CURRENT_TOKEN(ts).t_op; +#endif + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc); + if (!pn) + return NULL; + + /* Scan the optional function name into funAtom. */ + funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL; + + /* Find the nearest variable-declaring scope and use it as our parent. */ + fp = cx->fp; + varobj = fp->varobj; + + /* + * 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)) { + /* + * 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! + */ + JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); + JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); + if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop)) + return NULL; + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!prop || pobj != varobj) { + if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom, + JSVAL_VOID, + js_GetLocalVariable, + js_SetLocalVariable, + JSPROP_ENUMERATE | JSPROP_SHARED, + SPROP_HAS_SHORTID, fp->fun->nvars, + NULL)) { + return NULL; + } + fp->fun->nvars++; + } + } +#endif + } + + fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, + 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, + &prop)) { + return NULL; + } + dupflag = 0; + if (prop) { + ok = JS_TRUE; + if (pobj == fun->object && + ((JSScopeProperty *) prop)->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, prop); + if (!ok) + return NULL; + prop = 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 + +#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; + + /* + * Absent use of the new scoped local GC roots API around compiler calls, + * we need to atomize here to protect against a GC activation. Atoms are + * protected from GC during compilation by the JS_FRIEND_API entry points + * in this file. There doesn't seem to be any gain in switching from the + * atom-keeping method to the bulkier, slower scoped local roots method. + */ + 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_OPERAND; + while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { + ts->flags &= ~TSF_OPERAND; + pn2 = Statement(cx, ts, tc); + if (!pn2) + return NULL; + ts->flags |= TSF_OPERAND; + + /* 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_OPERAND; + 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_OPERAND; + while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { + ts->flags &= ~TSF_OPERAND; + 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_OPERAND; + } + ts->flags &= ~TSF_OPERAND; + 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; + + CHECK_RECURSION(); + + ts->flags |= TSF_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + +#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; + if (cx->version != JSVERSION_ECMA_3) { + /* + * All legacy and extended versions must do automatic semicolon + * insertion after do-while. See the testcase and discussion in + * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. + */ + (void) js_MatchToken(cx, ts, TOK_SEMI); + return pn; + } + 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); + ts->flags |= TSF_OPERAND; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + 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) that pn1 is part of a for/in. */ + pn1->pn_extra |= PNX_FORINVAR; + + /* Generate a final POP only if the var has an initializer. */ + pn2 = pn1->pn_head; + if (pn2->pn_expr) + pn1->pn_extra |= PNX_POPVAR; + } 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); + ts->flags |= TSF_OPERAND; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + if (tt == 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); + ts->flags |= TSF_OPERAND; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + if (tt == 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; + + /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ + ts->flags |= TSF_OPERAND; + tt = js_PeekTokenSameLine(cx, ts); + ts->flags &= ~TSF_OPERAND; + 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_SYNTAX_ERROR); + 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: + if (!js_ReportCompileErrorNumber(cx, ts, NULL, + JSREPORT_WARNING | JSREPORT_STRICT, + JSMSG_DEPRECATED_USAGE, + js_with_statement_str)) { + return NULL; + } + + 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); + + 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 |= PNX_POPVAR; + 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_OPERAND; + tt = js_PeekTokenSameLine(cx, ts); + ts->flags &= ~TSF_OPERAND; + 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 = 0; /* 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 (!fun) { + prop = NULL; /* don't lookup global variables at compile time */ + } else { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + return NULL; + } + if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *)prop; + 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_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + + 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_OPERAND; + matched = js_MatchToken(cx, ts, TOK_RP); + ts->flags &= ~TSF_OPERAND; + 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_OPERAND; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + 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 = CURRENT_TOKEN(ts).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 tt switch case + * should set notsharp. + */ +#endif + + CHECK_RECURSION(); + + ts->flags |= TSF_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + +#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 = 0; + +#if JS_HAS_SHARP_VARS + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else +#endif + PN_INIT_LIST(pn); + + ts->flags |= TSF_OPERAND; + matched = js_MatchToken(cx, ts, TOK_RB); + ts->flags &= ~TSF_OPERAND; + if (!matched) { + for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) { + ts->flags |= TSF_OPERAND; + tt = js_PeekToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + if (tt == TOK_RB) { + pn->pn_extra |= PNX_ENDCOMMA; + 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; + } else { + JSAtomListElement *ale; + JSStackFrame *fp; + JSStmtInfo *stmt; + + /* Measure optimizable global variable uses. */ + ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); + if (ale && + !(fp = cx->fp)->fun && + fp->scopeChain == fp->varobj && + !js_InWithStatement(tc) && + !js_InCatchBlock(tc, pn->pn_atom)) { + tc->globalUses++; + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (STMT_IS_LOOP(stmt)) { + tc->loopyGlobalUses++; + break; + } + } + } + } + } + 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/dom/js/jsparse.h b/src/dom/js/jsparse.h new file mode 100644 index 000000000..9ce12b391 --- /dev/null +++ b/src/dom/js/jsparse.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 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 + * pn_val: constant value if lookup or table switch + * 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' + * if TOK_VAR, its pn_extra may have PNX_POPVAR + * and PNX_FORINVAR bits set + * 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: PN_ENDCOMMA 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, or + * JSOP_REGEXP + * 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 0x01 /* TOK_PLUS list has string term */ +#define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */ +#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */ +#define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node, + which is left kid of TOK_FOR */ +#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */ + +/* + * 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/dom/js/jsprf.c b/src/dom/js/jsprf.c new file mode 100644 index 000000000..591313ef7 --- /dev/null +++ b/src/dom/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); +} + +/* +** BuildArgArray 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 improve 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 ) + free( 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 ) + free( 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 ) ) + free( 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 ) ){ + free( 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) +{ + free(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) { + free(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[-1] = '\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) { + free(ss.base); + } + return 0; + } + return ss.base; +} + diff --git a/src/dom/js/jsprf.h b/src/dom/js/jsprf.h new file mode 100644 index 000000000..e8cc46c0c --- /dev/null +++ b/src/dom/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/dom/js/jsprvtd.h b/src/dom/js/jsprvtd.h new file mode 100644 index 000000000..f5f1e77f6 --- /dev/null +++ b/src/dom/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/dom/js/jspubtd.h b/src/dom/js/jspubtd.h new file mode 100644 index 000000000..5f19a15ce --- /dev/null +++ b/src/dom/js/jspubtd.h @@ -0,0 +1,582 @@ +/* -*- 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); + +/* + * The optional JSClass.reserveSlots hook allows a class to make computed + * per-instance object slots reservations, in addition to or instead of using + * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve + * a constant-per-class number of slots. Implementations of this hook should + * return the number of slots to reserve, not including any reserved by using + * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. + * + * NB: called with obj locked by the JSObjectOps-specific mutual exclusion + * mechanism appropriate for obj, so don't nest other operations that might + * also lock obj. + */ +typedef uint32 +(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); + +/* 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 JSBool +(* 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); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, 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/dom/js/jsregexp.c b/src/dom/js/jsregexp.c new file mode 100644 index 000000000..4d909d4e2 --- /dev/null +++ b/src/dom/js/jsregexp.c @@ -0,0 +1,3831 @@ +/* -*- 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) ((unsigned)((op) - REOP_SIMPLE_START) < \ + (unsigned)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 */ + uint16 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; + uint16 flags; + uint16 parenCount; + uint16 classCount; /* number of [] encountered */ + uint16 treeDepth; /* maximum depth of parse tree */ + size_t progLength; /* estimated bytecode length */ + 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 */ + uint16 parenIndex; /* start index of saved paren contents */ + uint16 parenCount; /* # of saved paren contents */ + uint16 saveStateStackTop; /* 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 stateStackLimit; + + REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ + REBackTrackData *backTrackSP; + size_t backTrackStackSize; + 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) { + parenIndex = state->parenCount; + if (state->cp == state->cpend) { + /* + * If we are at the end of the regexp and we're short one or more + * operands, the regexp must have the form /x|/ or some such, with + * left parentheses making us short more than one operand. + */ + 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 + 1 < state->cpend && + *state->cp == '?' && + (state->cp[1] == '=' || + state->cp[1] == '!' || + state->cp[1] == ':')) { + switch (state->cp[1]) { + case '=': + op = REOP_ASSERT; + /* ASSERT, , ... ASSERTTEST */ + state->progLength += 4; + break; + case '!': + op = REOP_ASSERT_NOT; + /* ASSERTNOT, , ... ASSERTNOTTEST */ + state->progLength += 4; + break; + default: + op = REOP_LPARENNON; + break; + } + state->cp += 2; + } else { + op = REOP_LPAREN; + /* LPAREN, , ... RPAREN, */ + state->progLength += 6; + 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--) { + if (i < 0) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + /* 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--) { + if (i < 0) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + ++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; + case '+': + case '*': + case '?': + case '{': + js_ReportCompileErrorNumber(state->context, state->tokenStream, + NULL, JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp); + result = JS_FALSE; + goto out; + 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; +} + +/* + * Hack two bits in CompilerState.flags, for use within FindParenCount to flag + * its being on the stack, and to propagate errors to its callers. + */ +#define JSREG_FIND_PAREN_COUNT 0x8000 +#define JSREG_FIND_PAREN_ERROR 0x4000 + +/* + * Magic return value from FindParenCount and GetDecimalValue, to indicate + * overflow beyond GetDecimalValue's max parameter, or a computed maximum if + * its findMax parameter is non-null. + */ +#define OVERFLOW_VALUE ((uintN)-1) + +static uintN +FindParenCount(CompilerState *state) +{ + CompilerState temp; + int i; + + if (state->flags & JSREG_FIND_PAREN_COUNT) + return OVERFLOW_VALUE; + + /* + * Copy state into temp, flag it so we never report an invalid backref, + * and reset its members to parse the entire regexp. This is obviously + * suboptimal, but GetDecimalValue calls us only if a backref appears to + * refer to a forward parenthetical, which is rare. + */ + temp = *state; + temp.flags |= JSREG_FIND_PAREN_COUNT; + temp.cp = temp.cpbegin; + temp.parenCount = 0; + temp.classCount = 0; + temp.progLength = 0; + temp.treeDepth = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + temp.classCache[i].start = NULL; + + if (!ParseRegExp(&temp)) { + state->flags |= JSREG_FIND_PAREN_ERROR; + return OVERFLOW_VALUE; + } + return temp.parenCount; +} + +/* + * Extract and return a decimal value at state->cp. The initial character c + * has already been read. Return OVERFLOW_VALUE if the result exceeds max. + * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in + * state->flags to discover whether an error occurred under findMax. + */ +static uintN +GetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), + CompilerState *state) +{ + uintN value = JS7_UNDEC(c); + JSBool overflow = (value > max && (!findMax || value > findMax(state))); + + /* The following restriction allows simpler overflow checks. */ + JS_ASSERT(max <= ((uintN)-1 - 9) / 10); + while (state->cp < state->cpend) { + c = *state->cp; + if (!JS7_ISDEC(c)) + break; + value = 10 * value + JS7_UNDEC(c); + if (!overflow && value > max && (!findMax || value > findMax(state))) + overflow = JS_TRUE; + ++state->cp; + } + return overflow ? OVERFLOW_VALUE : 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) +{ + uintN max = 0; + JSBool inRange = JS_FALSE; + jschar c, rangeStart = 0; + uintN n, digit, nDigits, i; + + 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; + + 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)) { + if (!js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_INVALID_BACKREF)) { + return JS_FALSE; + } + c = 0; + } else { + doOctal: + num = 0; + while (state->cp < state->cpend) { + c = *state->cp; + if (c < '0' || '7' < c) + break; + state->cp++; + tmp = 8 * num + (uintN)JS7_UNDEC(c); + if (tmp > 0377) + break; + num = tmp; + } + 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 = GetDecimalValue(c, state->parenCount, FindParenCount, state); + if (state->flags & JSREG_FIND_PAREN_ERROR) + return JS_FALSE; + if (num == OVERFLOW_VALUE) { + if (!JS_HAS_STRICT_OPTION(state->context)) { + state->cp = termStart; + goto doOctal; + } + if (!js_ReportCompileErrorNumber(state->context, + state->tokenStream, + NULL, + JSREPORT_WARNING | + JSREPORT_STRICT, + (c >= '8') + ? JSMSG_INVALID_BACKREF + : JSMSG_BAD_BACKREF)) { + return JS_FALSE; + } + num = 0x10000; + } + JS_ASSERT(1 <= num && num <= 0x10000); + 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++; + } + for (i = 0; i < CLASS_CACHE_SIZE; i++) { + if (!state->classCache[i].start) { + state->classCache[i].start = termStart; + state->classCache[i].length = state->result->u.ucclass.kidlen; + state->classCache[i].index = state->classCount; + break; + } + if (state->classCache[i].length == + state->result->u.ucclass.kidlen) { + for (n = 0; ; n++) { + if (n == state->classCache[i].length) { + state->result->u.ucclass.index = + state->classCache[i].index; + goto claim; + } + if (state->classCache[i].start[n] != termStart[n]) + break; + } + } + } + state->result->u.ucclass.index = state->classCount++; + claim: + /* + * 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; + 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 = (uint16)-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 = (uint16)-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; + uintN min, max; + jschar c; + const jschar *errp = state->cp++; + + c = *state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + min = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + + if (min == OVERFLOW_VALUE) { + err = JSMSG_MIN_TOO_BIG; + goto quantError; + } + if (c == ',') { + c = *++state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + max = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + if (max == OVERFLOW_VALUE) { + err = JSMSG_MAX_TOO_BIG; + goto quantError; + } + if (min > max) { + err = JSMSG_OUT_OF_ORDER; + goto quantError; + } + } else { + max = (uintN)-1; + } + } 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) { + 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; + uintN 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->nrefs = 1; + re->classCount = state.classCount; + if (re->classCount) { + re->classList = (RECharSet *) + JS_malloc(cx, re->classCount * sizeof(RECharSet)); + if (!re->classList) { + js_DestroyRegExp(cx, re); + re = NULL; + goto out; + } + } else { + re->classList = NULL; + } + endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); + if (!endPC) { + js_DestroyRegExp(cx, re); + re = NULL; + goto out; + } + *endPC++ = REOP_END; + JS_ASSERT(endPC <= (re->program + (state.progLength + 1))); + + re->flags = flags; + re->cloneIndex = 0; + re->parenCount = state.parenCount; + 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, + uintN 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); + + ptrdiff_t btsize = gData->backTrackStackSize; + ptrdiff_t btincr = ((char *)result + sz) - + ((char *)gData->backTrackStack + btsize); + + if (btincr > 0) { + ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; + + btincr = JS_ROUNDUP(btincr, btsize); + JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, + &gData->pool, btsize, btincr); + if (!gData->backTrackStack) + return NULL; + gData->backTrackStackSize = btsize + btincr; + 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->saveStateStackTop = gData->stateStackTop; + JS_ASSERT(gData->stateStackTop); + memcpy(result + 1, gData->stateStack, + sizeof(REProgState) * result->saveStateStackTop); + + /* FIXME: parenCount should be uintN */ + JS_ASSERT(parenCount >= 0); + if (parenCount > 0) { + result->parenIndex = parenIndex; + memcpy((char *)(result + 1) + + sizeof(REProgState) * result->saveStateStackTop, + &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 >> 3); + 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 >> 3); + uintN byteIndex2 = (uintN)(c2 >> 3); + + 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; + + JSBool inRange = JS_FALSE; + jschar rangeStart = 0; + uintN byteLength, n; + jschar c, thisCh; + intN nDigits, i; + + JS_ASSERT(!charSet->converted); + charSet->converted = JS_TRUE; + + byteLength = (charSet->length >> 3) + 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) +{ + if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { + if (re->classList) { + uintN i; + 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) +{ + uint16 limit = gData->stateStackLimit; + size_t sz = sizeof(REProgState) * limit; + + JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, &gData->pool, sz, sz); + if (!gData->stateStack) { + gData->ok = JS_FALSE; + return JS_FALSE; + } + gData->stateStackLimit = limit + limit; + return JS_TRUE; +} + +#define PUSH_STATE_STACK(data) \ + JS_BEGIN_MACRO \ + ++(data)->stateStackTop; \ + if ((data)->stateStackTop == (data)->stateStackLimit && \ + !ReallocStateStack((data))) { \ + return NULL; \ + } \ + JS_END_MACRO + +/* + * 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; + uintN 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) { + case REOP_BOL: + if (x->cp != gData->cpbegin) { + if (!gData->cx->regExpStatics.multiline && + !(gData->regexp->flags & JSREG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(x->cp[-1])) + break; + } + result = x; + break; + case REOP_EOL: + if (x->cp != gData->cpend) { + if (!gData->cx->regExpStatics.multiline && + !(gData->regexp->flags & JSREG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(*x->cp)) + 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 >> 3; + if (charSet->length != 0 && + ch <= charSet->length && + (charSet->u.bits[index] & (1 << (ch & 0x7)))) { + 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 >> 3; + if (charSet->length == 0 || + ch > charSet->length || + !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { + result = x; + result->cp++; + } + } + break; + default: + JS_ASSERT(JS_FALSE); + } + if (result) { + if (update) + *startpc = pc; + else + x->cp = startcp; + return result; + } + x->cp = startcp; + return NULL; +} + +static REMatchState * +ExecuteREBytecode(REGlobalData *gData, REMatchState *x) +{ + REMatchState *result = NULL; + 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; + } + 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 >> 3; + if ((charSet->length == 0 || + matchCh1 > charSet->length || + !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ + 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; + PUSH_STATE_STACK(gData); + 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; + } + 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; + } + 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; + PUSH_STATE_STACK(gData); + 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) && + pc == nextpc) { + result = NULL; + break; + } + 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; + PUSH_STATE_STACK(gData); + 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) + 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; + result = (!result) ? x : NULL; + break; + + case REOP_END: + if (x) + return x; + break; + + case REOP_STAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (uint16)-1; + goto quantcommon; + case REOP_PLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (uint16)-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; + } + op = (REOp) *nextpc++; + result = x; + } + curState->index = startcp - gData->cpbegin; + curState->continue_op = REOP_REPEAT; + curState->continue_pc = pc; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !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; + do { + --gData->stateStackTop; + if (!result) { + /* Failed, see if we have enough children. */ + if (curState->u.quantifier.min == 0) + goto repeatDone; + goto break_switch; + } + if (curState->u.quantifier.min == 0 && + x->cp == gData->cpbegin + curState->index) { + /* matched an empty string, that'll get us nowhere */ + result = NULL; + goto break_switch; + } + 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) + 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) + goto repeatDone; + result = NULL; + goto break_switch; + } + result = x; + } + curState->index = startcp - gData->cpbegin; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !PushBackTrackState(gData, REOP_REPEAT, + pc, x, startcp, + curState->parenSoFar, + parenSoFar - + curState->parenSoFar)) { + return NULL; + } + } while (*nextpc == REOP_ENDCHILD); + pc = nextpc; + op = (REOp) *pc++; + parenSoFar = curState->parenSoFar; + continue; + + repeatDone: + result = x; + pc += GET_OFFSET(pc); + goto break_switch; + + case REOP_MINIMALSTAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (uint16)-1; + goto minimalquantcommon; + case REOP_MINIMALPLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (uint16)-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; + PUSH_STATE_STACK(gData); + 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) { + /* + * 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; + PUSH_STATE_STACK(gData); + op = (REOp) *pc++; + continue; + } + /* Don't need to adjust pc since we're going to pop. */ + break; + } + 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; + PUSH_STATE_STACK(gData); + op = (REOp) *pc++; + continue; + } + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + 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; + } + break_switch:; + } + /* + * If the match failed and there's a backtrack option, take it. + * Otherwise this is a complete and utter failure. + */ + if (!result) { + if (gData->cursz == 0) + return NULL; + 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->saveStateStackTop; + JS_ASSERT(gData->stateStackTop); + + memcpy(gData->stateStack, backTrackData + 1, + sizeof(REProgState) * backTrackData->saveStateStackTop); + curState = &gData->stateStack[gData->stateStackTop - 1]; + + if (backTrackData->parenCount) { + memcpy(&x->parens[backTrackData->parenIndex], + (char *)(backTrackData + 1) + + sizeof(REProgState) * backTrackData->saveStateStackTop, + 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; + } + 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->backTrackStackSize = 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->stateStackLimit = 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 = NULL; + + /* + * 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); + ok = gData.ok; + if (!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 = js_EmptySubString; + } 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; + uint32 flagsword; + JSObject *obj; + + if (xdr->mode == JSXDR_ENCODE) { + re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp); + if (!re) + return JS_FALSE; + source = re->source; + flagsword = ((uint32)re->cloneIndex << 16) | re->flags; + } + if (!JS_XDRString(xdr, &source) || + !JS_XDRUint32(xdr, &flagsword)) { + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); + if (!obj) + return JS_FALSE; + re = js_NewRegExp(xdr->cx, NULL, source, (uint16)flagsword, JS_FALSE); + if (!re) + return JS_FALSE; + if (!JS_SetPrivate(xdr->cx, obj, re) || + !js_SetLastIndex(xdr->cx, obj, 0)) { + js_DestroyRegExp(xdr->cx, re); + return JS_FALSE; + } + re->cloneIndex = (uint16)(flagsword >> 16); + *objp = obj; + } + 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}; + +JSBool +js_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, ok2; + JSObject *obj2; + size_t length, nbytes; + const jschar *cp, *start, *end; + jschar *nstart, *ncp, *tmp; + + 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 created; + } + } + 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); + } + } + + /* Escape any naked slashes in the regexp source. */ + length = JSSTRING_LENGTH(str); + start = JSSTRING_CHARS(str); + end = start + length; + nstart = ncp = NULL; + for (cp = start; cp < end; cp++) { + if (*cp == '/' && (cp == start || cp[-1] != '\\')) { + nbytes = (++length + 1) * sizeof(jschar); + if (!nstart) { + nstart = (jschar *) JS_malloc(cx, nbytes); + if (!nstart) + return JS_FALSE; + ncp = nstart + (cp - start); + js_strncpy(nstart, start, cp - start); + } else { + tmp = (jschar *) JS_realloc(cx, nstart, nbytes); + if (!tmp) { + JS_free(cx, nstart); + return JS_FALSE; + } + ncp = tmp + (ncp - nstart); + nstart = tmp; + } + *ncp++ = '\\'; + } + if (nstart) + *ncp++ = *cp; + } + + if (nstart) { + /* Don't forget to store the backstop after the new string. */ + JS_ASSERT((size_t)(ncp - nstart) == length); + *ncp = 0; + str = js_NewString(cx, nstart, length, 0); + if (!str) { + JS_free(cx, nstart); + return JS_FALSE; + } + argv[0] = STRING_TO_JSVAL(str); + } + } + + re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); +created: + if (!re) + return JS_FALSE; + JS_LOCK_OBJ(cx, obj); + oldre = (JSRegExp *) JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, re); + ok2 = js_SetLastIndex(cx, obj, 0); + JS_UNLOCK_OBJ(cx, obj); + if (!ok) { + js_DestroyRegExp(cx, re); + return JS_FALSE; + } + if (oldre) + js_DestroyRegExp(cx, oldre); + *rval = OBJECT_TO_JSVAL(obj); + return ok2; +} + +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, js_regexp_toString, 0,0,0}, +#endif + {js_toString_str, js_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/dom/js/jsregexp.h b/src/dom/js/jsregexp.h new file mode 100644 index 000000000..998352df5 --- /dev/null +++ b/src/dom/js/jsregexp.h @@ -0,0 +1,180 @@ +/* -*- 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 $*) */ + uint16 parenCount; /* number of valid elements in parens[] */ + uint16 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 { + JSPackedBool converted; + JSPackedBool 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 */ + uint16 flags; /* flags, see jsapi.h's JSREG_* defines */ + uint16 cloneIndex; /* index in fp->vars or funobj->slots of + cloned regexp object */ + uint16 parenCount; /* number of parenthesized submatches */ + uint16 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); + +/* + * Export js_regexp_toString to the decompiler. + */ +extern JSBool +js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +/* + * Create, serialize/deserialize, or clone a 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); + +/* + * Get and set the per-object (clone or clone-parent) lastIndex slot. + */ +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/dom/js/jsscan.c b/src/dom/js/jsscan.c new file mode 100644 index 000000000..995c1e660 --- /dev/null +++ b/src/dom/js/jsscan.c @@ -0,0 +1,1424 @@ +/* -*- 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->flags & TSF_OWNFILENAME) + JS_free(cx, (void *) ts->filename); + 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); + } + + nl = ts->saveEOL; + if (!nl) { + /* + * Any one of \n, \r, or \r\n ends a line (the 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; + ts->saveEOL = nl; + } else { + ts->saveEOL = NULL; + } + 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 '\\'; +} + +static JSToken * +NewToken(JSTokenStream *ts) +{ + JSToken *tp; + + 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; + return tp; +} + +JSTokenType +js_GetToken(JSContext *cx, JSTokenStream *ts) +{ + JSTokenType tt; + int32 c, qc; + JSToken *tp; + JSAtom *atom; + JSBool hadUnicodeEscape; + +#define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base) +#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i) +#define TOKENBUF_LENGTH() (ts->tokenbuf.ptr - ts->tokenbuf.base) +#define TOKENBUF_BASE() (ts->tokenbuf.base) +#define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i]) +#define TOKENBUF_TO_ATOM() (js_AtomizeChars(cx, \ + TOKENBUF_BASE(), \ + TOKENBUF_LENGTH(), \ + 0)) + +#define ADD_TO_TOKENBUF(c) \ + JS_BEGIN_MACRO \ + if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) \ + goto error; \ + JS_END_MACRO + + /* 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)); + + tp = NewToken(ts); + if (c == EOF) { + tt = TOK_EOF; + goto out; + } + + 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(); + for (;;) { + ADD_TO_TOKENBUF(c); + 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); + + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto 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; + tt = kw->tokentype; + goto out; + } + } + tp->t_op = JSOP_NAME; + tp->t_atom = atom; + tt = TOK_NAME; + goto out; + } + + if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { + jsint radix; + const jschar *endptr; + jsdouble dval; + + radix = 10; + INIT_TOKENBUF(); + + if (c == '0') { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + if (JS_TOLOWER(c) == 'x') { + ADD_TO_TOKENBUF(c); + 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")) { + goto error; + } + radix = 10; + } + } + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + } + + if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { + if (c == '.') { + do { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + } while (JS7_ISDEC(c)); + } + if (JS_TOLOWER(c) == 'e') { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + if (c == '+' || c == '-') { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + } + if (!JS7_ISDEC(c)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_MISSING_EXPONENT); + goto error; + } + do { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + } while (JS7_ISDEC(c)); + } + } + + /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */ + UngetChar(ts, c); + ADD_TO_TOKENBUF(0); + + if (radix == 10) { + if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_OUT_OF_MEMORY); + goto error; + } + } else { + if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_OUT_OF_MEMORY); + goto error; + } + } + tp->t_dval = dval; + tt = TOK_NUMBER; + goto out; + } + + if (c == '"' || c == '\'') { + qc = c; + INIT_TOKENBUF(); + while ((c = GetChar(ts)) != qc) { + if (c == '\n' || c == EOF) { + UngetChar(ts, c); + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_STRING); + goto 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') { + int32 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; + } + } + ADD_TO_TOKENBUF(c); + } + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + tp->pos.end.lineno = (uint16)ts->lineno; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + tt = TOK_STRING; + goto out; + } + + switch (c) { + case '\n': + tt = TOK_EOL; + break; + + case ';': tt = TOK_SEMI; break; + case '[': tt = TOK_LB; break; + case ']': tt = TOK_RB; break; + case '{': tt = TOK_LC; break; + case '}': tt = TOK_RC; break; + case '(': tt = TOK_LP; break; + case ')': tt = TOK_RP; break; + case ',': tt = TOK_COMMA; break; + case '?': tt = TOK_HOOK; break; + + case '.': + tt = TOK_DOT; + 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; + tt = TOK_COLON; + break; + + case '|': + if (MatchChar(ts, c)) { + tt = TOK_OR; + } else if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITOR; + tt = TOK_ASSIGN; + } else { + tt = TOK_BITOR; + } + break; + + case '^': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITXOR; + tt = TOK_ASSIGN; + } else { + tt = TOK_BITXOR; + } + break; + + case '&': + if (MatchChar(ts, c)) { + tt = TOK_AND; + } else if (MatchChar(ts, '=')) { + tp->t_op = JSOP_BITAND; + tt = TOK_ASSIGN; + } else { + tt = 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 + tt = TOK_EQOP; + } else { + tp->t_op = JSOP_NOP; + tt = 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 + tt = TOK_EQOP; + } else { + tp->t_op = JSOP_NOT; + tt = 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; + tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; + } else { + tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT; + tt = TOK_RELOP; + } + break; + + case '>': + if (MatchChar(ts, c)) { + tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH; + tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP; + } else { + tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT; + tt = TOK_RELOP; + } + break; + + case '*': + tp->t_op = JSOP_MUL; + tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR; + break; + + case '/': + if (MatchChar(ts, '/')) { + /* + * Hack for source filters such as the Mozilla XUL preprocessor: + * "//@line 123\n" sets the number of the *next* line after the + * comment to 123. + */ + if (JS_HAS_ATLINE_OPTION(cx)) { + jschar cp[5]; + uintN i, line, temp; + char filename[1024]; + + if (PeekChars(ts, 5, cp) && + cp[0] == '@' && + cp[1] == 'l' && + cp[2] == 'i' && + cp[3] == 'n' && + cp[4] == 'e') { + SkipChars(ts, 5); + while ((c = GetChar(ts)) != '\n' && JS_ISSPACE(c)) + continue; + if (JS7_ISDEC(c)) { + line = JS7_UNDEC(c); + while ((c = GetChar(ts)) != EOF && JS7_ISDEC(c)) { + temp = 10 * line + JS7_UNDEC(c); + if (temp < line) { + /* Ignore overlarge line numbers. */ + goto skipline; + } + line = temp; + } + while (c != '\n' && JS_ISSPACE(c)) + c = GetChar(ts); + i = 0; + if (c == '"') { + while ((c = GetChar(ts)) != EOF && c != '"') { + if (c == '\n') { + UngetChar(ts, c); + goto skipline; + } + if ((c >> 8) != 0 || i >= sizeof filename - 1) + goto skipline; + filename[i++] = (char) c; + } + if (c == '"') { + while ((c = GetChar(ts)) != '\n' && + JS_ISSPACE(c)) { + continue; + } + } + } + filename[i] = '\0'; + if (c == '\n') { + if (i > 0) { + if (ts->flags & TSF_OWNFILENAME) + JS_free(cx, (void *) ts->filename); + ts->filename = JS_strdup(cx, filename); + if (!ts->filename) + goto error; + ts->flags |= TSF_OWNFILENAME; + } + ts->lineno = line; + } + } + UngetChar(ts, c); + } + } + +skipline: + while ((c = GetChar(ts)) != EOF && c != '\n') + continue; + 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); + goto error; + } + goto retry; + } + +#if JS_HAS_REGEXPS + if (ts->flags & TSF_OPERAND) { + JSObject *obj; + uintN flags; + + INIT_TOKENBUF(); + while ((c = GetChar(ts)) != '/') { + if (c == '\n' || c == EOF) { + UngetChar(ts, c); + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_REGEXP); + goto error; + } + if (c == '\\') { + ADD_TO_TOKENBUF(c); + c = GetChar(ts); + } + ADD_TO_TOKENBUF(c); + } + 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); + goto error; + } + obj = js_NewRegExpObject(cx, ts, + TOKENBUF_BASE(), + TOKENBUF_LENGTH(), + flags); + if (!obj) + goto error; + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) + goto error; + + /* + * If the regexp's script is one-shot, we can avoid the extra + * fork-on-exec costs of JSOP_REGEXP by selecting JSOP_OBJECT. + * Otherwise, to avoid incorrect proto, parent, and lastIndex + * sharing among threads and sequentially across re-execution, + * select JSOP_REGEXP. + */ + tp->t_op = (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)) + ? JSOP_OBJECT + : JSOP_REGEXP; + tp->t_atom = atom; + tt = TOK_OBJECT; + break; + } +#endif /* JS_HAS_REGEXPS */ + + tp->t_op = JSOP_DIV; + tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; + break; + + case '%': + tp->t_op = JSOP_MOD; + tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; + break; + + case '~': + tp->t_op = JSOP_BITNOT; + tt = TOK_UNARYOP; + break; + + case '+': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_ADD; + tt = TOK_ASSIGN; + } else if (MatchChar(ts, c)) { + tt = TOK_INC; + } else { + tp->t_op = JSOP_POS; + tt = TOK_PLUS; + } + break; + + case '-': + if (MatchChar(ts, '=')) { + tp->t_op = JSOP_SUB; + tt = TOK_ASSIGN; + } else if (MatchChar(ts, c)) { + if (PeekChar(ts) == '>' && !(ts->flags & TSF_DIRTYLINE)) + goto skipline; + tt = TOK_DEC; + } else { + tp->t_op = JSOP_NEG; + tt = 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); + goto 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)) { + goto error; + } + } + if (c == '=') + tt = TOK_DEFSHARP; + else if (c == '#') + tt = TOK_USESHARP; + else + goto badchar; + break; + } + + badchar: +#endif /* JS_HAS_SHARP_VARS */ + + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_ILLEGAL_CHARACTER); + goto error; + } + +out: + JS_ASSERT(tt < TOK_LIMIT); + tp->pos.end.index = ts->linepos + + (ts->linebuf.ptr - ts->linebuf.base) - + ts->ungetpos; + tp->type = tt; + return tt; + +error: + tt = TOK_ERROR; + ts->flags |= TSF_ERROR; + goto out; + +#undef INIT_TOKENBUF +#undef TRIM_TOKENBUF +#undef TOKENBUF_LENGTH +#undef TOKENBUF_BASE +#undef TOKENBUF_CHAR +#undef TOKENBUF_TO_ATOM +#undef ADD_TO_TOKENBUF +} + +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/dom/js/jsscan.h b/src/dom/js/jsscan.h new file mode 100644 index 000000000..121b35df3 --- /dev/null +++ b/src/dom/js/jsscan.h @@ -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 + * 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 */ + jschar *saveEOL; /* save next end of line in userbuf, to + optimize for very long lines */ +}; + +#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_OPERAND 0x08 /* looking for operand, not operator */ +#define TSF_NLFLAG 0x20 /* last linebuf ended with \n */ +#define TSF_CRFLAG 0x40 /* linebuf would have ended with \r */ +#define TSF_DIRTYLINE 0x80 /* non-whitespace since start of line */ +#define TSF_OWNFILENAME 0x100 /* ts->filename is malloc'd */ + +/* 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/dom/js/jsscope.c b/src/dom/js/jsscope.c new file mode 100644 index 000000000..e3603b18f --- /dev/null +++ b/src/dom/js/jsscope.c @@ -0,0 +1,1639 @@ +/* -*- 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 DUMP_SCOPE_STATS +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 DUMP_SCOPE_STATS + 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 DUMP_SCOPE_STATS + +#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; +} + +#include "jsprf.h" + +static void +DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) +{ + char buf[10]; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + uintN i; + + fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", + level, "", + JSVAL_IS_INT(sprop->id) + ? (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), + buf) + : JS_GetStringBytes(ATOM_TO_STRING((JSAtom *) sprop->id)), + (void *) sprop->getter, (void *) sprop->setter, + (unsigned long) sprop->slot, sprop->attrs, sprop->flags, + sprop->shortid); + kids = sprop->kids; + if (kids) { + ++level; + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + DumpSubtree(kid, level, fp); + } + } while ((chunk = chunk->next) != NULL); + } else { + kid = kids; + DumpSubtree(kid, level, fp); + } + } +} + +#endif /* DUMP_SCOPE_STATS */ + +void +js_SweepScopeProperties(JSRuntime *rt) +{ + JSArena **ap, *a; + JSScopeProperty *limit, *sprop, *parent, *kids, *kid; + uintN liveCount; + PropTreeKidsChunk *chunk, *nextChunk; + uintN i; + +#ifdef DUMP_SCOPE_STATS + 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.0, var = 0.0, sigma = 0.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.0) ? sqrt(var) : 0.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 DUMP_SCOPE_STATS + livePropCapacity += limit - (JSScopeProperty *) a->base; + totalLiveCount += liveCount; +#endif + ap = &a->next; + } + } + +#ifdef DUMP_SCOPE_STATS + fprintf(logfp, " arenautil %g%%\n", + (totalLiveCount * 100.0) / livePropCapacity); + fflush(logfp); +#endif + +#ifdef DUMP_PROPERTY_TREE + { + FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); + if (dumpfp) { + JSPropertyTreeEntry *pte, *end; + + pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; + end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); + while (pte < end) { + if (pte->child) + DumpSubtree(pte->child, 0, dumpfp); + pte++; + } + fclose(dumpfp); + } + } +#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/dom/js/jsscope.h b/src/dom/js/jsscope.h new file mode 100644 index 000000000..8ac7ef073 --- /dev/null +++ b/src/dom/js/jsscope.h @@ -0,0 +1,389 @@ +/* -*- 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_HAS_STUB_GETTER(sprop) (!(sprop)->getter) +#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) + +#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/dom/js/jsscript.c b/src/dom/js/jsscript.c new file mode 100644 index 000000000..7e5aefc30 --- /dev/null +++ b/src/dom/js/jsscript.c @@ -0,0 +1,1284 @@ +/* -*- 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; + } + + /* + * Compile the new script using the caller's scope chain, a la eval(). + * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's + * flags, because compilation is here separated from execution, and the + * run-time scope chain may not match the compile-time. JSFRAME_EVAL is + * tested in jsemit.c and jsscan.c to optimize based on identity of run- + * and compile-time scope. + */ + 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); + JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); + version = (uint32)script->version | (script->numGlobalVars << 16); + 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 & 0xffff); + script->numGlobalVars = (uint16) (version >> 16); + + /* 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 magic < _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); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + } + } + + 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. + */ +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_sftbl_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_sftbl_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_sftbl_entry, js_free_sftbl_entry +}; + +JSBool +js_InitRuntimeScriptState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!rt->scriptFilenameTableLock) { + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; + } +#endif + if (!rt->scriptFilenameTable) { + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + if (!rt->scriptFilenameTable) { + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &table_alloc_ops, NULL); + } + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + if (!rt->scriptFilenameTable) { + js_FinishRuntimeScriptState(cx); /* free lock if threadsafe */ + return JS_FALSE; + } + } + return JS_TRUE; +} + +void +js_FinishRuntimeScriptState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + if (rt->scriptFilenameTable) { + JS_HashTableDestroy(rt->scriptFilenameTable); + rt->scriptFilenameTable = NULL; + } +#ifdef JS_THREADSAFE + if (rt->scriptFilenameTableLock) { + JS_DESTROY_LOCK(rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = NULL; + } +#endif +} + +#ifdef DEBUG_brendan +size_t sftbl_savings = 0; +#endif + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSRuntime *rt = cx->runtime; + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep; + ScriptFilenameEntry *sfe; + + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + table = rt->scriptFilenameTable; + hash = JS_HashString(filename); + hep = JS_HashTableRawLookup(table, hash, filename); + sfe = (ScriptFilenameEntry *) *hep; +#ifdef DEBUG_brendan + if (sfe) + sftbl_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(rt->scriptFilenameTableLock); + 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(rt->scriptFilenameTable, + js_script_filename_sweeper, + rt); +#ifdef DEBUG_brendan + printf("script filename table savings so far: %u\n", sftbl_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); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + 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)); + script->numGlobalVars = cg->treeContext.numGlobalVars; + 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) { + JS_KEEP_ATOMS(rt); + hook(cx, script->filename, script->lineno, script, fun, + rt->newScriptHookData); + JS_UNKEEP_ATOMS(rt); + } +} + +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)); + JS_ASSERT(fun->interpreted); + return fun->u.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/dom/js/jsscript.h b/src/dom/js/jsscript.h new file mode 100644 index 000000000..6284917af --- /dev/null +++ b/src/dom/js/jsscript.h @@ -0,0 +1,179 @@ +/* -*- 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 */ + uint16 version; /* JS version under which script was compiled */ + uint16 numGlobalVars; /* declared global var/const/function count */ + 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_InitRuntimeScriptState(JSContext *cx); + +extern void +js_FinishRuntimeScriptState(JSContext *cx); + +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/dom/js/jsshell.msg b/src/dom/js/jsshell.msg new file mode 100644 index 000000000..4b811ac01 --- /dev/null +++ b/src/dom/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/dom/js/jsstddef.h b/src/dom/js/jsstddef.h new file mode 100644 index 000000000..0d87b0c0c --- /dev/null +++ b/src/dom/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/dom/js/jsstr.c b/src/dom/js/jsstr.c new file mode 100644 index 000000000..e143ab8df --- /dev/null +++ b/src/dom/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/dom/js/jsstr.h b/src/dom/js/jsstr.h new file mode 100644 index 000000000..202d0d9ea --- /dev/null +++ b/src/dom/js/jsstr.h @@ -0,0 +1,448 @@ +/* -*- 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) + +/* + * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a + * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier + * definition of "word", we should rename this macro to something regexp-y. + */ +#define JS_ISWORD(c) ((c) < 128 && (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) ((jschar) ((JS_CCODE(c) & 0x00100000) \ + ? (c) - ((int32)JS_CCODE(c) >> 22) \ + : (c))) +#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ + ? (c) + ((int32)JS_CCODE(c) >> 22) \ + : (c))) + +#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/dom/js/jstypes.h b/src/dom/js/jstypes.h new file mode 100644 index 000000000..35fc12e54 --- /dev/null +++ b/src/dom/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/dom/js/jsutil.c b/src/dom/js/jsutil.c new file mode 100644 index 000000000..6e4c21cad --- /dev/null +++ b/src/dom/js/jsutil.c @@ -0,0 +1,157 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ +#include "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_smprintf_free(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/dom/js/jsutil.h b/src/dom/js/jsutil.h new file mode 100644 index 000000000..a34096d93 --- /dev/null +++ b/src/dom/js/jsutil.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ + +#ifndef jsutil_h___ +#define jsutil_h___ + +JS_BEGIN_EXTERN_C + +#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/dom/js/jsxdrapi.c b/src/dom/js/jsxdrapi.c new file mode 100644 index 000000000..e956072f9 --- /dev/null +++ b/src/dom/js/jsxdrapi.c @@ -0,0 +1,686 @@ +/* -*- 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 "jsnum.h" +#include "jsobj.h" /* js_XDRObject */ +#include "jsscript.h" /* js_XDRScript */ +#include "jsstr.h" +#include "jsxdrapi.h" + +#ifdef DEBUG +#define DBG(x) x +#else +#define DBG(x) ((void)0) +#endif + +typedef struct JSXDRMemState { + JSXDRState state; + char *base; + uint32 count; + uint32 limit; +} JSXDRMemState; + +#define MEM_BLOCK 8192 +#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) + +#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) +#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) +#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) + +#define MEM_LEFT(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_DECODE && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ + JSMSG_END_OF_DATA); \ + return 0; \ + } \ + JS_END_MACRO + +#define MEM_NEED(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_ENCODE) { \ + if (MEM_LIMIT(xdr) && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ + void *data_ = 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) +{ + jsdpun u; + + if (xdr->mode == JSXDR_ENCODE) + u.d = **dp; + if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + *dp = JS_NewDouble(xdr->cx, u.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/dom/js/jsxdrapi.h b/src/dom/js/jsxdrapi.h new file mode 100644 index 000000000..874a62eeb --- /dev/null +++ b/src/dom/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/dom/js/prmjtime.c b/src/dom/js/prmjtime.c new file mode 100644 index 000000000..774f83999 --- /dev/null +++ b/src/dom/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/dom/js/prmjtime.h b/src/dom/js/prmjtime.h new file mode 100644 index 000000000..6a94a11b1 --- /dev/null +++ b/src/dom/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/dom/js/resource.h b/src/dom/js/resource.h new file mode 100644 index 000000000..9301810e4 --- /dev/null +++ b/src/dom/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/dom/ls.h b/src/dom/ls.h new file mode 100644 index 000000000..b6f98bf8b --- /dev/null +++ b/src/dom/ls.h @@ -0,0 +1,940 @@ +#ifndef __LS_H__ +#define __LS_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + +#include "dom.h" +#include "events.h" +#include "traversal.h" + +#include "io/domstream.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace ls +{ + + + +//Local definitions +//The idl said Object. Since this is undefined, we will +//use our own class which is designed to be a bit similar to +//java.io streams + +typedef dom::io::InputStream LSInputStream; +typedef dom::io::OutputStream LSOutputStream; +typedef dom::io::Reader LSReader; +typedef dom::io::Writer LSWriter; + + +//local definitions +typedef dom::DOMString DOMString; +typedef dom::DOMConfiguration DOMConfiguration; +typedef dom::Node Node; +typedef dom::Document Document; +typedef dom::Element Element; + + +//forward declarations +class LSParser; +class LSSerializer; +class LSInput; +class LSOutput; +class LSParserFilter; +class LSSerializerFilter; + + + +/*######################################################################### +## LSException +#########################################################################*/ + +/** + * Maybe this should inherit from DOMException? + */ +class LSException +{ + +public: + + LSException(const DOMString &reasonMsg) + { msg = reasonMsg; } + + LSException(short theCode) + { + code = theCode; + } + + virtual ~LSException() throw() + {} + + /** + * + */ + unsigned short code; + + /** + * + */ + DOMString msg; + + /** + * Get a string, translated from the code. + * Like std::exception. Not in spec. + */ + const char *what() + { return msg.c_str(); } + + + +}; + + +/** + * LSExceptionCode + */ +typedef enum + { + PARSE_ERR = 81, + SERIALIZE_ERR = 82 + } XPathExceptionCode; + + +/*######################################################################### +## LSParserFilter +#########################################################################*/ + +/** + * + */ +class LSParserFilter +{ +public: + + // Constants returned by startElement and acceptNode + typedef enum + { + FILTER_ACCEPT = 1, + FILTER_REJECT = 2, + FILTER_SKIP = 3, + FILTER_INTERRUPT = 4 + } ReturnValues; + + + /** + * + */ + virtual unsigned short startElement(const Element *elementArg) =0; + + /** + * + */ + virtual unsigned short acceptNode(const Node *nodeArg) =0; + + /** + * + */ + virtual unsigned long getWhatToShow() =0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~LSParserFilter() {} + + + +}; + +/*######################################################################### +## LSInput +#########################################################################*/ + +/** + * + */ +class LSInput +{ +public: + + /** + * + */ + virtual LSReader *getCharacterStream() const + { return characterStream; } + + /** + * + */ + virtual void setCharacterStream(const LSReader *val) + { characterStream = (LSReader *)val; } + + /** + * + */ + virtual LSInputStream *getByteStream() const + { return byteStream; } + + /** + * + */ + virtual void setByteStream(const LSInputStream *val) + { byteStream = (LSInputStream *)val; } + + /** + * + */ + virtual DOMString getStringData() const + { return stringData; } + + /** + * + */ + virtual void setStringData(const DOMString &val) + { stringData = val; } + + /** + * + */ + virtual DOMString getSystemId() const + { return systemId; } + + /** + * + */ + virtual void setSystemId(const DOMString &val) + { systemId = val; } + + /** + * + */ + virtual DOMString getPublicId() const + { return publicId; } + + /** + * + */ + virtual void setPublicId(const DOMString &val) + { publicId = val; } + + /** + * + */ + virtual DOMString getBaseURI() const + { return baseURI; } + + /** + * + */ + virtual void setBaseURI(const DOMString &val) + { baseURI = val; } + + /** + * + */ + virtual DOMString getEncoding() const + { return encoding; } + + /** + * + */ + virtual void setEncoding(const DOMString &val) + { encoding = val; } + + /** + * + */ + virtual bool getCertifiedText() const + { return certifiedText; } + + /** + * + */ + virtual void setCertifiedText(bool val) + { certifiedText = val; } + + //################## + //# Non-API methods + //################## + + + /** + * + */ + LSInput() + { + characterStream = NULL; + byteStream = NULL; + stringData = ""; + systemId = ""; + publicId = ""; + baseURI = ""; + encoding = ""; + certifiedText = false; + } + + + + /** + * + */ + LSInput(const LSInput &other) + { + characterStream = other.characterStream; + byteStream = other.byteStream; + stringData = other.stringData; + systemId = other.systemId; + publicId = other.publicId; + baseURI = other.baseURI; + encoding = other.encoding; + certifiedText = other.certifiedText; + } + + /** + * + */ + virtual ~LSInput() + {} + +private: + + LSReader *characterStream; + LSInputStream *byteStream; + DOMString stringData; + DOMString systemId; + DOMString publicId; + DOMString baseURI; + DOMString encoding; + bool certifiedText; + + +}; + + +/*######################################################################### +## LSParser +#########################################################################*/ + +/** + * + */ +class LSParser +{ +public: + + + /** + * + */ + virtual DOMConfiguration *getDomConfig() + { return NULL; } + + /** + * + */ + virtual LSParserFilter *getFilter() + { return filter; } + + /** + * + */ + virtual void setFilter(const LSParserFilter *val) + { filter = (LSParserFilter *)val; } + + /** + * + */ + virtual bool getAsync() + { return false; } + + /** + * + */ + virtual bool getBusy() + { return false; } + + /** + * + */ + virtual Document *parse(const LSInput &input) + throw(dom::DOMException, LSException) + { return NULL; } + + + /** + * + */ + virtual Document *parseURI(const DOMString &uri) + throw(dom::DOMException, LSException) + { return NULL; } + + typedef enum + { + ACTION_APPEND_AS_CHILDREN = 1, + ACTION_REPLACE_CHILDREN = 2, + ACTION_INSERT_BEFORE = 3, + ACTION_INSERT_AFTER = 4, + ACTION_REPLACE = 5 + } ActionTypes; + + + /** + * + */ + virtual Node *parseWithContext(const LSInput &input, + const Node *contextArg, + unsigned short action) + throw(dom::DOMException, LSException) + { return NULL; } + + /** + * + */ + virtual void abort() + {} + + + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSParser() + { + filter = NULL; + } + + /** + * + */ + LSParser(const LSParser &other) + { + filter = other.filter; + } + + /** + * + */ + virtual ~LSParser() {} + +protected: + + LSParserFilter *filter; +}; + + + +/*######################################################################### +## LSResourceResolver +#########################################################################*/ + +/** + * + */ +class LSResourceResolver +{ +public: + + /** + * + */ + virtual LSInput resolveResource(const DOMString &type, + const DOMString &namespaceURI, + const DOMString &publicId, + const DOMString &systemId, + const DOMString &baseURI) + { + LSInput input; + //do something + return input; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSResourceResolver() {} + + /** + * + */ + LSResourceResolver(const LSResourceResolver &other) + { + } + + /** + * + */ + virtual ~LSResourceResolver() {} + + + +}; + +/*######################################################################### +## LSOutput +#########################################################################*/ + +/** + * + */ +class LSOutput +{ +public: + + /** + * + */ + virtual LSWriter *getCharacterStream() const + { return characterStream; } + + /** + * + */ + virtual void setCharacterStream(const LSWriter *val) + { characterStream = (LSWriter *)val; } + + /** + * + */ + virtual LSOutputStream *getByteStream() const + { return byteStream; } + + /** + * + */ + virtual void setByteStream(const LSOutputStream *val) + { byteStream = (LSOutputStream *) val; } + + /** + * + */ + virtual DOMString getSystemId() const + { return systemId; } + + /** + * + */ + virtual void setSystemId(const DOMString &val) + { systemId = val; } + + /** + * + */ + virtual DOMString getEncoding() const + { return encoding; } + + /** + * + */ + virtual void setEncoding(const DOMString &val) + { encoding = val; } + + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSOutput() + { + characterStream = NULL; + byteStream = NULL; + systemId = ""; + encoding = ""; + } + + + /** + * + */ + LSOutput(const LSOutput &other) + { + characterStream = other.characterStream; + byteStream = other.byteStream; + systemId = other.systemId; + encoding = other.encoding; + } + + /** + * + */ + virtual ~LSOutput() + {} + +private: + + LSWriter *characterStream; + LSOutputStream *byteStream; + DOMString systemId; + DOMString encoding; + +}; + + +/*######################################################################### +## LSSerializer +#########################################################################*/ + +/** + * + */ +class LSSerializer +{ +public: + + /** + * + */ + virtual DOMConfiguration *getDomConfig() + { return NULL; } + + /** + * + */ + virtual DOMString getNewLine() + { return newLine; } + /** + * + */ + virtual void setNewLine(const DOMString &val) + { newLine = val; } + + /** + * + */ + virtual LSSerializerFilter *getFilter() + { return filter; } + + /** + * + */ + virtual void setFilter(const LSSerializerFilter *val) + { filter = (LSSerializerFilter *)val; } + + /** + * + */ + virtual bool write(const Node *nodeArg, + const LSOutput &destination) + throw (LSException) + { return false; } + + /** + * + */ + virtual bool writeToURI(const Node *nodeArg, + const DOMString &uri) + throw(LSException) + { return false; } + + /** + * + */ + virtual DOMString writeToString(const Node *nodeArg) + throw(dom::DOMException, LSException) + { + DOMString str; + return str; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSSerializer() + { + filter = NULL; + newLine = "\n"; + } + + /** + * + */ + LSSerializer(const LSSerializer &other) + { + filter = other.filter; + newLine = other.newLine; + } + + /** + * + */ + virtual ~LSSerializer() {} + +protected: + + LSSerializerFilter *filter; + DOMString newLine; + +}; + +/*######################################################################### +## LSProgressEvent +#########################################################################*/ + +/** + * + */ +class LSProgressEvent : virtual public events::Event +{ +public: + + /** + * + */ + virtual LSInput &getInput() + { + return input; + } + + /** + * + */ + virtual unsigned long getPosition() + { return position; } + + /** + * + */ + virtual unsigned long getTotalSize() + { return totalSize; } + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSProgressEvent(const LSInput &inputArg, unsigned long positionArg, + unsigned long totalSizeArg) : input((LSInput &)inputArg) + { + position = positionArg; + totalSize = totalSizeArg; + } + + + /** + * + */ + LSProgressEvent(const LSProgressEvent &other) + : events::Event(other) , input(other.input) + { + position = other.position; + totalSize = other.totalSize; + } + + + /** + * + */ + virtual ~LSProgressEvent() {} + +protected: + + LSInput &input; + unsigned long position; + unsigned long totalSize; + +}; + +/*######################################################################### +## LSLoadEvent +#########################################################################*/ + +/** + * + */ +class LSLoadEvent : public events::Event +{ +public: + + /** + * + */ + virtual Document *getNewDocument() + { return newDocument; } + + /** + * + */ + virtual LSInput &getInput() + { return input; } + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSLoadEvent(const LSInput &inputArg, const Document *docArg) + : input((LSInput &)inputArg) + { newDocument = (Document *)docArg; } + + /** + * + */ + LSLoadEvent(const LSLoadEvent &other) : events::Event(other) , input(other.input) + { + newDocument = other.newDocument; + } + + /** + * + */ + virtual ~LSLoadEvent() {} + +protected: + + Document *newDocument; + + LSInput &input; + + +}; + + + +/*######################################################################### +## LSSerializerFilter +#########################################################################*/ + +/** + * + */ +class LSSerializerFilter : virtual public traversal::NodeFilter +{ +public: + + /** + * + */ + virtual unsigned long getWhatToShow() =0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~LSSerializerFilter() {} +}; + + + + +/*######################################################################### +## DOMImplementationLS +#########################################################################*/ + +/** + * + */ +class DOMImplementationLS +{ +public: + + typedef enum + { + MODE_SYNCHRONOUS = 1, + MODE_ASYNCHRONOUS = 2 + } DOMImplementationLSMode; + + /** + * To use, for this and subclasses: + * LSParser &parser = myImplementation.createLSParser(mode, schemaType); + */ + virtual LSParser &createLSParser(unsigned short mode, + const DOMString &schemaType) + throw (dom::DOMException) =0; + + /** + * To use, for this and subclasses: + * LSSerializer &serializer = myImplementation.createLSSerializer(); + * + */ + virtual LSSerializer &createLSSerializer() =0; + + /** + * + */ + virtual LSInput createLSInput() =0; + + /** + * + */ + virtual LSOutput createLSOutput() =0; + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DOMImplementationLS() {} +}; + + + + +} //namespace ls +} //namespace dom +} //namespace w3c +} //namespace org + + +#endif // __LS_H__ + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + diff --git a/src/dom/lsimpl.cpp b/src/dom/lsimpl.cpp new file mode 100644 index 000000000..636dc6750 --- /dev/null +++ b/src/dom/lsimpl.cpp @@ -0,0 +1,440 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "domimpl.h" +#include "events.h" +#include "traversal.h" +#include "lsimpl.h" + +#include + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace ls +{ + + + +/*######################################################################### +## LSParserImpl +#########################################################################*/ + + +/** + * + */ +bool LSParserImpl::getBusy() +{ + return false; +} + +/** + * + */ +Document *LSParserImpl::parse(const LSInput &input) + throw(dom::DOMException, LSException) +{ + + //#### Check the various inputs of 'input' in order, according + //# to the L&S spec + LSReader *lsreader = input.getCharacterStream(); + if (lsreader) + { + DOMString buf; + while (true) + { + int ch = lsreader->get(); + if (ch < 0) + break; + buf.push_back(ch); + } + XmlReader reader; + Document *doc = reader.parse(buf); + return doc; + } + + LSInputStream *inputStream = input.getByteStream(); + if (inputStream) + { + DOMString buf; + while (true) + { + int ch = inputStream->get(); + if (ch < 0) + break; + buf.push_back(ch); + } + XmlReader reader; + Document *doc = reader.parse(buf); + return doc; + } + + DOMString stringData = input.getStringData(); + if (stringData.size() > 0) + { + XmlReader reader; + Document *doc = reader.parse(stringData); + return doc; + } + + DOMString systemId = input.getSystemId(); + if (systemId.size() > 0) + { + //lets not do this yet + return NULL; + } + + DOMString publicId = input.getPublicId(); + if (publicId.size() > 0) + { + //lets not do this yet + return NULL; + } + + return NULL; +} + + +/** + * + */ +Document *LSParserImpl::parseURI(const DOMString &uri) + throw(dom::DOMException, LSException) +{ + return NULL; +} + + /** + * + */ +Node *LSParserImpl::parseWithContext(const LSInput &input, + const Node *contextArg, + unsigned short action) + throw(dom::DOMException, LSException) +{ + return NULL; +} + + + +//################## +//# Non-API methods +//################## + + + + + + + + + + + +/*######################################################################### +## LSSerializerImpl +#########################################################################*/ + + +/** + * + */ +bool LSSerializerImpl::write( + const Node *nodeArg, + const LSOutput &destination) + throw (LSException) +{ + outbuf = ""; + indent = 0; + + writeNode(nodeArg); + + //## Check in order specified in the L&S specs + LSWriter *writer = destination.getCharacterStream(); + if (writer) + { + for (unsigned int i=0 ; iput(ch); + } + return true; + } + + LSOutputStream *outputStream = destination.getByteStream(); + if (outputStream) + { + for (unsigned int i=0 ; iput(ch); + } + return true; + } + + DOMString systemId = destination.getSystemId(); + if (systemId.size() > 0) + { + //DO SOMETHING + return true; + } + + return false; +} + +/** + * + */ +bool LSSerializerImpl::writeToURI(const Node *nodeArg, + const DOMString &uriArg) + throw(LSException) +{ + outbuf = ""; + indent = 0; + + writeNode(nodeArg); + + DOMString uri = uriArg; + char *fileName = (char *) uri.c_str(); //temporary hack + FILE *f = fopen(fileName, "rb"); + if (!f) + return false; + for (unsigned int i=0 ; i') + outbuf.append(">"); + else if (ch == '"') + outbuf.append("""); + else if (ch == '\'') + outbuf.append("'"); + else + outbuf.push_back(ch); + } +} + +/** + * + */ +void LSSerializerImpl::writeNode(const Node *nodeArg) +{ + Node *node = (Node *)nodeArg; + + int type = node->getNodeType(); + + switch (type) + { + + //############# + //# DOCUMENT + //############# + case Node::DOCUMENT_NODE: + { + Document *doc = dynamic_cast(node); + writeNode(doc->getDocumentElement()); + } + break; + + //############# + //# TEXT + //############# + case Node::TEXT_NODE: + { + poxml(node->getNodeValue()); + } + break; + + + //############# + //# CDATA + //############# + case Node::CDATA_SECTION_NODE: + { + pos("getNodeValue()); + pos("]]>"); + } + break; + + + //############# + //# ELEMENT + //############# + case Node::ELEMENT_NODE: + { + + indent+=2; + + NamedNodeMap attributes = node->getAttributes(); + int nrAttrs = attributes.getLength(); + + //### Start open tag + spaces(); + po("<"); + pos(node->getNodeName()); + //if (nrAttrs>0) + // pos(newLine); + + //### Attributes + for (int i=0 ; igetNodeName()); + po("=\""); + pos(attr->getNodeValue()); + po("\""); + //pos(newLine); + } + + //### Finish open tag + //if (nrAttrs>0) + // spaces(); + po(">"); + //pos(newLine); + + //### Contents + //spaces(); + pos(node->getNodeValue()); + + //### Children + for (Node *child = node->getFirstChild() ; + child ; + child=child->getNextSibling()) + { + writeNode(child); + } + + //### Close tag + //spaces(); + po("getNodeName()); + po(">"); + pos(newLine); + + indent-=2; + } + break; + + }//switch + +} + + + + + + + + + + +} //namespace ls +} //namespace dom +} //namespace w3c +} //namespace org + + + + + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + diff --git a/src/dom/lsimpl.h b/src/dom/lsimpl.h new file mode 100644 index 000000000..ee6360143 --- /dev/null +++ b/src/dom/lsimpl.h @@ -0,0 +1,376 @@ +#ifndef __LSIMPL_H__ +#define __LSIMPL_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + +#include "domimpl.h" +#include "events.h" +#include "traversal.h" +#include "ls.h" + + +#include "xmlreader.h" + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace ls +{ + + +/*######################################################################### +## LSParserImpl +#########################################################################*/ + +/** + * + */ +class LSParserImpl : virtual public LSParser +{ +public: + + typedef enum + { + PARSE_AS_DATA = 0, + PARSE_AS_DOCUMENT = 1 + } ParsingModes; + + /** + * + */ + virtual bool getBusy(); + + /** + * + */ + virtual Document *parse(const LSInput &input) + throw(dom::DOMException, LSException); + + + /** + * + */ + virtual Document *parseURI(const DOMString &uri) + throw(dom::DOMException, LSException); + + /** + * + */ + virtual Node *parseWithContext(const LSInput &input, + const Node *contextArg, + unsigned short action) + throw(dom::DOMException, LSException); + + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSParserImpl() + {} + + /** + * + */ + LSParserImpl(const LSParserImpl &other) : LSParser(other) + {} + + /** + * + */ + virtual ~LSParserImpl() + {} + + + + //################## + //# Internals + //################## + + +protected: + + XmlReader reader; + LSParserFilter *filter; + +}; + + + + +/*######################################################################### +## LSParserFilterImpl +#########################################################################*/ + +/** + * + */ +class LSParserFilterImpl : virtual public LSParserFilter +{ +public: + + /** + * + */ + virtual unsigned short startElement(const Element *elementArg) + { return 0; } + + /** + * + */ + virtual unsigned short acceptNode(const Node *nodeArg) + { return 0; } + + /** + * + */ + virtual unsigned long getWhatToShow() + { return 0; } + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~LSParserFilterImpl() + {} + + + +}; + +/*######################################################################### +## LSSerializerImpl +#########################################################################*/ + +/** + * + */ +class LSSerializerImpl : virtual public LSSerializer +{ +public: + + + /** + * + */ + virtual bool write(const Node *nodeArg, + const LSOutput &destination) + throw (LSException); + + /** + * + */ + virtual bool writeToURI(const Node *nodeArg, + const DOMString &uri) + throw(LSException); + + /** + * + */ + virtual DOMString writeToString(const Node *nodeArg) + throw(dom::DOMException, LSException); + + //################## + //# Non-API methods + //################## + + /** + * + */ + LSSerializerImpl() + { + indent = 0; + } + + /** + * + */ + virtual ~LSSerializerImpl() + {} + + + +protected: + + /** + * + */ + void writeNode(const Node *nodeArg); + +private: + + void spaces(); + + void po(char *fmt, ...); + + void pos(const DOMString &str); + + void poxml(const DOMString &str); + + DOMString outbuf; + + int indent; + + DOMConfiguration *domConfig; + + LSSerializerFilter *filter; + + + +}; + + + + +/*######################################################################### +## LSSerializerFilterImpl +#########################################################################*/ + +/** + * + */ +class LSSerializerFilterImpl : virtual public LSSerializerFilter +{ +public: + + /** + * + */ + virtual unsigned long getWhatToShow() + { return 0; } + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~LSSerializerFilterImpl() + {} +}; + + + +/*######################################################################### +## DOMImplementationLSImpl +#########################################################################*/ + +/** + * + */ +class DOMImplementationLSImpl : virtual public DOMImplementationLS +{ +public: + + /** + * + */ + virtual LSParser &createLSParser(unsigned short mode, + const DOMString &schemaType) + throw (dom::DOMException) + { + LSParserImpl newParser; + parser = newParser; + return parser; + } + + + /** + * + */ + virtual LSSerializer &createLSSerializer() + { + LSSerializerImpl newSerializer; + serializer = newSerializer; + return serializer; + } + + + /** + * + */ + virtual LSInput createLSInput() + { + LSInput input; + return input; + } + + /** + * + */ + virtual LSOutput createLSOutput() + { + LSOutput output; + return output; + } + + //################## + //# Non-API methods + //################## + + /** + * + */ + virtual ~DOMImplementationLSImpl() {} + +protected: + + LSParserImpl parser; + LSSerializerImpl serializer; +}; + + + + + + +} //namespace ls +} //namespace dom +} //namespace w3c +} //namespace org + + + + +#endif /* __LSIMPL_H__ */ + +/*######################################################################### +## E N D O F F I L E +#########################################################################*/ + diff --git a/src/dom/mingwenv.bat b/src/dom/mingwenv.bat new file mode 100644 index 000000000..996566e7b --- /dev/null +++ b/src/dom/mingwenv.bat @@ -0,0 +1,2 @@ +set PATH=c:\mingw\bin;%PATH% +set RM=del diff --git a/src/dom/minidom.cpp b/src/dom/minidom.cpp new file mode 100644 index 000000000..6038b7dc5 --- /dev/null +++ b/src/dom/minidom.cpp @@ -0,0 +1,690 @@ + + + +#include +#include +#include +#include +#include +#include + + +#include "minidom.h" + +namespace MiniDom +{ + + + +//######################################################################## +//# E L E M E N T +//######################################################################## + +void Element::findElementsRecursive(std::vector&res, const DOMString &name) +{ + if (getName() == name) + res.push_back(this); + for (int i=0; ifindElementsRecursive(res, name); +} + +std::vector Element::findElements(const DOMString &name) +{ + std::vector res; + findElementsRecursive(res, name); + return res; +} + +DOMString Element::getAttribute(const DOMString &name) +{ + for (int i=0 ; i\n"); + + //Between the tags + if (value.size() > 0) + { + for (int i=0;iwriteIndentedRecursive(f, indent+2); + + //Closing tag + for (int i=0; i\n", name.c_str()); +} + +void Element::writeIndented(FILE *f) +{ + writeIndentedRecursive(f, 0); +} + +void Element::print() +{ + writeIndented(stdout); +} + + +//######################################################################## +//# P A R S E R +//######################################################################## + + + +typedef struct + { + char *escaped; + char value; + } EntityEntry; + +static EntityEntry entities[] = +{ + { "&" , '&' }, + { "<" , '<' }, + { ">" , '>' }, + { "'", '\'' }, + { """, '"' }, + { NULL , '\0' } +}; + + + +void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr) +{ + long line = 1; + long col = 1; + for (long i=0 ; i= parselen) + return -1; + currentPosition = pos; + int ch = parsebuf[pos]; + //printf("ch:%c\n", ch); + return ch; +} + + + +int Parser::match(long p0, const char *text) +{ + int p = p0; + while (*text) + { + if (peek(p) != *text) + return p0; + p++; text++; + } + return p; +} + + + +int Parser::skipwhite(long p) +{ + + while (p p) + { + p = p2; + while (p"); + if (p2 > p) + { + p = p2; + break; + } + p++; + } + } + XMLCh b = peek(p); + if (!isspace(b)) + break; + p++; + } + return p; +} + +/* modify this to allow all chars for an element or attribute name*/ +int Parser::getWord(int p0, DOMString &buf) +{ + int p = p0; + while (p' || b=='=') + break; + buf.push_back(b); + p++; + } + return p; +} + +int Parser::getQuoted(int p0, DOMString &buf, int do_i_parse) +{ + + int p = p0; + if (peek(p) != '"' && peek(p) != '\'') + return p0; + p++; + + while ( pvalue ; ee++) + { + int p2 = match(p, ee->escaped); + if (p2>p) + { + buf.push_back(ee->value); + p = p2; + found = true; + break; + } + } + if (!found) + { + error("unterminated entity"); + return false; + } + } + else + { + buf.push_back(b); + p++; + } + } + return p; +} + +int Parser::parseVersion(int p0) +{ + //printf("### parseVersion: %d\n", p0); + + int p = p0; + + p = skipwhite(p0); + + if (peek(p) != '<') + return p0; + + p++; + if (p>=parselen || peek(p)!='?') + return p0; + + p++; + + DOMString buf; + + while (p=parselen || peek(p)!='<') + return p0; + + p++; + + if (peek(p)!='!' || peek(p+1)=='-') + return p0; + p++; + + DOMString buf; + while (p') + { + p++; + break; + } + buf.push_back(ch); + p++; + } + + //printf("Got doctype:%s\n",buf.c_str()); + return p; +} + +int Parser::parseElement(int p0, Element *par,int depth) +{ + + int p = p0; + + int p2 = p; + + p = skipwhite(p); + + //## Get open tag + XMLCh ch = peek(p); + if (ch!='<') + return p0; + + p++; + + DOMString openTagName; + p = skipwhite(p); + p = getWord(p, openTagName); + //printf("####tag :%s\n", openTagName.c_str()); + p = skipwhite(p); + + //Add element to tree + Element *n = new Element(openTagName); + n->parent = par; + par->addChild(n); + + // Get attributes + if (peek(p) != '>') + { + while (p') + break; + else if (ch=='/' && p') + { + p++; + //printf("quick close\n"); + return p; + } + } + DOMString attrName; + p2 = getWord(p, attrName); + if (p2==p) + break; + //printf("name:%s",buf); + p=p2; + p = skipwhite(p); + ch = peek(p); + //printf("ch:%c\n",ch); + if (ch!='=') + break; + p++; + p = skipwhite(p); + // ch = parsebuf[p]; + // printf("ch:%c\n",ch); + DOMString attrVal; + p2 = getQuoted(p, attrVal, true); + p=p2+1; + //printf("name:'%s' value:'%s'\n",attrName.c_str(),attrVal.c_str()); + char *namestr = (char *)attrName.c_str(); + if (strncmp(namestr, "xmlns:", 6)==0) + n->addNamespace(attrName, attrVal); + else + n->addAttribute(attrName, attrVal); + } + } + + bool cdata = false; + + p++; + // ### Get intervening data ### */ + DOMString data; + while (pp) + { + p = p2; + while (p"); + if (p2 > p) + { + p = p2; + break; + } + p++; + } + } + + ch = peek(p); + //# END TAG + if (ch=='<' && !cdata && peek(p+1)=='/') + { + break; + } + //# CDATA + p2 = match(p, " p) + { + cdata = true; + p = p2; + continue; + } + + //# CHILD ELEMENT + if (ch == '<') + { + p2 = parseElement(p, n, depth+1); + if (p2 == p) + { + /* + printf("problem on element:%s. p2:%d p:%d\n", + openTagName.c_str(), p2, p); + */ + return p0; + } + p = p2; + continue; + } + //# ENTITY + if (ch=='&' && !cdata) + { + bool found = false; + for (EntityEntry *ee = entities ; ee->value ; ee++) + { + int p2 = match(p, ee->escaped); + if (p2>p) + { + data.push_back(ee->value); + p = p2; + found = true; + break; + } + } + if (!found) + { + error("unterminated entity"); + return -1; + } + continue; + } + + //# NONE OF THE ABOVE + data.push_back(ch); + p++; + }/*while*/ + + + n->value = data; + //printf("%d : data:%s\n",p,data.c_str()); + + //## Get close tag + p = skipwhite(p); + ch = peek(p); + if (ch != '<') + { + error("no < for end tag\n"); + return p0; + } + p++; + ch = peek(p); + if (ch != '/') + { + error("no / on end tag"); + return p0; + } + p++; + ch = peek(p); + p = skipwhite(p); + DOMString closeTagName; + p = getWord(p, closeTagName); + if (openTagName != closeTagName) + { + error("Mismatched closing tag. Expected . Got '%S'.", + openTagName.c_str(), closeTagName.c_str()); + return p0; + } + p = skipwhite(p); + if (peek(p) != '>') + { + error("no > on end tag for '%s'", closeTagName.c_str()); + return p0; + } + p++; + // printf("close element:%s\n",closeTagName.c_str()); + p = skipwhite(p); + return p; +} + + + + +Element *Parser::parse(XMLCh *buf,int pos,int len) +{ + parselen = len; + parsebuf = buf; + Element *rootNode = new Element("root"); + pos = parseVersion(pos); + pos = parseDoctype(pos); + pos = parseElement(pos, rootNode, 0); + return rootNode; +} + + +Element *Parser::parse(const char *buf, int pos, int len) +{ + + XMLCh *charbuf = (XMLCh *)malloc((len+1) * sizeof(XMLCh)); + long i = 0; + while (i< len) + { + charbuf[i] = (XMLCh)buf[i]; + i++; + } + charbuf[i] = '\0'; + Element *n = parse(charbuf, 0, len-1); + free(charbuf); + return n; +} + +Element *Parser::parse(const DOMString &buf) +{ + long len = buf.size(); + XMLCh *charbuf = (XMLCh *)malloc((len+1) * sizeof(XMLCh)); + long i = 0; + while (i< len) + { + charbuf[i] = (XMLCh)buf[i]; + i++; + } + charbuf[i] = '\0'; + Element *n = parse(charbuf, 0, len-1); + free(charbuf); + return n; +} + +Element *Parser::parseFile(const char *fileName) +{ + + //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh + if (!fileName) + return NULL; + + FILE *f = fopen(fileName, "rb"); + if (!f) + return NULL; + + struct stat statBuf; + if (fstat(fileno(f),&statBuf)<0) + { + fclose(f); + return NULL; + } + long filelen = statBuf.st_size; + + //printf("length:%d\n",filelen); + XMLCh *charbuf = (XMLCh *)malloc((filelen+1) * sizeof(XMLCh)); + for (XMLCh *p=charbuf ; !feof(f) ; p++) + { + *p = (XMLCh)fgetc(f); + } + fclose(f); + charbuf[filelen] = '\0'; + + + /* + printf("nrbytes:%d\n",wc_count); + printf("buf:%ls\n======\n",charbuf); + */ + Element *n = parse(charbuf, 0, filelen-1); + free(charbuf); + return n; +} + + + + + + + +}//namespace MiniDom +//######################################################################## +//# T E S T +//######################################################################## + +bool doTest(char *fileName) +{ + MiniDom::Parser parser; + + MiniDom::Element *elem = parser.parseFile(fileName); + + if (!elem) + { + printf("Parsing failed\n"); + return false; + } + + elem->print(); + + delete elem; + + return true; +} + + + +int main(int argc, char **argv) +{ + if (argc != 2) + { + printf("usage: %s \n", argv[0]); + return 1; + } + + if (!doTest(argv[1])) + return 1; + + return 0; +} + + + +//######################################################################## +//# E N D O F F I L E +//######################################################################## + + diff --git a/src/dom/minidom.h b/src/dom/minidom.h new file mode 100644 index 000000000..9451691ff --- /dev/null +++ b/src/dom/minidom.h @@ -0,0 +1,270 @@ +#include +#include + + +namespace MiniDom +{ +typedef std::string DOMString; +typedef unsigned int XMLCh; + + +class Namespace +{ +public: + Namespace() + {} + + Namespace(const DOMString &prefixArg, const DOMString &namespaceURIArg) + { + prefix = prefixArg; + namespaceURI = namespaceURIArg; + } + + Namespace(const Namespace &other) + { + prefix = other.prefix; + namespaceURI = other.namespaceURI; + } + + virtual ~Namespace() + {} + + virtual DOMString getPrefix() + { return prefix; } + + virtual DOMString getNamespaceURI() + { return namespaceURI; } + +protected: + + DOMString prefix; + DOMString namespaceURI; + +}; + +class Attribute +{ +public: + Attribute() + {} + + Attribute(const DOMString &nameArg, const DOMString &valueArg) + { + name = nameArg; + value = valueArg; + } + + Attribute(const Attribute &other) + { + name = other.name; + value = other.value; + } + + virtual ~Attribute() + {} + + virtual DOMString getName() + { return name; } + + virtual DOMString getValue() + { return value; } + +protected: + + DOMString name; + DOMString value; + +}; + + +class Element +{ +friend class Parser; + +public: + Element() + { + parent = NULL; + } + + Element(const DOMString &nameArg) + { + parent = NULL; + name = nameArg; + } + + Element(const DOMString &nameArg, const DOMString &valueArg) + { + parent = NULL; + name = nameArg; + value = valueArg; + } + + Element(const Element &other) + { + parent = other.parent; + children = other.children; + attributes = other.attributes; + namespaces = other.namespaces; + name = other.name; + value = other.value; + } + + virtual ~Element() + { + for (int i=0 ; i getChildren() + { return children; } + + std::vector findElements(const DOMString &name); + + DOMString getAttribute(const DOMString &name); + + void addChild(Element *child); + + void addAttribute(const DOMString &name, const DOMString &value); + + void addNamespace(const DOMString &prefix, const DOMString &namespaceURI); + + + /** + * Prettyprint an XML tree to an output stream. Elements are indented + * according to element hierarchy. + * @param f a stream to receive the output + * @param elem the element to output + */ + void writeIndented(FILE *f); + + /** + * Prettyprint an XML tree to standard output. This is the equivalent of + * writeIndented(stdout). + * @param elem the element to output + */ + void print(); + +protected: + + + void findElementsRecursive(std::vector&res, const DOMString &name); + + void writeIndentedRecursive(FILE *f, int indent); + + Element *parent; + + std::vectorchildren; + + std::vector attributes; + std::vector namespaces; + + DOMString name; + DOMString value; + +}; + + + + + +class Parser +{ +public: + Parser() + { init(); } + + virtual ~Parser() + {} + + /** + * Parse XML in a char buffer. + * @param buf a character buffer to parse + * @param pos position to start parsing + * @param len number of chars, from pos, to parse. + * @return a pointer to the root of the XML document; + */ + Element *parse(const char *buf,int pos,int len); + + /** + * Parse XML in a char buffer. + * @param buf a character buffer to parse + * @param pos position to start parsing + * @param len number of chars, from pos, to parse. + * @return a pointer to the root of the XML document; + */ + Element *parse(const DOMString &buf); + + /** + * Parse a named XML file. The file is loaded like a data file; + * the original format is not preserved. + * @param fileName the name of the file to read + * @return a pointer to the root of the XML document; + */ + Element *parseFile(const char *fileName); + + + + +private: + + void init() + { + keepGoing = true; + currentNode = NULL; + parselen = 0; + parsebuf = NULL; + currentPosition = 0; + } + + void getLineAndColumn(long pos, long *lineNr, long *colNr); + + void error(char *fmt, ...); + + int peek(long pos); + + int match(long pos, const char *text); + + int skipwhite(long p); + + int getWord(int p0, DOMString &buf); + + int getQuoted(int p0, DOMString &buf, int do_i_parse); + + int parseVersion(int p0); + + int parseDoctype(int p0); + + int parseElement(int p0, Element *par,int depth); + + Element *parse(XMLCh *buf,int pos,int len); + + bool keepGoing; + Element *currentNode; + long parselen; + XMLCh *parsebuf; + DOMString cdatabuf; + long currentPosition; + int colNr; + + +}; + + + +}//namespace MiniDom + + +//######################################################################## +//# E N D O F F I L E +//######################################################################## + diff --git a/src/dom/odf/SvgOdg.cpp b/src/dom/odf/SvgOdg.cpp new file mode 100644 index 000000000..f55114701 --- /dev/null +++ b/src/dom/odf/SvgOdg.cpp @@ -0,0 +1,1551 @@ +/** + * + * This is a small experimental class for converting between + * SVG and OpenDocument .odg files. This code is not intended + * to be a permanent solution for SVG-to-ODG conversion. Rather, + * it is a quick-and-easy test bed for ideas which will be later + * recoded into C++. + * + * --------------------------------------------------------------------- + * + * SvgOdg - A program to experiment with conversions between SVG and ODG + * Copyright (C) 2006 Bob Jamison + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * For more information, please write to rwjj@earthlink.net + * + */ + + +/** + * + */ +public class SvgOdg +{ + + + +/** + * Namespace declarations + */ +public static final String SVG_NS = + "http://www.w3.org/2000/svg"; +public static final String XLINK_NS = + "http://www.w3.org/1999/xlink"; +public static final String ODF_NS = + "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; +public static final String ODG_NS = + "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"; +public static final String ODSVG_NS = + "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"; + + +DecimalFormat nrfmt; +//static final double pxToCm = 0.0339; +static final double pxToCm = 0.0275; +static final double piToRad = 0.0174532925; +BufferedWriter out; +BufferedReader in; +int imageNr; +int styleNr; + +//######################################################################## +//# M E S S A G E S +//######################################################################## + +/** + * + */ +void err(String msg) +{ + System.out.println("SvgOdg ERROR:" + msg); +} + +/** + * + */ +void trace(String msg) +{ + System.out.println("SvgOdg:" + msg); +} + + + + +//######################################################################## +//# I N P U T / O U T P U T +//######################################################################## + +boolean po(String s) +{ + try + { + out.write(s); + } + catch(IOException e) + { + return false; + } + return true; +} + +//######################################################################## +//# U T I L I T Y +//######################################################################## + +public void dumpDocument(Document doc) +{ + String s = ""; + try + { + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer trans = factory.newTransformer(); + DOMSource source = new DOMSource(doc); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + StreamResult result = new StreamResult(bos); + trans.transform(source, result); + byte buf[] = bos.toByteArray(); + s = new String(buf); + } + catch (javax.xml.transform.TransformerException e) + { + } + trace("doc:" + s); +} + + +//######################################################################## +//# I N N E R C L A S S ImageInfo +//######################################################################## +public class ImageInfo +{ +String name; +String newName; +byte buf[]; + +public String getName() +{ + return name; +} + +public String getNewName() +{ + return newName; +} + + +public byte[] getBuf() +{ + return buf; +} + +public ImageInfo(String name, String newName, byte buf[]) +{ + this.name = name; + this.name = newName; + this.buf = buf; +} + +} +//######################################################################## +//# I N N E R C L A S S StyleInfo +//######################################################################## +public class StyleInfo +{ + +String name; +public String getName() +{ + return name; +} + +String cssStyle; +public String getCssStyle() +{ + return cssStyle; +} + +String stroke; +public String getStroke() +{ + return stroke; +} + +String strokeColor; +public String getStrokeColor() +{ + return strokeColor; +} + +String strokeWidth; +public String getStrokeWidth() +{ + return strokeWidth; +} + +String fill; +public String getFill() +{ + return fill; +} + +String fillColor; +public String getFillColor() +{ + return fillColor; +} + +public StyleInfo(String name, String cssStyle) +{ + this.name = name; + this.cssStyle = cssStyle; + fill = "none"; + stroke = "none"; +} + +} +//######################################################################## +//# E N D I N N E R C L A S S E S +//######################################################################## + + + + +//######################################################################## +//# V A R I A B L E S +//######################################################################## + +/** + * ODF content.xml file + */ +Document content; +public Document getContent() +{ + return content; +} + +/** + * ODF meta.xml file + */ +Document meta; +public Document getMeta() +{ + return meta; +} + +/** + * SVG file + */ +Document svg; +public Document getSvg() +{ + return svg; +} + +/** + * Loaded ODF or SVG images + */ +ArrayList images; +public ArrayList getImages() +{ + return images; +} + +/** + * CSS styles + */ +HashMap styles; +public HashMap getStyles() +{ + return styles; +} + + + + + +//######################################################################## +//# S V G T O O D F +//######################################################################## + +class PathData +{ +String cmd; +double nr[]; +PathData(String s, double buf[]) +{ + cmd=s; nr = buf; +} +} + +double getPathNum(StringTokenizer st) +{ + if (!st.hasMoreTokens()) + return 0.0; + String s = st.nextToken(); + double nr = Double.parseDouble(s); + return nr; +} + +String parsePathData(String pathData, double bounds[]) +{ + double minx = Double.MAX_VALUE; + double maxx = Double.MIN_VALUE; + double miny = Double.MAX_VALUE; + double maxy = Double.MIN_VALUE; + //trace("#### pathData:" + pathData); + ArrayList data = new ArrayList(); + StringTokenizer st = new StringTokenizer(pathData, " ,"); + while (true) + { + String s = st.nextToken(); + if ( s.equals("z") || s.equals("Z") ) + { + PathData pd = new PathData(s, new double[0]); + data.add(pd); + break; + } + else if ( s.equals("h") || s.equals("H") ) + { + double d[] = new double[1]; + d[0] = getPathNum(st) * pxToCm; + if (d[0] < minx) minx = d[0]; + else if (d[0] > maxx) maxx = d[0]; + PathData pd = new PathData(s, d); + data.add(pd); + } + else if ( s.equals("v") || s.equals("V") ) + { + double d[] = new double[1]; + d[0] = getPathNum(st) * pxToCm; + if (d[0] < miny) miny = d[0]; + else if (d[0] > maxy) maxy = d[0]; + PathData pd = new PathData(s, d); + data.add(pd); + } + else if ( s.equals("m") || s.equals("M") || + s.equals("l") || s.equals("L") || + s.equals("t") || s.equals("T") ) + { + double d[] = new double[2]; + d[0] = getPathNum(st) * pxToCm; + d[1] = getPathNum(st) * pxToCm; + if (d[0] < minx) minx = d[0]; + else if (d[0] > maxx) maxx = d[0]; + if (d[1] < miny) miny = d[1]; + else if (d[1] > maxy) maxy = d[1]; + PathData pd = new PathData(s, d); + data.add(pd); + } + else if ( s.equals("q") || s.equals("Q") || + s.equals("s") || s.equals("S") ) + { + double d[] = new double[4]; + d[0] = getPathNum(st) * pxToCm; + d[1] = getPathNum(st) * pxToCm; + if (d[0] < minx) minx = d[0]; + else if (d[0] > maxx) maxx = d[0]; + if (d[1] < miny) miny = d[1]; + else if (d[1] > maxy) maxy = d[1]; + d[2] = getPathNum(st) * pxToCm; + d[3] = getPathNum(st) * pxToCm; + if (d[2] < minx) minx = d[2]; + else if (d[2] > maxx) maxx = d[2]; + if (d[3] < miny) miny = d[3]; + else if (d[3] > maxy) maxy = d[3]; + PathData pd = new PathData(s, d); + data.add(pd); + } + else if ( s.equals("c") || s.equals("C") ) + { + double d[] = new double[6]; + d[0] = getPathNum(st) * pxToCm; + d[1] = getPathNum(st) * pxToCm; + if (d[0] < minx) minx = d[0]; + else if (d[0] > maxx) maxx = d[0]; + if (d[1] < miny) miny = d[1]; + else if (d[1] > maxy) maxy = d[1]; + d[2] = getPathNum(st) * pxToCm; + d[3] = getPathNum(st) * pxToCm; + if (d[2] < minx) minx = d[2]; + else if (d[2] > maxx) maxx = d[2]; + if (d[3] < miny) miny = d[3]; + else if (d[3] > maxy) maxy = d[3]; + d[4] = getPathNum(st) * pxToCm; + d[5] = getPathNum(st) * pxToCm; + if (d[4] < minx) minx = d[4]; + else if (d[4] > maxx) maxx = d[4]; + if (d[5] < miny) miny = d[5]; + else if (d[5] > maxy) maxy = d[5]; + PathData pd = new PathData(s, d); + data.add(pd); + } + else if ( s.equals("a") || s.equals("A") ) + { + double d[] = new double[6]; + d[0] = getPathNum(st) * pxToCm; + d[1] = getPathNum(st) * pxToCm; + if (d[0] < minx) minx = d[0]; + else if (d[0] > maxx) maxx = d[0]; + if (d[1] < miny) miny = d[1]; + else if (d[1] > maxy) maxy = d[1]; + d[2] = getPathNum(st) * piToRad;//angle + d[3] = getPathNum(st) * piToRad;//angle + d[4] = getPathNum(st) * pxToCm; + d[5] = getPathNum(st) * pxToCm; + if (d[4] < minx) minx = d[4]; + else if (d[4] > maxx) maxx = d[4]; + if (d[5] < miny) miny = d[5]; + else if (d[5] > maxy) maxy = d[5]; + PathData pd = new PathData(s, d); + data.add(pd); + } + //trace("x:" + x + " y:" + y); + } + + trace("minx:" + minx + " maxx:" + maxx + + " miny:" + miny + " maxy:" + maxy); + + StringBuffer buf = new StringBuffer(); + for (PathData pd : data) + { + buf.append(pd.cmd); + buf.append(" "); + for (double d:pd.nr) + { + buf.append(nrfmt.format(d * 1000.0)); + buf.append(" "); + } + } + + bounds[0] = minx; + bounds[1] = miny; + bounds[2] = maxx; + bounds[3] = maxy; + + return buf.toString(); +} + + + +boolean parseTransform(String transStr, AffineTransform trans) +{ + trace("== transform:"+ transStr); + StringTokenizer st = new StringTokenizer(transStr, ")"); + while (st.hasMoreTokens()) + { + String chunk = st.nextToken(); + StringTokenizer st2 = new StringTokenizer(chunk, " ,("); + if (!st2.hasMoreTokens()) + continue; + String name = st2.nextToken(); + trace(" ++name:"+ name); + if (name.equals("matrix")) + { + double v[] = new double[6]; + for (int i=0 ; i<6 ; i++) + { + if (!st2.hasMoreTokens()) + break; + v[i] = Double.parseDouble(st2.nextToken()) * pxToCm; + } + AffineTransform mat = new AffineTransform(v); + trans.concatenate(mat); + } + else if (name.equals("translate")) + { + double dx = 0.0; + double dy = 0.0; + if (!st2.hasMoreTokens()) + continue; + dx = Double.parseDouble(st2.nextToken()) * pxToCm; + if (st2.hasMoreTokens()) + dy = Double.parseDouble(st2.nextToken()) * pxToCm; + trans.translate(dx, dy); + } + else if (name.equals("scale")) + { + double sx = 1.0; + double sy = 1.0; + if (!st2.hasMoreTokens()) + continue; + sx = sy = Double.parseDouble(st2.nextToken()); + if (st2.hasMoreTokens()) + sy = Double.parseDouble(st2.nextToken()); + trans.scale(sx, sy); + } + else if (name.equals("rotate")) + { + double r = 0.0; + double cx = 0.0; + double cy = 0.0; + if (!st2.hasMoreTokens()) + continue; + r = Double.parseDouble(st2.nextToken()) * piToRad; + if (st2.hasMoreTokens()) + { + cx = Double.parseDouble(st2.nextToken()) * pxToCm; + if (!st2.hasMoreTokens()) + continue; + cy = Double.parseDouble(st2.nextToken()) * pxToCm; + trans.rotate(r, cx, cy); + } + else + { + trans.rotate(r); + } + } + else if (name.equals("skewX")) + { + double angle = 0.0; + if (!st2.hasMoreTokens()) + continue; + angle = Double.parseDouble(st2.nextToken()); + trans.shear(angle, 0.0); + } + else if (name.equals("skewY")) + { + double angle = 0.0; + if (!st2.hasMoreTokens()) + continue; + angle = Double.parseDouble(st2.nextToken()); + trans.shear(0.0, angle); + } + } + return true; +} + + + +String coordToOdg(String sval) +{ + double nr = Double.parseDouble(sval); + nr = nr * pxToCm; + String s = nrfmt.format(nr) + "cm"; + return s; +} + + +boolean writeSvgAttributes(Element elem, AffineTransform trans) +{ + NamedNodeMap attrs = elem.getAttributes(); + String ename = elem.getLocalName(); + for (int i=0 ; i\n"); + NodeList children = elem.getChildNodes(); + for (int i=0 ; i\n"); + } + else if (tagName.equals("text")) + { + String x = coordToOdg(elem.getAttribute("x")); + String y = coordToOdg(elem.getAttribute("y")); + String width = "5cm"; + String height = "2cm"; + String txt = elem.getTextContent(); + po("\n"); + po(" \n"); + po(" " + txt + "\n"); + po(" \n"); + po("\n"); + return true; + } + else if (tagName.equals("image")) + { + String x = coordToOdg(elem.getAttribute("x")); + String y = coordToOdg(elem.getAttribute("y")); + String width = coordToOdg(elem.getAttribute("width")); + String height = coordToOdg(elem.getAttribute("height")); + String imageName = elem.getAttributeNS(XLINK_NS, "href"); + po("\n"); + po(" \n"); + po("\n"); + return true; + } + else if (tagName.equals("path")) + { + double bounds[] = new double[4]; + String d = elem.getAttribute("d"); + String newd = parsePathData(d, bounds); + double x = bounds[0]; + double y = bounds[1]; + double width = (bounds[2]-bounds[0]); + double height = (bounds[3]-bounds[1]); + po("\n"); + //po(" svg:d=\"" + d + "\"/>\n"); + return true; + } + + else + { + //Verbatim tab mapping + po("\n"); + po("\n"); + } + + return true; +} + + +boolean writeOdfContent(ZipOutputStream outs) +{ + try + { + ZipEntry ze = new ZipEntry("content.xml"); + outs.putNextEntry(ze); + out = new BufferedWriter(new OutputStreamWriter(outs)); + } + catch (IOException e) + { + return false; + } + + NodeList res = svg.getElementsByTagNameNS(SVG_NS, "svg"); + if (res.getLength() < 1) + { + err("saveOdf: no root in .svg file"); + return false; + } + Element root = (Element)res.item(0); + + + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po(" \n"); + po("\n"); + po("\n"); + po(" \n"); + po("\n"); + + //## Dump our style table + for (StyleInfo s : styles.values()) + { + po("\n"); + po(" \n"); + po("\n"); + } + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n"); + po("\n\n\n"); + AffineTransform trans = new AffineTransform(); + //trans.scale(12.0, 12.0); + po("\n"); + writeOdfContent(root, trans); + po("\n"); + po("\n\n\n"); + + po("\n"); + po("\n"); + po("\n"); + po("\n"); + + + try + { + out.flush(); + outs.closeEntry(); + } + catch (IOException e) + { + err("writeOdfContent:" + e); + return false; + } + return true; +} + +boolean writeOdfMeta(ZipOutputStream outs) +{ + try + { + ZipEntry ze = new ZipEntry("meta.xml"); + outs.putNextEntry(ze); + out = new BufferedWriter(new OutputStreamWriter(outs)); + } + catch (IOException e) + { + return false; + } + + po("\n"); + po("\n"); + po("\n"); + po(" Inkscape-0.43\n"); + po(" clark kent\n"); + po(" 2005-12-10T10:55:13\n"); + po(" clark kent\n"); + po(" 2005-12-10T10:56:20\n"); + po(" en-US\n"); + po(" 2\n"); + po(" PT1M13S\n"); + po(" \n"); + po(" \n"); + po(" \n"); + po(" \n"); + po(" \n"); + po("\n"); + po("\n"); + + + try + { + out.flush(); + outs.closeEntry(); + } + catch (IOException e) + { + err("writeOdfContent:" + e); + return false; + } + return true; +} + + +boolean writeOdfManifest(ZipOutputStream outs) +{ + try + { + ZipEntry ze = new ZipEntry("META-INF/manifest.xml"); + outs.putNextEntry(ze); + out = new BufferedWriter(new OutputStreamWriter(outs)); + } + catch (IOException e) + { + return false; + } + + po("\n"); + po("\n"); + po("\n"); + po(" \n"); + po(" \n"); + po(" \n"); + po(" \n"); + for (int i=0 ; i\n"); + } + po("\n"); + + try + { + out.flush(); + outs.closeEntry(); + } + catch (IOException e) + { + err("writeOdfContent:" + e); + return false; + } + return true; +} + +boolean writeOdfImages(ZipOutputStream outs) +{ + for (int i=0 ; i style.length()-2) + continue; + String attrName = style.substring(0, pos); + String attrVal = style.substring(pos+1); + trace(" =" + attrName + ':' + attrVal); + if ("stroke".equals(attrName)) + { + si.stroke = "solid"; + si.strokeColor = attrVal; + } + else if ("stroke-width".equals(attrName)) + { + si.strokeWidth = attrVal; + } + else if ("fill".equals(attrName)) + { + si.fill = "solid"; + si.fillColor = attrVal; + } + } + styles.put(css, si); + return true; +} + +boolean readSvg(InputStream ins) +{ + //### LOAD XML + try + { + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = + factory.newDocumentBuilder(); + builder.setEntityResolver(new EntityResolver() + { + public InputSource resolveEntity(String publicId, String systemId) + { + return new InputSource(new ByteArrayInputStream(new byte[0])); + } + }); + Document doc = builder.parse(ins); + svg = doc; + } + catch (javax.xml.parsers.ParserConfigurationException e) + { + err("making DOM parser:"+e); + return false; + } + catch (org.xml.sax.SAXException e) + { + err("parsing svg document:"+e); + return false; + } + catch (IOException e) + { + err("parsing svg document:"+e); + return false; + } + //dumpDocument(svg); + + //### LOAD IMAGES + imageNr = 0; + images = new ArrayList(); + NodeList res = svg.getElementsByTagNameNS(SVG_NS, "image"); + for (int i=0 ; i(); + res = svg.getElementsByTagName("*"); + for (int i=0 ; i 0) + parseCss(style); + } + + return true; +} + +boolean readSvg(String fileName) +{ + try + { + FileInputStream fis = new FileInputStream(fileName); + if (!readSvg(fis)) + return false; + fis.close(); + } + catch (IOException e) + { + err("opening svg file:"+e); + return false; + } + return true; +} + + +//######################################################################## +//# O D F T O S V G +//######################################################################## + +/** + * + */ +public boolean readOdfEntry(ZipInputStream zis, ZipEntry ent) +{ + String fileName = ent.getName(); + trace("fileName:" + fileName); + if (fileName.length() < 4) + return true; + String ext = fileName.substring(fileName.length() - 4); + trace("ext:" + ext); + ArrayList arr = new ArrayList(); + try + { + while (true) + { + int inb = zis.read(); + if (inb < 0) + break; + arr.add((byte)inb); + } + } + catch (IOException e) + { + return false; + } + byte buf[] = new byte[arr.size()]; + for (int i=0 ; i(); + styleNr = 0; + styles = new HashMap(); + ZipInputStream zis = new ZipInputStream(ins); + while (true) + { + try + { + ZipEntry ent = zis.getNextEntry(); + if (ent == null) + break; + if (!readOdfEntry(zis, ent)) + return false; + zis.closeEntry(); + } + catch (IOException e) + { + err("reading zip entry"); + return false; + } + } + + return true; +} + + +/** + * + */ +public boolean readOdf(String fileName) +{ + boolean ret = true; + try + { + FileInputStream fis = new FileInputStream(fileName); + ret = readOdf(fis); + fis.close(); + } + catch (IOException e) + { + err("reading " + fileName + " : " + e); + ret = false; + } + return true; +} + + + + +public boolean writeSvgElement(Element elem) +{ + String ns = elem.getNamespaceURI(); + String tagName = elem.getLocalName(); + trace("tag:" + tagName + " : " + ns); + if (ns.equals(ODSVG_NS)) + { + po("<"); po(tagName); + NamedNodeMap attrs = elem.getAttributes(); + for (int i=0 ; i\n"); + } + NodeList children = elem.getChildNodes(); + for (int i=0 ; i\n"); + } + return true; +} + + +public boolean saveSvg(String svgFileName) +{ + trace("====== Saving images ==========="); + try + { + for (int i=0 ; i\n"); + po("\n"); + + writeSvgElement(page); + + po("\n"); + + try + { + out.close(); + } + catch (IOException e) + { + err("save:close:" + e); + return false; + } + return true; +} + + + +//######################################################################## +//# C O N S T R U C T O R +//######################################################################## + +SvgOdg() +{ + //init, if necessary + nrfmt = new DecimalFormat("#.####"); +} + + +//######################################################################## +//# C O M M A N D L I N E +//######################################################################## + +public boolean odfToSvg(String odfName, String svgName) +{ + if (!readOdf(odfName)) + return false; + if (!saveSvg(svgName)) + return false; + return true; +} + +public boolean svgToOdf(String svgName, String odfName) +{ + if (!readSvg(svgName)) + return false; + System.out.println("ok"); + if (!saveOdf(odfName)) + return false; + return true; +} + +void usage() +{ + System.out.println("usage: SvgOdf input_file.odg output_file.svg"); + System.out.println(" SvgOdf input_file.svg output_file.odg"); +} + + +boolean parseArguments(String argv[]) +{ + if (argv.length != 2) + { + usage(); + return false; + } + + String fileName1 = argv[0]; + String fileName2 = argv[1]; + + if (fileName1.length()<5 || fileName2.length()<5) + { + System.out.println("one or more file names is too short"); + usage(); + return false; + } + + String ext1 = fileName1.substring(fileName1.length()-4); + String ext2 = fileName2.substring(fileName2.length()-4); + //System.out.println("ext1:"+ext1+" ext2:"+ext2); + + //##### ODG -> SVG ##### + if ((ext1.equals(".odg") || ext1.equals(".odf") || ext1.equals(".zip") ) && + ext2.equals(".svg")) + { + if (!odfToSvg(argv[0], argv[1])) + { + System.out.println("Conversion from ODG to SVG failed"); + return false; + } + } + + //##### SVG -> ODG ##### + else if (ext1.equals(".svg") && + ( ext2.equals(".odg") || ext2.equals(".odf") || ext2.equals(".zip") ) ) + { + if (!svgToOdf(fileName1, fileName2)) + { + System.out.println("Conversion from SVG to ODG failed"); + return false; + } + } + + //##### none of the above ##### + else + { + usage(); + return false; + } + return true; +} + + +public static void main(String argv[]) +{ + SvgOdg svgodg = new SvgOdg(); + svgodg.parseArguments(argv); +} + + +} + +//######################################################################## +//# E N D O F F I L E +//######################################################################## + diff --git a/src/dom/odf/odfdocument.cpp b/src/dom/odf/odfdocument.cpp new file mode 100644 index 000000000..ba6cfc63b --- /dev/null +++ b/src/dom/odf/odfdocument.cpp @@ -0,0 +1,161 @@ +/** + * + * This class contains an ODF Document. + * Initially, we are just concerned with .odg content.xml + resources + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2006 Bob Jamison + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * For more information, please write to rwjj@earthlink.net + * + */ + +#include "odfdocument.h" + + +namespace odf +{ + + +//######################################################################## +//# I M A G E D A T A +//######################################################################## + + +/** + * + */ +ImageData::ImageData(const std::string &fname, + const std::vector &buf) +{ + fileName = fname; + data = buf; +} + +/** + * + */ +ImageData::ImageData(const ImageData &other) +{ + fileName = other.fileName; + data = other.data; +} + +/** + * + */ +ImageData::~ImageData() +{ +} + +/** + * + */ +std::string ImageData::getFileName() +{ + return fileName; +} + +/** + * + */ +void ImageData::setFileName(const std::string &val) +{ + fileName = val; +} + +/** + * + */ +std::vector &ImageData::getData() +{ + return data; +} + +/** + * + */ +void ImageData::setData(const std::vector &buf) +{ + data = buf; +} + + + + + +//######################################################################## +//# O D F D O C U M E N T +//######################################################################## + + + +/** + * + */ +OdfDocument::OdfDocument() +{ +} + + +/** + * + */ +OdfDocument::OdfDocument(const OdfDocument &other) +{ + content = other.content; + images = other.images; +} + + +/** + * + */ +OdfDocument::~OdfDocument() +{ +} + +/** + * + */ +bool OdfDocument::readFile(const std::string &fileName) +{ + return true; +} + +/** + * + */ +bool OdfDocument::writeFile(const std::string &fileName) +{ + return true; +} + + + + + +} //namespace odf + + + + +//######################################################################## +//# E N D O F F I L E +//######################################################################## + diff --git a/src/dom/odf/odfdocument.h b/src/dom/odf/odfdocument.h new file mode 100644 index 000000000..faae4dc4d --- /dev/null +++ b/src/dom/odf/odfdocument.h @@ -0,0 +1,153 @@ +#ifndef __ODF_DOCUMENT_H__ +#define __ODF_DOCUMENT_H__ +/** + * + * This class contains an ODF Document. + * Initially, we are just concerned with .odg content.xml + resources + * + * --------------------------------------------------------------------- + * + * Copyright (C) 2006 Bob Jamison + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * For more information, please write to rwjj@earthlink.net + * + */ + +#include +#include + +#include "dom.h" + +namespace odf +{ + + +//######################################################################## +//# I M A G E D A T A +//######################################################################## + +/** + * + */ +class ImageData +{ +public: + + /** + * + */ + ImageData(const std::string &fileName, + const std::vector &buf); + + /** + * + */ + ImageData(const ImageData &other); + + /** + * + */ + virtual ~ImageData(); + + /** + * + */ + virtual std::string getFileName(); + + /** + * + */ + virtual void setFileName(const std::string &val); + + /** + * + */ + virtual std::vector &getData(); + + /** + * + */ + virtual void setData(const std::vector &buf); + +private: + + std::string fileName; + + std::vector data; + +}; + + + + + +//######################################################################## +//# O D F D O C U M E N T +//######################################################################## + + +/** + * + */ +class OdfDocument +{ +public: + + /** + * + */ + OdfDocument(); + + /** + * Copy constructor + */ + OdfDocument(const OdfDocument &other); + + /** + * + */ + virtual ~OdfDocument(); + + /** + * + */ + virtual bool readFile(const std::string &fileName); + + /** + * + */ + virtual bool writeFile(const std::string &fileName); + + +private: + + org::w3c::dom::Document *content; + + std::vector images; + +}; + +} //namespace odf + + + +#endif /*__ODF_DOCUMENT_H__*/ + +//######################################################################## +//# E N D O F F I L E +//######################################################################## + diff --git a/src/dom/phoebedom.h b/src/dom/phoebedom.h new file mode 100644 index 000000000..4f8143843 --- /dev/null +++ b/src/dom/phoebedom.h @@ -0,0 +1,86 @@ +#ifndef __PHOBEDOM_H__ +#define __PHOBEDOM_H__ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ + + +#include "domimpl.h" +#include "lsimpl.h" +#include "svglsimpl.h" + +namespace phoebe +{ + +class PhoebeDOMImplementation : public org::w3c::dom::ls::DOMImplementationLSImpl +{ + +public: + + /** + * + */ + PhoebeDOMImplementation() + {} + + org::w3c::dom::ls::LSParser createLSParser(unsigned short mode, + const org::w3c::dom::DOMString &schemaType) + throw (org::w3c::dom::DOMException) + { + if (schemaType == "svg" || schemaType == "SVG") + { + org::w3c::dom::ls::SVGLSParserImpl parser; + return parser; + } + else + { + org::w3c::dom::ls::LSParser parser; + return parser; + } + } + + + /** + * + */ + virtual ~PhoebeDOMImplementation() + {} + +private: + + + +}; + + + +} // namespace phoebe + + +#endif /* __PHOBEDOM_H__ */ + diff --git a/src/dom/prop-css.cpp b/src/dom/prop-css.cpp new file mode 100644 index 000000000..59da4b54f --- /dev/null +++ b/src/dom/prop-css.cpp @@ -0,0 +1,1161 @@ +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * 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 + */ +#include + + +struct CssProp_def +{ + char *name; + char *values; + char *defaultValue; + char *appliesTo; + bool inherited; + char *percentages; + char *mediaGroups; +}; + +typedef struct CssProp_def CssProp; + +static CssProp cssProps[] = +{ + +{ +"azimuth", +" | [[ left-side | far-left | left | center-left | center | center-right | right | far-right | right-side ] || behind ] | leftwards | rightwards | inherit", +"center", +"", +true, +"", +"aural" +}, + + +{ +"background-attachment", +"scroll | fixed | inherit", +"scroll", +"", +false, +"", +"visual" +}, + + +{ +"background-color", +" | transparent | inherit", +"transparent", +"", +false, +"", +"visual" +}, + + +{ +"background-image", +" | none | inherit", +"none", +"", +false, +"", +"visual" +}, + + +{ +"background-position", +"[ [ | | left | center | right ] [ | | top | center | bottom ]? ] | [ [ left | center | right ] || [ top | center | bottom ] ] | inherit", +"0% 0%", +"", +false, +"refer to the size of the box itself", +"visual" +}, + + +{ +"background-repeat", +"repeat | repeat-x | repeat-y | no-repeat | inherit", +"repeat", +"", +false, +"", +"visual" +}, + + +{ +"background", +"['background-color' || 'background-image' || 'background-repeat' || 'background-attachment' || 'background-position'] | inherit", +"see individual properties", +"", +false, +"allowed on 'background-position", +"visual" +}, + + +{ +"border-collapse", +"collapse | separate | inherit", +"separate", +"table' and 'inline-table' elements", +true, +"", +"visual" +}, + + +{ +"border-color", +"[ | transparent ]{1,4} | inherit", +"see individual properties", +"", +false, +"", +"visual" +}, + + +{ +"border-spacing", +" ? | inherit", +"0", +"table' and 'inline-table' elements", +true, +"", +"visual" +}, + + +{ +"border-style", +"{1,4} | inherit", +"see individual properties", +"", +false, +"", +"visual" +}, + + +{ +"border-top' 'border-right' 'border-bottom' 'border-left", +"[ || || 'border-top-color' ] | inherit", +"see individual properties", +"", +false, +"", +"visual" +}, + + +{ +"border-top-color' 'border-right-color' 'border-bottom-color' 'border-left-color", +" | transparent | inherit", +"the value of the 'color' property", +"", +false, +"", +"visual" +}, + + +{ +"border-top-style' 'border-right-style' 'border-bottom-style' 'border-left-style", +" | inherit", +"none", +"", +false, +"", +"visual" +}, + + +{ +"border-top-width' 'border-right-width' 'border-bottom-width' 'border-left-width", +" | inherit", +"medium", +"", +false, +"", +"visual" +}, + + +{ +"border-width", +"{1,4} | inherit", +"see individual properties", +"", +false, +"", +"visual" +}, + + +{ +"border", +"[ || || 'border-top-color' ] | inherit", +"see individual properties", +"", +false, +"", +"visual" +}, + + +{ +"bottom", +" | | auto | inherit", +"auto", +"positioned elements", +false, +"refer to height of containing block", +"visual" +}, + + +{ +"caption-side", +"top | bottom | inherit", +"top", +"table-caption' elements", +true, +"", +"visual" +}, + + +{ +"clear", +"none | left | right | both | inherit", +"none", +"block-level elements", +false, +"", +"visual" +}, + + +{ +"clip", +" | auto | inherit", +"auto", +"absolutely positioned elements", +false, +"", +"visual" +}, + + +{ +"color", +" | inherit", +"depends on user agent", +"", +true, +"", +"visual" +}, + + +{ +"content", +"normal | [ | | | attr() | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit", +"normal", +":before and :after pseudo-elements", +false, +"", +"all " +}, + + +{ +"counter-increment", +"[ ? ]+ | none | inherit", +"none", +"", +false, +"", +"all " +}, + + +{ +"counter-reset", +"[ ? ]+ | none | inherit", +"none", +"", +false, +"", +"all " +}, + + +{ +"cue-after", +" | none | inherit", +"none", +"", +false, +"", +"aural" +}, + + +{ +"cue-before", +" | none | inherit", +"none", +"", +false, +"", +"aural" +}, + + +{ +"cue", +"[ 'cue-before' || 'cue-after' ] | inherit", +"see individual properties", +"", +false, +"", +"aural" +}, + + +{ +"cursor", +"[ [ ,]* [ auto | crosshair | default | pointer | move | e-resize | ne-resize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | text | wait | help | progress ] ] | inherit", +"auto", +"", +true, +"", +"visual, interactive " +}, + + +{ +"direction", +"ltr | rtl | inherit", +"ltr", +"all elements, but see prose", +true, +"", +"visual" +}, + + +{ +"display", +"inline | block | list-item | run-in | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | none | inherit", +"inline", +"", +false, +"", +"all " +}, + + +{ +"elevation", +" | below | level | above | higher | lower | inherit", +"level", +"", +true, +"", +"aural" +}, + + +{ +"empty-cells", +"show | hide | inherit", +"show", +"table-cell' elements", +true, +"", +"visual" +}, + + +{ +"float", +"left | right | none | inherit", +"none", +"all, but see 9.7", +false, +"", +"visual" +}, + + +{ +"font-family", +"[[ | ] [, | ]* ] | inherit", +"depends on user agent", +"", +true, +"", +"visual" +}, + + +{ +"font-size", +" | | | | inherit", +"medium", +"", +true, +"refer to parent element's font size", +"visual" +}, + + +{ +"font-style", +"normal | italic | oblique | inherit", +"normal", +"", +true, +"", +"visual" +}, + + +{ +"font-variant", +"normal | small-caps | inherit", +"normal", +"", +true, +"", +"visual" +}, + + +{ +"font-weight", +"normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit", +"normal", +"", +true, +"", +"visual" +}, + + +{ +"font", +"[ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit", +"see individual properties", +"", +true, +"see individual properties", +"visual" +}, + + +{ +"height", +" | | auto | inherit", +"auto", +"all elements but non-replaced inline elements, table columns, and column groups", +false, +"see prose", +"visual" +}, + + +{ +"left", +" | | auto | inherit", +"auto", +"positioned elements", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"letter-spacing", +"normal | | inherit", +"normal", +"", +true, +"", +"visual" +}, + + +{ +"line-height", +"normal | | | | inherit", +"normal", +"", +true, +"refer to the font size of the element itself", +"visual" +}, + + +{ +"list-style-image", +" | none | inherit", +"none", +"elements with 'display: list-item", +true, +"", +"visual" +}, + + +{ +"list-style-position", +"inside | outside | inherit", +"outside", +"elements with 'display: list-item", +true, +"", +"visual" +}, + + +{ +"list-style-type", +"disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | none | inherit", +"disc", +"elements with 'display: list-item", +true, +"", +"visual" +}, + + +{ +"list-style", +"[ 'list-style-type' || 'list-style-position' || 'list-style-image' ] | inherit", +"see individual properties", +"elements with 'display: list-item", +true, +"", +"visual" +}, + + +{ +"margin-right' 'margin-left", +" | inherit", +"0", +"all elements except elements with table display types other than table and inline-table", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"margin-top' 'margin-bottom", +" | inherit", +"0", +"all elements except elements with table display types other than table and inline-table", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"margin", +"{1,4} | inherit", +"see individual properties", +"all elements except elements with table display types other than table and inline-table", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"max-height", +" | | none | inherit", +"none", +"all elements except non-replaced inline elements and table elements", +false, +"see prose", +"visual" +}, + + +{ +"max-width", +" | | none | inherit", +"none", +"all elements except non-replaced inline elements and table elements", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"min-height", +" | | inherit", +"0", +"all elements except non-replaced inline elements and table elements", +false, +"see prose", +"visual" +}, + + +{ +"min-width", +" | | inherit", +"0", +"all elements except non-replaced inline elements and table elements", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"orphans", +" | inherit", +"2", +"block-level elements", +true, +"", +"visual, paged " +}, + + +{ +"outline-color", +" | invert | inherit", +"invert", +"", +false, +"", +"visual, interactive " +}, + + +{ +"outline-style", +" | inherit", +"none", +"", +false, +"", +"visual, interactive " +}, + + +{ +"outline-width", +" | inherit", +"medium", +"", +false, +"", +"visual, interactive " +}, + + +{ +"outline", +"[ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit", +"see individual properties", +"", +false, +"", +"visual, interactive " +}, + + +{ +"overflow", +"visible | hidden | scroll | auto | inherit", +"visible", +"block-level and replaced elements, table cells, inline blocks", +false, +"", +"visual" +}, + + +{ +"padding-top' 'padding-right' 'padding-bottom' 'padding-left", +" | inherit", +"0", +"all elements except elements with table display types other than table, inline-table, and table-cell", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"padding", +"{1,4} | inherit", +"see individual properties", +"all elements except elements with table display types other than table, inline-table, and table-cell", +false, +"refer to width of containing block", +"visual" +}, + + +{ +"page-break-after", +"auto | always | avoid | left | right | inherit", +"auto", +"block-level elements", +false, +"", +"visual, paged " +}, + + +{ +"page-break-before", +"auto | always | avoid | left | right | inherit", +"auto", +"block-level elements", +false, +"", +"visual, paged " +}, + + +{ +"page-break-inside", +"avoid | auto | inherit", +"auto", +"block-level elements", +true, +"", +"visual, paged " +}, + + +{ +"pause-after", +"