From: ishmal Date: Mon, 5 Mar 2007 10:34:59 +0000 (+0000) Subject: update JS X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=864f6d5e6b021a4a55a2554749b1cf629be1de43;p=inkscape.git update JS --- diff --git a/src/dom/Makefile.mingw b/src/dom/Makefile.mingw index 33b6aed9e..b47437265 100644 --- a/src/dom/Makefile.mingw +++ b/src/dom/Makefile.mingw @@ -203,6 +203,7 @@ js/jsscript.o \ js/jsstr.o \ js/jsutil.o \ js/jsxdrapi.o \ +js/jsxml.o \ js/prmjtime.o \ js/fdlibm/e_acos.o \ js/fdlibm/e_acosh.o \ diff --git a/src/dom/js/README.ink b/src/dom/js/README.ink new file mode 100644 index 000000000..bbb53278c --- /dev/null +++ b/src/dom/js/README.ink @@ -0,0 +1,10 @@ +README for Inkscape's JS Embedding + +Note that we edited jstypes.h to +#if defined(_WIN32) && !defined(__MWERKS__) && !defined(__GNUC__) + +So that we can statically link JS to Inkscape on MinGW. + + +bob +6 Mar 07 diff --git a/src/dom/js/fdlibm/.cvsignore b/src/dom/js/fdlibm/.cvsignore deleted file mode 100644 index bb5cc66ee..000000000 --- a/src/dom/js/fdlibm/.cvsignore +++ /dev/null @@ -1,7 +0,0 @@ -*.pdb -*.ncb -*.opt -*.plg -Debug -Release -Makefile diff --git a/src/dom/js/fdlibm/Makefile.in b/src/dom/js/fdlibm/Makefile.in deleted file mode 100644 index fdec7b7e8..000000000 --- a/src/dom/js/fdlibm/Makefile.in +++ /dev/null @@ -1,127 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Communicator client code, released -# March 31, 1998. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient 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 deleted file mode 100644 index de378025c..000000000 --- a/src/dom/js/fdlibm/Makefile.ref +++ /dev/null @@ -1,192 +0,0 @@ -# -*- 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_rem_pio2.c b/src/dom/js/fdlibm/e_rem_pio2.c index 6f9423551..c9d261875 100644 --- a/src/dom/js/fdlibm/e_rem_pio2.c +++ b/src/dom/js/fdlibm/e_rem_pio2.c @@ -126,7 +126,8 @@ pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ #endif { fd_twoints u, ux, uz; - double z,w,t,r,fn; + double z = 0; + double w,t,r,fn; double tx[3]; int e0,i,j,nx,n,ix,hx; diff --git a/src/dom/js/fdlibm/fdlibm.h b/src/dom/js/fdlibm/fdlibm.h index 8e25214f0..e623be56e 100644 --- a/src/dom/js/fdlibm/fdlibm.h +++ b/src/dom/js/fdlibm/fdlibm.h @@ -61,13 +61,13 @@ #define __LITTLE_ENDIAN #endif -#if defined(linux) && (defined(__i386__) || defined(__x86_64__)) +#if defined(linux) && (defined(__i386__) || defined(__x86_64__) || defined(__ia64) || (defined(__mips) && defined(__MIPSEL__))) #define __LITTLE_ENDIAN #endif /* End here. The rest is the standard file. */ -#ifdef __NEWVALID /* special setup for Sun test regime */ +#ifdef SOLARIS /* special setup for Sun test regime */ #if defined(i386) || defined(i486) || \ defined(intel) || defined(x86) || defined(i86pc) #define __LITTLE_ENDIAN diff --git a/src/dom/js/fdlibm/fdlibm.mak b/src/dom/js/fdlibm/fdlibm.mak deleted file mode 100644 index 436c1c450..000000000 --- a/src/dom/js/fdlibm/fdlibm.mak +++ /dev/null @@ -1,1453 +0,0 @@ -# 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 deleted file mode 100644 index 5904c4940..000000000 Binary files a/src/dom/js/fdlibm/fdlibm.mdp and /dev/null differ diff --git a/src/dom/js/fdlibm/k_cos.c b/src/dom/js/fdlibm/k_cos.c index 17b505d58..1d18c8034 100644 --- a/src/dom/js/fdlibm/k_cos.c +++ b/src/dom/js/fdlibm/k_cos.c @@ -107,7 +107,8 @@ C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ #endif { fd_twoints u; - double a,hz,z,r,qx; + double qx = 0; + double a,hz,z,r; int ix; u.d = x; ix = __HI(u)&0x7fffffff; /* ix = |x|'s high word*/ diff --git a/src/dom/js/js.c b/src/dom/js/js.c index 77c778b92..b39616670 100644 --- a/src/dom/js/js.c +++ b/src/dom/js/js.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -90,8 +91,11 @@ #include /* for isatty() */ #endif -#define EXITCODE_RUNTIME_ERROR 3 -#define EXITCODE_FILE_NOT_FOUND 4 +typedef enum JSShellExitCode { + EXITCODE_RUNTIME_ERROR = 3, + EXITCODE_FILE_NOT_FOUND = 4, + EXITCODE_OUT_OF_MEMORY = 5 +} JSShellExitCode; size_t gStackChunkSize = 8192; static size_t gMaxStackSize = 0; @@ -101,133 +105,6 @@ 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 @@ -236,6 +113,7 @@ static JSDJContext *_jsdjc; #endif /* JSDEBUGGER */ static JSBool reportWarnings = JS_TRUE; +static JSBool compileOnly = JS_FALSE; typedef enum JSShellErrNum { #define MSG_DEF(name, number, count, exception, format) \ @@ -278,10 +156,6 @@ GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { 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); @@ -348,7 +222,8 @@ Process(JSContext *cx, JSObject *obj, char *filename) ungetc(ch, file); script = JS_CompileFileHandle(cx, obj, filename, file); if (script) { - (void)JS_ExecuteScript(cx, obj, script, &result); + if (!compileOnly) + (void)JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } return; @@ -379,21 +254,18 @@ Process(JSContext *cx, JSObject *obj, char *filename) /* 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 + script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", 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; + if (!compileOnly) { + 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); } @@ -406,7 +278,7 @@ 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"); + fprintf(gErrFile, "usage: js [-PswWxC] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); return 2; } @@ -417,10 +289,15 @@ 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); + if (script) { + if (script->filename) + fprintf(gErrFile, "%s:", script->filename); + fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", + script->lineno, gBranchLimit); + } else { + fprintf(gErrFile, "native branch callback (%u callbacks)\n", + gBranchLimit); + } gBranchCount = 0; return JS_FALSE; } @@ -454,10 +331,12 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) case 'b': case 'c': case 'f': + case 'e': case 'v': case 'S': ++i; break; + default:; } } @@ -511,6 +390,10 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) JS_ToggleOptions(cx, JSOPTION_STRICT); break; + case 'x': + JS_ToggleOptions(cx, JSOPTION_XML); + break; + case 'P': if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { JSObject *gobj; @@ -531,6 +414,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) case 'b': gBranchLimit = atoi(argv[++i]); JS_SetBranchCallback(cx, my_BranchCallback); + JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); break; case 'c': @@ -545,12 +429,33 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) Process(cx, obj, argv[i]); /* * XXX: js -f foo.js should interpret foo.js and then - * drop into interactive mode, but that breaks test + * drop into interactive mode, but that breaks the test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; + case 'e': + { + jsval rval; + + if (++i == argc) { + return usage(); + } + + /* Pass a filename of -e to imitate PERL */ + JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), + "-e", 1, &rval); + + isInteractive = JS_FALSE; + break; + + } + case 'C': + compileOnly = JS_TRUE; + isInteractive = JS_FALSE; + break; + case 'S': if (++i == argc) { return usage(); @@ -587,6 +492,7 @@ static struct { {"strict", JSOPTION_STRICT}, {"werror", JSOPTION_WERROR}, {"atline", JSOPTION_ATLINE}, + {"xml", JSOPTION_XML}, {0, 0} }; @@ -677,7 +583,9 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!script) { ok = JS_FALSE; } else { - ok = JS_ExecuteScript(cx, obj, script, &result); + ok = !compileOnly + ? JS_ExecuteScript(cx, obj, script, &result) + : JS_TRUE; JS_DestroyScript(cx, script); } JS_SetOptions(cx, oldopts); @@ -689,6 +597,77 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +/* + * function readline() + * Provides a hook for scripts to read a line from stdin. + */ +static JSBool +ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#define BUFSIZE 256 + FILE *from; + char *buf, *tmp; + size_t bufsize, buflength, gotlength; + JSString *str; + + from = stdin; + buflength = 0; + bufsize = BUFSIZE; + buf = JS_malloc(cx, bufsize); + if (!buf) + return JS_FALSE; + + while ((gotlength = + js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { + buflength += gotlength; + + /* Are we done? */ + if (buf[buflength - 1] == '\n') { + buf[buflength - 1] = '\0'; + break; + } + + /* Else, grow our buffer for another pass. */ + tmp = JS_realloc(cx, buf, bufsize * 2); + if (!tmp) { + JS_free(cx, buf); + return JS_FALSE; + } + + bufsize *= 2; + buf = tmp; + } + + /* Treat the empty string specially. */ + if (buflength == 0) { + *rval = JS_GetEmptyStringValue(cx); + JS_free(cx, buf); + return JS_TRUE; + } + + /* Shrink the buffer to the real size. */ + tmp = JS_realloc(cx, buf, buflength); + if (!tmp) { + JS_free(cx, buf); + return JS_FALSE; + } + + buf = tmp; + + /* + * Turn buf into a JSString. Note that buflength includes the trailing null + * character. + */ + str = JS_NewString(cx, buf, buflength - 1); + if (!str) { + JS_free(cx, buf); + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + static JSBool Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -723,10 +702,6 @@ Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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) { @@ -774,7 +749,7 @@ ValueToScript(JSContext *cx, jsval v) JSScript *script; JSFunction *fun; - if (JSVAL_IS_OBJECT(v) && + if (!JSVAL_IS_PRIMITIVE(v) && JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) { script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); } else { @@ -790,15 +765,19 @@ static JSBool GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, int32 *ip) { + jsval v; uintN intarg; JSScript *script; *scriptp = cx->fp->down->script; *ip = 0; if (argc != 0) { + v = argv[0]; intarg = 0; - if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) { - script = ValueToScript(cx, argv[0]); + if (!JSVAL_IS_PRIMITIVE(v) && + (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass || + JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) { + script = ValueToScript(cx, v); if (!script) return JS_FALSE; *scriptp = script; @@ -1052,7 +1031,8 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } } - js_Disassemble(cx, script, lines, stdout); + if (!js_Disassemble(cx, script, lines, stdout)) + return JS_FALSE; SrcNotes(cx, script); TryNotes(cx, script); } @@ -1114,8 +1094,8 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, while (line1 < line2) { if (!fgets(linebuf, LINE_BUF_LEN, file)) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - JSSMSG_UNEXPECTED_EOF, - script->filename); + JSSMSG_UNEXPECTED_EOF, + script->filename); goto bail; } line1++; @@ -1206,11 +1186,14 @@ DumpScope(JSContext *cx, JSObject *obj, FILE *fp) if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; fprintf(fp, "%3u %p", i, sprop); - if (JSVAL_IS_INT(sprop->id)) { + if (JSID_IS_INT(sprop->id)) { fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); + } else if (JSID_IS_ATOM(sprop->id)) { + JSAtom *atom = JSID_TO_ATOM(sprop->id); + fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom)); } else { - fprintf(fp, " \"%s\"", - js_AtomToPrintableString(cx, (JSAtom *)sprop->id)); + jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id)); + fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v)); } #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) @@ -1267,11 +1250,11 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); if (!atom) return JS_FALSE; - if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop)) + if (!js_FindProperty(cx, ATOM_TO_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)) + if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value)) return JS_FALSE; } if (!prop || !JSVAL_IS_OBJECT(value)) { @@ -1309,16 +1292,16 @@ DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) atom = js_ValueToStringAtom(cx, argv[1]); if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_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); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); } @@ -1509,10 +1492,131 @@ Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_SealObject(cx, target, deep); } +static JSBool +GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *vobj, *aobj, *pdobj; + JSBool ok; + JSPropertyDescArray pda; + JSPropertyDesc *pd; + uint32 i; + jsval v; + + if (!JS_ValueToObject(cx, argv[0], &vobj)) + return JS_FALSE; + if (!vobj) + return JS_TRUE; + + aobj = JS_NewArrayObject(cx, 0, NULL); + if (!aobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(aobj); + + ok = JS_GetPropertyDescArray(cx, vobj, &pda); + if (!ok) + return JS_FALSE; + pd = pda.array; + for (i = 0; i < pda.length; i++) { + pdobj = JS_NewObject(cx, NULL, NULL, NULL); + if (!pdobj) { + ok = JS_FALSE; + break; + } + + ok = JS_SetProperty(cx, pdobj, "id", &pd->id) && + JS_SetProperty(cx, pdobj, "value", &pd->value) && + (v = INT_TO_JSVAL(pd->flags), + JS_SetProperty(cx, pdobj, "flags", &v)) && + (v = INT_TO_JSVAL(pd->slot), + JS_SetProperty(cx, pdobj, "slot", &v)) && + JS_SetProperty(cx, pdobj, "alias", &pd->alias); + if (!ok) + break; + + v = OBJECT_TO_JSVAL(pdobj); + ok = JS_SetElement(cx, aobj, i, &v); + if (!ok) + break; + } + JS_PutPropertyDescArray(cx, &pda); + return ok; +} + +static JSBool +GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSScript *script; + + script = ValueToScript(cx, argv[0]); + if (!script) + return JS_FALSE; + *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script)); + return JS_TRUE; +} + +static JSBool +ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + int32 i; + + if (!JS_ValueToInt32(cx, argv[0], &i)) + return JS_FALSE; + return JS_NewNumberValue(cx, i, rval); +} + +static JSBool +StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = JS_StringsAreUTF8 () ? JSVAL_TRUE : JSVAL_FALSE; + return JS_TRUE; +} + +static const char* badUtf8 = "...\xC0..."; +static const char* bigUtf8 = "...\xFB\xBF\xBF\xBF\xBF..."; +static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; + +static JSBool +TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + intN mode = 1; + jschar chars[20]; + size_t charsLength = 5; + char bytes[20]; + size_t bytesLength = 20; + if (argc && !JS_ValueToInt32(cx, *argv, &mode)) + return JS_FALSE; + + /* The following throw errors if compiled with UTF-8. */ + switch (mode) { + /* mode 1: malformed UTF-8 string. */ + case 1: + JS_NewStringCopyZ(cx, badUtf8); + break; + /* mode 2: big UTF-8 character. */ + case 2: + JS_NewStringCopyZ(cx, bigUtf8); + break; + /* mode 3: bad surrogate character. */ + case 3: + JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); + break; + /* mode 4: use a too small buffer. */ + case 4: + JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); + break; + default: + JS_ReportError(cx, "invalid mode parameter"); + return JS_FALSE; + } + return !JS_IsExceptionPending (cx); +} + static JSFunctionSpec shell_functions[] = { {"version", Version, 0}, {"options", Options, 0}, {"load", Load, 1}, + {"readline", ReadLine, 0}, {"print", Print, 0}, {"help", Help, 0}, {"quit", Quit, 0}, @@ -1521,6 +1625,8 @@ static JSFunctionSpec shell_functions[] = { {"untrap", Untrap, 2}, {"line2pc", LineToPC, 0}, {"pc2line", PCToLine, 0}, + {"stringsAreUtf8", StringsAreUtf8, 0}, + {"testUtf8", TestUtf8, 1}, #ifdef DEBUG {"dis", Disassemble, 1}, {"dissrc", DisassWithSrc, 1}, @@ -1539,6 +1645,9 @@ static JSFunctionSpec shell_functions[] = { {"intern", Intern, 1}, {"clone", Clone, 1}, {"seal", Seal, 1, 0, 1}, + {"getpda", GetPDA, 1}, + {"getslx", GetSLX, 1}, + {"toint32", ToInt32, 1}, {0} }; @@ -1548,6 +1657,7 @@ 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", + "readline() Read a single line from stdin", "print([exp ...]) Evaluate and print expressions", "help([name ...]) Display usage and help messages", "quit() Quit the shell", @@ -1556,6 +1666,8 @@ static char *shell_help_messages[] = { "untrap(fun[, pc]) Remove a trap", "line2pc([fun,] line) Map line number to PC", "pc2line(fun[, pc]) Map PC to line number", + "stringsAreUTF8() Check if strings are UTF-8 encoded", + "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", #ifdef DEBUG "dis([fun]) Disassemble functions into bytecodes", "dissrc([fun]) Disassemble functions with source lines", @@ -1574,20 +1686,23 @@ static char *shell_help_messages[] = { "intern(str) Internalize str in the atom table", "clone(fun[, scope]) Clone function object", "seal(obj[, deep]) Seal object, or object graph if deep", + "getpda(obj) Get the property descriptors for obj", + "getslx(obj) Get script line extent", + "toint32(n) Testing hook for JS_ValueToInt32", 0 }; static void ShowHelpHeader(void) { - fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description"); - fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "==========="); + fprintf(gOutFile, "%-14s %-22s %s\n", "Command", "Usage", "Description"); + fprintf(gOutFile, "%-14s %-22s %s\n", "=======", "=====", "==========="); } static void ShowHelpForCommand(uintN n) { - fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]); + fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]); } static JSBool @@ -1671,8 +1786,36 @@ its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +static JSBool +its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + char *name; + JSObject *method; + + if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method)) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(method); + + if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) { + JSString *valstr = JS_ValueToString(cx, *rval); + if (valstr) { + JS_ReportError(cx, "can't bind method %s to non-callable object %s", + name, JS_GetStringBytes(valstr)); + } + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE)) + return JS_FALSE; + + return JS_SetParent(cx, method, obj); +} + static JSFunctionSpec its_methods[] = { {"item", its_item, 0}, + {"bindMethod", its_bindMethod, 2}, {0} }; @@ -1917,8 +2060,13 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) } fputs("^\n", gErrFile); out: - if (!JSREPORT_IS_WARNING(report->flags)) - gExitCode = EXITCODE_RUNTIME_ERROR; + if (!JSREPORT_IS_WARNING(report->flags)) { + if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { + gExitCode = EXITCODE_OUT_OF_MEMORY; + } else { + gExitCode = EXITCODE_RUNTIME_ERROR; + } + } JS_free(cx, prefix); } @@ -2204,14 +2352,89 @@ defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, attrs); } +static JSBool +Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* function evaluate(source, filename, lineno) { ... } */ + JSString *source; + const char *filename = ""; + jsuint lineno = 0; + uint32 oldopts; + JSBool ok; + + if (argc == 0) { + *rval = JSVAL_VOID; + return JS_TRUE; + } + + if (!JS_ConvertArguments(cx, argc, argv, "S/su", + &source, &filename, &lineno)) { + return JS_FALSE; + } + + oldopts = JS_GetOptions(cx); + JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); + ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source), + JS_GetStringLength(source), filename, + lineno, rval); + JS_SetOptions(cx, oldopts); + + return ok; +} + #include #include +/* + * Returns a JS_malloc'd string (that the caller needs to JS_free) + * containing the directory (non-leaf) part of |from| prepended to |leaf|. + * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf. + * Returns NULL to indicate an error. + */ +static char * +MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf) +{ + size_t dirlen; + char *dir; + const char *slash = NULL, *cp; + + cp = from; + while (*cp) { + if (*cp == '/' +#ifdef XP_WIN + || *cp == '\\' +#endif + ) { + slash = cp; + } + + ++cp; + } + + if (!slash) { + /* We were given a leaf or |from| was empty. */ + return JS_strdup(cx, leaf); + } + + /* Else, we were given a real pathname, return that + the leaf. */ + dirlen = slash - from + 1; + dir = JS_malloc(cx, dirlen + strlen(leaf) + 1); + if (!dir) + return NULL; + + strncpy(dir, from, dirlen); + strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */ + + return dir; +} + static JSBool snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; const char *filename; + char *pathname; + JSStackFrame *fp; int fd, cc; JSBool ok; size_t len; @@ -2222,15 +2445,23 @@ snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!str) return JS_FALSE; filename = JS_GetStringBytes(str); - fd = open(filename, O_RDONLY); + + /* Get the currently executing script's name. */ + fp = JS_GetScriptedCaller(cx, NULL); + JS_ASSERT(fp && fp->script->filename); + pathname = MakeAbsolutePathname(cx, fp->script->filename, filename); + if (!pathname) + return JS_FALSE; + + fd = open(pathname, O_RDONLY); ok = JS_TRUE; len = 0; buf = NULL; if (fd < 0) { - JS_ReportError(cx, "can't open %s: %s", filename, strerror(errno)); + JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); ok = JS_FALSE; } else if (fstat(fd, &sb) < 0) { - JS_ReportError(cx, "can't stat %s", filename); + JS_ReportError(cx, "can't stat %s", pathname); ok = JS_FALSE; } else { len = sb.st_size; @@ -2239,12 +2470,13 @@ snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) ok = JS_FALSE; } else if ((cc = read(fd, buf, len)) != len) { JS_free(cx, buf); - JS_ReportError(cx, "can't read %s: %s", filename, + JS_ReportError(cx, "can't read %s: %s", pathname, (cc < 0) ? strerror(errno) : "short read"); ok = JS_FALSE; } } close(fd); + JS_free(cx, pathname); if (!ok) return ok; buf[len] = '\0'; @@ -2263,7 +2495,6 @@ int main(int argc, char **argv, char **envp) { int stackDummy; - JSVersion version; JSRuntime *rt; JSContext *cx; JSObject *glob, *it, *envobj; @@ -2287,47 +2518,6 @@ main(int argc, char **argv, char **envp) 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++; @@ -2352,10 +2542,6 @@ main(int argc, char **argv, char **envp) 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; @@ -2417,6 +2603,9 @@ main(int argc, char **argv, char **envp) if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0)) return 1; + if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0)) + return 1; + if (!JS_EvaluateScript(cx, glob, Object_prototype, sizeof Object_prototype - 1, NULL, 0, &v)) { @@ -2436,10 +2625,6 @@ main(int argc, char **argv, char **envp) JSD_DebuggerOff(_jsdc); #endif /* JSDEBUGGER */ -#ifdef MAC_TEST_HACK - fclose(gTestResultFile); -#endif - JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); diff --git a/src/dom/js/js.mak b/src/dom/js/js.mak deleted file mode 100644 index a155abfa2..000000000 --- a/src/dom/js/js.mak +++ /dev/null @@ -1,4025 +0,0 @@ -# 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 index dc6c534f7..5df1ead9e 100644 --- a/src/dom/js/js.msg +++ b/src/dom/js/js.msg @@ -224,7 +224,7 @@ MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated reg 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_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_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") @@ -249,3 +249,42 @@ MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot ind 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") +MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") +MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") +MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") +MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") +MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") +MSG_DEF(JSMSG_NAME_AFTER_DBLDOT, 175, 0, JSEXN_SYNTAXERR, "missing name after .. operator") +MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") +MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}") +MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") +MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") +MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") +MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") +MSG_DEF(JSMSG_IS_NOT_XML_OBJECT, 182, 1, JSEXN_TYPEERR, "{0} is not an XML object") +MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") +MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") +MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 0, JSEXN_SYNTAXERR, "XML tag name mismatch") +MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") +MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") +MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") +MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") +MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") +MSG_DEF(JSMSG_END_OF_XML_ENTITY, 191, 0, JSEXN_SYNTAXERR, "unexpected end of XML entity") +MSG_DEF(JSMSG_BAD_XML_QNAME, 192, 0, JSEXN_SYNTAXERR, "invalid XML qualified name") +MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") +MSG_DEF(JSMSG_BAD_XMLLIST_PUT, 194, 1, JSEXN_TYPEERR, "can't set property {0} in XMLList") +MSG_DEF(JSMSG_UNKNOWN_XML_ENTITY, 195, 1, JSEXN_TYPEERR, "unknown XML entity {0}") +MSG_DEF(JSMSG_BAD_XML_NCR, 196, 1, JSEXN_TYPEERR, "malformed XML character {0}") +MSG_DEF(JSMSG_UNDEFINED_XML_NAME, 197, 1, JSEXN_REFERENCEERR, "reference to undefined XML name {0}") +MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attribute {0}") +MSG_DEF(JSMSG_TOO_MANY_FUN_VARS, 199, 0, JSEXN_SYNTAXERR, "too many local variables") +MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 200, 0, JSEXN_INTERNALERR, "array initialiser too large") +MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX, 201, 0, JSEXN_INTERNALERR, "regular expression too complex") +MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 202, 1, JSEXN_TYPEERR, "wrong construtor called for {0}") +MSG_DEF(JSMSG_SELF_MODIFYING_SCRIPT, 203, 0, JSEXN_TYPEERR, "self-modifying script detected") +MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 204, 0, JSEXN_INTERNALERR, "buffer too small") +MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 205, 1, JSEXN_TYPEERR, "bad surrogate character {0}") +MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 206, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") +MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 207, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") +MSG_DEF(JSMSG_USER_DEFINED_ERROR, 208, 0, JSEXN_ERR, "JS_ReportError was called") diff --git a/src/dom/js/jsapi.c b/src/dom/js/jsapi.c index 5b7200a49..344fc2469 100644 --- a/src/dom/js/jsapi.c +++ b/src/dom/js/jsapi.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -81,8 +82,12 @@ #include "jsfile.h" #endif +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #ifdef HAVE_VA_LIST_AS_ARRAY -#define JS_ADDRESSOF_VA_LIST(ap) (ap) +#define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) #else #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif @@ -242,19 +247,10 @@ JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, *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; + obj = js_ValueToFunctionObject(cx, sp, 0); + if (!obj) + return JS_FALSE; + *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj); break; case 'v': *va_arg(ap, jsval *) = *sp; @@ -456,7 +452,6 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) { JSBool ok, b; JSObject *obj; - JSFunction *fun; JSString *str; jsdouble d, *dp; @@ -472,19 +467,9 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) *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); - } + *vp = v; + obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); + ok = (obj != NULL); break; case JSTYPE_STRING: str = js_ValueToString(cx, v); @@ -495,7 +480,7 @@ JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) case JSTYPE_NUMBER: ok = js_ValueToNumber(cx, v, &d); if (ok) { - dp = js_NewDouble(cx, d); + dp = js_NewDouble(cx, d, 0); ok = (dp != NULL); if (ok) *vp = DOUBLE_TO_JSVAL(dp); @@ -598,27 +583,42 @@ JS_TypeOfValue(JSContext *cx, jsval v) CHECK_REQUEST(cx); if (JSVAL_IS_OBJECT(v)) { - /* XXX JSVAL_IS_OBJECT(v) is true for null too! Can we change ECMA? */ + type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ 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 { + if (obj) { + ops = obj->map->ops; +#if JS_HAS_XML_SUPPORT + if (ops == &js_XMLObjectOps.base) { + type = JSTYPE_XML; + } else +#endif + { + /* + * ECMA 262, 11.4.3 says that any native object that implements + * [[Call]] should be of type "function". Note that RegExp and + * Script are both of type "function" for compatibility with + * older SpiderMonkeys. + */ + clasp = OBJ_GET_CLASS(cx, obj); + if ((ops == &js_ObjectOps) + ? (clasp->call + ? (clasp == &js_RegExpClass || clasp == &js_ScriptClass) + : 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; - } + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState + .callAtom), + &v)) { + JS_ClearPendingException(cx); + } else if (JSVAL_IS_FUNCTION(cx, v)) { + type = JSTYPE_FUNCTION; + } #endif - type = JSTYPE_OBJECT; + } + } } } else if (JSVAL_IS_NUMBER(v)) { type = JSTYPE_NUMBER; @@ -687,7 +687,9 @@ JS_NewRuntime(uint32 maxbytes) rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->requestDone) goto bad; - js_SetupLocks(8, 16); /* this is asymmetric with JS_ShutDown. */ + /* this is asymmetric with JS_ShutDown: */ + if (!js_SetupLocks(8, 16)) + goto bad; rt->rtLock = JS_NEW_LOCK(); if (!rt->rtLock) goto bad; @@ -731,6 +733,7 @@ JS_DestroyRuntime(JSRuntime *rt) } #endif + js_FreeRuntimeScriptState(rt); js_FinishAtomState(&rt->atomState); js_FinishGC(rt); #ifdef JS_THREADSAFE @@ -968,7 +971,7 @@ JS_ContextIterator(JSRuntime *rt, JSContext **iterp) JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx) { - return cx->version; + return cx->version & JSVERSION_MASK; } JS_PUBLIC_API(JSVersion) @@ -976,22 +979,15 @@ JS_SetVersion(JSContext *cx, JSVersion version) { JSVersion oldVersion; - oldVersion = cx->version; + JS_ASSERT(version != JSVERSION_UNKNOWN); + JS_ASSERT((version & ~JSVERSION_MASK) == 0); + + oldVersion = cx->version & JSVERSION_MASK; 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 */ - + cx->version = (cx->version & ~JSVERSION_MASK) | version; + js_OnVersionChange(cx); return oldVersion; } @@ -1006,7 +1002,8 @@ static struct v2smap { {JSVERSION_1_4, "1.4"}, {JSVERSION_ECMA_3, "ECMAv3"}, {JSVERSION_1_5, "1.5"}, - {JSVERSION_DEFAULT, "default"}, + {JSVERSION_1_6, "1.6"}, + {JSVERSION_DEFAULT, js_default_str}, {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ }; @@ -1038,11 +1035,20 @@ JS_GetOptions(JSContext *cx) return cx->options; } +#define SYNC_OPTIONS_TO_VERSION(cx) \ + JS_BEGIN_MACRO \ + if ((cx)->options & JSOPTION_XML) \ + (cx)->version |= JSVERSION_HAS_XML; \ + else \ + (cx)->version &= ~JSVERSION_HAS_XML; \ + JS_END_MACRO + JS_PUBLIC_API(uint32) JS_SetOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options = options; + SYNC_OPTIONS_TO_VERSION(cx); return oldopts; } @@ -1051,13 +1057,14 @@ JS_ToggleOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options ^= options; + SYNC_OPTIONS_TO_VERSION(cx); return oldopts; } JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void) { - return "JavaScript-C 1.5 2004-09-24"; + return "JavaScript-C 1.6 2006-11-19"; } @@ -1071,6 +1078,9 @@ JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj) { cx->globalObject = obj; +#if JS_HAS_XML_SUPPORT + cx->xmlSettingFlags = 0; +#endif } static JSObject * @@ -1085,7 +1095,7 @@ InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) /* If cx has no global object, use obj so prototypes can be found. */ if (!cx->globalObject) - cx->globalObject = obj; + JS_SetGlobalObject(cx, obj); /* Record Function and Object in cx->resolvingTable, if we are resolving. */ table = cx->resolvingTable; @@ -1093,13 +1103,13 @@ InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) if (resolving) { rt = cx->runtime; key.obj = obj; - key.id = (jsid) rt->atomState.FunctionAtom; + key.id = ATOM_TO_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; + key.id = ATOM_TO_JSID(rt->atomState.ObjectAtom); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); } @@ -1145,8 +1155,8 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) { /* 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)) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } } @@ -1174,8 +1184,11 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) #if JS_HAS_ERROR_EXCEPTIONS js_InitExceptionClasses(cx, obj) && #endif +#if JS_HAS_XML_SUPPORT + js_InitXMLClasses(cx, obj) && +#endif #if JS_HAS_FILE_OBJECT - js_InitFileClass(cx, obj, JS_TRUE) && + js_InitFileClass(cx, obj) && #endif js_InitDateClass(cx, obj); } @@ -1210,6 +1223,14 @@ static struct { #endif #if JS_HAS_SCRIPT_OBJECT {js_InitScriptClass, ATOM_OFFSET(Script)}, +#endif +#if JS_HAS_XML_SUPPORT + {js_InitXMLClass, ATOM_OFFSET(XML)}, + {js_InitNamespaceClass, ATOM_OFFSET(Namespace)}, + {js_InitQNameClass, ATOM_OFFSET(QName)}, +#endif +#if JS_HAS_FILE_OBJECT + {js_InitFileClass, ATOM_OFFSET(File)}, #endif {NULL, 0} }; @@ -1282,6 +1303,13 @@ static JSStdName standard_class_names[] = { {js_InitExceptionClasses, LAZILY_PINNED_ATOM(URIError)}, #endif +#if JS_HAS_XML_SUPPORT + {js_InitAnyNameClass, LAZILY_PINNED_ATOM(AnyName)}, + {js_InitAttributeNameClass, LAZILY_PINNED_ATOM(AttributeName)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(XMLList)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(isXMLName)}, +#endif + {NULL, 0, NULL} }; @@ -1337,12 +1365,12 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, rt = cx->runtime; #if JS_HAS_UNDEFINED - /* See if we're resolving 'undefined', and define it if so. */ + /* Check whether we're resolving 'undefined', and define it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (idstr == ATOM_TO_STRING(atom)) { *resolved = JS_TRUE; - return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, - JSPROP_PERMANENT, NULL); + return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL); } #endif @@ -1395,17 +1423,10 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, } static JSBool -HasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom, JSBool *ownp) +AlreadyHasOwnProperty(JSObject *obj, JSAtom *atom) { - 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_ASSERT(OBJ_IS_NATIVE(obj)); + return SCOPE_GET_PROPERTY(OBJ_SCOPE(obj), ATOM_TO_JSID(atom)) != NULL; } JS_PUBLIC_API(JSBool) @@ -1413,20 +1434,17 @@ 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. */ + /* Check whether 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)) { + if (!AlreadyHasOwnProperty(obj, atom) && + !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } #endif @@ -1434,15 +1452,106 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) /* 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)) + if (!AlreadyHasOwnProperty(obj, atom) && + !standard_class_atoms[i].init(cx, obj)) { return JS_FALSE; + } } return JS_TRUE; } +static JSIdArray * +AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) +{ + jsint i, length; + + i = *ip; + length = ida->length; + if (i >= length) { + ida = js_SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); + if (!ida) + return NULL; + JS_ASSERT(i < ida->length); + } + ida->vector[i] = ATOM_TO_JSID(atom); + *ip = i + 1; + return ida; +} + +static JSIdArray * +EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, + jsint *ip, JSBool *foundp) +{ + *foundp = AlreadyHasOwnProperty(obj, atom); + if (*foundp) + ida = AddAtomToArray(cx, atom, ida, ip); + return ida; +} + +JS_PUBLIC_API(JSIdArray *) +JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, + JSIdArray *ida) +{ + JSRuntime *rt; + jsint i, j, k; + JSAtom *atom; + JSBool found; + JSObjectOp init; + + CHECK_REQUEST(cx); + rt = cx->runtime; + if (ida) { + i = ida->length; + } else { + ida = js_NewIdArray(cx, 8); + if (!ida) + return NULL; + i = 0; + } + +#if JS_HAS_UNDEFINED + /* Check whether 'undefined' has been resolved and enumerate it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); + if (!ida) + return NULL; +#endif + + /* Enumerate only classes that *have* been resolved. */ + for (j = 0; standard_class_atoms[j].init; j++) { + atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); + ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); + if (!ida) + return NULL; + + if (found) { + init = standard_class_atoms[j].init; + + for (k = 0; standard_class_names[k].init; k++) { + if (standard_class_names[k].init == init) { + atom = StdNameToAtom(cx, &standard_class_names[k]); + ida = AddAtomToArray(cx, atom, ida, &i); + if (!ida) + return NULL; + } + } + + if (init == js_InitObjectClass) { + for (k = 0; object_prototype_names[k].init; k++) { + atom = StdNameToAtom(cx, &object_prototype_names[k]); + ida = AddAtomToArray(cx, atom, ida, &i); + if (!ida) + return NULL; + } + } + } + } + + /* Trim to exact length via js_SetIdArrayLength. */ + return js_SetIdArrayLength(cx, ida, i); +} + #undef ATOM_OFFSET #undef OFFSET_TO_ATOM @@ -1500,7 +1609,7 @@ JS_PUBLIC_API(jsdouble *) JS_NewDouble(JSContext *cx, jsdouble d) { CHECK_REQUEST(cx); - return js_NewDouble(cx, d); + return js_NewDouble(cx, d, 0); } JS_PUBLIC_API(JSBool) @@ -1559,6 +1668,7 @@ JS_ClearNewbornRoots(JSContext *cx) for (i = 0; i < GCX_NTYPES; i++) cx->newborn[i] = NULL; cx->lastAtom = NULL; + cx->lastInternalResult = JSVAL_NULL; } JS_PUBLIC_API(JSBool) @@ -1723,6 +1833,9 @@ JS_GC(JSContext *cx) JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx) { +#ifdef WAY_TOO_MUCH_GC + JS_GC(cx); +#else JSRuntime *rt; uint32 bytes, lastBytes; @@ -1730,7 +1843,7 @@ JS_MaybeGC(JSContext *cx) bytes = rt->gcBytes; lastBytes = rt->gcLastBytes; if ((bytes > 8192 && bytes > lastBytes + lastBytes / 2) || - rt->gcMallocBytes > rt->gcMaxBytes) { + rt->gcMallocBytes > rt->gcMaxMallocBytes) { /* * 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 @@ -1738,6 +1851,7 @@ JS_MaybeGC(JSContext *cx) */ JS_GC(cx); } +#endif } JS_PUBLIC_API(JSGCCallback) @@ -1763,6 +1877,19 @@ JS_IsAboutToBeFinalized(JSContext *cx, void *thing) return js_IsAboutToBeFinalized(cx, thing); } +JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) +{ + switch (key) { + case JSGC_MAX_BYTES: + rt->gcMaxBytes = value; + break; + case JSGC_MAX_MALLOC_BYTES: + rt->gcMaxMallocBytes = value; + break; + } +} + JS_PUBLIC_API(intN) JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) { @@ -1783,7 +1910,7 @@ JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) CHECK_REQUEST(cx); JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); - str = (JSString *) js_AllocGCThing(cx, (uintN) type); + str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString)); if (!str) return NULL; str->length = length; @@ -1802,32 +1929,9 @@ JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) 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; @@ -1855,7 +1959,7 @@ JS_ValueToId(JSContext *cx, jsval v, jsid *idp) atom = js_ValueToStringAtom(cx, v); if (!atom) return JS_FALSE; - *idp = (jsid)atom; + *idp = ATOM_TO_JSID(atom); } return JS_TRUE; } @@ -1893,7 +1997,8 @@ JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) if (type == JSTYPE_STRING) return JS_TRUE; #endif - return js_TryValueOf(cx, obj, type, vp); + js_TryValueOf(cx, obj, type, vp); + return JS_TRUE; } JS_PUBLIC_API(void) @@ -1909,9 +2014,10 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, { JSAtom *atom; JSObject *proto, *ctor; + JSTempValueRooter tvr; + jsval cval, rval; JSBool named; JSFunction *fun; - jsval junk; CHECK_REQUEST(cx); atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); @@ -1923,9 +2029,13 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, if (!proto) return NULL; + /* After this point, control must exit via label bad or out. */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(proto), &tvr); + if (!constructor) { /* Lacking a constructor, name the prototype (e.g., Math). */ - named = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(proto), + named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(proto), NULL, NULL, 0, NULL); if (!named) goto bad; @@ -1944,8 +2054,22 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, */ fun->clasp = clasp; - /* Connect constructor and prototype by named properties. */ + /* + * Optionally construct the prototype object, before the class has + * been fully initialized. Allow the ctor to replace proto with a + * different object, as is done for operator new -- and as at least + * XML support requires. + */ ctor = fun->object; + if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { + cval = OBJECT_TO_JSVAL(ctor); + if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) + goto bad; + if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) + proto = JSVAL_TO_OBJECT(rval); + } + + /* Connect constructor and prototype by named properties. */ if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_READONLY | JSPROP_PERMANENT)) { goto bad; @@ -1968,13 +2092,16 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { goto bad; } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); return proto; bad: if (named) - (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk); - cx->newborn[GCX_OBJECT] = NULL; - return NULL; + (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); + proto = NULL; + goto out; } #ifdef JS_THREADSAFE @@ -2012,6 +2139,12 @@ JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) return JS_FALSE; } +JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return js_HasInstance(cx, obj, v, bp); +} + JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj) { @@ -2091,7 +2224,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) CHECK_REQUEST(cx); if (!OBJ_GET_PROPERTY(cx, proto, - (jsid)cx->runtime->atomState.constructorAtom, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval)) { return NULL; } @@ -2106,8 +2239,8 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) { - JS_ASSERT(((jsid)obj & JSVAL_TAGMASK) == 0); - *idp = (jsid) obj | JSVAL_INT; + JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); + *idp = OBJECT_TO_JSID(obj); return JS_TRUE; } @@ -2211,14 +2344,14 @@ DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, JSAtom *atom; if (attrs & JSPROP_INDEX) { - id = INT_TO_JSVAL((jsint)name); + id = INT_TO_JSID(JS_PTR_TO_INT32(name)); atom = NULL; attrs &= ~JSPROP_INDEX; } else { atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - id = (jsid)atom; + id = ATOM_TO_JSID(atom); } if (flags != 0 && OBJ_IS_NATIVE(obj)) { return js_DefineNativeProperty(cx, obj, id, value, getter, setter, @@ -2242,12 +2375,12 @@ DefineUCProperty(JSContext *cx, JSObject *obj, if (!atom) return JS_FALSE; if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, (jsid)atom, value, + return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, flags, tinyid, NULL); } - return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter, - attrs, NULL); + return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, NULL); } JS_PUBLIC_API(JSObject *) @@ -2336,7 +2469,7 @@ LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } static JSBool @@ -2349,7 +2482,7 @@ LookupUCProperty(JSContext *cx, JSObject *obj, 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); + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } JS_PUBLIC_API(JSBool) @@ -2380,7 +2513,7 @@ JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, ok = JS_FALSE; } else { sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, (jsid)atom, + ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) @@ -2416,7 +2549,8 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) static JSBool GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN *attrsp, JSBool *foundp) + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, JSPropertyOp *setterp) { JSObject *obj2; JSProperty *prop; @@ -2424,17 +2558,31 @@ GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; + if (!prop || obj != obj2) { + *attrsp = 0; *foundp = JS_FALSE; + if (getterp) + *getterp = NULL; + if (setterp) + *setterp = NULL; if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE; } *foundp = JS_TRUE; - ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); + if (ok && OBJ_IS_NATIVE(obj)) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + + if (getterp) + *getterp = sprop->getter; + if (setterp) + *setterp = sprop->setter; + } OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } @@ -2449,7 +2597,7 @@ SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop || obj != obj2) { *foundp = JS_FALSE; @@ -2459,12 +2607,11 @@ SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, } *foundp = JS_TRUE; - ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_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) @@ -2472,7 +2619,20 @@ JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_Atomize(cx, name, strlen(name), 0), - attrsp, foundp); + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrsp, foundp, getterp, setterp); } JS_PUBLIC_API(JSBool) @@ -2530,12 +2690,9 @@ JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, 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); + ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, + &obj2, &prop) + : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; @@ -2550,7 +2707,39 @@ JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp) +{ + JSAtom *atom; + jsid id; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + id = ATOM_TO_JSID(atom); + +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, vp); + if (!obj) + return JS_FALSE; + } else +#endif + { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + } + + *objp = obj; + return JS_TRUE; } JS_PUBLIC_API(JSBool) @@ -2562,7 +2751,7 @@ JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2584,7 +2773,7 @@ JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSBool) @@ -2606,7 +2795,20 @@ JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrsp, foundp); + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrsp, foundp, getterp, setterp); } JS_PUBLIC_API(JSBool) @@ -2678,7 +2880,7 @@ JS_GetUCProperty(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2692,7 +2894,7 @@ JS_SetUCProperty(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2706,7 +2908,7 @@ JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSObject *) @@ -2749,7 +2951,7 @@ 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, + return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, getter, setter, attrs, NULL); } @@ -2777,7 +2979,7 @@ JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) return JS_FALSE; } sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, INT_TO_JSVAL(alias), + ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) @@ -2794,7 +2996,7 @@ JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) JSProperty *prop; CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) { *foundp = (prop != NULL); if (prop) @@ -2811,7 +3013,7 @@ JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) JSProperty *prop; CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; @@ -2821,14 +3023,14 @@ 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); + return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(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); + return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); } JS_PUBLIC_API(JSBool) @@ -2844,7 +3046,7 @@ 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); + return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); } JS_PUBLIC_API(void) @@ -2891,25 +3093,22 @@ JS_Enumerate(JSContext *cx, JSObject *obj) 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; + + if (i == ida->length) { + ida = js_SetIdArrayLength(cx, ida, ida->length * 2); + if (!ida) + goto error; + vector = &ida->vector[0]; + } vector[i++] = id; } - ida->length = i; - return ida; + return js_SetIdArrayLength(cx, ida, i); error: if (iter_state != JSVAL_NULL) @@ -2919,6 +3118,182 @@ error: return NULL; } +/* + * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's + * prop_iterator_class somehow... + * + preserve the OBJ_ENUMERATE API while optimizing the native object case + * + native case here uses a JSScopeProperty *, but that iterates in reverse! + * + so we make non-native match, by reverse-iterating after JS_Enumerating + */ +#define JSSLOT_ITER_INDEX (JSSLOT_PRIVATE + 1) + +#if JSSLOT_ITER_INDEX >= JS_INITIAL_NSLOTS +# error "JSSLOT_ITER_INDEX botch!" +#endif + +static void +prop_iter_finalize(JSContext *cx, JSObject *obj) +{ + jsval v; + jsint i; + JSIdArray *ida; + + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX); + if (JSVAL_IS_VOID(v)) + return; + + i = JSVAL_TO_INT(v); + if (i >= 0) { + /* Non-native case: destroy the ida enumerated when obj was created. */ + ida = (JSIdArray *) JS_GetPrivate(cx, obj); + if (ida) + JS_DestroyIdArray(cx, ida); + } +} + +static uint32 +prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) +{ + jsval v; + jsint i, n; + JSScopeProperty *sprop; + JSIdArray *ida; + jsid id; + + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(!JSVAL_IS_VOID(v)); + + i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX)); + if (i < 0) { + /* Native case: just mark the next property to visit. */ + sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); + if (sprop) + MARK_SCOPE_PROPERTY(sprop); + } else { + /* Non-native case: mark each id in the JSIdArray private. */ + ida = (JSIdArray *) JSVAL_TO_PRIVATE(v); + for (i = 0, n = ida->length; i < n; i++) { + id = ida->vector[i]; + if (JSID_IS_ATOM(id)) + GC_MARK_ATOM(cx, JSID_TO_ATOM(id), arg); + else if (JSID_IS_OBJECT(id)) + GC_MARK(cx, JSID_TO_OBJECT(id), "id", arg); + } + } + return 0; +} + +static JSClass prop_iter_class = { + "PropertyIterator", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, prop_iter_mark, NULL +}; + +JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj) +{ + JSObject *iterobj; + JSScope *scope; + void *pdata; + jsint index; + JSIdArray *ida; + + CHECK_REQUEST(cx); + iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); + if (!iterobj) + return NULL; + + if (OBJ_IS_NATIVE(obj)) { + /* Native case: start with the last property in obj's own scope. */ + scope = OBJ_SCOPE(obj); + pdata = (scope->object == obj) ? scope->lastProp : NULL; + index = -1; + } else { + JSTempValueRooter tvr; + + /* + * Non-native case: enumerate a JSIdArray and keep it via private. + * + * Note: we have to make sure that we root obj around the call to + * JS_Enumerate to protect against multiple allocations under it. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(iterobj), &tvr); + ida = JS_Enumerate(cx, obj); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ida) + goto bad; + pdata = ida; + index = ida->length; + } + + /* iterobj can not escape to other threads here. */ + iterobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(pdata); + iterobj->slots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); + return iterobj; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) +{ + jsint i; + JSObject *obj; + JSScope *scope; + JSScopeProperty *sprop; + JSIdArray *ida; + + CHECK_REQUEST(cx); + i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX)); + if (i < 0) { + /* Native case: private data is a property tree node pointer. */ + obj = OBJ_GET_PARENT(cx, iterobj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); + scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->object == obj); + sprop = (JSScopeProperty *) JS_GetPrivate(cx, iterobj); + + /* + * If the next property mapped by scope in the property tree ancestor + * line is not enumerable, or it's an alias, or one or more properties + * were deleted from the "middle" of the scope-mapped ancestor line + * and the next property was among those deleted, skip it and keep on + * trying to find an enumerable property that is still in scope. + */ + while (sprop && + (!(sprop->attrs & JSPROP_ENUMERATE) || + (sprop->flags & SPROP_IS_ALIAS) || + (SCOPE_HAD_MIDDLE_DELETE(scope) && + !SCOPE_HAS_PROPERTY(scope, sprop)))) { + sprop = sprop->parent; + } + + if (!sprop) { + *idp = JSVAL_VOID; + } else { + if (!JS_SetPrivate(cx, iterobj, sprop->parent)) + return JS_FALSE; + *idp = sprop->id; + } + } else { + /* Non-native case: use the ida enumerated when iterobj was created. */ + ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); + JS_ASSERT(i <= ida->length); + if (i == 0) { + *idp = JSVAL_VOID; + } else { + *idp = ida->vector[--i]; + OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); + } + } + return JS_TRUE; +} + JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) @@ -3011,12 +3386,12 @@ JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) } JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop) +JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) { JSObjectPrincipalsFinder oldfop; - oldfop = cx->findObjectPrincipals; - cx->findObjectPrincipals = fop; + oldfop = rt->findObjectPrincipals; + rt->findObjectPrincipals = fop; return oldfop; } @@ -3075,21 +3450,112 @@ JS_GetFunctionFlags(JSFunction *fun) return fun->flags; } +JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun) +{ + return fun->nargs; +} + JS_PUBLIC_API(JSBool) JS_ObjectIsFunction(JSContext *cx, JSObject *obj) { return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; } +JS_STATIC_DLL_CALLBACK(JSBool) +js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + jsval fsv; + JSFunctionSpec *fs; + JSObject *tmp; + + if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) + return JS_FALSE; + fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); + + /* + * We know that argv[0] is valid because JS_DefineFunctions, which is our + * only (indirect) referrer, defined us as requiring at least one argument + * (notice how it passes fs->nargs + 1 as the next-to-last argument to + * JS_DefineFunction). + */ + if (JSVAL_IS_PRIMITIVE(argv[0])) { + /* + * Make sure that this is an object or null, as required by the generic + * functions. + */ + if (!js_ValueToObject(cx, argv[0], &tmp)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(tmp); + } + + /* + * Copy all actual (argc) and required but missing (fs->nargs + 1 - argc) + * args down over our |this| parameter, argv[-1], which is almost always + * the class constructor object, e.g. Array. Then call the corresponding + * prototype native method with our first argument passed as |this|. + */ + memmove(argv - 1, argv, JS_MAX(fs->nargs + 1U, argc) * sizeof(jsval)); + + /* + * Follow Function.prototype.apply and .call by using the global object as + * the 'this' param if no args. + */ + JS_ASSERT(cx->fp->argv == argv); + if (!js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), cx->fp)) + return JS_FALSE; + + /* + * Protect against argc - 1 underflowing below. By calling js_ComputeThis, + * we made it as if the static was called with one parameter. + */ + if (argc == 0) + argc = 1; + + return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval); +} + JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) { + uintN flags; + JSObject *ctor; JSFunction *fun; CHECK_REQUEST(cx); + ctor = NULL; for (; fs->name; fs++) { - fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, - fs->flags); + flags = fs->flags; + + /* + * Define a generic arity N+1 static method for the arity N prototype + * method if flags contains JSFUN_GENERIC_NATIVE. + */ + if (flags & JSFUN_GENERIC_NATIVE) { + if (!ctor) { + ctor = JS_GetConstructor(cx, obj); + if (!ctor) + return JS_FALSE; + } + + flags &= ~JSFUN_GENERIC_NATIVE; + fun = JS_DefineFunction(cx, ctor, fs->name, + js_generic_native_method_dispatcher, + fs->nargs + 1, flags); + if (!fun) + return JS_FALSE; + fun->extra = fs->extra; + + /* + * As jsapi.h notes, fs must point to storage that lives as long + * as fun->object lives. + */ + if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) + return JS_FALSE; + } + + fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); if (!fun) return JS_FALSE; fun->extra = fs->extra; @@ -3169,7 +3635,7 @@ JS_CompileScript(JSContext *cx, JSObject *obj, JSScript *script; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); @@ -3187,7 +3653,7 @@ JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSScript *script; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; script = JS_CompileUCScriptForPrincipals(cx, obj, principals, @@ -3206,6 +3672,24 @@ JS_CompileUCScript(JSContext *cx, JSObject *obj, filename, lineno); } +#if JS_HAS_EXCEPTIONS +# define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ + JS_BEGIN_MACRO \ + if (!(result)) \ + js_ReportUncaughtException(cx); \ + JS_END_MACRO +#else +# define LAST_FRAME_EXCEPTION_CHECK(cx,result) /* nothing */ +#endif + +#define LAST_FRAME_CHECKS(cx,result) \ + JS_BEGIN_MACRO \ + if (!(cx)->fp) { \ + (cx)->lastInternalResult = JSVAL_NULL; \ + LAST_FRAME_EXCEPTION_CHECK(cx, result); \ + } \ + JS_END_MACRO + JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, @@ -3222,10 +3706,7 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, if (!ts) return NULL; script = CompileTokenStream(cx, obj, ts, mark, NULL); -#if JS_HAS_EXCEPTIONS - if (!script && !cx->fp) - js_ReportUncaughtException(cx); -#endif + LAST_FRAME_CHECKS(cx, script); return script; } @@ -3241,7 +3722,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, JSErrorReporter older; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_TRUE; @@ -3255,13 +3736,14 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); if (ts) { older = JS_SetErrorReporter(cx, NULL); - if (!js_ParseTokenStream(cx, obj, ts)) { + if (!js_ParseTokenStream(cx, obj, ts) && + (ts->flags & TSF_UNEXPECTED_EOF)) { /* * 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; + result = JS_FALSE; } JS_SetErrorReporter(cx, older); @@ -3287,10 +3769,7 @@ JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) if (!ts) return NULL; script = CompileTokenStream(cx, obj, ts, mark, NULL); -#if JS_HAS_EXCEPTIONS - if (!script && !cx->fp) - js_ReportUncaughtException(cx); -#endif + LAST_FRAME_CHECKS(cx, script); return script; } @@ -3322,10 +3801,7 @@ JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, JSPRINCIPALS_HOLD(cx, ts->principals); } script = CompileTokenStream(cx, obj, ts, mark, NULL); -#if JS_HAS_EXCEPTIONS - if (!script && !cx->fp) - js_ReportUncaughtException(cx); -#endif + LAST_FRAME_CHECKS(cx, script); return script; } @@ -3369,7 +3845,7 @@ JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, JSFunction *fun; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, @@ -3389,7 +3865,7 @@ JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, JSFunction *fun; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, @@ -3449,11 +3925,10 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); if (!argAtom) break; - if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_SHARED, + JSPROP_PERMANENT | JSPROP_SHARED, SPROP_HAS_SHORTID, i)) { break; } @@ -3468,9 +3943,9 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, goto out; } if (obj && funAtom) { - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)funAtom, + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), OBJECT_TO_JSVAL(fun->object), - NULL, NULL, 0, NULL)) { + NULL, NULL, JSPROP_ENUMERATE, NULL)) { return NULL; } } @@ -3478,10 +3953,7 @@ 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 + LAST_FRAME_CHECKS(cx, fun); return fun; } @@ -3549,15 +4021,12 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) { + JSBool ok; + 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; + ok = js_Execute(cx, obj, script, NULL, 0, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; } JS_PUBLIC_API(JSBool) @@ -3593,15 +4062,16 @@ JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, - const char *bytes, uintN length, + const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + size_t length = nbytes; jschar *chars; JSBool ok; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); @@ -3612,15 +4082,16 @@ JS_EvaluateScript(JSContext *cx, JSObject *obj, JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, - const char *bytes, uintN length, + const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + size_t length = nbytes; jschar *chars; JSBool ok; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, @@ -3660,10 +4131,7 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, 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 + LAST_FRAME_CHECKS(cx, ok); JS_DestroyScript(cx, script); return ok; } @@ -3672,50 +4140,54 @@ JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; + 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; + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, + rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; } JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; 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); +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + JSAtom *atom; + + ops = (JSXMLObjectOps *) obj->map->ops; + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + obj = ops->getMethod(cx, obj, ATOM_TO_JSID(atom), &fval); + if (!obj) + return JS_FALSE; + } else #endif + if (!JS_GetProperty(cx, obj, name, &fval)) return JS_FALSE; - } - return JS_TRUE; + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; } JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; + CHECK_REQUEST(cx); - 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; + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; } JS_PUBLIC_API(JSBranchCallback) @@ -3769,15 +4241,16 @@ JS_NewString(JSContext *cx, char *bytes, size_t length) { jschar *chars; JSString *str; + size_t charsLength = length; CHECK_REQUEST(cx); /* Make a Unicode vector from the 8-bit char codes in bytes. */ - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &charsLength); if (!chars) return NULL; /* Free chars (but not bytes, which caller frees on error) if we fail. */ - str = js_NewString(cx, chars, length, 0); + str = js_NewString(cx, chars, charsLength, 0); if (!str) { JS_free(cx, chars); return NULL; @@ -3796,7 +4269,7 @@ JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) JSString *str; CHECK_REQUEST(cx); - js = js_InflateString(cx, s, n); + js = js_InflateString(cx, s, &n); if (!js) return NULL; str = js_NewString(cx, js, n, 0); @@ -3816,7 +4289,7 @@ JS_NewStringCopyZ(JSContext *cx, const char *s) if (!s) return cx->runtime->emptyString; n = strlen(s); - js = js_InflateString(cx, s, n); + js = js_InflateString(cx, s, &n); if (!js) return NULL; str = js_NewString(cx, js, n, 0); @@ -3960,6 +4433,30 @@ JS_MakeStringImmutable(JSContext *cx, JSString *str) return JS_TRUE; } +JS_PUBLIC_API(JSBool) +JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, + size_t *dstlenp) +{ + return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); +} + +JS_PUBLIC_API(JSBool) +JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, + size_t *dstlenp) +{ + return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); +} + +JS_PUBLIC_API(JSBool) +JS_StringsAreUTF8 () +{ +#ifdef JS_C_STRINGS_ARE_UTF8 + return JS_TRUE; +#else + return JS_FALSE; +#endif +} + /************************************************************************/ JS_PUBLIC_API(void) @@ -4067,7 +4564,7 @@ JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) JSObject *obj; CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; obj = js_NewRegExpObject(cx, NULL, chars, length, flags); @@ -4195,8 +4692,21 @@ JS_PUBLIC_API(JSBool) JS_ReportPendingException(JSContext *cx) { #if JS_HAS_EXCEPTIONS + JSBool save, ok; + CHECK_REQUEST(cx); - return js_ReportUncaughtException(cx); + + /* + * Set cx->creatingException to suppress the standard error-to-exception + * conversion done by all {js,JS}_Report* functions except for OOM. The + * cx->creatingException flag was added to suppress recursive divergence + * under js_ErrorToException, but it serves for our purposes here too. + */ + save = cx->creatingException; + cx->creatingException = JS_TRUE; + ok = js_ReportUncaughtException(cx); + cx->creatingException = save; + return ok; #else return JS_TRUE; #endif @@ -4259,7 +4769,7 @@ JS_DropExceptionState(JSContext *cx, JSExceptionState *state) JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v) { -#if JS_HAS_EXCEPTIONS +#if JS_HAS_ERROR_EXCEPTIONS CHECK_REQUEST(cx); return js_ErrorFromException(cx, v); #else @@ -4267,6 +4777,13 @@ JS_ErrorFromException(JSContext *cx, jsval v) #endif } +JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp) +{ + return js_ErrorToException(cx, message, reportp); +} + #ifdef JS_THREADSAFE JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx) diff --git a/src/dom/js/jsapi.h b/src/dom/js/jsapi.h index 8f491ffec..9fe07ac60 100644 --- a/src/dom/js/jsapi.h +++ b/src/dom/js/jsapi.h @@ -134,6 +134,19 @@ JS_BEGIN_EXTERN_C #define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ #define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ +/* + * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in + * JSFunctionSpec arrays that specify generic native prototype methods, i.e., + * methods of a class prototype that are exposed as static methods taking an + * extra leading argument: the generic |this| parameter. + * + * If you set this flag in a JSFunctionSpec struct's flags initializer, then + * that struct must live at least as long as the native static method object + * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically + * JSFunctionSpec structs are allocated in static arrays. + */ +#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA + /* * Well-known JS values. The extern'd variables are initialized when the * first JSContext is created by JS_NewContext (see below). @@ -446,6 +459,17 @@ JS_StringToVersion(const char *string); option supported for the XUL preprocessor and kindred beasts. */ +#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: + parse as a token, + not backward compatible with + the comment-hiding hack used + in HTML script tags. */ +#define JSOPTION_NATIVE_BRANCH_CALLBACK \ + JS_BIT(7) /* the branch callback set by + JS_SetBranchCallback may be + called with a null script + parameter, by native code + that loops intensively */ extern JS_PUBLIC_API(uint32) JS_GetOptions(JSContext *cx); @@ -495,6 +519,15 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, extern JS_PUBLIC_API(JSBool) JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); +/* + * Enumerate any already-resolved standard class ids into ida, or into a new + * JSIdArray if ida is null. Return the augmented array on success, null on + * failure with ida (if it was non-null on entry) destroyed. + */ +extern JS_PUBLIC_API(JSIdArray *) +JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, + JSIdArray *ida); + extern JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx); @@ -706,8 +739,16 @@ JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); extern JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(JSContext *cx, void *thing); +typedef enum JSGCParamKey { + JSGC_MAX_BYTES = 0, /* maximum nominal heap before last ditch GC */ + JSGC_MAX_MALLOC_BYTES = 1 /* # of JS_malloc bytes before last ditch GC */ +} JSGCParamKey; + +extern JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); + /* - * Add an external string finalizer, one created by JS_NewExternalString (see + * Add a finalizer for external strings created by JS_NewExternalString (see * below) using a type-code returned from this function, and that understands * how to free or release the memory pointed at by JS_GetStringChars(str). * @@ -792,6 +833,18 @@ struct JSClass { JSReserveSlotsOp reserveSlots; }; +struct JSExtendedClass { + JSClass base; + JSEqualityOp equality; + JSObjectOp outerObject; + JSObjectOp innerObject; + jsword reserved0; + jsword reserved1; + jsword reserved2; + jsword reserved3; + jsword reserved4; +}; + #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 */ @@ -801,6 +854,9 @@ struct JSClass { object in prototype chain passed in via *objp in/out parameter */ +#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class + prototype */ +#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ /* * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or @@ -816,8 +872,15 @@ struct JSClass { >> JSCLASS_RESERVED_SLOTS_SHIFT) \ & JSCLASS_RESERVED_SLOTS_MASK) +#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ + JSCLASS_RESERVED_SLOTS_WIDTH) + +/* True if JSClass is really a JSExtendedClass. */ +#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) + /* Initializer for unused members of statically initialized JSClass structs. */ #define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 +#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 /* For detailed comments on these function pointer types, see jspubtd.h. */ struct JSObjectOps { @@ -850,6 +913,15 @@ struct JSObjectOps { JSSetRequiredSlotOp setRequiredSlot; }; +struct JSXMLObjectOps { + JSObjectOps base; + JSGetMethodOp getMethod; + JSSetMethodOp setMethod; + JSEnumerateValuesOp enumerateValues; + JSEqualityOp equality; + JSConcatenateOp concatenate; +}; + /* * 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 @@ -878,6 +950,16 @@ JS_ValueToId(JSContext *cx, jsval v, jsid *idp); extern JS_PUBLIC_API(JSBool) JS_IdToValue(JSContext *cx, jsid id, jsval *vp); +/* + * The magic XML namespace id is int-tagged, but not a valid integer jsval. + * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) + * should handle this id specially before converting id via JSVAL_TO_INT. + */ +#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) + +/* + * JSNewResolveOp flag bits. + */ #define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ #define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ #define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ @@ -948,6 +1030,9 @@ JS_GetClass(JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); +extern JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + extern JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj); @@ -1019,6 +1104,18 @@ extern JS_PUBLIC_API(JSBool) JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp); +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + /* * Set the attributes of a property on a given object. * @@ -1052,6 +1149,10 @@ JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, extern JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); +extern JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp); + extern JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); @@ -1079,6 +1180,18 @@ JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp); +/* + * The same, but if the property is native, return its getter and setter via + * *getterp and *setterp, respectively (and only if the out parameter pointer + * is not null). + */ +extern JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp); + /* * Set the attributes of a property on a given object. * @@ -1169,6 +1282,22 @@ JS_ClearScope(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JSObject *obj); +/* + * Create an object to iterate over enumerable properties of obj, in arbitrary + * property definition order. NB: This differs from longstanding for..in loop + * order, which uses order of property definition in obj. + */ +extern JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj); + +/* + * Return true on success with *idp containing the id of the next enumerable + * property to visit using iterobj, or JSVAL_VOID if there is no such property + * left to visit. Return false on error. + */ +extern JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); + extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); @@ -1189,12 +1318,16 @@ JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); */ struct JSPrincipals { char *codebase; + + /* XXX unspecified and unused by Mozilla code -- can we remove these? */ 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 *); + + void (* JS_DLL_CALLBACK destroy)(JSContext *cx, JSPrincipals *); + JSBool (* JS_DLL_CALLBACK subsume)(JSPrincipals *, JSPrincipals *); }; #ifdef JS_THREADSAFE @@ -1219,7 +1352,7 @@ extern JS_PUBLIC_API(JSPrincipalsTranscoder) JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); extern JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSContext *cx, JSObjectPrincipalsFinder fop); +JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); /************************************************************************/ @@ -1259,6 +1392,12 @@ JS_GetFunctionId(JSFunction *fun); extern JS_PUBLIC_API(uintN) JS_GetFunctionFlags(JSFunction *fun); +/* + * Return the arity (length) of fun. + */ +extern JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun); + /* * Infallible predicate to test whether obj is a function object (faster than * comparing obj's class name to "Function", but equivalent unless someone has @@ -1625,6 +1764,44 @@ JS_UndependString(JSContext *cx, JSString *str); extern JS_PUBLIC_API(JSBool) JS_MakeStringImmutable(JSContext *cx, JSString *str); +/* + * Return JS_TRUE if C (char []) strings passed via the API and internally + * are UTF-8. The source must be compiled with JS_C_STRINGS_ARE_UTF8 defined + * to get UTF-8 support. + */ +JS_PUBLIC_API(JSBool) +JS_StringsAreUTF8(); + +/* + * Character encoding support. + * + * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size + * of the destination buffer before the call; on return, *dstlenp contains the + * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually + * stored. To determine the necessary destination buffer size, make a sizing + * call that passes NULL for dst. + * + * On errors, the functions report the error. In that case, *dstlenp contains + * the number of characters or bytes transferred so far. If cx is NULL, no + * error is reported on failure, and the functions simply return JS_FALSE. + * + * NB: Neither function stores an additional zero byte or jschar after the + * transcoded string. + * + * If the source has been compiled with the #define JS_C_STRINGS_ARE_UTF8 to + * enable UTF-8 interpretation of C char[] strings, then JS_EncodeCharacters + * encodes to UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create + * addititional errors if the character sequence is malformed. If UTF-8 + * support is disabled, the functions deflate and inflate, respectively. + */ +JS_PUBLIC_API(JSBool) +JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, + size_t *dstlenp); + +JS_PUBLIC_API(JSBool) +JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, + size_t *dstlenp); + /************************************************************************/ /* @@ -1813,6 +1990,14 @@ JS_DropExceptionState(JSContext *cx, JSExceptionState *state); extern JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v); +/* + * Given a reported error's message and JSErrorReport struct pointer, throw + * the corresponding exception on cx. + */ +extern JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp); + #ifdef JS_THREADSAFE /* diff --git a/src/dom/js/jsarena.c b/src/dom/js/jsarena.c index 2abcacd2b..8b2c8a541 100644 --- a/src/dom/js/jsarena.c +++ b/src/dom/js/jsarena.c @@ -78,11 +78,11 @@ JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) } #endif if (align == 0) - align = JS_ARENA_DEFAULT_ALIGN; + 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); + JS_ARENA_ALIGN(pool, &pool->first + 1); pool->current = &pool->first; pool->arenasize = size; #ifdef JS_ARENAMETER @@ -159,27 +159,38 @@ JS_ArenaAllocate(JSArenaPool *pool, size_t nb) jsuword extra, hdrsz, gross, sz; void *p; - /* Search pool from current forward till we find or make enough space. */ + /* + * Search pool from current forward till we find or make enough space. + * + * NB: subtract nb from a->limit in the loop condition, instead of adding + * nb to a->avail, to avoid overflowing a 32-bit address space (possible + * when running a 32-bit program on a 64-bit system where the kernel maps + * the heap up against the top of the 32-bit address space). + * + * Thanks to Juergen Kreileder , who brought this up in + * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. + */ JS_ASSERT((nb & pool->mask) == 0); - for (a = pool->current; a->avail + nb > a->limit; pool->current = a) { + for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; + pool->current = a) { ap = &a->next; if (!*ap) { /* Not enough space in pool -- 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); + if (gross < nb) + return NULL; + 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. + * Insist on exact arenasize match to avoid leaving alloc'able + * space after an oversized allocation as it grows. */ sz = JS_UPTRDIFF(b->limit, b); - if (extra - ? sz >= gross && sz <= gross + pool->arenasize - : sz == gross) { + if (sz == gross) { *bp = b->next; JS_RELEASE_LOCK(arena_freelist_lock); b->next = NULL; @@ -193,7 +204,7 @@ JS_ArenaAllocate(JSArenaPool *pool, size_t nb) JS_RELEASE_LOCK(arena_freelist_lock); b = (JSArena *) malloc(gross); if (!b) - return 0; + return NULL; b->next = NULL; b->limit = (jsuword)b + gross; JS_COUNT_ARENA(pool,++); @@ -247,6 +258,7 @@ JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) extra = HEADER_SIZE(pool); /* oversized header holds ap */ hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ gross = hdrsz + aoff; + JS_ASSERT(gross > aoff); a = (JSArena *) realloc(a, gross); if (!a) return NULL; @@ -312,34 +324,34 @@ FreeArenaList(JSArenaPool *pool, JSArena *head, JSBool reallyFree) ap = &head->next; a = *ap; if (!a) - return; + return; #ifdef DEBUG do { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - a->avail = a->base; - JS_CLEAR_UNUSED(a); + 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); + 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); + /* 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; + *ap = arena_freelist; + arena_freelist = a; JS_RELEASE_LOCK(arena_freelist_lock); - head->next = NULL; + head->next = NULL; } pool->current = head; @@ -351,14 +363,14 @@ 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); + 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); + 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; - } + FreeArenaList(pool, a, JS_TRUE); + return; + } } } @@ -439,17 +451,17 @@ 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; - } - } + 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 } @@ -543,9 +555,9 @@ JS_DumpArenaStats(FILE *fp) else variance /= nallocs * (nallocs - 1); sigma = sqrt(variance); - } else { - mean = variance = sigma = 0; - } + } else { + mean = variance = sigma = 0; + } fprintf(fp, "\n%s allocation statistics:\n", stats->name); fprintf(fp, " number of arenas: %u\n", stats->narenas); diff --git a/src/dom/js/jsarena.h b/src/dom/js/jsarena.h index e52398a1a..5370f8f82 100644 --- a/src/dom/js/jsarena.h +++ b/src/dom/js/jsarena.h @@ -113,19 +113,30 @@ struct JSArenaPool { 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)) + JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) #define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ + JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) + +/* + * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb + * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit + * address space (possible when running a 32-bit program on a 64-bit system + * where the kernel maps the heap up against the top of the 32-bit address + * space). + * + * Thanks to Juergen Kreileder , who brought this up in + * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. + */ +#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ JS_BEGIN_MACRO \ JSArena *_a = (pool)->current; \ size_t _nb = JS_ARENA_ALIGN(pool, nb); \ jsuword _p = _a->avail; \ - jsuword _q = _p + _nb; \ - JS_ASSERT(_q >= _p); \ - if (_q > _a->limit) \ + if ((guard) || _p > _a->limit - _nb) \ _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ else \ - _a->avail = _q; \ + _a->avail = _p + _nb; \ p = (type) _p; \ JS_ArenaCountAllocation(pool, nb); \ JS_END_MACRO @@ -138,9 +149,9 @@ struct JSArenaPool { 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; \ + _nb = JS_ARENA_ALIGN(pool, _nb); \ + if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ + _a->avail = (jsuword)(p) + _nb; \ JS_ArenaCountInplaceGrowth(pool, size, incr); \ } else if ((jsuword)(p) == _a->base) { \ p = (type) JS_ArenaRealloc(pool, p, size, incr); \ diff --git a/src/dom/js/jsarray.c b/src/dom/js/jsarray.c index a05a6ee3c..ef94be4e5 100644 --- a/src/dom/js/jsarray.c +++ b/src/dom/js/jsarray.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -48,6 +49,7 @@ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" +#include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsfun.h" @@ -63,7 +65,7 @@ #define MAXSTR "4294967295" /* - * Determine if the id represents an array index. + * Determine if the id represents an array index or an XML property index. * * An id is an array index according to ECMA by (15.4): * @@ -78,8 +80,8 @@ * 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) +JSBool +js_IdIsIndex(jsval id, jsuint *indexp) { JSString *str; jschar *cp; @@ -93,7 +95,10 @@ IdIsIndex(jsid id, jsuint *indexp) return JS_TRUE; } - /* It must be a string. */ + /* NB: id should be a string, but jsxml.c may call us with an object id. */ + if (!JSVAL_IS_STRING(id)) + return JS_FALSE; + str = JSVAL_TO_STRING(id); cp = JSSTRING_CHARS(str); if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { @@ -108,9 +113,8 @@ IdIsIndex(jsid id, jsuint *indexp) cp++; } } - /* Make sure all characters were consumed and that it couldn't - * have overflowed. - */ + + /* Ensure that all characters were consumed and we didn't overflow. */ if (*cp == 0 && (oldIndex < (MAXINDEX / 10) || (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) @@ -138,7 +142,7 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) *lengthp = (jsuint) i; return JS_TRUE; } - + if (!js_ValueToNumber(cx, v, &d)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); @@ -160,57 +164,94 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) JSBool js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { + JSTempValueRooter tvr; jsid id; + JSBool ok; 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; + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); + if (ok) { + /* + * Short-circuit, because js_ValueToECMAUint32 fails when called + * during init time. + */ + if (JSVAL_IS_INT(tvr.u.value)) { + i = JSVAL_TO_INT(tvr.u.value); + *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ + } else { + ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp); + } } - return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp); + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; } static JSBool -IndexToValue(JSContext *cx, jsuint length, jsval *vp) +IndexToValue(JSContext *cx, jsuint index, jsval *vp) { - if (length <= JSVAL_INT_MAX) { - *vp = INT_TO_JSVAL(length); + if (index <= JSVAL_INT_MAX) { + *vp = INT_TO_JSVAL(index); return JS_TRUE; } - return js_NewDoubleValue(cx, (jsdouble)length, vp); + return js_NewDoubleValue(cx, (jsdouble)index, vp); } static JSBool -IndexToId(JSContext *cx, jsuint length, jsid *idp) +IndexToId(JSContext *cx, jsuint index, jsid *idp) { JSString *str; JSAtom *atom; - if (length <= JSVAL_INT_MAX) { - *idp = (jsid) INT_TO_JSVAL(length); + if (index <= JSVAL_INT_MAX) { + *idp = INT_TO_JSID(index); } else { - str = js_NumberToString(cx, (jsdouble)length); + str = js_NumberToString(cx, (jsdouble)index); if (!str) return JS_FALSE; atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; - *idp = (jsid)atom; + *idp = ATOM_TO_JSID(atom); } return JS_TRUE; } +static JSBool +PropertyExists(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + + *foundp = prop != NULL; + if (*foundp) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + + return JS_TRUE; +} + +#define JSID_HOLE JSVAL_NULL + +static JSBool +IndexToExistingId(JSContext *cx, JSObject *obj, jsuint index, jsid *idp) +{ + JSBool exists; + + if (!IndexToId(cx, index, idp)) + return JS_FALSE; + if (!PropertyExists(cx, obj, *idp, &exists)) + return JS_FALSE; + if (!exists) + *idp = JSID_HOLE; + return JS_TRUE; +} + JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) { @@ -219,7 +260,7 @@ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) if (!IndexToValue(cx, length, &v)) return JS_FALSE; - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); return OBJ_SET_PROPERTY(cx, obj, id, &v); } @@ -227,17 +268,19 @@ JSBool js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { JSErrorReporter older; + JSTempValueRooter tvr; 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_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); + ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); JS_SetErrorReporter(cx, older); - if (!ok) - return JS_FALSE; - return ValueIsLength(cx, v, lengthp); + if (ok) + ok = ValueIsLength(cx, tvr.u.value, lengthp); + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; } /* @@ -278,7 +321,7 @@ array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsuint index, length; - if (!(IdIsIndex(id, &index))) + if (!js_IdIsIndex(id, &index)) return JS_TRUE; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -294,7 +337,7 @@ array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { jsuint length; - if (cx->version == JSVERSION_1_2) { + if (JS_VERSION_IS_1_2(cx)) { if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; switch (type) { @@ -323,14 +366,20 @@ 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; + JSTempValueRooter tvr; + JSAtom *atom; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return JS_FALSE; + } ok = js_GetLengthProperty(cx, obj, &length); if (!ok) @@ -398,24 +447,28 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, sepstr = NULL; seplen = JSSTRING_LENGTH(sep); + /* Use rval to locally root each element value as we loop and convert. */ +#define v (*rval) + 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))) { + if ((!literalize || JS_VERSION_IS_1_2(cx)) && + (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); - } + atom = cx->runtime->atomState.toLocaleStringAtom; + JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); + ok = js_ValueToObject(cx, v, &tvr.u.object) && + js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!ok) + goto done; + str = js_ValueToString(cx, v); } else { str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); } @@ -426,9 +479,18 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, } /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ - growth = (nchars + (sepstr ? seplen : 0) + - JSSTRING_LENGTH(str) + - 3 + 1) * sizeof(jschar); + tmplen = JSSTRING_LENGTH(str); + growth = (nchars + (sepstr ? seplen : 0) + tmplen + 3 + 1); + if (nchars > growth || tmplen > growth || + growth > (size_t)-1 / sizeof(jschar)) { + if (chars) { + free(chars); + chars = NULL; + } + JS_ReportOutOfMemory(cx); + goto done; + } + growth *= sizeof(jschar); if (!chars) { chars = (jschar *) malloc(growth); if (!chars) @@ -447,7 +509,6 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, } sepstr = JSSTRING_CHARS(sep); - tmplen = JSSTRING_LENGTH(str); js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); nchars += tmplen; } @@ -471,6 +532,8 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, return ok; } +#undef v + make_string: if (!chars) { JS_ReportOutOfMemory(cx); @@ -510,7 +573,7 @@ array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * JS1.2 arrays convert to array literals, with a comma followed by a space * between each element. */ - literalize = (cx->version == JSVERSION_1_2); + literalize = JS_VERSION_IS_1_2(cx); return array_join_sub(cx, obj, literalize ? &comma_space : &comma, literalize, rval, JS_FALSE); } @@ -533,6 +596,8 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) jsid id; for (index = 0; index < length; index++) { + JS_ASSERT(vector[index] != JSVAL_HOLE); + if (!IndexToId(cx, index, &id)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, obj, id, &vector[index])) @@ -549,7 +614,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) if (!IndexToValue(cx, length, &v)) return JS_FALSE; - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, array_length_getter, array_length_setter, JSPROP_PERMANENT, @@ -585,42 +650,76 @@ array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { jsuint len, half, i; jsid id, id2; - jsval v, v2; + jsval *tmproot, *tmproot2; + JSBool idexists, id2exists, ok; if (!js_GetLengthProperty(cx, obj, &len)) return JS_FALSE; + /* + * When len > JSVAL_INT_MAX + 1 the loop below accesses indexes greater + * than JSVAL_INT_MAX. For such indexes the corresponding ids are atoms. + * We use JS_KEEP_ATOMS to protect them against GC since OBJ_GET_PROPERTY + * can potentially execute an arbitrary script. See bug 341956. + * + * After this point control must flow through label out: to exit. + */ + if (len > JSVAL_INT_MAX + 1) + JS_KEEP_ATOMS(cx->runtime); + + /* + * Use argv[argc] and argv[argc + 1] as local roots to hold temporarily + * array elements for GC-safe swap. + */ + tmproot = argv + argc; + tmproot2 = argv + argc + 1; half = len / 2; for (i = 0; i < half; i++) { + /* + * Get both values while checking for holes to make sure they don't + * get filled. + */ 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. */ + goto bad; + if (!PropertyExists(cx, obj, id, &idexists)) + goto bad; + if (idexists && !OBJ_GET_PROPERTY(cx, obj, id, tmproot)) + goto bad; - 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; + if (!IndexToId(cx, len - i - 1, &id2)) + goto bad; + if (!PropertyExists(cx, obj, id2, &id2exists)) + goto bad; + if (id2exists && !OBJ_GET_PROPERTY(cx, obj, id2, tmproot2)) + goto bad; + + /* Exchange the values. */ + if (idexists) { + if (!OBJ_SET_PROPERTY(cx, obj, id2, tmproot)) + goto bad; + } else { + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, tmproot)) + goto bad; + } + if (id2exists) { + if (!OBJ_SET_PROPERTY(cx, obj, id, tmproot2)) + goto bad; + } else { + if (!OBJ_DELETE_PROPERTY(cx, obj, id, tmproot2)) + goto bad; } - 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; } + ok = JS_TRUE; + out: + if (len > JSVAL_INT_MAX + 1) + JS_UNKEEP_ATOMS(cx->runtime); *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; + return ok; + + bad: + ok = JS_FALSE; + goto out; } typedef struct HSortArgs { @@ -666,7 +765,7 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) 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. @@ -703,16 +802,13 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) #undef MEMCPY } -JSBool -js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg) +void +js_HeapSort(void *vec, size_t nel, void *pivot, 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; @@ -724,15 +820,13 @@ js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg) 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; + JSContext *context; + jsval fval; + jsval *localroot; /* need one local root, for sort_compare */ + JSBool status; } CompareArgs; static int @@ -742,46 +836,75 @@ sort_compare(const void *a, const void *b, void *arg) CompareArgs *ca = (CompareArgs *) arg; JSContext *cx = ca->context; jsdouble cmp = -1; - jsval fval, argv[2], rval; + jsval fval, argv[2], special; JSBool ok; fval = ca->fval; - if (fval == JSVAL_NULL) { + + /* + * By ECMA 262, 15.4.4.11, existence of the property with value undefined + * takes precedence over an undefined property (which we call a "hole"). + */ + if (av == JSVAL_HOLE || bv == JSVAL_HOLE) + special = JSVAL_HOLE; + else if (av == JSVAL_VOID || bv == JSVAL_VOID) + special = JSVAL_VOID; + else + special = JSVAL_NULL; + + if (special != JSVAL_NULL) { + if (av == bv) + cmp = 0; + else if (av != special) + cmp = -1; + else + cmp = 1; + } else 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; + /* + * Set our local root to astr in case the second js_ValueToString + * displaces the newborn root in cx, and the GC nests under that + * call. Don't bother guarding the local root store with an astr + * non-null test. If we tag null as a string, the GC will untag, + * null-test, and avoid dereferencing null. + */ + astr = js_ValueToString(cx, av); + *ca->localroot = STRING_TO_JSVAL(astr); + if (astr && (bstr = js_ValueToString(cx, bv))) + 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); + fval, 2, argv, ca->localroot); if (ok) { - ok = js_ValueToNumber(cx, rval, &cmp); + ok = js_ValueToNumber(cx, *ca->localroot, &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; + if (ok) { + 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; } + if (!ok) + ca->status = ok; } return (int)cmp; } @@ -794,17 +917,13 @@ sort_compare_strings(const void *a, const void *b, void *arg) 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; + jsval fval, *vec, *pivotroot; CompareArgs ca; jsuint len, newlen, i; - jsval *vec; + JSStackFrame *fp; jsid id; size_t nbytes; @@ -835,80 +954,70 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* - * Test for size_t overflow, which could lead to indexing beyond the end - * of the malloc'd vector. + * We need a temporary array of len jsvals to hold elements of the array. + * Check that its size does not overflow size_t, which would allow for + * indexing beyond the end of the malloc'd vector. */ - nbytes = len * sizeof(jsval); - if (nbytes != (double) len * sizeof(jsval)) { + if (len > ((size_t) -1) / sizeof(jsval)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } + nbytes = ((size_t) len) * sizeof(jsval); + vec = (jsval *) JS_malloc(cx, nbytes); if (!vec) return JS_FALSE; -#if JS_HAS_SPARSE_ARRAYS - newlen = 0; -#else - newlen = len; -#endif + /* Root vec, clearing it first in case a GC nests while we're filling it. */ + memset(vec, 0, nbytes); + fp = cx->fp; + fp->vars = vec; + fp->nvars = len; + newlen = 0; for (i = 0; i < len; i++) { - ca.status = IndexToId(cx, i, &id); + ca.status = IndexToExistingId(cx, obj, 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++; + + if (id == JSID_HOLE) { + vec[i] = JSVAL_HOLE; + all_strings = JS_FALSE; + continue; } -#endif + newlen++; + 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]); + all_strings &= JSVAL_IS_STRING(vec[i]); } ca.context = cx; ca.fval = fval; + ca.localroot = argv + argc; /* local GC root for temporary string */ + pivotroot = argv + argc + 1; /* local GC root for pivot val */ 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; - } + js_HeapSort(vec, (size_t) len, pivotroot, sizeof(jsval), + all_strings ? sort_compare_strings : sort_compare, + &ca); 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++; - } + + /* Re-create any holes that sorted to the end of the array. */ + while (len > newlen) { + jsval junk; + + if (!IndexToId(cx, --len, &id)) + return JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; } -#endif } out: @@ -943,7 +1052,7 @@ array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * return the new array length. */ length += argc; - if (cx->version == JSVERSION_1_2) { + if (JS_VERSION_IS_1_2(cx)) { *rval = argc ? argv[argc-1] : JSVAL_VOID; } else { if (!IndexToValue(cx, length, rval)) @@ -957,20 +1066,26 @@ array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint index; jsid id; + JSBool ok; 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)) + if (!IndexToId(cx, index, &id)) return JS_FALSE; - if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + /* See comments in array_reverse. */ + if (index > JSVAL_INT_MAX) + JS_KEEP_ATOMS(cx->runtime); + ok = OBJ_GET_PROPERTY(cx, obj, id, rval) && + OBJ_DELETE_PROPERTY(cx, obj, id, &junk); + if (index > JSVAL_INT_MAX) + JS_UNKEEP_ATOMS(cx->runtime); + if (!ok) return JS_FALSE; } return js_SetLengthProperty(cx, obj, index); @@ -981,7 +1096,7 @@ array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, i; jsid id, id2; - jsval v, junk; + jsval junk; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1000,16 +1115,23 @@ array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) for (i = 1; i <= length; i++) { if (!IndexToId(cx, i, &id)) return JS_FALSE; - if (!IndexToId(cx, i - 1, &id2)) + if (!OBJ_GET_PROPERTY(cx, obj, id, &argv[0])) return JS_FALSE; - if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + + /* Get id after value to avoid nested GC hazards. */ + if (!IndexToId(cx, i - 1, &id2)) return JS_FALSE; - if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) + if (!OBJ_SET_PROPERTY(cx, obj, id2, &argv[0])) return JS_FALSE; } } - /* Delete the only or last element. */ + /* + * Delete the only or the last element. We recreate id when it is an + * atom to protect against a nested GC during the last iteration. + */ + if (length > JSVAL_INT_MAX && !IndexToId(cx, length, &id)) + return JS_FALSE; if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) return JS_FALSE; } @@ -1023,11 +1145,7 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsuint length, last; uintN i; jsid id, id2; - jsval v; -#if JS_HAS_SPARSE_ARRAYS - JSObject *obj2; - JSProperty *prop; -#endif + jsval *vp, junk; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1035,24 +1153,25 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, /* Slide up the array to make room for argc at the bottom. */ if (length > 0) { last = length; + vp = argv + argc; while (last--) { - if (!IndexToId(cx, last, &id)) + if (!IndexToExistingId(cx, obj, last, &id)) return JS_FALSE; + if (id != JSID_HOLE) { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + } + + /* Get id after value to avoid nested GC hazards. */ 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; + if (id == JSID_HOLE) { + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; + } else { + if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) + return JS_FALSE; } - 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; } } @@ -1075,16 +1194,20 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsval *vp, junk; jsuint length, begin, end, count, delta, last; - uintN i; jsdouble d; jsid id, id2; - jsval v; JSObject *obj2; + uintN i; - /* Nothing to do if no args. Otherwise lock and load length. */ + /* + * Nothing to do if no args. Otherwise point vp at our one explicit local + * root and get length. + */ if (argc == 0) return JS_TRUE; + vp = argv + argc; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1122,7 +1245,7 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) argv++; } - if (count == 1 && cx->version == JSVERSION_1_2) { + if (count == 1 && JS_VERSION_IS_1_2(cx)) { /* * JS lacks "list context", whereby in Perl one turns the single * scalar that's spliced out into an array just by assigning it to @@ -1139,7 +1262,7 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) return JS_FALSE; } else { - if (cx->version != JSVERSION_1_2 || count > 0) { + if (!JS_VERSION_IS_1_2(cx) || 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 @@ -1154,15 +1277,22 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* 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)) + if (!IndexToExistingId(cx, obj, last, &id)) return JS_FALSE; - if (!IndexToId(cx, last - begin, &id2)) + if (id == JSID_HOLE) + continue; /* don't fill holes in the new array */ + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) return JS_FALSE; - if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + + /* Get id after value to avoid nested GC hazards. */ + if (!IndexToId(cx, last - begin, &id2)) return JS_FALSE; - if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v)) + if (!OBJ_SET_PROPERTY(cx, obj2, id2, vp)) return JS_FALSE; } + + if (!js_SetLengthProperty(cx, obj2, end - begin)) + return JS_FALSE; } } } @@ -1173,27 +1303,45 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) last = length; /* (uint) end could be 0, so can't use vanilla >= test */ while (last-- > end) { - if (!IndexToId(cx, last, &id)) + if (!IndexToExistingId(cx, obj, last, &id)) return JS_FALSE; + if (id != JSID_HOLE) { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + } + + /* Get id after value to avoid nested GC hazards. */ 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; + if (id != JSID_HOLE) { + if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) + return JS_FALSE; + } else { + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; + } } length += delta; } else if (argc < count) { delta = count - (jsuint)argc; for (last = end; last < length; last++) { - if (!IndexToId(cx, last, &id)) + if (!IndexToExistingId(cx, obj, last, &id)) return JS_FALSE; + if (id != JSID_HOLE) { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + } + + /* Get id after value to avoid nested GC hazards. */ 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; + if (id != JSID_HOLE) { + if (!OBJ_SET_PROPERTY(cx, obj, id2, vp)) + return JS_FALSE; + } else { + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; + } } length -= delta; } @@ -1218,12 +1366,15 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsval *vp, v; JSObject *nobj, *aobj; jsuint length, alength, slot; uintN i; - jsval v; jsid id, id2; + /* Hoist the explicit local root address computation. */ + vp = argv + argc; + /* Treat obj as the first argument; see ECMA 15.4.4.4. */ --argv; JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); @@ -1242,20 +1393,30 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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)) { + ATOM_TO_JSID(cx->runtime->atomState + .lengthAtom), + vp)) { return JS_FALSE; } - if (!ValueIsLength(cx, v, &alength)) + if (!ValueIsLength(cx, *vp, &alength)) return JS_FALSE; for (slot = 0; slot < alength; slot++) { - if (!IndexToId(cx, slot, &id)) + if (!IndexToExistingId(cx, aobj, slot, &id)) return JS_FALSE; - if (!IndexToId(cx, length + slot, &id2)) + if (id == JSID_HOLE) { + /* + * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent + * properties. + */ + continue; + } + if (!OBJ_GET_PROPERTY(cx, aobj, id, vp)) return JS_FALSE; - if (!OBJ_GET_PROPERTY(cx, aobj, id, &v)) + + /* Get id after value to avoid nested GC hazards. */ + if (!IndexToId(cx, length + slot, &id2)) return JS_FALSE; - if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp)) return JS_FALSE; } length += alength; @@ -1263,28 +1424,34 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } } + *vp = v; if (!IndexToId(cx, length, &id)) return JS_FALSE; - if (!OBJ_SET_PROPERTY(cx, nobj, id, &v)) + if (!OBJ_SET_PROPERTY(cx, nobj, id, vp)) return JS_FALSE; length++; } - return JS_TRUE; + return js_SetLengthProperty(cx, nobj, length); } static JSBool array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsval *vp; JSObject *nobj; jsuint length, begin, end, slot; jsdouble d; jsid id, id2; - jsval v; + /* Hoist the explicit local root address computation. */ + vp = argv + argc; + + /* 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); if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1319,20 +1486,316 @@ array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } } + if (begin > end) + begin = end; + for (slot = begin; slot < end; slot++) { - if (!IndexToId(cx, slot, &id)) + if (!IndexToExistingId(cx, obj, slot, &id)) + return JS_FALSE; + if (id == JSID_HOLE) + continue; + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) return JS_FALSE; + + /* Get id after value to avoid nested GC hazards. */ if (!IndexToId(cx, slot - begin, &id2)) return JS_FALSE; - if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp)) return JS_FALSE; - if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v)) + } + return js_SetLengthProperty(cx, nobj, end - begin); +} +#endif /* JS_HAS_SEQUENCE_OPS */ + +#if JS_HAS_ARRAY_EXTRAS + +static JSBool +array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval, JSBool isLast) +{ + jsuint length, i, stop; + jsint direction; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + if (length == 0) + goto not_found; + + if (argc <= 1) { + i = isLast ? length - 1 : 0; + } else { + jsdouble start; + + if (!js_ValueToNumber(cx, argv[1], &start)) return JS_FALSE; + start = js_DoubleToInteger(start); + if (start < 0) { + start += length; + i = (start < 0) ? 0 : (jsuint)start; + } else if (start >= length) { + i = length - 1; + } else { + i = (jsuint)start; + } } - *rval = OBJECT_TO_JSVAL(nobj); + + if (isLast) { + stop = 0; + direction = -1; + } else { + stop = length - 1; + direction = 1; + } + + for (;;) { + jsid id; + jsval v; + + if (!IndexToExistingId(cx, obj, (jsuint)i, &id)) + return JS_FALSE; + if (id != JSID_HOLE) { + if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) + return JS_FALSE; + if (js_StrictlyEqual(v, argv[0])) + return js_NewNumberValue(cx, i, rval); + } + + if (i == stop) + goto not_found; + i += direction; + } + + not_found: + *rval = INT_TO_JSVAL(-1); return JS_TRUE; } -#endif /* JS_HAS_SEQUENCE_OPS */ + +static JSBool +array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE); +} + +static JSBool +array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE); +} + +/* Order is important; extras that use a caller's predicate must follow MAP. */ +typedef enum ArrayExtraMode { + FOREACH, + MAP, + FILTER, + SOME, + EVERY +} ArrayExtraMode; + +static JSBool +array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, + ArrayExtraMode mode) +{ + jsval *vp, *sp, *origsp, *oldsp; + jsuint length, newlen, i; + JSObject *callable, *thisp, *newarr; + void *mark; + JSStackFrame *fp; + JSBool ok, b; + + /* Hoist the explicit local root address computation. */ + vp = argv + argc; + + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + + /* + * First, get or compute our callee, so that we error out consistently + * when passed a non-callable object. + */ + callable = js_ValueToCallableObject(cx, &argv[0], 0); + if (!callable) + return JS_FALSE; + + /* + * Set our initial return condition, used for zero-length array cases + * (and pre-size our map return to match our known length, for all cases). + */ +#ifdef __GNUC__ /* quell GCC overwarning */ + newlen = 0; + newarr = NULL; + ok = JS_TRUE; +#endif + switch (mode) { + case MAP: + case FILTER: + newlen = (mode == MAP) ? length : 0; + newarr = js_NewArrayObject(cx, newlen, NULL); + if (!newarr) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(newarr); + break; + case SOME: + *rval = JSVAL_FALSE; + break; + case EVERY: + *rval = JSVAL_TRUE; + break; + case FOREACH: + break; + } + + if (length == 0) + return JS_TRUE; + + if (argc > 1) { + if (!js_ValueToObject(cx, argv[1], &thisp)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(thisp); + } else { + thisp = NULL; + } + + /* We call with 3 args (value, index, array), plus room for rval. */ + origsp = js_AllocStack(cx, 2 + 3 + 1, &mark); + if (!origsp) + return JS_FALSE; + + /* Lift current frame to include our args. */ + fp = cx->fp; + oldsp = fp->sp; + + for (i = 0; i < length; i++) { + jsid id; + jsval rval2; + + ok = IndexToExistingId(cx, obj, i, &id); + if (!ok) + break; + if (id == JSID_HOLE) + continue; + ok = OBJ_GET_PROPERTY(cx, obj, id, vp); + if (!ok) + break; + + /* + * Push callable and 'this', then args. We must do this for every + * iteration around the loop since js_Invoke uses origsp[0] for rval + * storage and some native functions use origsp[1] for local rooting. + */ + sp = origsp; + *sp++ = OBJECT_TO_JSVAL(callable); + *sp++ = OBJECT_TO_JSVAL(thisp); + *sp++ = *vp; + *sp++ = INT_TO_JSVAL(i); + *sp++ = OBJECT_TO_JSVAL(obj); + + /* Do the call. */ + fp->sp = sp; + ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); + rval2 = fp->sp[-1]; + fp->sp = oldsp; + if (!ok) + break; + + if (mode > MAP) { + if (rval2 == JSVAL_NULL) { + b = JS_FALSE; + } else if (JSVAL_IS_BOOLEAN(rval2)) { + b = JSVAL_TO_BOOLEAN(rval2); + } else { + ok = js_ValueToBoolean(cx, rval2, &b); + if (!ok) + goto out; + } + } + + switch (mode) { + case FOREACH: + break; + case MAP: + /* + * We reconstruct id once again when it is a GC thing since scripts + * can trigger GC that collects it. See bug 341956. + */ + if (i > JSVAL_INT_MAX) { + ok = IndexToId(cx, i, &id); + if (!ok) + goto out; + } + ok = OBJ_SET_PROPERTY(cx, newarr, id, &rval2); + if (!ok) + goto out; + break; + case FILTER: + if (!b) + break; + /* Filter passed *vp, push as result. */ + ok = IndexToId(cx, newlen++, &id); + if (!ok) + goto out; + ok = OBJ_SET_PROPERTY(cx, newarr, id, vp); + if (!ok) + goto out; + break; + case SOME: + if (b) { + *rval = JSVAL_TRUE; + goto out; + } + break; + case EVERY: + if (!b) { + *rval = JSVAL_FALSE; + goto out; + } + break; + } + } + + out: + js_FreeStack(cx, mark); + if (ok && mode == FILTER) + ok = js_SetLengthProperty(cx, newarr, newlen); + return ok; +} + +static JSBool +array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_extra(cx, obj, argc, argv, rval, FOREACH); +} + +static JSBool +array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_extra(cx, obj, argc, argv, rval, MAP); +} + +static JSBool +array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_extra(cx, obj, argc, argv, rval, FILTER); +} + +static JSBool +array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_extra(cx, obj, argc, argv, rval, SOME); +} + +static JSBool +array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return array_extra(cx, obj, argc, argv, rval, EVERY); +} +#endif static JSFunctionSpec array_methods[] = { #if JS_HAS_TOSOURCE @@ -1343,22 +1806,32 @@ static JSFunctionSpec array_methods[] = { /* 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}, + {"join", array_join, 1,JSFUN_GENERIC_NATIVE,0}, + {"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2}, + {"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2}, #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}, + {"push", array_push, 1,JSFUN_GENERIC_NATIVE,0}, + {"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0}, + {"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1}, + {"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1}, + {"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1}, #endif /* Python-esque sequence methods. */ #if JS_HAS_SEQUENCE_OPS - {"concat", array_concat, 0,0,0}, - {"slice", array_slice, 0,0,0}, + {"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1}, + {"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1}, +#endif + +#if JS_HAS_ARRAY_EXTRAS + {"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, + {"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, + {"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,1}, + {"map", array_map, 1,JSFUN_GENERIC_NATIVE,1}, + {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,1}, + {"some", array_some, 1,JSFUN_GENERIC_NATIVE,1}, + {"every", array_every, 1,JSFUN_GENERIC_NATIVE,1}, #endif {0,0,0,0,0} @@ -1381,7 +1854,7 @@ Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (argc == 0) { length = 0; vector = NULL; - } else if (cx->version == JSVERSION_1_2) { + } else if (JS_VERSION_IS_1_2(cx)) { length = (jsuint) argc; vector = argv; } else if (argc > 1) { diff --git a/src/dom/js/jsarray.h b/src/dom/js/jsarray.h index cbb2aedf1..0f43bee07 100644 --- a/src/dom/js/jsarray.h +++ b/src/dom/js/jsarray.h @@ -47,6 +47,9 @@ JS_BEGIN_EXTERN_C +extern JSBool +js_IdIsIndex(jsval id, jsuint *indexp); + extern JSClass js_ArrayClass; extern JSObject * @@ -69,8 +72,9 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); */ 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); +extern void +js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, + JSComparator cmp, void *arg); JS_END_EXTERN_C diff --git a/src/dom/js/jsatom.c b/src/dom/js/jsatom.c index ede2350b6..6de628aef 100644 --- a/src/dom/js/jsatom.c +++ b/src/dom/js/jsatom.c @@ -50,6 +50,7 @@ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" +#include "jsconfig.h" #include "jsgc.h" #include "jslock.h" #include "jsnum.h" @@ -59,19 +60,12 @@ 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; + return js_ValueToPrintableString(cx, ATOM_KEY(atom)); } +#if JS_HAS_ERROR_EXCEPTIONS extern const char js_Error_str[]; /* trivial, from jsexn.h */ +#endif /* * Keep this in sync with jspubtd.h -- an assertion below will insist that @@ -79,11 +73,13 @@ extern const char js_Error_str[]; /* trivial, from jsexn.h */ */ const char *js_type_str[] = { "undefined", - "object", + js_object_str, "function", "string", "number", "boolean", + "null", + "xml", }; const char *js_boolean_str[] = { @@ -98,11 +94,15 @@ 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_Namespace_str[] = "Namespace"; const char js_Number_str[] = "Number"; const char js_Object_str[] = "Object"; +const char js_QName_str[] = "QName"; const char js_RegExp_str[] = "RegExp"; const char js_Script_str[] = "Script"; const char js_String_str[] = "String"; +const char js_XML_str[] = "XML"; +const char js_File_str[] = "File"; const char js_anonymous_str[] = "anonymous"; const char js_arguments_str[] = "arguments"; const char js_arity_str[] = "arity"; @@ -111,6 +111,7 @@ const char js_caller_str[] = "caller"; const char js_class_prototype_str[] = "prototype"; const char js_constructor_str[] = "constructor"; const char js_count_str[] = "__count__"; +const char js_each_str[] = "each"; const char js_eval_str[] = "eval"; const char js_getter_str[] = "getter"; const char js_get_str[] = "get"; @@ -119,7 +120,9 @@ 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_object_str[] = "object"; const char js_parent_str[] = "__parent__"; +const char js_private_str[] = "private"; const char js_proto_str[] = "__proto__"; const char js_setter_str[] = "setter"; const char js_set_str[] = "set"; @@ -128,6 +131,19 @@ const char js_toString_str[] = "toString"; const char js_toLocaleString_str[] = "toLocaleString"; const char js_valueOf_str[] = "valueOf"; +#if JS_HAS_XML_SUPPORT +const char js_etago_str[] = ""; +const char js_qualifier_str[] = "::"; +const char js_space_str[] = " "; +const char js_stago_str[] = "<"; +const char js_star_str[] = "*"; +const char js_starQualifier_str[] = "*::"; +const char js_tagc_str[] = ">"; +const char js_xml_str[] = "xml"; +#endif + #ifdef NARCISSUS const char js_call_str[] = "__call__"; const char js_construct_str[] = "__construct__"; @@ -136,9 +152,9 @@ const char js_ExecutionContext_str[] = "ExecutionContext"; const char js_current_str[] = "current"; #endif -#define HASH_OBJECT(o) ((JSHashNumber)(o) >> JSVAL_TAGBITS) +#define HASH_OBJECT(o) (JS_PTR_TO_UINT32(o) >> JSVAL_TAGBITS) #define HASH_INT(i) ((JSHashNumber)(i)) -#define HASH_DOUBLE(dp) ((JSHashNumber)(JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) +#define HASH_DOUBLE(dp) ((JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) #define HASH_BOOLEAN(b) ((JSHashNumber)(b)) JS_STATIC_DLL_CALLBACK(JSHashNumber) @@ -291,14 +307,20 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(BooleanAtom, js_Boolean_str); FROB(CallAtom, js_Call_str); FROB(DateAtom, js_Date_str); +#if JS_HAS_ERROR_EXCEPTIONS FROB(ErrorAtom, js_Error_str); +#endif FROB(FunctionAtom, js_Function_str); FROB(MathAtom, js_Math_str); + FROB(NamespaceAtom, js_Namespace_str); FROB(NumberAtom, js_Number_str); FROB(ObjectAtom, js_Object_str); + FROB(QNameAtom, js_QName_str); FROB(RegExpAtom, js_RegExp_str); FROB(ScriptAtom, js_Script_str); FROB(StringAtom, js_String_str); + FROB(XMLAtom, js_XML_str); + FROB(FileAtom, js_File_str); FROB(anonymousAtom, js_anonymous_str); FROB(argumentsAtom, js_arguments_str); FROB(arityAtom, js_arity_str); @@ -307,6 +329,7 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(classPrototypeAtom, js_class_prototype_str); FROB(constructorAtom, js_constructor_str); FROB(countAtom, js_count_str); + FROB(eachAtom, js_each_str); FROB(evalAtom, js_eval_str); FROB(getAtom, js_get_str); FROB(getterAtom, js_getter_str); @@ -324,6 +347,19 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(toLocaleStringAtom, js_toLocaleString_str); FROB(valueOfAtom, js_valueOf_str); +#if JS_HAS_XML_SUPPORT + FROB(etagoAtom, js_etago_str); + FROB(namespaceAtom, js_namespace_str); + FROB(ptagcAtom, js_ptagc_str); + FROB(qualifierAtom, js_qualifier_str); + FROB(spaceAtom, js_space_str); + FROB(stagoAtom, js_stago_str); + FROB(starAtom, js_star_str); + FROB(starQualifierAtom, js_starQualifier_str); + FROB(tagcAtom, js_tagc_str); + FROB(xmlAtom, js_xml_str); +#endif + #ifdef NARCISSUS FROB(callAtom, js_call_str); FROB(constructAtom, js_construct_str); @@ -445,7 +481,7 @@ js_atom_sweeper(JSHashEntry *he, intN i, void *arg) return HT_ENUMERATE_NEXT; } JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); - atom->entry.key = NULL; + atom->entry.key = atom->entry.value = NULL; atom->flags = 0; return HT_ENUMERATE_REMOVE; } @@ -595,6 +631,12 @@ out: return atom; } +/* + * To put an atom into the hidden subspace. XOR its keyHash with this value, + * which is (sqrt(2)-1) in 32-bit fixed point. + */ +#define HIDDEN_ATOM_SUBSPACE_KEYHASH 0x6A09E667 + JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { @@ -606,6 +648,8 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) JSAtom *atom; keyHash = js_HashString(str); + if (flags & ATOM_HIDDEN) + keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH; key = STRING_TO_JSVAL(str); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); @@ -651,7 +695,7 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) } atom = (JSAtom *)he; - atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED); + atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); cx->lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); @@ -675,12 +719,15 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) */ #define ATOMIZE_BUF_MAX 32 jschar inflated[ATOMIZE_BUF_MAX]; + size_t inflatedLength = ATOMIZE_BUF_MAX - 1; if (length < ATOMIZE_BUF_MAX) { - js_InflateStringToBuffer(inflated, bytes, length); + js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); + inflated[inflatedLength] = 0; chars = inflated; } else { - chars = js_InflateString(cx, bytes, length); + inflatedLength = length; + chars = js_InflateString(cx, bytes, &inflatedLength); if (!chars) return NULL; flags |= ATOM_NOCOPY; @@ -689,7 +736,7 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) str = ALIGN(buf, JSString); str->chars = chars; - str->length = length; + str->length = inflatedLength; atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars)) JS_free(cx, chars); @@ -805,12 +852,18 @@ js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) 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, + al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr, JS_CompareValues, JS_CompareValues, &temp_alloc_ops, cx); if (!al->table) return NULL; + /* + * Set ht->nentries explicitly, because we are moving entries + * from al to ht, not calling JS_HashTable(Raw|)Add. + */ + al->table->nentries = al->count; + /* Insert each ale on al->list into the new hash table. */ for (ale2 = al->list; ale2; ale2 = next) { next = ALE_NEXT(ale2); diff --git a/src/dom/js/jsatom.h b/src/dom/js/jsatom.h index 6f486c337..f8ce3f90f 100644 --- a/src/dom/js/jsatom.h +++ b/src/dom/js/jsatom.h @@ -58,11 +58,13 @@ 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_HIDDEN 0x08 /* atom is in special hidden subspace */ #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 */ + JSHashEntry entry; /* key is jsval, value keyword info or + unhidden atom if ATOM_HIDDEN */ uint32 flags; /* pinned, interned, and mark flags */ jsatomid number; /* atom serial number and hash code */ }; @@ -95,14 +97,14 @@ struct JSAtomListElement { }; #define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) -#define ALE_INDEX(ale) ((jsatomid) (ale)->entry.value) +#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((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_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) +#define ALE_SET_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op)) #define ALE_SET_VALUE(ale,val) ((ale)->entry.value = (JSHashEntry *)(val)) #define ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link)) @@ -153,6 +155,9 @@ struct JSAtomState { jsatomid number; /* one beyond greatest atom number */ jsatomid liveAtoms; /* number of live atoms after last GC */ + /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ + JSAtom *emptyAtom; + /* Type names and value literals. */ JSAtom *typeAtoms[JSTYPE_LIMIT]; JSAtom *booleanAtoms[2]; @@ -167,11 +172,15 @@ struct JSAtomState { JSAtom *ErrorAtom; JSAtom *FunctionAtom; JSAtom *MathAtom; + JSAtom *NamespaceAtom; JSAtom *NumberAtom; JSAtom *ObjectAtom; + JSAtom *QNameAtom; JSAtom *RegExpAtom; JSAtom *ScriptAtom; JSAtom *StringAtom; + JSAtom *XMLAtom; + JSAtom *FileAtom; JSAtom *anonymousAtom; JSAtom *argumentsAtom; JSAtom *arityAtom; @@ -180,6 +189,8 @@ struct JSAtomState { JSAtom *classPrototypeAtom; JSAtom *constructorAtom; JSAtom *countAtom; + JSAtom *eachAtom; + JSAtom *etagoAtom; JSAtom *evalAtom; JSAtom *getAtom; JSAtom *getterAtom; @@ -187,18 +198,29 @@ struct JSAtomState { JSAtom *inputAtom; JSAtom *lengthAtom; JSAtom *nameAtom; + JSAtom *namespaceAtom; JSAtom *noSuchMethodAtom; JSAtom *parentAtom; JSAtom *protoAtom; + JSAtom *ptagcAtom; + JSAtom *qualifierAtom; JSAtom *setAtom; JSAtom *setterAtom; + JSAtom *spaceAtom; + JSAtom *stagoAtom; + JSAtom *starAtom; + JSAtom *starQualifierAtom; + JSAtom *tagcAtom; JSAtom *toLocaleStringAtom; JSAtom *toSourceAtom; JSAtom *toStringAtom; JSAtom *valueOfAtom; + JSAtom *xmlAtom; /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { + JSAtom *AnyNameAtom; + JSAtom *AttributeNameAtom; JSAtom *EvalErrorAtom; JSAtom *InfinityAtom; JSAtom *InternalErrorAtom; @@ -208,6 +230,7 @@ struct JSAtomState { JSAtom *SyntaxErrorAtom; JSAtom *TypeErrorAtom; JSAtom *URIErrorAtom; + JSAtom *XMLListAtom; JSAtom *decodeURIAtom; JSAtom *decodeURIComponentAtom; JSAtom *defineGetterAtom; @@ -215,10 +238,12 @@ struct JSAtomState { JSAtom *encodeURIAtom; JSAtom *encodeURIComponentAtom; JSAtom *escapeAtom; + JSAtom *functionNamespaceURIAtom; JSAtom *hasOwnPropertyAtom; JSAtom *isFiniteAtom; JSAtom *isNaNAtom; JSAtom *isPrototypeOfAtom; + JSAtom *isXMLNameAtom; JSAtom *lookupGetterAtom; JSAtom *lookupSetterAtom; JSAtom *parseFloatAtom; @@ -254,11 +279,15 @@ 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_Namespace_str[]; extern const char js_Number_str[]; extern const char js_Object_str[]; +extern const char js_QName_str[]; extern const char js_RegExp_str[]; extern const char js_Script_str[]; extern const char js_String_str[]; +extern const char js_XML_str[]; +extern const char js_File_str[]; extern const char js_anonymous_str[]; extern const char js_arguments_str[]; extern const char js_arity_str[]; @@ -267,6 +296,8 @@ 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_etago_str[]; +extern const char js_each_str[]; extern const char js_eval_str[]; extern const char js_getter_str[]; extern const char js_get_str[]; @@ -274,15 +305,26 @@ 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_namespace_str[]; extern const char js_noSuchMethod_str[]; +extern const char js_object_str[]; extern const char js_parent_str[]; +extern const char js_private_str[]; extern const char js_proto_str[]; +extern const char js_ptagc_str[]; +extern const char js_qualifier_str[]; extern const char js_setter_str[]; extern const char js_set_str[]; +extern const char js_space_str[]; +extern const char js_stago_str[]; +extern const char js_star_str[]; +extern const char js_starQualifier_str[]; +extern const char js_tagc_str[]; extern const char js_toSource_str[]; extern const char js_toString_str[]; extern const char js_toLocaleString_str[]; extern const char js_valueOf_str[]; +extern const char js_xml_str[]; #ifdef NARCISSUS extern const char js_call_str[]; diff --git a/src/dom/js/jsbool.c b/src/dom/js/jsbool.c index 75d4ee05a..33d5c507e 100644 --- a/src/dom/js/jsbool.c +++ b/src/dom/js/jsbool.c @@ -54,7 +54,7 @@ #include "jsobj.h" #include "jsstr.h" -static JSClass boolean_class = { +JSClass js_BooleanClass = { "Boolean", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, @@ -67,23 +67,23 @@ static JSClass boolean_class = { static JSBool bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsval v; char buf[32]; JSString *str; - if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) - return JS_FALSE; + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, 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); + 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]); + js_BooleanClass.name, + js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); str = JS_NewStringCopyZ(cx, buf); if (!str) - return JS_FALSE; + return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } @@ -91,21 +91,21 @@ bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsval v; JSAtom *atom; JSString *str; - if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) - return JS_FALSE; + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, 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); + 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; + return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } @@ -113,8 +113,8 @@ bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 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; + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) + return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } @@ -123,16 +123,11 @@ 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}, + {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) { @@ -140,15 +135,15 @@ Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsval bval; if (argc != 0) { - if (!js_ValueToBoolean(cx, argv[0], &b)) - return JS_FALSE; - bval = BOOLEAN_TO_JSVAL(b); + if (!js_ValueToBoolean(cx, argv[0], &b)) + return JS_FALSE; + bval = BOOLEAN_TO_JSVAL(b); } else { - bval = JSVAL_FALSE; + bval = JSVAL_FALSE; } if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = bval; - return JS_TRUE; + *rval = bval; + return JS_TRUE; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); return JS_TRUE; @@ -159,10 +154,10 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj) { JSObject *proto; - proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1, - NULL, boolean_methods, NULL, NULL); + proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, + NULL, boolean_methods, NULL, NULL); if (!proto) - return NULL; + return NULL; OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); return proto; } @@ -172,9 +167,9 @@ js_BooleanToObject(JSContext *cx, JSBool b) { JSObject *obj; - obj = js_NewObject(cx, &boolean_class, NULL, NULL); + obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL); if (!obj) - return NULL; + return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); return obj; } @@ -192,27 +187,27 @@ js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) jsdouble d; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - b = JS_FALSE; + 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; - } + if (!JS_VERSION_IS_ECMA(cx)) { + 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; + 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; + 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; + 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); + b = JSVAL_TO_BOOLEAN(v); } *bp = b; diff --git a/src/dom/js/jsbool.h b/src/dom/js/jsbool.h index c07910f59..770b94c6d 100644 --- a/src/dom/js/jsbool.h +++ b/src/dom/js/jsbool.h @@ -45,6 +45,17 @@ JS_BEGIN_EXTERN_C +/* + * Crypto-booleans, not visible to script but used internally by the engine. + * + * JSVAL_HOLE is a useful value for identifying a hole in an array. It's also + * used in the interpreter to represent "no exception pending". In general it + * can be used to represent "no value". + */ +#define JSVAL_HOLE BOOLEAN_TO_JSVAL(2) + +extern JSClass js_BooleanClass; + extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); diff --git a/src/dom/js/jsclist.h b/src/dom/js/jsclist.h index 2eafe8e40..604ec0ec9 100644 --- a/src/dom/js/jsclist.h +++ b/src/dom/js/jsclist.h @@ -52,35 +52,35 @@ typedef struct JSCListStr { /* ** 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); \ +#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); \ +#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) +#define JS_NEXT_LINK(_e) \ + ((_e)->next) /* ** Return the element preceding element "_e" */ -#define JS_PREV_LINK(_e) \ - ((_e)->prev) +#define JS_PREV_LINK(_e) \ + ((_e)->prev) /* ** Append an element "_e" to the end of the list "_l" @@ -99,10 +99,10 @@ typedef struct JSCListStr { /* ** 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; \ +#define JS_REMOVE_LINK(_e) \ + JS_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ JS_END_MACRO /* @@ -110,11 +110,11 @@ typedef struct JSCListStr { ** 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_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + (_e)->next = (_e); \ + (_e)->prev = (_e); \ JS_END_MACRO /* @@ -128,9 +128,9 @@ typedef struct JSCListStr { ** Initialize a circular list */ #define JS_INIT_CLIST(_l) \ - JS_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ + JS_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ JS_END_MACRO #define JS_INIT_STATIC_CLIST(_l) \ diff --git a/src/dom/js/jscntxt.c b/src/dom/js/jscntxt.c index de254ca9c..43041f97b 100644 --- a/src/dom/js/jscntxt.c +++ b/src/dom/js/jscntxt.c @@ -63,6 +63,27 @@ #include "jsscript.h" #include "jsstr.h" +void +js_OnVersionChange(JSContext *cx) +{ +#if !JS_BUG_FALLIBLE_EQOPS + if (JS_VERSION_IS_1_2(cx)) { + 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 */ +} + +void +js_SetVersion(JSContext *cx, JSVersion version) +{ + cx->version = version; + js_OnVersionChange(cx); +} + JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { @@ -131,15 +152,22 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) * as well as "first". */ if (first) { + /* + * Both atomState and the scriptFilenameTable may be left over from a + * previous episode of non-zero contexts alive in rt, so don't re-init + * either table if it's not necessary. Just repopulate atomState with + * well-known internal atoms, and with the reserved identifiers added + * by the scanner. + */ ok = (rt->atomState.liveAtoms == 0) ? js_InitAtomState(cx, &rt->atomState) : js_InitPinnedAtoms(cx, &rt->atomState); if (ok) ok = js_InitScanner(cx); + if (ok && !rt->scriptFilenameTable) + ok = js_InitRuntimeScriptState(rt); if (ok) ok = js_InitRuntimeNumberState(cx); - if (ok) - ok = js_InitRuntimeScriptState(cx); if (ok) ok = js_InitRuntimeStringState(cx); if (!ok) { @@ -242,8 +270,9 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode) 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); + /* Also free the script filename table if it exists and is empty. */ + if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) + js_FinishRuntimeScriptState(rt); /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_GC(rt); @@ -334,7 +363,7 @@ resolving_HashKey(JSDHashTable *table, const void *ptr) { const JSResolvingKey *key = (const JSResolvingKey *)ptr; - return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id; + return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id; } JS_PUBLIC_API(JSBool) @@ -454,7 +483,7 @@ js_EnterLocalRootScope(JSContext *cx) mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); if (mark < 0) return JS_FALSE; - lrs->scopeMark = (uint16) mark; + lrs->scopeMark = (uint32) mark; return JS_TRUE; } @@ -490,9 +519,9 @@ js_LeaveLocalRootScope(JSContext *cx) /* Pop the scope, restoring lrs->scopeMark. */ lrc = lrs->topChunk; m = mark & JSLRS_CHUNK_MASK; - lrs->scopeMark = JSVAL_TO_INT(lrc->roots[m]); + lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); lrc->roots[m] = JSVAL_NULL; - lrs->rootCount = (uint16) mark; + lrs->rootCount = (uint32) mark; /* * Free the stack eagerly, risking malloc churn. The alternative would @@ -584,7 +613,7 @@ js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) * 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) { + if ((uint32)(n + 1) == 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_LOCAL_ROOTS); return -1; @@ -604,7 +633,7 @@ js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) } lrs->rootCount = n + 1; lrc->roots[m] = v; - return (int) m; + return (int) n; } void @@ -619,19 +648,26 @@ js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) mark = lrs->scopeMark; lrc = lrs->topChunk; - while (--n > mark) { + do { + while (--n > mark) { #ifdef GC_MARK_DEBUG - char name[22]; - JS_snprintf(name, sizeof name, "", n); + char name[22]; + JS_snprintf(name, sizeof name, "", n); #else - const char *name = NULL; + 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; + } m = n & JSLRS_CHUNK_MASK; - JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); - JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL); + mark = JSVAL_TO_INT(lrc->roots[m]); if (m == 0) lrc = lrc->down; - } + } while (n != 0); + JS_ASSERT(!lrc); } static void @@ -807,8 +843,9 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, for (i = 0; i < argCount; i++) { if (charArgs) { char *charArg = va_arg(ap, char *); + size_t charArgLength = strlen(charArg); reportp->messageArgs[i] - = js_InflateString(cx, charArg, strlen(charArg)); + = js_InflateString(cx, charArg, &charArgLength); if (!reportp->messageArgs[i]) goto error; } @@ -826,12 +863,16 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, */ if (argCount > 0) { if (efs->format) { - const char *fmt; + jschar *buffer, *fmt, *out; const jschar *arg; - jschar *out; int expandedArgs = 0; - size_t expandedLength - = strlen(efs->format) + size_t expandedLength; + size_t len = strlen (efs->format); + buffer = fmt = js_InflateString (cx, efs->format, &len); + if (!buffer) + goto error; + expandedLength + = len - (3 * argCount) /* exclude the {n} */ + totalArgsLength; /* @@ -840,14 +881,15 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, */ reportp->ucmessage = out = (jschar *) JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); - if (!out) + if (!out) { + JS_free (cx, buffer); goto error; - fmt = efs->format; + } while (*fmt) { if (*fmt == '{') { if (isdigit(fmt[1])) { int d = JS7_UNDEC(fmt[1]); - JS_ASSERT(expandedArgs < argCount); + JS_ASSERT(d < argCount); arg = reportp->messageArgs[d]; js_strncpy(out, arg, argLengths[d]); out += argLengths[d]; @@ -856,13 +898,11 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, continue; } } - /* - * is this kosher? - */ - *out++ = (unsigned char)(*fmt++); + *out++ = *fmt++; } JS_ASSERT(expandedArgs == argCount); *out = 0; + JS_free (cx, buffer); *messagep = js_DeflateString(cx, reportp->ucmessage, (size_t)(out - reportp->ucmessage)); @@ -875,11 +915,13 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, * entire message. */ if (efs->format) { + size_t len; *messagep = JS_strdup(cx, efs->format); if (!*messagep) goto error; + len = strlen(*messagep); reportp->ucmessage - = js_InflateString(cx, *messagep, strlen(*messagep)); + = js_InflateString(cx, *messagep, &len); if (!reportp->ucmessage) goto error; } diff --git a/src/dom/js/jscntxt.h b/src/dom/js/jscntxt.h index 630d6a63f..bf49c8b8e 100644 --- a/src/dom/js/jscntxt.h +++ b/src/dom/js/jscntxt.h @@ -54,6 +54,7 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsregexp.h" +#include "jsutil.h" JS_BEGIN_EXTERN_C @@ -71,25 +72,51 @@ typedef struct JSPropertyTreeEntry { JSScopeProperty *child; } JSPropertyTreeEntry; +/* + * Forward declaration for opaque JSRuntime.nativeIteratorStates. + */ +typedef struct JSNativeIteratorState JSNativeIteratorState; + struct JSRuntime { /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ JSRuntimeState state; /* Garbage collector state, used by jsgc.c. */ - JSArenaPool gcArenaPool; + JSArenaPool gcArenaPool[GC_NUM_FREELISTS]; + JSGCThing *gcFreeList[GC_NUM_FREELISTS]; JSDHashTable gcRootsHash; JSDHashTable *gcLocksHash; - JSGCThing *gcFreeList; jsrefcount gcKeepAtoms; uint32 gcBytes; uint32 gcLastBytes; uint32 gcMaxBytes; + uint32 gcMaxMallocBytes; uint32 gcLevel; uint32 gcNumber; JSPackedBool gcPoke; JSPackedBool gcRunning; JSGCCallback gcCallback; uint32 gcMallocBytes; + + /* + * API compatibility requires keeping GCX_PRIVATE bytes separate from the + * original GC types' byte tally. Otherwise embeddings that configure a + * good limit for pre-GCX_PRIVATE versions of the engine will see memory + * over-pressure too often, possibly leading to failed last-ditch GCs. + * + * The new XML GC-thing types do add to gcBytes, and they're larger than + * the original GC-thing type size (8 bytes on most architectures). So a + * user who enables E4X may want to increase the maxbytes value passed to + * JS_NewRuntime. TODO: Note this in the API docs. + */ + uint32 gcPrivateBytes; + +#if JS_HAS_XML_SUPPORT + /* Lists of JSXML private data structures to be finalized. */ + JSXMLNamespace *gcDoomedNamespaces; + JSXMLQName *gcDoomedQNames; + JSXML *gcDoomedXML; +#endif #ifdef JS_GCMETER JSGCStats gcStats; #endif @@ -204,6 +231,9 @@ struct JSRuntime { /* Security principals serialization support. */ JSPrincipalsTranscoder principalsTranscoder; + /* Optional hook to find principals for an object in this runtime. */ + JSObjectPrincipalsFinder findObjectPrincipals; + /* Shared scope property tree, and allocator for its nodes. */ JSDHashTable propertyTreeHash; JSScopeProperty *propertyFreeList; @@ -211,6 +241,7 @@ struct JSRuntime { /* Script filename table. */ struct JSHashTable *scriptFilenameTable; + JSCList scriptFilenamePrefixes; #ifdef JS_THREADSAFE PRLock *scriptFilenameTableLock; #endif @@ -220,6 +251,22 @@ struct JSRuntime { const char *decimalSeparator; const char *numGrouping; + /* + * Weak references to lazily-created, well-known XML singletons. + * + * NB: Singleton objects must be carefully disconnected from the rest of + * the object graph usually associated with a JSContext's global object, + * including the set of standard class objects. See jsxml.c for details. + */ + JSObject *anynameObject; + JSObject *functionNamespaceObject; + + /* + * A helper list for the GC, so it can mark native iterator states. See + * js_MarkNativeIteratorStates for details. + */ + JSNativeIteratorState *nativeIteratorStates; + #ifdef DEBUG /* Function invocation metering. */ jsrefcount inlineCalls; @@ -310,7 +357,7 @@ typedef struct JSResolvingEntry { typedef struct JSLocalRootChunk JSLocalRootChunk; -#define JSLRS_CHUNK_SHIFT 6 +#define JSLRS_CHUNK_SHIFT 8 #define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) #define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) @@ -320,13 +367,106 @@ struct JSLocalRootChunk { }; typedef struct JSLocalRootStack { - uint16 scopeMark; - uint16 rootCount; + uint32 scopeMark; + uint32 rootCount; JSLocalRootChunk *topChunk; JSLocalRootChunk firstChunk; } JSLocalRootStack; -#define JSLRS_NULL_MARK ((uint16) -1) +#define JSLRS_NULL_MARK ((uint32) -1) + +typedef struct JSTempValueRooter JSTempValueRooter; +typedef void +(* JS_DLL_CALLBACK JSTempValueMarker)(JSContext *cx, JSTempValueRooter *tvr); + +typedef union JSTempValueUnion { + jsval value; + JSObject *object; + JSTempValueMarker marker; + jsval *array; +} JSTempValueUnion; + +/* + * Context-linked stack of temporary GC roots. + * + * If count is -1, then u.value contains the single value to root. + * If count is -2, then u.marker holds a mark hook that is executed to mark + * the values. + * If count >= 0, then u.array points to a stack-allocated vector of jsvals. + * + * To root a single GC-thing pointer, which need not be tagged and stored as a + * jsval, use JS_PUSH_SINGLE_TEMP_ROOT. The (jsval)(val) cast works because a + * GC-thing is aligned on a 0 mod 8 boundary, and object has the 0 jsval tag. + * So any GC-thing may be tagged as if it were an object and untagged, if it's + * then used only as an opaque pointer until discriminated by other means than + * tag bits (this is how the GC mark function uses its |thing| parameter -- it + * consults GC-thing flags stored separately from the thing to decide the type + * of thing). + * + * Alternatively, if a single pointer to rooted JSObject * is required, use + * JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr). Then &tvr.u.object gives the + * necessary pointer, which puns tvr.u.value safely because object tag bits + * are all zeroes. + * + * If you need to protect a result value that flows out of a C function across + * several layers of other functions, use the js_LeaveLocalRootScopeWithResult + * internal API (see further below) instead. + */ +struct JSTempValueRooter { + JSTempValueRooter *down; + ptrdiff_t count; + JSTempValueUnion u; +}; + +#define JS_PUSH_TEMP_ROOT_COMMON(cx,tvr) \ + JS_BEGIN_MACRO \ + JS_ASSERT((cx)->tempValueRooters != (tvr)); \ + (tvr)->down = (cx)->tempValueRooters; \ + (cx)->tempValueRooters = (tvr); \ + JS_END_MACRO + +#define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ + JS_BEGIN_MACRO \ + JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ + (tvr)->count = -1; \ + (tvr)->u.value = (val); \ + JS_END_MACRO + +#define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \ + JS_BEGIN_MACRO \ + JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ + JS_ASSERT((ptrdiff_t)(cnt) >= 0); \ + (tvr)->count = (ptrdiff_t)(cnt); \ + (tvr)->u.array = (arr); \ + JS_END_MACRO + +#define JS_PUSH_TEMP_ROOT_MARKER(cx,marker_,tvr) \ + JS_BEGIN_MACRO \ + JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ + (tvr)->count = -2; \ + (tvr)->u.marker = (marker_); \ + JS_END_MACRO + +#define JS_PUSH_TEMP_ROOT_OBJECT(cx,obj,tvr) \ + JS_BEGIN_MACRO \ + JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ + (tvr)->count = -1; \ + (tvr)->u.object = (obj); \ + JS_END_MACRO + +#define JS_POP_TEMP_ROOT(cx,tvr) \ + JS_BEGIN_MACRO \ + JS_ASSERT((cx)->tempValueRooters == (tvr)); \ + (cx)->tempValueRooters = (tvr)->down; \ + JS_END_MACRO + +#define JS_TEMP_ROOT_EVAL(cx,cnt,val,expr) \ + JS_BEGIN_MACRO \ + JSTempValueRooter tvr; \ + JS_PUSH_TEMP_ROOT(cx, cnt, val, &tvr); \ + (expr); \ + JS_POP_TEMP_ROOT(cx, &tvr); \ + JS_END_MACRO struct JSContext { JSCList links; @@ -338,7 +478,7 @@ struct JSContext { jsuword stackLimit; /* Runtime version control identifier and equality operators. */ - JSVersion version; + uint16 version; jsbytecode jsop_eq; jsbytecode jsop_ne; @@ -361,6 +501,9 @@ struct JSContext { /* Atom root for the last-looked-up atom on this context. */ JSAtom *lastAtom; + /* Root for the result of the most recent js_InternalInvoke call. */ + jsval lastInternalResult; + /* Regular expression class statics (XXX not shared globally). */ JSRegExpStatics regExpStatics; @@ -404,10 +547,20 @@ struct JSContext { JSPackedBool rval2set; #endif +#if JS_HAS_XML_SUPPORT + /* + * Bit-set formed from binary exponentials of the XML_* tiny-ids defined + * for boolean settings in jsxml.c, plus an XSF_CACHE_VALID bit. Together + * these act as a cache of the boolean XML.ignore* and XML.prettyPrinting + * property values associated with this context's global object. + */ + uint8 xmlSettingFlags; +#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. + * NB: creatingException packs with rval2set, #if JS_HAS_LVALUE_RETURN; + * with xmlSettingFlags, #if JS_HAS_XML_SUPPORT; and with throwing below. */ JSPackedBool creatingException; @@ -435,22 +588,108 @@ struct JSContext { /* 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. */ + /* Optional stack of heap-allocated scoped local GC roots. */ JSLocalRootStack *localRootStack; + + /* Stack of thread-stack-allocated temporary GC roots. */ + JSTempValueRooter *tempValueRooters; }; +#ifdef __cplusplus +/* FIXME(bug 332648): Move this into a public header. */ +class JSAutoTempValueRooter +{ + public: + JSAutoTempValueRooter(JSContext *cx, size_t len, jsval *vec) + : mContext(cx) { + JS_PUSH_TEMP_ROOT(mContext, len, vec, &mTvr); + } + JSAutoTempValueRooter(JSContext *cx, jsval v) + : mContext(cx) { + JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr); + } + + ~JSAutoTempValueRooter() { + JS_POP_TEMP_ROOT(mContext, &mTvr); + } + + private: + static void *operator new(size_t) { return 0; } + static void operator delete(void *, size_t) { } + + JSContext *mContext; + JSTempValueRooter mTvr; +}; +#endif + +/* + * Slightly more readable macros for testing per-context option settings (also + * to hide bitset implementation detail). + * + * JSOPTION_XML must be handled specially in order to propagate from compile- + * to run-time (from cx->options to script->version/cx->version). To do that, + * we copy JSOPTION_XML from cx->options into cx->version as JSVERSION_HAS_XML + * whenever options are set, and preserve this XML flag across version number + * changes done via the JS_SetVersion API. + * + * But when executing a script or scripted function, the interpreter changes + * cx->version, including the XML flag, to script->version. Thus JSOPTION_XML + * is a compile-time option that causes a run-time version change during each + * activation of the compiled script. That version change has the effect of + * changing JS_HAS_XML_OPTION, so that any compiling done via eval enables XML + * support. If an XML-enabled script or function calls a non-XML function, + * the flag bit will be cleared during the callee's activation. + * + * Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into + * that API's version parameter. + * + * Note also that script->version must contain this XML option flag in order + * for XDR'ed scripts to serialize and deserialize with that option preserved + * for detection at run-time. We can't copy other compile-time options into + * script->version because that would break backward compatibility (certain + * other options, e.g. JSOPTION_VAROBJFIX, are analogous to JSOPTION_XML). + */ +#define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0) +#define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT) +#define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR) +#define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO) +#define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE) + +#define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */ +#define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */ + +#define JSVERSION_NUMBER(cx) ((cx)->version & JSVERSION_MASK) +#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ + JSVERSION_NUMBER(cx) >= JSVERSION_1_6) + +#define JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) \ + JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK) + /* - * Slightly more readable macros, also to hide bitset implementation detail. - * XXX beware non-boolean truth values, which belie the bitset hiding claim! + * Wrappers for the JSVERSION_IS_* macros from jspubtd.h taking JSContext *cx + * and masking off the XML flag and any other high order bits. */ -#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) +#define JS_VERSION_IS_ECMA(cx) JSVERSION_IS_ECMA(JSVERSION_NUMBER(cx)) +#define JS_VERSION_IS_1_2(cx) (JSVERSION_NUMBER(cx) == JSVERSION_1_2) +/* + * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context + * data that depends on version. + */ +extern void +js_OnVersionChange(JSContext *cx); + +/* + * Unlike the JS_SetVersion API, this function stores JSVERSION_HAS_XML and + * any future non-version-number flags induced by compiler options. + */ +extern void +js_SetVersion(JSContext *cx, JSVersion version); + +/* + * Create and destroy functions for JSContext, which is manually allocated + * and exclusively owned. + */ extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); diff --git a/src/dom/js/jsconfig.h b/src/dom/js/jsconfig.h index 44a64d870..5902499f9 100644 --- a/src/dom/js/jsconfig.h +++ b/src/dom/js/jsconfig.h @@ -41,14 +41,14 @@ * JS configuration macros. */ #ifndef JS_VERSION -#define JS_VERSION 150 +#define JS_VERSION 160 #endif /* * Compile-time JS version configuration. The JS version numbers lie on the * number line like so: * - * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 + * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 * ^ ^ * | | * basis for ECMAv1 close to ECMAv2 @@ -133,6 +133,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 100 @@ -191,6 +193,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 110 @@ -249,6 +253,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 120 @@ -307,6 +313,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 130 @@ -365,6 +373,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 140 @@ -423,6 +433,8 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #elif JS_VERSION == 150 @@ -481,6 +493,68 @@ #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 */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ + +#elif JS_VERSION == 160 + +#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 */ +#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ +#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #else diff --git a/src/dom/js/jsconfig.mk b/src/dom/js/jsconfig.mk deleted file mode 100644 index a3b886732..000000000 --- a/src/dom/js/jsconfig.mk +++ /dev/null @@ -1,181 +0,0 @@ -# -*- 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 index aa6c04c4d..e02f33615 100644 --- a/src/dom/js/jscpucfg.c +++ b/src/dom/js/jscpucfg.c @@ -49,38 +49,36 @@ #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 +#define INT64 _int64 #endif /* __GNUC__ */ #else -#define INT64 long +#define INT64 long #endif #else #if defined(HPUX) || defined(__QNX__) || defined(_SCO_DS) || defined(UNIXWARE) -#define INT64 long +#define INT64 long #else -#define INT64 long long -#endif +#define INT64 long long #endif #endif #endif /* CROSS_COMPILE */ +#ifdef __GNUC__ +#define NS_NEVER_INLINE __attribute__((noinline)) +#else +#define NS_NEVER_INLINE +#endif + typedef void *prword; struct align_short { @@ -102,7 +100,7 @@ struct align_int64 { struct align_fakelonglong { char c; struct { - long hi, lo; + long hi, lo; } a; }; struct align_float { @@ -132,17 +130,17 @@ static int Log2(unsigned int n) int log2 = 0; if (n & (n-1)) - log2++; + log2++; if (n >> 16) - log2 += 16, n >>= 16; + log2 += 16, n >>= 16; if (n >> 8) - log2 += 8, n >>= 8; + log2 += 8, n >>= 8; if (n >> 4) - log2 += 4, n >>= 4; + log2 += 4, n >>= 4; if (n >> 2) - log2 += 2, n >>= 2; + log2 += 2, n >>= 2; if (n >> 1) - log2++; + log2++; return log2; } @@ -156,7 +154,7 @@ static void BitsPerByte(void) bpb = 8; } -static int StackGrowthDirection(int *dummy1addr) +static int NS_NEVER_INLINE StackGrowthDirection(int *dummy1addr) { int dummy2; @@ -190,26 +188,26 @@ int main(int argc, char **argv) #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; + 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; + 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 */ @@ -222,10 +220,10 @@ int main(int argc, char **argv) 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 - + /* 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 + * (|static| is used to get the same functionality for compilers * which do not honor |volatile|...). */ volatile static union { @@ -287,37 +285,37 @@ int main(int argc, char **argv) printf("#undef IS_BIG_ENDIAN\n\n"); } else { fprintf(stderr, "%s: unknown byte order" - "(big_endian=%d, little_endian=%d, ntests=%d)!\n", + "(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; + 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); + 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); + /* this machine doesn't actually support int64's */ + align_of_int64 = ALIGN_OF(fakelonglong); } else { - align_of_int64 = ALIGN_OF(int64); + 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); + 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 */ diff --git a/src/dom/js/jscpucfg.h b/src/dom/js/jscpucfg.h index 897ee577c..7b8b2add4 100644 --- a/src/dom/js/jscpucfg.h +++ b/src/dom/js/jscpucfg.h @@ -42,58 +42,13 @@ #include "jsosdep.h" -#ifdef XP_MAC -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 - -#define JS_BYTES_PER_BYTE 1L -#define JS_BYTES_PER_SHORT 2L -#define JS_BYTES_PER_INT 4L -#define JS_BYTES_PER_INT64 8L -#define JS_BYTES_PER_LONG 4L -#define JS_BYTES_PER_FLOAT 4L -#define JS_BYTES_PER_DOUBLE 8L -#define JS_BYTES_PER_WORD 4L -#define JS_BYTES_PER_DWORD 8L - -#define JS_BITS_PER_BYTE 8L -#define JS_BITS_PER_SHORT 16L -#define JS_BITS_PER_INT 32L -#define JS_BITS_PER_INT64 64L -#define JS_BITS_PER_LONG 32L -#define JS_BITS_PER_FLOAT 32L -#define JS_BITS_PER_DOUBLE 64L -#define JS_BITS_PER_WORD 32L - -#define JS_BITS_PER_BYTE_LOG2 3L -#define JS_BITS_PER_SHORT_LOG2 4L -#define JS_BITS_PER_INT_LOG2 5L -#define JS_BITS_PER_INT64_LOG2 6L -#define JS_BITS_PER_LONG_LOG2 5L -#define JS_BITS_PER_FLOAT_LOG2 5L -#define JS_BITS_PER_DOUBLE_LOG2 6L -#define JS_BITS_PER_WORD_LOG2 5L - -#define JS_ALIGN_OF_SHORT 2L -#define JS_ALIGN_OF_INT 4L -#define JS_ALIGN_OF_LONG 4L -#define JS_ALIGN_OF_INT64 2L -#define JS_ALIGN_OF_FLOAT 4L -#define JS_ALIGN_OF_DOUBLE 4L -#define JS_ALIGN_OF_POINTER 4L -#define JS_ALIGN_OF_WORD 4L - -#define JS_BYTES_PER_WORD_LOG2 2L -#define JS_BYTES_PER_DWORD_LOG2 3L -#define PR_WORDS_PER_DWORD_LOG2 1L - -#elif defined(XP_WIN) || defined(XP_OS2) +#if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) #ifdef __WATCOMC__ #define HAVE_VA_LIST_AS_ARRAY #endif -#if defined( _WIN32) || defined(XP_OS2) +#if defined(_WIN32) || defined(XP_OS2) || defined(WINCE) #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN @@ -137,7 +92,7 @@ #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 */ +#endif /* _WIN32 || XP_OS2 || WINCE*/ #if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ #define IS_LITTLE_ENDIAN 1 @@ -193,7 +148,7 @@ #else -#error "Must define one of XP_BEOS, XP_MAC, XP_OS2, XP_WIN, or XP_UNIX" +#error "Must define one of XP_BEOS, XP_OS2, XP_WIN, or XP_UNIX" #endif diff --git a/src/dom/js/jsdate.c b/src/dom/js/jsdate.c index 4d85b1ef7..a76502c38 100644 --- a/src/dom/js/jsdate.c +++ b/src/dom/js/jsdate.c @@ -179,18 +179,18 @@ TimeWithinDay(jsdouble t) jsdouble result; result = fmod(t, msPerDay); if (result < 0) - result += msPerDay; + result += msPerDay; return result; } #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ - ? 366 : 365) + ? 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)) + - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) #define TimeFromYear(y) (DayFromYear(y) * msPerDay) static jsint @@ -231,28 +231,28 @@ MonthFromTime(jsdouble t) d = DayWithinYear(t, year); if (d < (step = 31)) - return 0; + return 0; step += (InLeapYear(t) ? 29 : 28); if (d < step) - return 1; + return 1; if (d < (step += 31)) - return 2; + return 2; if (d < (step += 30)) - return 3; + return 3; if (d < (step += 31)) - return 4; + return 4; if (d < (step += 30)) - return 5; + return 5; if (d < (step += 31)) - return 6; + return 6; if (d < (step += 31)) - return 7; + return 7; if (d < (step += 30)) - return 8; + return 8; if (d < (step += 31)) - return 9; + return 9; if (d < (step += 30)) - return 10; + return 10; return 11; } @@ -264,38 +264,38 @@ DateFromTime(jsdouble t) d = DayWithinYear(t, year); if (d <= (next = 30)) - return d + 1; + return d + 1; step = next; next += (InLeapYear(t) ? 29 : 28); if (d <= next) - return d - step; + return d - step; step = next; if (d <= (next += 31)) - return d - step; + return d - step; step = next; if (d <= (next += 30)) - return d - step; + return d - step; step = next; if (d <= (next += 31)) - return d - step; + return d - step; step = next; if (d <= (next += 30)) - return d - step; + return d - step; step = next; if (d <= (next += 31)) - return d - step; + return d - step; step = next; if (d <= (next += 31)) - return d - step; + return d - step; step = next; if (d <= (next += 30)) - return d - step; + return d - step; step = next; if (d <= (next += 31)) - return d - step; + return d - step; step = next; if (d <= (next += 30)) - return d - step; + return d - step; step = next; return d - step; } @@ -307,10 +307,72 @@ WeekDay(jsdouble t) result = (jsint) Day(t) + 4; result = result % 7; if (result < 0) - result += 7; + result += 7; return (intN) result; } +#define MakeTime(hour, min, sec, ms) \ +((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) + +static jsdouble +MakeDay(jsdouble year, jsdouble month, jsdouble date) +{ + JSBool leap; + jsdouble yearday; + jsdouble monthday; + + year += floor(month / 12); + + month = fmod(month, 12.0); + if (month < 0) + month += 12; + + leap = (DaysInYear((jsint) year) == 366); + + yearday = floor(TimeFromYear(year) / msPerDay); + monthday = DayFromMonth(month, leap); + + return yearday + monthday + date - 1; +} + +#define MakeDate(day, time) ((day) * msPerDay + (time)) + +/* + * Years and leap years on which Jan 1 is a Sunday, Monday, etc. + * + * yearStartingWith[0][i] is an example non-leap year where + * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. + * + * yearStartingWith[1][i] is an example leap year where + * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. + */ +static jsint yearStartingWith[2][7] = { + {1978, 1973, 1974, 1975, 1981, 1971, 1977}, + {1984, 1996, 1980, 1992, 1976, 1988, 1972} +}; + +/* + * Find a year for which any given date will fall on the same weekday. + * + * This function should be used with caution when used other than + * for determining DST; it hasn't been proven not to produce an + * incorrect year for times near year boundaries. + */ +static jsint +EquivalentYearForDST(jsint year) { + jsint day; + JSBool isLeapYear; + + day = (jsint) DayFromYear(year) + 4; + day = day % 7; + if (day < 0) + day += 7; + + isLeapYear = (DaysInYear(year) == 366); + + return yearStartingWith[isLeapYear][day]; +} + /* LocalTZA gets set by js_InitDateClass() */ static jsdouble LocalTZA; @@ -324,7 +386,20 @@ DaylightSavingTA(jsdouble t) /* abort if NaN */ if (JSDOUBLE_IS_NaN(t)) - return t; + return t; + + /* + * If earlier than 1970 or after 2038, potentially beyond the ken of + * many OSes, map it to an equivalent year before asking. + */ + if (t < 0.0 || t > 2145916800000.0) { + jsint year; + jsdouble day; + + year = EquivalentYearForDST(YearFromTime(t)); + day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + t = MakeDate(day, TimeWithinDay(t)); + } /* put our t in an LL, and map it to usec for prtime */ JSLL_D2L(PR_t, t); @@ -354,7 +429,7 @@ HourFromTime(jsdouble t) { intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); if (result < 0) - result += (intN)HoursPerDay; + result += (intN)HoursPerDay; return result; } @@ -363,7 +438,7 @@ MinFromTime(jsdouble t) { intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); if (result < 0) - result += (intN)MinutesPerHour; + result += (intN)MinutesPerHour; return result; } @@ -372,7 +447,7 @@ SecFromTime(jsdouble t) { intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); if (result < 0) - result += (intN)SecondsPerMinute; + result += (intN)SecondsPerMinute; return result; } @@ -381,43 +456,13 @@ msFromTime(jsdouble t) { intN result = (intN) fmod(t, msPerSecond); if (result < 0) - result += (intN)msPerSecond; + 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) + && !((d < 0 ? -d : d) > HalfTimeDomain)) \ + ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) /** * end of ECMA 'support' functions @@ -464,28 +509,28 @@ static int ttb[] = { /* helper for date_parse */ static JSBool date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, - int count, int ignoreCase) + 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 (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; + result = JS_TRUE; } return result; @@ -494,7 +539,7 @@ date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, /* 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 min, jsdouble sec, jsdouble msec) { jsdouble day; jsdouble msec_time; @@ -520,30 +565,30 @@ date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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; - } + 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; + 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; + array[2] = 1; d = date_msecFromDate(array[0], array[1], array[2], - array[3], array[4], array[5], array[6]); + array[3], array[4], array[5], array[6]); d = TIMECLIP(d); return js_NewNumberValue(cx, d, rval); @@ -568,115 +613,120 @@ date_parseString(JSString *str, jsdouble *result) jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */ int prevc = 0; JSBool seenplusminus = JS_FALSE; + int temp; + JSBool seenmonthname = JS_FALSE; if (limit == 0) - goto syntax; + 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) { + c = s[i]; + i++; + if (c <= ' ' || c == ',' || c == '-') { + if (c == '-' && '0' <= s[i] && s[i] <= '9') { + prevc = c; + } + continue; + } + if (c == '(') { /* comments) */ + int depth = 1; + while (i < limit) { + c = s[i]; + i++; + if (c == '(') depth++; + else if (c == ')') + if (--depth <= 0) + break; + } + continue; + } + if ('0' <= c && c <= '9') { + n = c - '0'; + while (i < limit && '0' <= (c = s[i]) && c <= '9') { + n = n * 10 + c - '0'; + i++; + } + + /* allow TZA before the year, so + * 'Wed Nov 05 21:49:11 GMT-0800 1997' + * works */ + + /* uses of seenplusminus allow : in TZA, so Java + * no-timezone style of GMT+4:30 works + */ + + if ((prevc == '+' || prevc == '-')/* && year>=0 */) { + /* make ':' case below change tzoffset */ + seenplusminus = JS_TRUE; + + /* offset */ + if (n < 24) + n = n * 60; /* EG. "GMT-3" */ + else + n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ + if (prevc == '+') /* plus means east of GMT */ + n = -n; + if (tzoffset != 0 && tzoffset != -1) + goto syntax; + tzoffset = n; + } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { + if (c <= ' ' || c == ',' || c == '/' || i >= limit) + year = n; + else + goto syntax; + } else if (c == ':') { + if (hour < 0) + hour = /*byte*/ n; + else if (min < 0) + min = /*byte*/ n; + else + goto syntax; + } else if (c == '/') { + /* until it is determined that mon is the actual + month, keep it as 1-based rather than 0-based */ + if (mon < 0) + mon = /*byte*/ n; + else if (mday < 0) + mday = /*byte*/ n; + else + goto syntax; + } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { + goto syntax; + } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ + if (tzoffset < 0) + tzoffset -= n; + else + tzoffset += n; + } else if (hour >= 0 && min < 0) { + min = /*byte*/ n; + } else if (prevc == ':' && min >= 0 && sec < 0) { + sec = /*byte*/ n; + } else if (mon < 0) { + mon = /*byte*/n; + } else if (mon >= 0 && mday < 0) { + mday = /*byte*/ n; + } else if (mon >= 0 && mday >= 0 && year < 0) { + year = n; + } else { + goto syntax; + } + prevc = 0; + } else if (c == '/' || c == ':' || c == '+' || c == '-') { + prevc = c; + } else { + size_t st = i - 1; + int k; + while (i < limit) { + c = s[i]; + if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) + break; + i++; + } + if (i <= st + 1) + goto syntax; + for (k = (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 @@ -692,37 +742,113 @@ date_parseString(JSString *str, jsdouble *result) 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; - } + } else if (action <= 13) { /* month! */ + /* Adjust mon to be 1-based until the final values + for mon, mday and year are adjusted below */ + if (seenmonthname) { + goto syntax; + } + seenmonthname = JS_TRUE; + temp = /*byte*/ (action - 2) + 1; + + if (mon < 0) { + mon = temp; + } else if (mday < 0) { + mday = mon; + mon = temp; + } else if (year < 0) { + year = mon; + mon = temp; + } else { + goto syntax; + } + } else { + tzoffset = action - 10000; + } + } + break; + } + if (k < 0) + goto syntax; + prevc = 0; + } } if (year < 0 || mon < 0 || mday < 0) - goto syntax; + goto syntax; + /* + Case 1. The input string contains an English month name. + The form of the string can be month f l, or f month l, or + f l month which each evaluate to the same date. + If f and l are both greater than or equal to 70, or + both less than 70, the date is invalid. + The year is taken to be the greater of the values f, l. + If the year is greater than or equal to 70 and less than 100, + it is considered to be the number of years after 1900. + Case 2. The input string is of the form "f/m/l" where f, m and l are + integers, e.g. 7/16/45. + Adjust the mon, mday and year values to achieve 100% MSIE + compatibility. + a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. + i. If year < 100, it is the number of years after 1900 + ii. If year >= 100, it is the number of years after 0. + b. If 70 <= f < 100 + i. If m < 70, f/m/l is interpreted as + year/month/day where year is the number of years after + 1900. + ii. If m >= 70, the date is invalid. + c. If f >= 100 + i. If m < 70, f/m/l is interpreted as + year/month/day where year is the number of years after 0. + ii. If m >= 70, the date is invalid. + */ + if (seenmonthname) { + if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { + goto syntax; + } + if (mday > year) { + temp = year; + year = mday; + mday = temp; + } + if (year >= 70 && year < 100) { + year += 1900; + } + } else if (mon < 70) { /* (a) month/day/year */ + if (year < 100) { + year += 1900; + } + } else if (mon < 100) { /* (b) year/month/day */ + if (mday < 70) { + temp = year; + year = mon + 1900; + mon = mday; + mday = temp; + } else { + goto syntax; + } + } else { /* (c) year/month/day */ + if (mday < 70) { + temp = year; + year = mon; + mon = mday; + mday = temp; + } else { + goto syntax; + } + } + mon -= 1; /* convert month to 0-based */ if (sec < 0) - sec = 0; + sec = 0; if (min < 0) - min = 0; + min = 0; if (hour < 0) - 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); + jsdouble msec_time; + msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - *result = UTC(msec_time); - return JS_TRUE; + *result = UTC(msec_time); + return JS_TRUE; } msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); @@ -744,10 +870,10 @@ date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = js_ValueToString(cx, argv[0]); if (!str) - return JS_FALSE; + return JS_FALSE; if (!date_parseString(str, &result)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; } result = TIMECLIP(result); @@ -776,7 +902,7 @@ static jsdouble * date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) { if (!JS_InstanceOf(cx, obj, &date_class, argv)) - return NULL; + return NULL; return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); } @@ -788,7 +914,7 @@ 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_FALSE; return js_NewNumberValue(cx, *date, rval); } @@ -796,14 +922,17 @@ date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsdouble *date; jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); + JSVersion version; + + date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; - result = *date; + return JS_FALSE; + result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = YearFromTime(LocalTime(result)); @@ -816,9 +945,10 @@ date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * 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) + version = cx->version & JSVERSION_MASK; + if (version == JSVERSION_1_0 || + version == JSVERSION_1_1 || + version == JSVERSION_1_2) { if (result >= 1900 && result < 2000) result -= 1900; @@ -830,16 +960,16 @@ date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = YearFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); @@ -847,16 +977,16 @@ date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = YearFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -864,16 +994,16 @@ date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = MonthFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); @@ -881,16 +1011,16 @@ date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = MonthFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -902,11 +1032,11 @@ 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; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = LocalTime(result); result = DateFromTime(result); @@ -915,16 +1045,16 @@ date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = DateFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -936,11 +1066,11 @@ 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; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = LocalTime(result); result = WeekDay(result); @@ -949,16 +1079,16 @@ date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = WeekDay(result); return js_NewNumberValue(cx, result, rval); @@ -966,16 +1096,16 @@ date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = HourFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); @@ -983,16 +1113,16 @@ date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = HourFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -1000,16 +1130,16 @@ date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = MinFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); @@ -1017,16 +1147,16 @@ date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = MinFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -1036,16 +1166,16 @@ date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = SecFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -1055,16 +1185,16 @@ date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); + return js_NewNumberValue(cx, result, rval); result = msFromTime(result); return js_NewNumberValue(cx, result, rval); @@ -1072,12 +1202,12 @@ date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; /* @@ -1095,10 +1225,10 @@ 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; + return JS_FALSE; if (!js_ValueToNumber(cx, argv[0], &result)) - return JS_FALSE; + return JS_FALSE; result = TIMECLIP(result); @@ -1108,7 +1238,7 @@ date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - uintN maxargs, JSBool local, jsval *rval) + uintN maxargs, JSBool local, jsval *rval) { uintN i; jsdouble args[4], *argp, *stop; @@ -1120,13 +1250,13 @@ date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + 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); + 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 @@ -1137,46 +1267,46 @@ date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * d.setMilliseconds()" returns NaN. Blech. */ if (argc == 0) - argc = 1; /* should be safe, because length of all setters is 1 */ + argc = 1; /* should be safe, because length of all setters is 1 */ else if (argc > maxargs) - argc = maxargs; /* clamp argc */ + 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 (!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); + lorutime = LocalTime(result); else - lorutime = result; + lorutime = result; argp = args; stop = argp + argc; if (maxargs >= 4 && argp < stop) - hour = *argp++; + hour = *argp++; else - hour = HourFromTime(lorutime); + hour = HourFromTime(lorutime); if (maxargs >= 3 && argp < stop) - min = *argp++; + min = *argp++; else - min = MinFromTime(lorutime); + min = MinFromTime(lorutime); if (maxargs >= 2 && argp < stop) - sec = *argp++; + sec = *argp++; else - sec = SecFromTime(lorutime); + sec = SecFromTime(lorutime); if (maxargs >= 1 && argp < stop) - msec = *argp; + msec = *argp; else - msec = msFromTime(lorutime); + msec = msFromTime(lorutime); msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(Day(lorutime), msec_time); @@ -1184,7 +1314,7 @@ date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, /* fprintf(stderr, "%f\n", result); */ if (local) - result = UTC(result); + result = UTC(result); /* fprintf(stderr, "%f\n", result); */ @@ -1194,63 +1324,63 @@ date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + jsval *argv, uintN maxargs, JSBool local, jsval *rval) { uintN i; jsdouble lorutime; /* local or UTC version of *date */ @@ -1260,62 +1390,62 @@ date_makeDate(JSContext *cx, JSObject *obj, uintN argc, jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + 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 */ + argc = 1; /* should be safe, because length of all setters is 1 */ else if (argc > maxargs) - argc = maxargs; /* clamp argc */ + 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 (!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.; + if (argc < 3) + return js_NewNumberValue(cx, result, rval); + else + lorutime = +0.; } else { - if (local) - lorutime = LocalTime(result); - else - lorutime = result; + if (local) + lorutime = LocalTime(result); + else + lorutime = result; } argp = args; stop = argp + argc; if (maxargs >= 3 && argp < stop) - year = *argp++; + year = *argp++; else - year = YearFromTime(lorutime); + year = YearFromTime(lorutime); if (maxargs >= 2 && argp < stop) - month = *argp++; + month = *argp++; else - month = MonthFromTime(lorutime); + month = MonthFromTime(lorutime); if (maxargs >= 1 && argp < stop) - day = *argp++; + day = *argp++; else - day = DateFromTime(lorutime); + day = DateFromTime(lorutime); day = MakeDay(year, month, day); /* day within year */ result = MakeDate(day, TimeWithinDay(lorutime)); if (local) - result = UTC(result); + result = UTC(result); *date = TIMECLIP(result); return js_NewNumberValue(cx, *date, rval); @@ -1323,49 +1453,49 @@ date_makeDate(JSContext *cx, JSObject *obj, uintN argc, static JSBool date_setDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + 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) + 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) + 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) + 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) + 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) + 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) + jsval *argv, jsval *rval) { jsdouble t; jsdouble year; @@ -1374,27 +1504,27 @@ date_setYear(JSContext *cx, JSObject *obj, uintN argc, jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; result = *date; if (!js_ValueToNumber(cx, argv[0], &year)) - return JS_FALSE; + return JS_FALSE; if (!JSDOUBLE_IS_FINITE(year)) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); + *date = *cx->runtime->jsNaN; + return js_NewNumberValue(cx, *date, rval); } year = js_DoubleToInteger(year); if (!JSDOUBLE_IS_FINITE(result)) { - t = +0.0; + t = +0.0; } else { - t = LocalTime(result); + t = LocalTime(result); } if (year >= 0 && year <= 99) - year += 1900; + year += 1900; day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); result = MakeDate(day, TimeWithinDay(t)); @@ -1417,34 +1547,34 @@ static const char* months[] = static JSBool date_toGMTString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + jsval *argv, jsval *rval) { char buf[100]; JSString *str; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); + 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)); + 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; + return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } @@ -1461,22 +1591,22 @@ new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent) /* 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; + 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); - } + 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; + adjustedYear = (int16)year; } split->tm_usec = (int32) msFromTime(timeval) * 1000; @@ -1510,29 +1640,29 @@ date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) PRMJTime split; if (!JSDOUBLE_IS_FINITE(date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); + 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); + 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. @@ -1607,35 +1737,35 @@ date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) str = JS_NewStringCopyZ(cx, buf); if (!str) - return JS_FALSE; + 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) + 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; + return JS_FALSE; if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); + JS_snprintf(buf, sizeof buf, js_NaN_date_str); } else { - intN result_len; - jsdouble local = LocalTime(*date); - new_explode(local, &split, JS_FALSE); + 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); + /* 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); + /* 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] == '/' && @@ -1647,18 +1777,18 @@ date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, } if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - + return cx->localeCallbacks->localeToUnicode(cx, buf, rval); + str = JS_NewStringCopyZ(cx, buf); if (!str) - return JS_FALSE; + 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) + jsval *argv, jsval *rval) { /* Use '%#c' for windows, because '%c' is * backward-compatible and non-y2k with msvc; '%#c' requests that a @@ -1666,16 +1796,16 @@ date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, */ return date_toLocaleHelper(cx, obj, argc, argv, rval, #if defined(_WIN32) && !defined(__MWERKS__) - "%#c" + "%#c" #else - "%c" + "%c" #endif - ); + ); } static JSBool date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + jsval *argv, jsval *rval) { /* Use '%#x' for windows, because '%x' is * backward-compatible and non-y2k with msvc; '%#x' requests that a @@ -1683,37 +1813,54 @@ date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, */ return date_toLocaleHelper(cx, obj, argc, argv, rval, #if defined(_WIN32) && !defined(__MWERKS__) - "%#x" + "%#x" #else - "%x" + "%x" #endif - ); + ); } static JSBool date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + jsval *argv, jsval *rval) { return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X"); } +static JSBool +date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *fmt; + + if (argc == 0) + return date_toLocaleString(cx, obj, argc, argv, rval); + + fmt = JS_ValueToString(cx, argv[0]); + if (!fmt) + return JS_FALSE; + + return date_toLocaleHelper(cx, obj, argc, argv, rval, + JS_GetStringBytes(fmt)); +} + static JSBool date_toTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) + jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + 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) + jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; return date_format(cx, *date, FORMATSPEC_DATE, rval); } @@ -1723,7 +1870,7 @@ date_toDateString(JSContext *cx, JSObject *obj, uintN argc, static JSBool date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble *date; char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; @@ -1731,24 +1878,24 @@ date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + return JS_FALSE; numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date); if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; + JS_ReportOutOfMemory(cx); + return JS_FALSE; } bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr); if (!bytes) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; + JS_ReportOutOfMemory(cx); + return JS_FALSE; } str = JS_NewString(cx, bytes, strlen(bytes)); if (!str) { - free(bytes); - return JS_FALSE; + free(bytes); + return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; @@ -1757,18 +1904,18 @@ date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) - return JS_FALSE; + 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) + 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 @@ -1777,18 +1924,18 @@ date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, /* If called directly with no arguments, convert to a time number. */ if (argc == 0) - return date_getTime(cx, obj, argc, argv, rval); + 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); + 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); } @@ -1848,6 +1995,7 @@ static JSFunctionSpec date_methods[] = { {js_toLocaleString_str, date_toLocaleString, 0,0,0 }, {"toLocaleDateString", date_toLocaleDateString,0,0,0 }, {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 }, + {"toLocaleFormat", date_toLocaleFormat, 1,0,0 }, {"toDateString", date_toDateString, 0,0,0 }, {"toTimeString", date_toTimeString, 0,0,0 }, #if JS_HAS_TOSOURCE @@ -1863,9 +2011,9 @@ date_constructor(JSContext *cx, JSObject* obj) { jsdouble *date; - date = js_NewDouble(cx, 0.0); + date = js_NewDouble(cx, 0.0, 0); if (!date) - return NULL; + return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); return date; } @@ -1877,103 +2025,103 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; jsdouble d; - /* Date called as function */ + /* 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); + 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 */ + /* Date called as constructor. */ if (argc == 0) { - int64 us, ms, us2ms; - jsdouble msec_time; + int64 us, ms, us2ms; + jsdouble msec_time; - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; + 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); + us = PRMJ_Now(); + JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); + JSLL_DIV(ms, us, us2ms); + JSLL_L2D(msec_time, ms); - *date = msec_time; + *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); - } + 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 { + 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); + } + } + + 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; } @@ -1987,9 +2135,9 @@ js_InitDateClass(JSContext *cx, JSObject *obj) /* set static LocalTZA */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS, - NULL, date_methods, NULL, date_static_methods); + NULL, date_methods, NULL, date_static_methods); if (!proto) - return NULL; + return NULL; /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) @@ -1998,7 +2146,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj) /* Set the value of the Date.prototype date to NaN */ proto_date = date_constructor(cx, proto); if (!proto_date) - return NULL; + return NULL; *proto_date = *cx->runtime->jsNaN; return proto; @@ -2012,11 +2160,11 @@ js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) obj = js_NewObject(cx, &date_class, NULL, NULL); if (!obj) - return NULL; + return NULL; date = date_constructor(cx, obj); if (!date) - return NULL; + return NULL; *date = msec_time; return obj; @@ -2052,7 +2200,7 @@ js_DateGetYear(JSContext *cx, JSObject* obj) /* Preserve legacy API behavior of returning 0 for invalid dates. */ if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) YearFromTime(LocalTime(*date)); } @@ -2062,7 +2210,7 @@ js_DateGetMonth(JSContext *cx, JSObject* obj) jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) MonthFromTime(LocalTime(*date)); } @@ -2072,7 +2220,7 @@ js_DateGetDate(JSContext *cx, JSObject* obj) jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) DateFromTime(LocalTime(*date)); } @@ -2082,7 +2230,7 @@ js_DateGetHours(JSContext *cx, JSObject* obj) jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) HourFromTime(LocalTime(*date)); } @@ -2092,7 +2240,7 @@ js_DateGetMinutes(JSContext *cx, JSObject* obj) jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) MinFromTime(LocalTime(*date)); } @@ -2102,7 +2250,7 @@ js_DateGetSeconds(JSContext *cx, JSObject* obj) jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; + return 0; return (int) SecFromTime(*date); } @@ -2112,18 +2260,18 @@ js_DateSetYear(JSContext *cx, JSObject *obj, int year) jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) - return; + return; local = LocalTime(*date); /* reset date if it was NaN */ if (JSDOUBLE_IS_NaN(local)) - local = 0; + local = 0; local = date_msecFromDate(year, - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); *date = UTC(local); } @@ -2133,18 +2281,18 @@ js_DateSetMonth(JSContext *cx, JSObject *obj, int month) jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) - return; + return; local = LocalTime(*date); /* bail if date was NaN */ if (JSDOUBLE_IS_NaN(local)) - return; + return; local = date_msecFromDate(YearFromTime(local), - month, - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); + month, + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); *date = UTC(local); } @@ -2154,17 +2302,17 @@ js_DateSetDate(JSContext *cx, JSObject *obj, int date) jsdouble local; jsdouble *datep = date_getProlog(cx, obj, NULL); if (!datep) - return; + return; local = LocalTime(*datep); if (JSDOUBLE_IS_NaN(local)) - return; + return; local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - date, - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); + MonthFromTime(local), + date, + HourFromTime(local), + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); *datep = UTC(local); } @@ -2174,17 +2322,17 @@ js_DateSetHours(JSContext *cx, JSObject *obj, int hours) jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) - return; + return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) - return; + return; local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - hours, - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); + MonthFromTime(local), + DateFromTime(local), + hours, + MinFromTime(local), + SecFromTime(local), + msFromTime(local)); *date = UTC(local); } @@ -2194,17 +2342,17 @@ js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) - return; + return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) - return; + return; local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - minutes, - SecFromTime(local), - msFromTime(local)); + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + minutes, + SecFromTime(local), + msFromTime(local)); *date = UTC(local); } @@ -2214,17 +2362,17 @@ js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) - return; + return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) - return; + return; local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - seconds, - msFromTime(local)); + MonthFromTime(local), + DateFromTime(local), + HourFromTime(local), + MinFromTime(local), + seconds, + msFromTime(local)); *date = UTC(local); } diff --git a/src/dom/js/jsdate.h b/src/dom/js/jsdate.h index 790b4daf6..343314fab 100644 --- a/src/dom/js/jsdate.h +++ b/src/dom/js/jsdate.h @@ -65,7 +65,7 @@ js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time); */ extern JS_FRIEND_API(JSObject*) js_NewDateObject(JSContext* cx, int year, int mon, int mday, - int hour, int min, int sec); + int hour, int min, int sec); /* * Detect whether the internal date value is NaN. (Because failure is diff --git a/src/dom/js/jsdbgapi.c b/src/dom/js/jsdbgapi.c index 8713a86b9..cddbd4808 100644 --- a/src/dom/js/jsdbgapi.c +++ b/src/dom/js/jsdbgapi.c @@ -278,14 +278,18 @@ DropWatchPoint(JSContext *cx, JSWatchPoint *wp) } void -js_MarkWatchPoints(JSRuntime *rt) +js_MarkWatchPoints(JSContext *cx) { + JSRuntime *rt; JSWatchPoint *wp; + rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { MARK_SCOPE_PROPERTY(wp->sprop); + if (wp->sprop->attrs & JSPROP_SETTER) + JS_MarkGCThing(cx, wp->setter, "wp->setter", NULL); } } @@ -335,7 +339,7 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSRuntime *rt; JSWatchPoint *wp; JSScopeProperty *sprop; - jsval userid; + jsval propid, userid; JSScope *scope; JSBool ok; @@ -346,11 +350,14 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) sprop = wp->sprop; if (wp->object == obj && SPROP_USERID(sprop) == id) { JS_LOCK_OBJ(cx, obj); - userid = SPROP_USERID(sprop); + propid = ID_TO_VALUE(sprop->id); + userid = (sprop->flags & SPROP_HAS_SHORTID) + ? INT_TO_JSVAL(sprop->shortid) + : propid; scope = OBJ_SCOPE(obj); JS_UNLOCK_OBJ(cx, obj); HoldWatchPoint(wp); - ok = wp->handler(cx, obj, userid, + ok = wp->handler(cx, obj, propid, SPROP_HAS_VALID_SLOT(sprop, scope) ? OBJ_GET_SLOT(cx, obj, wp->sprop->slot) : JSVAL_VOID, @@ -361,14 +368,58 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) * 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); + JSObject *closure; + JSClass *clasp; + JSFunction *fun; + JSScript *script; + uintN nslots; + jsval smallv[5]; + jsval *argv; JSStackFrame frame; + closure = (JSObject *) wp->closure; + clasp = OBJ_GET_CLASS(cx, closure); + if (clasp == &js_FunctionClass) { + fun = (JSFunction *) JS_GetPrivate(cx, closure); + script = FUN_SCRIPT(fun); + } else if (clasp == &js_ScriptClass) { + fun = NULL; + script = (JSScript *) JS_GetPrivate(cx, closure); + } else { + fun = NULL; + script = NULL; + } + + nslots = 2; + if (fun) { + nslots += fun->nargs; + if (FUN_NATIVE(fun)) + nslots += fun->extra; + } + + if (nslots <= sizeof (smallv) / sizeof (smallv[0])) { + argv = smallv; + } else { + argv = JS_malloc(cx, nslots * sizeof(jsval)); + if (!argv) { + DropWatchPoint(cx, wp); + return JS_FALSE; + } + } + + argv[0] = OBJECT_TO_JSVAL(closure); + argv[1] = JSVAL_NULL; + memset(argv + 2, 0, (nslots - 2) * sizeof(jsval)); + memset(&frame, 0, sizeof(frame)); - frame.script = FUN_SCRIPT(fun); + frame.script = script; + if (script) + frame.pc = script->code; frame.fun = fun; + frame.argv = argv + 2; frame.down = cx->fp; + frame.scopeChain = OBJ_GET_PARENT(cx, closure); + cx->fp = &frame; ok = !wp->setter || ((sprop->attrs & JSPROP_SETTER) @@ -376,6 +427,8 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 1, vp, vp) : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp)); cx->fp = frame.down; + if (argv != smallv) + JS_free(cx, argv); } return DropWatchPoint(cx, wp); } @@ -393,6 +446,7 @@ js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval userid; funobj = JSVAL_TO_OBJECT(argv[-2]); + JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); wrapper = (JSFunction *) JS_GetPrivate(cx, funobj); userid = ATOM_KEY(wrapper->atom); *rval = argv[0]; @@ -408,12 +462,14 @@ js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) 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 (JSID_IS_ATOM(id)) { + atom = JSID_TO_ATOM(id); + } else if (JSID_IS_INT(id)) { + atom = js_AtomizeInt(cx, JSID_TO_INT(id), 0); if (!atom) return NULL; + } else { + atom = NULL; } wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, OBJ_GET_PARENT(cx, (JSObject *)setter), @@ -444,13 +500,13 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, } if (JSVAL_IS_INT(id)) { - propid = (jsid)id; + propid = INT_JSVAL_TO_JSID(id); atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; - propid = (jsid)atom; + propid = ATOM_TO_JSID(atom); } if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) @@ -473,7 +529,8 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, /* Clone the prototype property so we can watch the right object. */ jsval value; JSPropertyOp getter, setter; - uintN attrs; + uintN attrs, flags; + intN shortid; if (OBJ_IS_NATIVE(pobj)) { value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) @@ -482,18 +539,23 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, getter = sprop->getter; setter = sprop->setter; attrs = sprop->attrs; + flags = sprop->flags; + shortid = sprop->shortid; } else { - if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) { + if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) || + !OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) { OBJ_DROP_PROPERTY(cx, pobj, prop); return JS_FALSE; } - getter = setter = JS_PropertyStub; - attrs = JSPROP_ENUMERATE; + getter = setter = NULL; + flags = 0; + shortid = 0; } OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs, - &prop)) { + /* Recall that obj is native, whether or not pobj is native. */ + if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter, + attrs, flags, shortid, &prop)) { return JS_FALSE; } sprop = (JSScopeProperty *) prop; @@ -524,10 +586,8 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, 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); + JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); wp->setter = sprop->setter; wp->nrefs = 1; @@ -535,10 +595,16 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, sprop->getter, watcher); if (!sprop) { + /* Self-link wp->links so DropWatchPoint can JS_REMOVE_LINK it. */ + JS_INIT_CLIST(&wp->links); DropWatchPoint(cx, wp); ok = JS_FALSE; goto out; } + wp->sprop = sprop; + + /* Now that wp is fully initialized, append it to rt's wp list. */ + JS_APPEND_LINK(&wp->links, &rt->watchPointList); } wp->handler = handler; wp->closure = closure; @@ -628,6 +694,12 @@ JS_GetFunctionScript(JSContext *cx, JSFunction *fun) return FUN_SCRIPT(fun); } +JS_PUBLIC_API(JSNative) +JS_GetFunctionNative(JSContext *cx, JSFunction *fun) +{ + return FUN_NATIVE(fun); +} + JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script) { @@ -673,12 +745,16 @@ JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) 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) { + JSRuntime *rt = cx->runtime; + + if (rt->findObjectPrincipals) { + JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]); - if (fp->fun->object != callee) - return cx->findObjectPrincipals(cx, callee); - /* FALL THROUGH */ + if (fp->fun->object != callee) + return rt->findObjectPrincipals(cx, callee); + /* FALL THROUGH */ + } } if (fp->script) return fp->script->principals; @@ -688,11 +764,24 @@ JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) 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])); + JSRuntime *rt; + JSObject *callee; + JSPrincipals *principals, *callerPrincipals; + + rt = cx->runtime; + if (rt->findObjectPrincipals) { + callee = JSVAL_TO_OBJECT(fp->argv[-2]); + principals = rt->findObjectPrincipals(cx, callee); + } else { + principals = NULL; + } if (!caller) - return NULL; - return JS_StackFramePrincipals(cx, caller); + return principals; + callerPrincipals = JS_StackFramePrincipals(cx, caller); + return (callerPrincipals && principals && + callerPrincipals->subsume(callerPrincipals, principals)) + ? principals + : callerPrincipals; } JS_PUBLIC_API(void *) @@ -796,6 +885,12 @@ JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) return (fp->flags & JSFRAME_CONSTRUCTING) != 0; } +JS_PUBLIC_API(JSObject *) +JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) +{ + return fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; +} + JS_PUBLIC_API(JSBool) JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) { @@ -837,7 +932,7 @@ JS_GetScriptLineExtent(JSContext *cx, JSScript *script) JS_PUBLIC_API(JSVersion) JS_GetScriptVersion(JSContext *cx, JSScript *script) { - return script->version; + return script->version & JSVERSION_MASK; } /***************************************************************************/ @@ -865,7 +960,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, const char *filename, uintN lineno, jsval *rval) { - uint32 flags; + uint32 flags, options; JSScript *script; JSBool ok; @@ -875,10 +970,13 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, */ flags = fp->flags; fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; + options = cx->options; + cx->options = options | JSOPTION_COMPILE_N_GO; script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain, JS_StackFramePrincipals(cx, fp), bytes, length, filename, lineno); fp->flags = flags; + cx->options = options; if (!script) return JS_FALSE; @@ -890,14 +988,15 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, + const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + size_t length = nbytes; jschar *chars; JSBool ok; - chars = js_InflateString(cx, bytes, length); + chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, @@ -1258,3 +1357,49 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script) return nbytes; } + +JS_PUBLIC_API(uint32) +JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp) +{ + if (!fp) + fp = cx->fp; + while (fp) { + if (fp->script) { + return JS_GetScriptFilenameFlags(fp->script); + } + fp = fp->down; + } + return 0; + } + +JS_PUBLIC_API(uint32) +JS_GetScriptFilenameFlags(JSScript *script) +{ + JS_ASSERT(script); + if (!script->filename) + return JSFILENAME_NULL; + return js_GetScriptFilenameFlags(script->filename); +} + +JS_PUBLIC_API(JSBool) +JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags) +{ + if (!js_SaveScriptFilenameRT(rt, prefix, flags)) + return JS_FALSE; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_IsSystemObject(JSContext *cx, JSObject *obj) +{ + return (*js_GetGCThingFlags(obj) & GCF_SYSTEM) != 0; +} + +JS_PUBLIC_API(void) +JS_FlagSystemObject(JSContext *cx, JSObject *obj) +{ + uint8 *flagp; + + flagp = js_GetGCThingFlags(obj); + *flagp |= GCF_SYSTEM; +} diff --git a/src/dom/js/jsdbgapi.h b/src/dom/js/jsdbgapi.h index 90215a275..d1b13d9d4 100644 --- a/src/dom/js/jsdbgapi.h +++ b/src/dom/js/jsdbgapi.h @@ -53,14 +53,14 @@ 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); + 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); + JSTrapHandler *handlerp, void **closurep); extern JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script); @@ -81,11 +81,11 @@ 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); + JSWatchPointHandler handler, void *closure); extern JS_PUBLIC_API(JSBool) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler *handlerp, void **closurep); + JSWatchPointHandler *handlerp, void **closurep); extern JS_PUBLIC_API(JSBool) JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); @@ -99,7 +99,7 @@ JS_ClearAllWatchPoints(JSContext *cx); * header file "jsconfig.h" has been included. */ extern void -js_MarkWatchPoints(JSRuntime *rt); +js_MarkWatchPoints(JSContext *cx); extern JSScopeProperty * js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); @@ -131,6 +131,9 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun); +extern JS_PUBLIC_API(JSNative) +JS_GetFunctionNative(JSContext *cx, JSFunction *fun); + extern JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script); @@ -164,10 +167,11 @@ 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). + * This API is like JS_StackFramePrincipals(cx, caller), except that if + * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of + * the caller's principals and the object principals of fp's callee function + * object (fp->argv[-2]), which is eval, Function, or a similar eval-like + * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). * * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak * reference to the correct principals for the eval call to be secure, given @@ -221,6 +225,12 @@ JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(void) JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); +/** + * Return fp's callee function object (fp->argv[-2]) if it has one. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); + /************************************************************************/ extern JS_PUBLIC_API(const char *) @@ -250,7 +260,7 @@ JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); extern JS_PUBLIC_API(void) JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, - void *callerdata); + void *callerdata); /************************************************************************/ @@ -262,9 +272,9 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, extern JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval); /************************************************************************/ @@ -298,7 +308,7 @@ JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); extern JS_PUBLIC_API(JSBool) JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, - JSPropertyDesc *pd); + JSPropertyDesc *pd); extern JS_PUBLIC_API(JSBool) JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); @@ -340,6 +350,57 @@ JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); extern JS_PUBLIC_API(size_t) JS_GetScriptTotalSize(JSContext *cx, JSScript *script); +/* + * Get the top-most running script on cx starting from fp, or from the top of + * cx's frame stack if fp is null, and return its script filename flags. If + * the script has a null filename member, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); + +/* + * Get the script filename flags for the script. If the script + * doesn't have a filename, return JSFILENAME_NULL. + */ +extern JS_PUBLIC_API(uint32) +JS_GetScriptFilenameFlags(JSScript *script); + +/* + * Associate flags with a script filename prefix in rt, so that any subsequent + * script compilation will inherit those flags if the script's filename is the + * same as prefix, or if prefix is a substring of the script's filename. + * + * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining + * 31 bits up to the API client to define. The union of all 32 bits must not + * be a legal combination, however, in order to preserve JSFILENAME_NULL as a + * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit + * in JSFILENAME_NULL -- a script with a null filename member is presumed to + * be a "system" script. + */ +extern JS_PUBLIC_API(JSBool) +JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); + +#define JSFILENAME_NULL 0xffffffff /* null script filename */ +#define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ + +/* + * Return true if obj is a "system" object, that is, one flagged by a prior + * call to JS_FlagSystemObject(cx, obj). What "system" means is up to the API + * client, but it can be used to coordinate access control policies based on + * script filenames and their prefixes, using JS_FlagScriptFilenamePrefix and + * JS_GetTopScriptFilenameFlags. + */ +extern JS_PUBLIC_API(JSBool) +JS_IsSystemObject(JSContext *cx, JSObject *obj); + +/* + * Flag obj as a "system" object. The API client can flag system objects to + * optimize access control checks. The engine stores but does not interpret + * the per-object flag set by this call. + */ +extern JS_PUBLIC_API(void) +JS_FlagSystemObject(JSContext *cx, JSObject *obj); + JS_END_EXTERN_C #endif /* jsdbgapi_h___ */ diff --git a/src/dom/js/jsdhash.c b/src/dom/js/jsdhash.c index abcc36d05..cd3006685 100644 --- a/src/dom/js/jsdhash.c +++ b/src/dom/js/jsdhash.c @@ -91,7 +91,7 @@ JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry) JS_PUBLIC_API(JSDHashNumber) JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) { - return (JSDHashNumber)key >> 2; + return (JSDHashNumber)(unsigned long)key >> 2; } JS_PUBLIC_API(JSBool) @@ -207,7 +207,9 @@ JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, table->data = data; if (capacity < JS_DHASH_MIN_SIZE) capacity = JS_DHASH_MIN_SIZE; - log2 = JS_CeilingLog2(capacity); + + JS_CEILING_LOG2(log2, capacity); + capacity = JS_BIT(log2); if (capacity >= JS_DHASH_SIZE_LIMIT) return JS_FALSE; @@ -601,7 +603,7 @@ JS_PUBLIC_API(uint32) JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) { char *entryAddr, *entryLimit; - uint32 i, capacity, entrySize; + uint32 i, capacity, entrySize, ceiling; JSBool didRemove; JSDHashEntryHdr *entry; JSDHashOperator op; @@ -643,9 +645,11 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) 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)); + + JS_CEILING_LOG2(ceiling, capacity); + ceiling -= JS_DHASH_BITS - table->hashShift; + + (void) ChangeTable(table, ceiling); } return i; } diff --git a/src/dom/js/jsdhash.h b/src/dom/js/jsdhash.h index 68f593b1b..6beecadd1 100644 --- a/src/dom/js/jsdhash.h +++ b/src/dom/js/jsdhash.h @@ -439,7 +439,7 @@ JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, * 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) +extern JS_PUBLIC_API(void) JS_DHashTableSetAlphaBounds(JSDHashTable *table, float maxAlpha, float minAlpha); diff --git a/src/dom/js/jsdtoa.c b/src/dom/js/jsdtoa.c index 9f729fa8a..ff6731bfa 100644 --- a/src/dom/js/jsdtoa.c +++ b/src/dom/js/jsdtoa.c @@ -228,16 +228,6 @@ static double private_mem[PRIVATE_mem], *pmem_next = private_mem; #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__ @@ -989,7 +979,7 @@ static Bigint *diff(Bigint *a, Bigint *b) static double ulp(double x) { register Long L; - double a; + double a = 0; L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; #ifndef Sudden_Underflow @@ -1020,7 +1010,7 @@ static double b2d(Bigint *a, int32 *e) { ULong *xa, *xa0, w, y, z; int32 k; - double d; + double d = 0; #define d0 word0(d) #define d1 word1(d) #define set_d0(x) set_word0(d, x) @@ -1248,7 +1238,7 @@ JS_strtod(CONST char *s00, char **se, int *err) *err = 0; - bb = bd = bs = delta = NULL; + bb = bd = bs = delta = NULL; sign = nz0 = nz = 0; rv = 0.; @@ -2039,7 +2029,7 @@ static int32 quorem(Bigint *b, Bigint *S) /* 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, + * 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, @@ -2118,11 +2108,11 @@ js_dtoa(double d, int mode, JSBool biasUp, int ndigits, } return JS_TRUE; } - + b = NULL; /* initialize for abort protection */ S = NULL; mlo = mhi = NULL; - + if (!d) { no_digits: *decpt = 1; @@ -2390,7 +2380,9 @@ js_dtoa(double d, int mode, JSBool biasUp, int ndigits, goto no_digits; goto one_digit; } - for(i = 1;; i++) { + + /* Use true number of digits to limit looping. */ + for(i = 1; i<=k+1; i++) { L = (Long) (d / ds); d -= L*ds; #ifdef Check_FLT_ROUNDS @@ -2415,8 +2407,7 @@ js_dtoa(double d, int mode, JSBool biasUp, int ndigits, } break; } - if (!(d *= 10.)) - break; + d *= 10.; } goto ret1; } @@ -2829,7 +2820,7 @@ JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, dou } while (numEnd != p); *numEnd = '\0'; } - + if (exponentialNotation) { /* Insert a decimal point if more than one significand digit */ if (nDigits != 1) { @@ -2899,7 +2890,7 @@ divrem(Bigint *b, uint32 divisor) 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); @@ -2954,7 +2945,7 @@ JS_dtobasestr(int base, double d) /* 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); @@ -2995,7 +2986,7 @@ JS_dtobasestr(int base, double d) *pInt++ = *q; *q-- = ch; } - + df = d - di; if (df != 0.0) { /* We have a fraction. */ @@ -3003,7 +2994,7 @@ JS_dtobasestr(int base, double d) Bigint *b, *s, *mlo, *mhi; b = s = mlo = mhi = NULL; - + *p++ = '.'; b = d2b(df, &e, &bbits); if (!b) { @@ -3017,7 +3008,7 @@ JS_dtobasestr(int base, double d) } 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) diff --git a/src/dom/js/jsemit.c b/src/dom/js/jsemit.c index ece00fae1..c7e32f200 100644 --- a/src/dom/js/jsemit.c +++ b/src/dom/js/jsemit.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -155,12 +156,15 @@ UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) if (nuses < 0) nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ cg->stackDepth -= nuses; + JS_ASSERT(cg->stackDepth >= 0); 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); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, + js_GetErrorMessage, NULL, + JSMSG_STACK_UNDERFLOW, + cg->filename ? cg->filename : "stdin", + numBuf); } cg->stackDepth += cs->ndefs; if ((uintN)cg->stackDepth > cg->maxStackDepth) @@ -310,7 +314,7 @@ ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) 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). + (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode @@ -323,11 +327,10 @@ ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) 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. + proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose + nuses/ndefs counts help keep the stack balanced, but whose opcode format + distinguishes its backpatch delta immediate operand from a normal jump + offset. */ static int BalanceJumpTargets(JSJumpTarget **jtp) @@ -542,7 +545,7 @@ BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) const JSCodeSpec *cs; ptrdiff_t len, off; - pc = CG_BASE(cg); + pc = CG_BASE(cg) + cg->spanDepTodo; end = CG_NEXT(cg); while (pc < end) { op = (JSOp)*pc; @@ -607,6 +610,7 @@ BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) #endif /* JS_HAS_SWITCH_STATEMENT */ } + JS_ASSERT(len > 0); pc += len; } @@ -784,7 +788,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) } if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = SD_TARGET_OFFSET(sd) - pivot; + span = SD_SPAN(sd, pivot); if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { ptrdiff_t deltaFromTop = 0; @@ -906,7 +910,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) } oldpc = base + sd->before; - span = SD_TARGET_OFFSET(sd) - pivot; + span = SD_SPAN(sd, pivot); /* * If this jump didn't need to be extended, restore its span immediate @@ -1033,6 +1037,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) } } } + cg->main.lastNoteOffset += growth; /* * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's @@ -1108,7 +1113,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) } else { span = GET_JUMP_OFFSET(pc); } - JS_ASSERT(SD_TARGET_OFFSET(sd) == pivot + span); + JS_ASSERT(SD_SPAN(sd, pivot) == span); } JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); } @@ -1126,22 +1131,23 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) FreeJumpTargets(cg, cg->jumpTargets); cg->jumpTargets = NULL; cg->numSpanDeps = cg->numJumpTargets = 0; + cg->spanDepTodo = CG_OFFSET(cg); return JS_TRUE; } static JSBool EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) { + JSBool extend; ptrdiff_t jmp; jsbytecode *pc; - if (off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off) { - if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - } + extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; + if (extend && !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) { + if (jmp >= 0 && (extend || cg->spanDeps)) { pc = CG_CODE(cg, jmp); if (!AddSpanDep(cx, cg, pc, pc, off)) return JS_FALSE; @@ -1227,15 +1233,17 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, * 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 +static ptrdiff_t +EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) +{ + ptrdiff_t offset, delta; + + offset = CG_OFFSET(cg); + delta = offset - *lastp; + *lastp = offset; + JS_ASSERT(delta > 0); + return EmitJump(cx, cg, op, delta); +} /* Emit additional bytecode(s) for non-local jumps. */ static JSBool @@ -1294,7 +1302,7 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, 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); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmt->gosub); if (jmp < 0) return JS_FALSE; break; @@ -1318,10 +1326,13 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, break; case STMT_SUBROUTINE: - /* There's a retsub pc-offset on the stack that we need to pop. */ + /* + * There's a [exception or hole, retsub pc-index] pair 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) + if (js_Emit1(cx, cg, JSOP_POP2) < 0) return JS_FALSE; break; @@ -1335,10 +1346,9 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, static ptrdiff_t EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType) + ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) { intN index; - ptrdiff_t jmp; if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) return -1; @@ -1356,8 +1366,7 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, return -1; } - EMIT_BACKPATCH_OP(cx, cg, *last, JSOP_BACKPATCH, jmp); - return jmp; + return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); } static JSBool @@ -1473,7 +1482,22 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * nor can prop be deleted. */ prop = NULL; - ok = OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop); + if (OBJ_IS_NATIVE(obj)) { + ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &pobj, &prop); + if (!ok) + break; + if (prop) { + /* + * Any hidden property must be a formal arg or local var, + * which will shadow a global const of the same name. + */ + OBJ_DROP_PROPERTY(cx, pobj, prop); + break; + } + } + + ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (ok) { if (pobj == obj && (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { @@ -1483,9 +1507,10 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * variable object. Therefore we can get constant values * from our variable object here. */ - ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, + &attrs); if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) - ok = OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); @@ -1586,16 +1611,97 @@ IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, } /* - * Emit a bytecode and its 2-byte constant (atom) index immediate operand. + * Macro to emit a bytecode followed by a uint16 immediate operand stored in + * big-endian order, used for arg and var numbers as well as for atomIndexes. * NB: We use cx and cg from our caller's lexical environment, and return * false on error. */ +#define EMIT_UINT16_IMM_OP(op, i) \ + JS_BEGIN_MACRO \ + if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(i), ATOM_INDEX_LO(i)) < 0) \ + return JS_FALSE; \ + JS_END_MACRO + +/* + * Emit a bytecode and its 2-byte constant (atom) index immediate operand. + * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit + * immediate operand indexes the atom in script->atomMap. + * + * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in + * the scope chain in which the literal name was found, followed by the name + * as a string. This enables us to use the JOF_ELEM counterpart to op. + * + * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push + * the atom's value key. For JOF_PROP ops, the object being operated on has + * already been pushed, and JSOP_LITERAL will push the id, leaving the stack + * in the proper state for a JOF_ELEM counterpart. + * + * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special + * dispatch on op, but getting op's atom index from the stack instead of from + * an unsigned 16-bit immediate operand. + */ +static JSBool +EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) +{ + uint32 mode; + JSOp prefixOp; + ptrdiff_t off; + jsbytecode *pc; + + if (atomIndex >= JS_BIT(16)) { + mode = (js_CodeSpec[op].format & JOF_MODEMASK); + if (op != JSOP_SETNAME) { + prefixOp = (mode == JOF_NAME) + ? JSOP_FINDNAME + : (mode == JOF_PROP) + ? JSOP_LITERAL + : JSOP_LITOPX; + off = js_EmitN(cx, cg, prefixOp, 3); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_LITERAL_INDEX(pc, atomIndex); + } + + switch (op) { + case JSOP_DECNAME: op = JSOP_DECELEM; break; + case JSOP_DECPROP: op = JSOP_DECELEM; break; + case JSOP_DELNAME: op = JSOP_DELELEM; break; + case JSOP_DELPROP: op = JSOP_DELELEM; break; + case JSOP_FORNAME: op = JSOP_FORELEM; break; + case JSOP_FORPROP: op = JSOP_FORELEM; break; + case JSOP_GETPROP: op = JSOP_GETELEM; break; + case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; + case JSOP_INCNAME: op = JSOP_INCELEM; break; + case JSOP_INCPROP: op = JSOP_INCELEM; break; + case JSOP_INITPROP: op = JSOP_INITELEM; break; + case JSOP_NAME: op = JSOP_GETELEM; break; + case JSOP_NAMEDEC: op = JSOP_ELEMDEC; break; + case JSOP_NAMEINC: op = JSOP_ELEMINC; break; + case JSOP_PROPDEC: op = JSOP_ELEMDEC; break; + case JSOP_PROPINC: op = JSOP_ELEMINC; break; + case JSOP_BINDNAME: return JS_TRUE; + case JSOP_SETNAME: op = JSOP_SETELEM; break; + case JSOP_SETPROP: op = JSOP_SETELEM; break; + default: JS_ASSERT(mode == 0); break; + } + + return js_Emit1(cx, cg, op) >= 0; + } + + EMIT_UINT16_IMM_OP(op, atomIndex); + return JS_TRUE; +} + +/* + * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro + * caller's lexical environment, and embedding a false return on error. + * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?! + */ #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) { \ + if (!EmitAtomIndexOp(cx, op, atomIndex, cg)) \ return JS_FALSE; \ - } \ JS_END_MACRO static JSBool @@ -1608,8 +1714,7 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) 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; + return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg); } /* @@ -1646,6 +1751,21 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) return JS_TRUE; + /* QNAME references can never be optimized to use arg/var storage. */ + if (pn->pn_op == JSOP_QNAMEPART) + return JS_TRUE; + + /* + * A Script object can be used to split an eval into a compile step done + * at construction time, and an execute step done separately, possibly in + * a different scope altogether. We therefore cannot do any name-to-slot + * optimizations, but must lookup names at runtime. Note that script_exec + * ensures that its caller's frame has a Call object, so arg and var name + * lookups will succeed. + */ + if (cx->fp->flags & JSFRAME_SCRIPT_OBJECT) + 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. @@ -1730,7 +1850,7 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) * 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)) + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; if (sprop) { @@ -1954,12 +2074,30 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, return ok; } +/* + * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all + * uses of JSOP_GETMETHOD that implicitly qualify the method-property name with + * a function:: prefix. All other JSOP_GETMETHOD and JSOP_SETMETHOD uses must + * be explicit, so need a distinct source note (SRC_PCDELTA rather than PCBASE) + * for round-tripping through the beloved decompiler. + */ +#define JSPROP_IMPLICIT_FUNCTION_NAMESPACE 0x100 + +static jssrcnote +SrcNoteForPropOp(JSParseNode *pn, JSOp op) +{ + return ((op == JSOP_GETMETHOD && + !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) || + op == JSOP_SETMETHOD) + ? SRC_PCDELTA + : SRC_PCBASE; +} + 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 && @@ -2000,14 +2138,12 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) do { /* Walk back up the list, emitting annotated name ops. */ - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, + if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op), CG_OFFSET(cg) - pndown->pn_offset) < 0) { return JS_FALSE; } - ale = js_IndexAtom(cx, pndot->pn_atom, &cg->atomList); - if (!ale) + if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg)) return JS_FALSE; - EMIT_ATOM_INDEX_OP(pndot->pn_op, ALE_INDEX(ale)); /* Reverse the pn_expr link again. */ pnup = pndot->pn_expr; @@ -2019,17 +2155,17 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) return JS_FALSE; } - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0) + if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op), + 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) + if (!EmitAtomOp(cx, pn, op, cg)) return JS_FALSE; - EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale)); } return JS_TRUE; } @@ -2038,7 +2174,7 @@ static JSBool EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) { ptrdiff_t top; - JSParseNode *left, *right, *next; + JSParseNode *left, *right, *next, temp; jsint slot; top = CG_OFFSET(cg); @@ -2060,9 +2196,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(next->pn_dval, slot) && - (jsuint)slot < ATOM_INDEX_LIMIT) { + (jsuint)slot < JS_BIT(16)) { left->pn_offset = next->pn_offset = top; - EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot); + EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); left = next; next = left->pn_next; } @@ -2090,9 +2226,22 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) next = next->pn_next; } } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - left = pn->pn_left; - right = pn->pn_right; + if (pn->pn_arity == PN_NAME) { + /* + * Set left and right so pn appears to be a TOK_LB node, instead + * of a TOK_DOT node (see the TOK_FOR/IN case in js_EmitTree). + */ + left = pn->pn_expr; + right = &temp; + right->pn_type = TOK_STRING; + right->pn_arity = PN_NULLARY; + right->pn_op = JSOP_STRING; + right->pn_atom = pn->pn_atom; + } else { + JS_ASSERT(pn->pn_arity == PN_BINARY); + left = pn->pn_left; + right = pn->pn_right; + } /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ if (op == JSOP_GETELEM && @@ -2102,9 +2251,9 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(right->pn_dval, slot) && - (jsuint)slot < ATOM_INDEX_LIMIT) { + (jsuint)slot < JS_BIT(16)) { left->pn_offset = right->pn_offset = top; - EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot); + EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); return JS_TRUE; } } @@ -2124,6 +2273,8 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) { jsint ival; jsatomid atomIndex; + ptrdiff_t off; + jsbytecode *pc; JSAtom *atom; JSAtomListElement *ale; @@ -2132,22 +2283,33 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) 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); + + atomIndex = (jsatomid)ival; + if (atomIndex < JS_BIT(16)) { + EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex); + return JS_TRUE; + } + + if (atomIndex < JS_BIT(24)) { + off = js_EmitN(cx, cg, JSOP_UINT24, 3); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_LITERAL_INDEX(pc, 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; + return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg); } #if JS_HAS_SWITCH_STATEMENT @@ -2375,6 +2537,7 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, off = -1; if (switchOp == JSOP_CONDSWITCH) { intN caseNoteIndex = -1; + JSBool beforeCases = JS_TRUE; /* Emit code for evaluating cases and jumping to case statements. */ for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { @@ -2388,8 +2551,10 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, return JS_FALSE; } } - if (pn3->pn_type == TOK_DEFAULT) + if (!pn4) { + JS_ASSERT(pn3->pn_type == TOK_DEFAULT); continue; + } caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); if (caseNoteIndex < 0) return JS_FALSE; @@ -2397,12 +2562,13 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, if (off < 0) return JS_FALSE; pn3->pn_offset = off; - if (pn3 == pn2->pn_head) { + if (beforeCases) { /* Switch note's second offset is to first JSOP_CASE. */ if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, off - top)) { return JS_FALSE; } + beforeCases = JS_FALSE; } } @@ -2624,7 +2790,7 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, fun->u.script = js_NewScriptFromCG(cx, cg, fun); if (!fun->u.script) return JS_FALSE; - fun->interpreted = JS_TRUE; + JS_ASSERT(fun->interpreted); if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) fun->flags |= JSFUN_HEAVYWEIGHT; return JS_TRUE; @@ -2678,7 +2844,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSAtom *atom; JSAtomListElement *ale; jsatomid atomIndex; - intN noteIndex; + ptrdiff_t noteIndex; JSSrcNoteType noteType; jsbytecode *pc; JSOp op; @@ -2704,6 +2870,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSCodeGenerator *cg2; JSFunction *fun; +#if JS_HAS_XML_SUPPORT + if (pn->pn_arity == PN_NULLARY) { + if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) + return JS_FALSE; + break; + } +#endif + /* Generate code for the function's body. */ cg2mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); @@ -2716,7 +2890,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg->principals)) { return JS_FALSE; } - cg2->treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION; + cg2->treeContext.flags = (uint16) (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)); @@ -2765,25 +2939,43 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (cg->treeContext.flags & TCF_IN_FUNCTION) { JSObject *obj, *pobj; JSProperty *prop; + JSScopeProperty *sprop; uintN slot; obj = OBJ_GET_PARENT(cx, fun->object); - if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj, - &prop)) { + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom), + &pobj, &prop)) { return JS_FALSE; } + JS_ASSERT(prop && pobj == obj); - slot = ((JSScopeProperty *) prop)->shortid; + sprop = (JSScopeProperty *) prop; + JS_ASSERT(sprop->getter == js_GetLocalVariable); + slot = sprop->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); + if (atomIndex >= JS_BIT(16)) { + /* + * Lots of literals in the outer function, so we have to emit + * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot]. + */ + off = js_EmitN(cx, cg, JSOP_LITOPX, 3); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_LITERAL_INDEX(pc, atomIndex); + EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot); + } else { + /* Emit [JSOP_DEFLOCALFUN, var 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); @@ -2977,6 +3169,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); if (pn2->pn_type == TOK_IN) { + JSBool emitIFEQ; + /* Set stmtInfo type for later testing. */ stmtInfo.type = STMT_FOR_IN_LOOP; noteIndex = -1; @@ -3018,7 +3212,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); +#if JS_HAS_XML_SUPPORT + /* Emit a prefix opcode if 'for each (... in ...)' was used. */ + if (pn->pn_op != JSOP_NOP && js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; +#endif + /* Compile a JSOP_FOR* bytecode based on the left hand side. */ + emitIFEQ = JS_TRUE; switch (pn3->pn_type) { case TOK_VAR: pn3 = pn3->pn_head; @@ -3048,7 +3249,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn3->pn_attrs & JSPROP_READONLY) op = JSOP_GETVAR; atomIndex = (jsatomid) pn3->pn_slot; - EMIT_ATOM_INDEX_OP(op, atomIndex); + EMIT_UINT16_IMM_OP(op, atomIndex); } else { if (!EmitAtomOp(cx, pn3, op, cg)) return JS_FALSE; @@ -3056,10 +3257,24 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_DOT: - if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) + useful = JS_TRUE; + if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, + &useful)) { return JS_FALSE; - break; + } + if (!useful) { + if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) + return JS_FALSE; + break; + } + /* FALL THROUGH */ +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: +#endif +#if JS_HAS_LVALUE_RETURN + case TOK_LP: +#endif case TOK_LB: /* * We separate the first/next bytecode from the enumerator @@ -3067,6 +3282,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * expression (e.g., for (x[i++] in {}) should not bind x[i] * or increment i at all). */ + emitIFEQ = JS_FALSE; if (!js_Emit1(cx, cg, JSOP_FORELEM)) return JS_FALSE; @@ -3084,6 +3300,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (beq < 0) return JS_FALSE; +#if JS_HAS_LVALUE_RETURN + if (pn3->pn_type == TOK_LP) { + JS_ASSERT(pn3->pn_op == JSOP_SETCALL); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) + return JS_FALSE; + break; + } +#endif +#if JS_HAS_XML_SUPPORT + if (pn3->pn_type == TOK_UNARYOP) { + JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) + return JS_FALSE; + break; + } +#endif + /* Now that we're safely past the IFEQ, commit side effects. */ if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) return JS_FALSE; @@ -3092,7 +3329,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) default: JS_ASSERT(0); } - if (pn3->pn_type != TOK_LB) { + + if (emitIFEQ) { /* Annotate so the decompiler can find the loop-closing jump. */ noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); if (noteIndex < 0) @@ -3276,14 +3514,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* 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. @@ -3291,7 +3521,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * 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). + * EmitBackPatchOp and BackPatch). */ js_PushStatement(&cg->treeContext, &stmtInfo, pn->pn_kid3 ? STMT_FINALLY : STMT_BLOCK, @@ -3321,7 +3551,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn->pn_kid3) { if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; - EMIT_FINALLY_GOSUB(cx, cg, jmp); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.gosub); if (jmp < 0) return JS_FALSE; } @@ -3329,7 +3559,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* 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); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.catchJump); if (jmp < 0) return JS_FALSE; @@ -3358,7 +3588,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * * 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 + * 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. */ @@ -3371,9 +3601,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; if (catchJump != -1) { + JS_ASSERT(cg->stackDepth == depth); + /* Fix up and clean up previous catch block. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump); + /* Set cx->throwing to protect cx->exception from the GC. */ + if (!js_Emit1(cx, cg, JSOP_THROWING) < 0) + return JS_FALSE; + /* Compensate for the [leavewith]. */ cg->stackDepth++; JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth); @@ -3384,7 +3620,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } } else { /* Set stack to original depth (see SETSP comment above). */ - EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth); + EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); cg->stackDepth = depth; } @@ -3457,15 +3693,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* gosub , if required */ if (pn->pn_kid3) { - EMIT_FINALLY_GOSUB(cx, cg, jmp); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, + &stmtInfo.gosub); if (jmp < 0) return JS_FALSE; + JS_ASSERT(cg->stackDepth == depth); } /* 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); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, + &stmtInfo.catchJump); if (jmp < 0) return JS_FALSE; if (!iter->pn_kid2) /* leave iter at last catch */ @@ -3475,12 +3714,35 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } /* - * 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. + * We emit a [setsp][gosub] sequence for running finally code while + * letting an uncaught exception pass thrown from within the try in a + * try-finally. The [gosub] and [retsub] opcodes will take care of + * stacking and rethrowing any exception pending across the finally. + * + * For rethrowing after a try-catch(guard)-finally, we have a problem: + * all the guards have mismatched, leaving cx->exception still set but + * cx->throwing clear, so that no exception appears to be pending for + * [gosub] to stack and [retsub] to rethrow. We must emit a special + * [throwing] opcode in front of the [setsp][gosub]; this opcode will + * restore cx->throwing to true before running the finally. + * + * For rethrowing after a try-catch(guard) without a finally, we emit + * [setsp][exception][throw]. */ if (pn->pn_kid3 || (catchJump != -1 && iter->pn_kid1->pn_expr)) { + /* + * Last discriminant jumps to the rethrow code sequence if no + * discriminants match. Target catchJump at the beginning of the + * rethrow sequence, just in case a guard expression throws and + * leaves the stack unbalanced. + */ + if (catchJump != -1 && iter->pn_kid1->pn_expr) { + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump); + if (pn->pn_kid3 && !js_Emit1(cx, cg, JSOP_THROWING) < 0) + return JS_FALSE; + } + /* * Emit another stack fixup, because the catch could itself * throw an exception in an unbalanced state, and the finally @@ -3489,27 +3751,24 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * stack fixup. */ finallyCatch = CG_OFFSET(cg); - EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth); + EMIT_UINT16_IMM_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); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.gosub); 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); + JS_ASSERT((uintN)depth <= cg->maxStackDepth); + } else { + 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); } /* @@ -3521,12 +3780,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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. + * The stack budget must be balanced at this point. All [gosub] + * calls emitted before this point will push two stack slots, one + * for the pending exception (or JSVAL_HOLE if there is no pending + * exception) and one for the [retsub] pc-index. */ JS_ASSERT(cg->stackDepth == depth); - if ((uintN)++cg->stackDepth > cg->maxStackDepth) + cg->stackDepth += 2; + if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; /* Now indicate that we're emitting a subroutine body. */ @@ -3538,8 +3799,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_Emit1(cx, cg, JSOP_RETSUB) < 0) { return JS_FALSE; } + + /* Restore stack depth budget to its balanced state. */ + JS_ASSERT(cg->stackDepth == depth + 2); + cg->stackDepth = depth; } - js_PopStatementCG(cx, cg); + if (!js_PopStatementCG(cx, cg)) + return JS_FALSE; if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { @@ -3648,6 +3914,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (op == JSOP_ARGUMENTS) { if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; + } else if (pn2->pn_slot >= 0) { + EMIT_UINT16_IMM_OP(op, atomIndex); } else { EMIT_ATOM_INDEX_OP(op, atomIndex); } @@ -3698,6 +3966,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_LC: +#if JS_HAS_XML_SUPPORT + if (pn->pn_arity == PN_UNARY) { + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + } +#endif + + JS_ASSERT(pn->pn_arity == PN_LIST); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) @@ -3724,7 +4003,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } if (!useful) { CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; - if (!js_ReportCompileErrorNumber(cx, NULL, cg, + if (!js_ReportCompileErrorNumber(cx, cg, + JSREPORT_CG | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_USELESS_EXPR)) { @@ -3843,6 +4123,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + break; #endif default: JS_ASSERT(0); @@ -3859,7 +4148,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) switch (pn2->pn_type) { case TOK_NAME: if (pn2->pn_op != JSOP_SETNAME) { - EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETGVAR) + EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR) ? JSOP_GETGVAR : (pn2->pn_op == JSOP_SETARG) ? JSOP_GETARG @@ -3876,6 +4165,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_LB: #if JS_HAS_LVALUE_RETURN case TOK_LP: +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: #endif if (js_Emit1(cx, cg, JSOP_DUP2) < 0) return JS_FALSE; @@ -3899,17 +4191,22 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } /* 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; + if (pn2->pn_type != TOK_NAME && + js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op), + 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)) { + if (pn2->pn_slot >= 0) { + EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex); + } else { case TOK_DOT: - EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); + EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); + } } break; case TOK_LB: @@ -3919,6 +4216,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) return JS_FALSE; break; +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) + return JS_FALSE; + break; +#endif default:; } break; @@ -4035,6 +4338,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } } else { +#if JS_HAS_XML_SUPPORT + case TOK_DBLCOLON: + if (pn->pn_arity == PN_NAME) { + if (!js_EmitTree(cx, cg, pn->pn_expr)) + return JS_FALSE; + if (!EmitAtomOp(cx, pn, pn->pn_op, cg)) + return JS_FALSE; + break; + } +#endif /* Binary operators that evaluate both operands unconditionally. */ if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; @@ -4047,12 +4360,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #if JS_HAS_EXCEPTIONS case TOK_THROW: +#endif +#if JS_HAS_XML_SUPPORT + case TOK_AT: + case TOK_DEFAULT: + JS_ASSERT(pn->pn_arity == PN_UNARY); + /* FALL THROUGH */ #endif case TOK_UNARYOP: /* Unary op, including unary +/-. */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) + pn2 = pn->pn_kid; + if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - if (js_Emit1(cx, cg, pn->pn_op) < 0) + op = pn->pn_op; +#if JS_HAS_XML_SUPPORT + if (op == JSOP_XMLNAME && + js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pn2->pn_offset) < 0) { + return JS_FALSE; + } +#endif + if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; @@ -4062,6 +4390,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) pn2 = pn->pn_kid; JS_ASSERT(pn2->pn_type != TOK_RP); op = pn->pn_op; + + /* + * Allocate another stack slot for GC protection in case the initial + * value being post-incremented or -decremented is not a number, but + * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand + * uses and 1 definition, so we don't need an extra stack slot -- we + * can use the one allocated for the def. + */ + if (pn2->pn_type != TOK_NAME && + (js_CodeSpec[op].format & JOF_POST) && + (uintN)++cg->stackDepth > cg->maxStackDepth) { + cg->maxStackDepth = cg->stackDepth; + } + switch (pn2->pn_type) { case TOK_NAME: pn2->pn_op = op; @@ -4076,7 +4418,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) : JSOP_GETVAR; } atomIndex = (jsatomid) pn2->pn_slot; - EMIT_ATOM_INDEX_OP(op, atomIndex); + EMIT_UINT16_IMM_OP(op, atomIndex); } else { if (!EmitAtomOp(cx, pn2, op, cg)) return JS_FALSE; @@ -4101,14 +4443,31 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; #endif default: JS_ASSERT(0); } + + if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & JOF_POST)) + --cg->stackDepth; break; case TOK_DELETE: - /* Under ECMA 3, deleting a non-reference returns true. */ + /* + * Under ECMA 3, deleting a non-reference returns true -- but alas we + * must evaluate the operand if it appears it might have side effects. + */ pn2 = pn->pn_kid; switch (pn2->pn_type) { case TOK_NAME: @@ -4128,16 +4487,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg)) return JS_FALSE; break; +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: + if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) + return JS_FALSE; + break; +#endif case TOK_LB: if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) return JS_FALSE; break; default: + useful = JS_FALSE; + if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) + return JS_FALSE; + if (useful) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } if (js_Emit1(cx, cg, JSOP_TRUE) < 0) return JS_FALSE; } break; +#if JS_HAS_XML_SUPPORT + case TOK_FILTER: + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0); + if (jmp < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + break; +#endif + case TOK_DOT: /* * Pop a stack operand, convert it to object, get a property named by @@ -4150,6 +4539,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_LB: +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: +#endif /* * Pop two operands, convert the left one to object and the right one * to property name (atom or tagged int), get the named property, and @@ -4165,23 +4557,35 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * Emit function call or operator new (constructor call) code. * First, emit code for the left operand to evaluate the callable or * constructable object expression. + * + * For E4X, if this expression is a dotted member reference, select + * JSOP_GETMETHOD instead of JSOP_GETPROP. ECMA-357 separates XML + * method lookup from the normal property id lookup done for native + * objects. */ pn2 = pn->pn_head; +#if JS_HAS_XML_SUPPORT + if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) { + JS_ASSERT(pn2->pn_op == JSOP_GETPROP); + pn2->pn_op = JSOP_GETMETHOD; + pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE; + } +#endif 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. + * 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; + /* Remember start of callable-object bytecode for decompilation hint. */ + off = top; + /* - * Emit code for each argument in order, then emit the JSOP_*CALL or + * 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. */ @@ -4191,6 +4595,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } 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; @@ -4217,25 +4622,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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); + EMIT_UINT16_IMM_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); - } + for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { + if (!EmitNumberOp(cx, atomIndex, cg)) + return JS_FALSE; - /* Sub-optimal: holes in a sparse initializer are void-filled. */ + /* FIXME 260106: 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; @@ -4243,10 +4639,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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) { @@ -4281,7 +4676,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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); + EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); pn2 = pn2->pn_next; } #endif @@ -4335,11 +4730,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_DEFSHARP: if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); + EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); break; case TOK_USESHARP: - EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); + EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); break; #endif /* JS_HAS_SHARP_VARS */ #endif /* JS_HAS_INITIALIZERS */ @@ -4367,10 +4762,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } if (pn->pn_slot >= 0) { atomIndex = (jsatomid) pn->pn_slot; - EMIT_ATOM_INDEX_OP(op, atomIndex); + EMIT_UINT16_IMM_OP(op, atomIndex); break; } /* FALL THROUGH */ + +#if JS_HAS_XML_SUPPORT + case TOK_XMLATTR: + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: +#endif case TOK_STRING: case TOK_OBJECT: /* @@ -4391,6 +4794,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = EmitNumberOp(cx, pn->pn_dval, cg); break; +#if JS_HAS_XML_SUPPORT + case TOK_ANYNAME: +#endif case TOK_PRIMARY: if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; @@ -4403,6 +4809,148 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + break; + } + + JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); + switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) { + case TOK_XMLETAGO: + JS_ASSERT(0); + /* FALL THROUGH */ + case TOK_XMLPTAGC: + case TOK_XMLSTAGO: + break; + default: + if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) + return JS_FALSE; + } + + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (pn2->pn_type == TOK_LC && + js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + + if (pn->pn_extra & PNX_XMLROOT) { + if (pn->pn_count == 0) { + JS_ASSERT(pn->pn_type == TOK_XMLLIST); + atom = cx->runtime->atomState.emptyAtom; + ale = js_IndexAtom(cx, atom, &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + } + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + } +#ifdef DEBUG + else + JS_ASSERT(pn->pn_count != 0); +#endif + break; + + case TOK_XMLPTAGC: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + break; + } + /* FALL THROUGH */ + + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + { + uint32 i; + + if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) + return JS_FALSE; + + ale = js_IndexAtom(cx, + (pn->pn_type == TOK_XMLETAGO) + ? cx->runtime->atomState.etagoAtom + : cx->runtime->atomState.stagoAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + + JS_ASSERT(pn->pn_count != 0); + pn2 = pn->pn_head; + if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { + if (pn2->pn_type == TOK_LC && + js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if ((i & 1) && pn2->pn_type == TOK_LC) { + if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, + (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { + return JS_FALSE; + } + } + + ale = js_IndexAtom(cx, + (pn->pn_type == TOK_XMLPTAGC) + ? cx->runtime->atomState.ptagcAtom + : cx->runtime->atomState.tagcAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + } + + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_count != 0); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + } else { + JS_ASSERT(pn->pn_arity == PN_NULLARY); + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + } + break; + + case TOK_XMLPI: + ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); + if (!ale) + return JS_FALSE; + if (!EmitAtomIndexOp(cx, JSOP_STRING, ALE_INDEX(ale), cg)) + return JS_FALSE; + if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) + return JS_FALSE; + break; +#endif /* JS_HAS_XML_SUPPORT */ + default: JS_ASSERT(0); } @@ -4602,7 +5150,7 @@ js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, return sn; } -uintN +JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn) { uintN arity; @@ -4699,7 +5247,7 @@ void DumpSrcNoteSizeHist() fp = fopen("/tmp/srcnotes.hist", "w"); if (!fp) return; - setlinebuf(fp); + setvbuf(fp, NULL, _IONBF, 0); } fprintf(fp, "SrcNote size histogram:\n"); for (i = 0; i < NBINS; i++) { diff --git a/src/dom/js/jsemit.h b/src/dom/js/jsemit.h index 3c8c9c6ae..6ec4600b4 100644 --- a/src/dom/js/jsemit.h +++ b/src/dom/js/jsemit.h @@ -112,6 +112,7 @@ struct JSTreeContext { /* tree context for semantic checks */ #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 TCF_HAS_DEFXMLNS 0x100 /* default xml namespace = ...; parsed */ #define TREE_CONTEXT_INIT(tc) \ ((tc)->flags = (tc)->numGlobalVars = 0, \ @@ -172,19 +173,26 @@ struct JSJumpTarget { #define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) #define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) +#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ + JT_CLR_TAG((sd)->target)) #define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) #define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ JT_TO_BPDELTA((sd)->target)) -#define SD_TARGET_OFFSET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ - JT_CLR_TAG((sd)->target)->offset) + +/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ +#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ + ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ + : 0) 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 */ @@ -195,20 +203,27 @@ struct JSCodeGenerator { 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 */ + ptrdiff_t spanDepTodo; /* offset from main.base of potentially + unoptimized spandeps */ + uintN emitLevel; /* js_EmitTree recursion level */ JSAtomList constList; /* compile time constants */ JSCodeGenerator *parent; /* Enclosing function or global context */ @@ -391,13 +406,16 @@ typedef enum JSSrcNoteType { 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_PCDELTA = 7, /* distance from comma-operator to next POP, + or from CONDSWITCH to first CASE opcode -- + or SRC_PCBASE variant for obj.function::foo + gets and sets */ 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_PCBASE = 12, /* distance back from annotated get- or setprop + op to 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 */ diff --git a/src/dom/js/jsexn.c b/src/dom/js/jsexn.c index 6d3a182db..c637a5630 100644 --- a/src/dom/js/jsexn.c +++ b/src/dom/js/jsexn.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -51,11 +52,12 @@ #include "jsapi.h" #include "jscntxt.h" #include "jsconfig.h" +#include "jsdbgapi.h" #include "jsexn.h" #include "jsfun.h" #include "jsinterp.h" -#include "jsopcode.h" #include "jsnum.h" +#include "jsopcode.h" #include "jsscript.h" #if JS_HAS_ERROR_EXCEPTIONS @@ -92,34 +94,118 @@ typedef struct JSExnPrivate { JSErrorReport *errorReport; } JSExnPrivate; -/* - * Undo all the damage done by exn_newPrivate. - */ -static void -exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData) +static JSErrorReport * +CopyErrorReport(JSContext *cx, JSErrorReport *report) { - 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); + /* + * We use a single malloc block to make a deep copy of JSErrorReport with + * the following layout: + * JSErrorReport + * array of copies of report->messageArgs + * jschar array with characters for all messageArgs + * jschar array with characters for ucmessage + * jschar array with characters for uclinebuf and uctokenptr + * char array with characters for linebuf and tokenptr + * char array with characters for filename + * Such layout does not need any extra alignment padding. + */ + size_t filenameSize; + size_t linebufSize; + size_t uclinebufSize; + size_t ucmessageSize; + size_t i, argsArraySize, argsCopySize, argSize; + size_t mallocSize; + JSErrorReport *copy; + uint8 *cursor; + +#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) + + filenameSize = report->filename ? strlen(report->filename) + 1 : 0; + linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; + uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; + ucmessageSize = 0; + argsArraySize = 0; + argsCopySize = 0; + if (report->ucmessage) { + ucmessageSize = JS_CHARS_SIZE(report->ucmessage); if (report->messageArgs) { - args = report->messageArgs; - while (*args != NULL) - JS_free(cx, (void *)*args++); - JS_free(cx, (void *)report->messageArgs); + for (i = 0; report->messageArgs[i]; ++i) + argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); + + /* Non-null messageArgs should have at least one non-null arg. */ + JS_ASSERT(i != 0); + argsArraySize = (i + 1) * sizeof(const jschar *); + } + } + + /* + * The mallocSize can not overflow since it represents the sum of the + * sizes of already allocated objects. + */ + mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + + ucmessageSize + uclinebufSize + linebufSize + filenameSize; + cursor = (uint8 *)JS_malloc(cx, mallocSize); + if (!cursor) + return NULL; + + copy = (JSErrorReport *)cursor; + memset(cursor, 0, sizeof(JSErrorReport)); + cursor += sizeof(JSErrorReport); + + if (argsArraySize != 0) { + copy->messageArgs = (const jschar **)cursor; + cursor += argsArraySize; + for (i = 0; report->messageArgs[i]; ++i) { + copy->messageArgs[i] = (const jschar *)cursor; + argSize = JS_CHARS_SIZE(report->messageArgs[i]); + memcpy(cursor, report->messageArgs[i], argSize); + cursor += argSize; + } + copy->messageArgs[i] = NULL; + JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); + } + + if (report->ucmessage) { + copy->ucmessage = (const jschar *)cursor; + memcpy(cursor, report->ucmessage, ucmessageSize); + cursor += ucmessageSize; + } + + if (report->uclinebuf) { + copy->uclinebuf = (const jschar *)cursor; + memcpy(cursor, report->uclinebuf, uclinebufSize); + cursor += uclinebufSize; + if (report->uctokenptr) { + copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - + report->uclinebuf); + } + } + + if (report->linebuf) { + copy->linebuf = (const char *)cursor; + memcpy(cursor, report->linebuf, linebufSize); + cursor += linebufSize; + if (report->tokenptr) { + copy->tokenptr = copy->linebuf + (report->tokenptr - + report->linebuf); } - JS_free(cx, report); } - JS_free(cx, privateData); + + if (report->filename) { + copy->filename = (const char *)cursor; + memcpy(cursor, report->filename, filenameSize); + } + JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); + + /* Copy non-pointer members. */ + copy->lineno = report->lineno; + copy->errorNumber = report->errorNumber; + + /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ + copy->flags = report->flags; + +#undef JS_CHARS_SIZE + return copy; } /* @@ -128,99 +214,17 @@ exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData) 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; + newPrivate->errorReport = CopyErrorReport(cx, report); + if (!newPrivate->errorReport) { + JS_free(cx, newPrivate); + return 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 @@ -233,8 +237,11 @@ exn_finalize(JSContext *cx, JSObject *obj) if (!JSVAL_IS_VOID(privateValue)) { privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); - if (privateData) - exn_destroyPrivate(cx, privateData); + if (privateData) { + if (privateData->errorReport) + JS_free(cx, privateData->errorReport); + JS_free(cx, privateData); + } } } @@ -388,10 +395,15 @@ InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, stacklen = stackmax = 0; ok = JS_TRUE; +/* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ +#define STACK_LENGTH_LIMIT JS_BIT(20) + #define APPEND_CHAR_TO_STACK(c) \ JS_BEGIN_MACRO \ if (stacklen == stackmax) { \ void *ptr_; \ + if (stackmax >= STACK_LENGTH_LIMIT) \ + goto done; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ @@ -407,8 +419,12 @@ InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, JS_BEGIN_MACRO \ JSString *str_ = str; \ size_t length_ = JSSTRING_LENGTH(str_); \ - if (stacklen + length_ > stackmax) { \ + if (length_ > stackmax - stacklen) { \ void *ptr_; \ + if (stackmax >= STACK_LENGTH_LIMIT || \ + length_ >= STACK_LENGTH_LIMIT - stacklen) { \ + goto done; \ + } \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ @@ -425,7 +441,8 @@ InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, 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); + ok = checkAccess(cx, JSVAL_TO_OBJECT(fp->argv[-2]), callerid, + JSACC_READ, &v /* ignored */); if (!ok) { ok = JS_TRUE; break; @@ -487,6 +504,7 @@ InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK +#undef STACK_LENGTH_LIMIT done: if (checkAccess) { @@ -528,13 +546,27 @@ done: NULL, NULL, JSPROP_ENUMERATE); } +/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 + with these two functions. */ +static JSString * +FilenameToString(JSContext *cx, const char *filename) +{ + return JS_NewStringCopyZ(cx, filename); +} + +static const char * +StringToFilename(JSContext *cx, JSString *str) +{ + return JS_GetStringBytes(str); +} + static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool ok; - jsval pval; - int32 lineno; + uint32 lineno; JSString *message, *filename; + JSStackFrame *fp; if (cx->creatingException) return JS_FALSE; @@ -549,11 +581,12 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * prototype ourselves. */ ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), - (jsid)cx->runtime->atomState.classPrototypeAtom, - &pval); + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), + rval); if (!ok) goto out; - obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL); + obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(*rval), NULL); if (!obj) { ok = JS_FALSE; goto out; @@ -588,17 +621,29 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto out; } argv[1] = STRING_TO_JSVAL(filename); + fp = NULL; } else { - filename = cx->runtime->emptyString; + fp = JS_GetScriptedCaller(cx, NULL); + if (fp) { + filename = FilenameToString(cx, fp->script->filename); + if (!filename) { + ok = JS_FALSE; + goto out; + } + } else { + filename = cx->runtime->emptyString; + } } /* Set the 'lineNumber' property. */ if (argc > 2) { - ok = js_ValueToInt32(cx, argv[2], &lineno); + ok = js_ValueToECMAUint32(cx, argv[2], &lineno); if (!ok) goto out; } else { - lineno = 0; + if (!fp) + fp = JS_GetScriptedCaller(cx, NULL); + lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0; } ok = InitExceptionObject(cx, obj, message, filename, lineno); @@ -623,9 +668,13 @@ exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jschar *chars, *cp; size_t name_length, message_length, length; - if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.nameAtom), + &v)) { return JS_FALSE; + } name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; + *rval = STRING_TO_JSVAL(name); if (!JS_GetProperty(cx, obj, js_message_str, &v)) return JS_FALSE; @@ -669,37 +718,45 @@ exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsval v; + jsval *vp; JSString *name, *message, *filename, *lineno_as_str, *result; - int32 lineno; + uint32 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)) + vp = argv + argc; /* beginning of explicit local roots */ + + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.nameAtom), + rval)) { return JS_FALSE; - name = js_ValueToString(cx, v); + } + name = js_ValueToString(cx, *rval); if (!name) return JS_FALSE; + *rval = STRING_TO_JSVAL(name); - if (!JS_GetProperty(cx, obj, js_message_str, &v) || - !(message = js_ValueToSource(cx, v))) { + if (!JS_GetProperty(cx, obj, js_message_str, &vp[0]) || + !(message = js_ValueToSource(cx, vp[0]))) { return JS_FALSE; } + vp[0] = STRING_TO_JSVAL(message); - if (!JS_GetProperty(cx, obj, js_filename_str, &v) || - !(filename = js_ValueToSource(cx, v))) { + if (!JS_GetProperty(cx, obj, js_filename_str, &vp[1]) || + !(filename = js_ValueToSource(cx, vp[1]))) { return JS_FALSE; } + vp[1] = STRING_TO_JSVAL(filename); - if (!JS_GetProperty(cx, obj, js_lineno_str, &v) || - !js_ValueToInt32 (cx, v, &lineno)) { + if (!JS_GetProperty(cx, obj, js_lineno_str, &vp[2]) || + !js_ValueToECMAUint32 (cx, vp[2], &lineno)) { return JS_FALSE; } if (lineno != 0) { - if (!(lineno_as_str = js_ValueToString(cx, v))) { + lineno_as_str = js_ValueToString(cx, vp[2]); + if (!lineno_as_str) return JS_FALSE; - } lineno_length = JSSTRING_LENGTH(lineno_as_str); } else { lineno_as_str = NULL; @@ -777,7 +834,7 @@ exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSFunctionSpec exception_methods[] = { #if JS_HAS_TOSOURCE - {js_toSource_str, exn_toSource, 0,0,0}, + {js_toSource_str, exn_toSource, 0,0,3}, #endif {js_toString_str, exn_toString, 0,0,0}, {0,0,0,0,0} @@ -789,6 +846,9 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) int i; JSObject *protos[JSEXN_LIMIT]; + if (!js_EnterLocalRootScope(cx)) + return NULL; + /* Initialize the prototypes first. */ for (i = 0; exceptions[i].name != 0; i++) { JSAtom *atom; @@ -803,19 +863,19 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) : NULL, obj); if (!protos[i]) - return NULL; + break; /* 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; + break; /* Make a constructor function for the current name. */ fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); if (!fun) - return NULL; + break; /* Make this constructor make objects of class Exception. */ fun->clasp = &ExceptionClass; @@ -823,23 +883,27 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) /* Make the prototype and constructor links. */ if (!js_SetClassPrototype(cx, fun->object, protos[i], JSPROP_READONLY | JSPROP_PERMANENT)) { - return NULL; + break; } /* proto bootstrap bit from JS_InitClass omitted. */ nameString = JS_NewStringCopyZ(cx, exceptions[i].name); if (!nameString) - return NULL; + break; /* 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; + break; } } + js_LeaveLocalRootScope(cx); + if (exceptions[i].name) + return NULL; + /* * Add an empty message property. (To Exception.prototype only, * because this property will be the same for all the exception @@ -893,6 +957,8 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; JSExnType exn; + jsval tv[4]; + JSTempValueRooter tvr; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; @@ -935,56 +1001,37 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) */ if (cx->creatingException) return JS_FALSE; + + /* After this point the control must flow through the label out. */ cx->creatingException = JS_TRUE; + /* Protect the newly-created strings below from nesting GCs. */ + memset(tv, 0, sizeof tv); + JS_PUSH_TEMP_ROOT(cx, sizeof tv / sizeof tv[0], tv, &tvr); + /* * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. - * - * 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; - } + ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto); + if (!ok) + goto out; + tv[0] = OBJECT_TO_JSVAL(errProto); 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)); + tv[1] = OBJECT_TO_JSVAL(errObject); messageStr = JS_NewStringCopyZ(cx, message); if (!messageStr) { ok = JS_FALSE; goto out; } + tv[2] = STRING_TO_JSVAL(messageStr); if (reportp) { filenameStr = JS_NewStringCopyZ(cx, reportp->filename); @@ -992,6 +1039,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) ok = JS_FALSE; goto out; } + tv[3] = STRING_TO_JSVAL(filenameStr); lineno = reportp->lineno; } else { filenameStr = cx->runtime->emptyString; @@ -1014,10 +1062,13 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) } OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData)); + JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); + /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; out: + JS_POP_TEMP_ROOT(cx, &tvr); cx->creatingException = JS_FALSE; return ok; } @@ -1028,11 +1079,13 @@ out: JSBool js_ReportUncaughtException(JSContext *cx) { + jsval exn, *vp; JSObject *exnObject; + void *mark; + JSErrorReport *reportp, report; JSString *str; - jsval exn; - JSErrorReport *reportp; const char *bytes; + JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; @@ -1042,14 +1095,24 @@ js_ReportUncaughtException(JSContext *cx) /* * Because js_ValueToString below could error and an exception object - * could become unrooted, we must root exnObject. + * could become unrooted, we must root exnObject. Later, if exnObject is + * non-null, we need to root other intermediates, so allocate an operand + * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; + vp = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + mark = NULL; +#endif } else { exnObject = JSVAL_TO_OBJECT(exn); - if (!js_AddRoot(cx, &exnObject, "exn.report.root")) - return JS_FALSE; + vp = js_AllocStack(cx, 5, &mark); + if (!vp) { + ok = JS_FALSE; + goto out; + } + vp[0] = exn; } #if JS_HAS_ERROR_EXCEPTIONS @@ -1058,15 +1121,53 @@ js_ReportUncaughtException(JSContext *cx) reportp = NULL; #endif + /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); - bytes = str ? js_GetStringBytes(str) : "null"; + if (!str) { + bytes = "unknown (can't convert to string)"; + } else { + if (vp) + vp[1] = STRING_TO_JSVAL(str); + bytes = js_GetStringBytes(str); + } + ok = JS_TRUE; - 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. - */ + if (!reportp && + exnObject && + OBJ_GET_CLASS(cx, exnObject) == &ExceptionClass) { + const char *filename; + uint32 lineno; + + ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); + if (!ok) + goto out; + if (JSVAL_IS_STRING(vp[2])) + bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); + + ok = JS_GetProperty(cx, exnObject, js_filename_str, &vp[3]); + if (!ok) + goto out; + str = js_ValueToString(cx, vp[3]); + if (!str) { + ok = JS_FALSE; + goto out; + } + filename = StringToFilename(cx, str); + + ok = JS_GetProperty(cx, exnObject, js_lineno_str, &vp[4]); + if (!ok) + goto out; + ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); + if (!ok) + goto out; + + reportp = &report; + memset(&report, 0, sizeof report); + report.filename = filename; + report.lineno = (uintN) lineno; + } + + if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { @@ -1075,10 +1176,11 @@ js_ReportUncaughtException(JSContext *cx) js_ReportErrorAgain(cx, bytes, reportp); } - if (exnObject != NULL) - js_RemoveRoot(cx->runtime, &exnObject); JS_ClearPendingException(cx); - return JS_TRUE; +out: + if (exnObject) + js_FreeStack(cx, mark); + return ok; } #endif /* JS_HAS_EXCEPTIONS */ diff --git a/src/dom/js/jsfile.c b/src/dom/js/jsfile.c index ef5e93b29..2237dbd47 100644 --- a/src/dom/js/jsfile.c +++ b/src/dom/js/jsfile.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -45,12 +46,7 @@ #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) +#if defined(XP_WIN) || defined(XP_OS2) # include # include # include @@ -118,7 +114,7 @@ #define STDOUTPUT_NAME "Standard output stream" #define STDERROR_NAME "Standard error stream" -#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ +#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ /* Error handling */ typedef enum JSFileErrNum { @@ -135,10 +131,10 @@ typedef enum JSFileErrNum { JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { #if JSFILE_HAS_DFLT_MSG_STRINGS #define MSG_DEF(name, number, count, exception, format) \ - { format, count } , + { format, count }, #else #define MSG_DEF(name, number, count, exception, format) \ - { NULL, count } , + { NULL, count }, #endif #include "jsfile.msg" #undef MSG_DEF @@ -150,65 +146,64 @@ JSFile_GetErrorMessage(void *userRef, const char *locale, { if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) return &JSFile_ErrorFormatString[errorNumber]; - else - return NULL; + 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_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_WRITE \ + if (!file->isOpen) { \ + JS_ReportWarning(cx, \ + "File %s is closed, will open it for writing, proceeding", \ + file->path); \ + js_FileOpen(cx, obj, file, "write,append,create"); \ + } \ + if (!js_canWrite(cx, file)) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_CANNOT_WRITE, file->path); \ + goto out; \ } -#define JSFILE_CHECK_READ \ - if (!file->isOpen){ \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for reading, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "read"); \ - }else \ - if(!js_canRead(cx, file)){ \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_READ, file->path); \ - goto out; \ +#define JSFILE_CHECK_READ \ + if (!file->isOpen) { \ + JS_ReportWarning(cx, \ + "File %s is closed, will open it for reading, proceeding", \ + file->path); \ + js_FileOpen(cx, obj, file, "read"); \ + } \ + if (!js_canRead(cx, file)) { \ + JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ + JSFILEMSG_CANNOT_READ, file->path); \ + goto out; \ } -#define JSFILE_CHECK_OPEN(op) \ - if(!file->isOpen){ \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ - 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_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; \ +#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; \ } @@ -216,6 +211,8 @@ JSFile_GetErrorMessage(void *userRef, const char *locale, Security mechanism, should define a callback for this. The parameters are as follows: SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) + XXX Should this be a real function returning a JSBool result (and getting + some typesafety help from the compiler?). */ #define SECURITY_CHECK(cx, ps, op, file) \ /* Define a callback here... */ @@ -225,14 +222,13 @@ JSFile_GetErrorMessage(void *userRef, const char *locale, 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 + 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 */ @@ -248,26 +244,23 @@ 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 -------------------------- */ +/* 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 + if (!filename) + return JS_FALSE; + + return filename[0] == PIPE_SYMBOL || + filename[strlen(filename) - 1] == PIPE_SYMBOL; } static JSBool js_isAbsolute(const char *name) { #if defined(XP_WIN) || defined(XP_OS2) - return (strlen(name)>1)?((name[1]==':')?JS_TRUE:JS_FALSE):JS_FALSE; + return *name && name[1] == ':'; #else return (name[0] # if defined(XP_UNIX) || defined(XP_BEOS) @@ -275,31 +268,28 @@ js_isAbsolute(const char *name) # else != # endif - FILESEPARATOR)?JS_TRUE:JS_FALSE; + FILESEPARATOR); #endif } /* - Concatinates base and name to produce a valid filename. - Returned string must be freed. + * 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); + char* result = JS_malloc(cx, len + strlen(name) + 2); - if (!result) return NULL; + 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'; + if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { + result[len] = FILESEPARATOR; + result[len + 1] = '\0'; } strcat(result, name); return result; @@ -312,85 +302,96 @@ 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--; + + /* Chop off trailing seperators. */ + 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'; + + /* Now find the next separator. */ + while (index >= 0 && pathname[index] != FILESEPARATOR && + pathname[index] != FILESEPARATOR2) { + --index; + } + + /* Allocate and copy. */ + result = 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. -*/ + * Returns everything but the last component from a path name. + * Returned string must be freed. + */ static char * js_fileDirectoryName(JSContext *cx, const char *pathname) { - jsint index; - char *result; + char *result; + const char *cp, *end; + size_t pathsize; -#if defined(XP_WIN) || defined(XP_OS2) - char drive = '\0'; - const char *oldpathname = pathname; + end = pathname + strlen(pathname); + cp = end - 1; - /* First, get rid of the drive selector */ - if ((strlen(pathname)>=2)&&(pathname[1]==':')) { - drive = pathname[0]; - pathname = &pathname[2]; + /* If this is already a directory, chop off the trailing /s. */ + while (cp >= pathname) { + if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) + break; + --cp; } -#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'; + + if (cp < pathname && end != pathname) { + /* There were just /s, return the root. */ + result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */ + result[0] = FILESEPARATOR; + result[1] = '\0'; + return result; + } + + /* Now chop off the last portion. */ + while (cp >= pathname) { + if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) + break; + --cp; + } + + /* Check if this is a leaf. */ + if (cp < pathname) { + /* It is, return "pathname/". */ + if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { + /* Already has its terminating /. */ + return JS_strdup(cx, pathname); } - /* 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 + pathsize = end - pathname + 1; + result = JS_malloc(cx, pathsize + 1); + if (!result) + return NULL; + + strcpy(result, pathname); + result[pathsize - 1] = FILESEPARATOR; + result[pathsize] = '\0'; + + return result; } + /* Return everything up to and including the seperator. */ + pathsize = cp - pathname + 1; + result = JS_malloc(cx, pathsize + 1); + if (!result) + return NULL; + + strncpy(result, pathname, pathsize); + result[pathsize] = '\0'; + return result; } @@ -401,25 +402,27 @@ js_absolutePath(JSContext *cx, const char * path) JSString *str; jsval prop; - if (js_isAbsolute(path)){ + if (js_isAbsolute(path)) { return JS_strdup(cx, path); - }else{ + } else { obj = JS_GetGlobalObject(cx); if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); + 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); + JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); return JS_strdup(cx, path); } + str = JS_ValueToString(cx, prop); - if (!str ) { + 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); } @@ -438,26 +441,37 @@ js_canonicalPath(JSContext *cx, char *oldpath) /* This is probably optional */ /* Remove possible spaces in the beginning and end */ - while(i=0 && path[j]==' ') j--; + while (i < j && path[i] == ' ') + i++; + while (j >= 0 && path[j] == ' ') + j--; - tmp = JS_malloc(cx, j-i+2); - strncpy(tmp, &path[i], j-i+1); - tmp[j-i+1] = '\0'; + tmp = JS_malloc(cx, j-i+2); + if (!tmp) + return NULL; + + strncpy(tmp, path + i, j - i + 1); + tmp[j - i + 1] = '\0'; path = tmp; - /* pipe support */ - if(js_filenameHasAPipe(path)) - return JS_strdup(cx, path); - /* file:// support */ - if(!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) - return js_canonicalPath(cx, &path[strlen(URL_PREFIX)-1]); + /* Pipe support. */ + if (js_filenameHasAPipe(path)) + return path; - if (!js_isAbsolute(path)) - path = js_absolutePath(cx, path); - else - path = JS_strdup(cx, path); + /* file:// support. */ + if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { + tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); + JS_free(cx, path); + return tmp; + } + + if (!js_isAbsolute(path)) { + tmp = js_absolutePath(cx, path); + if (!tmp) + return NULL; + path = tmp; + } result = JS_strdup(cx, ""); @@ -466,30 +480,23 @@ js_canonicalPath(JSContext *cx, char *oldpath) 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) + if (back > 0) { back--; - else { + } 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; - } + result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1); + if (!result) + goto out; + strcpy(result, base); c = strlen(result); if (*tmp) { result[c] = FILESEPARATOR; - result[c+1] = '\0'; + result[c + 1] = '\0'; strcat(result, tmp); } JS_free(cx, tmp); @@ -504,12 +511,9 @@ js_canonicalPath(JSContext *cx, char *oldpath) 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; - } + if (!result) + goto out; + strcpy(result, dir); c = strlen(result); if (tmp[0]!='\0') { @@ -519,10 +523,16 @@ js_canonicalPath(JSContext *cx, char *oldpath) } strcat(result, tmp); } - JS_free(cx, tmp); - JS_free(cx, dir); - JS_free(cx, base); - JS_free(cx, current); + +out: + if (tmp) + JS_free(cx, tmp); + if (dir) + JS_free(cx, dir); + if (base) + JS_free(cx, base); + if (current) + JS_free(cx, current); return result; } @@ -581,46 +591,47 @@ js_canonicalPath(JSContext *cx, char *oldpath) #define LINE_SEPARATOR 0x2028 #define PARAGRAPH_SEPARATOR 0x2029 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, - unsigned char *tobufendp, uint16 onechar) + unsigned char *tobufendp, + uint16 onechar) { + int16 numUTF8bytes = 0; - int16 numUTF8bytes = 0; - - if((onechar == LINE_SEPARATOR)||(onechar == PARAGRAPH_SEPARATOR)) - { + if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { strcpy((char*)tobufp, "\n"); - return strlen((char*)tobufp);; + 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; - } + if (onechar < 0x80) { + numUTF8bytes = 1; + } else if (onechar < 0x800) { + numUTF8bytes = 2; + } else { + /* 0x800 >= onechar <= MAX_UCS2 */ + numUTF8bytes = 3; + } - tobufp += numUTF8bytes; - - /* return error if we don't have space for the whole character */ - if (tobufp > tobufendp) { - return(-1); - } + tobufp += numUTF8bytes; + /* return error if we don't have space for the whole character */ + if (tobufp > tobufendp) { + return(-1); + } - switch(numUTF8bytes) { + 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 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 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | TWO_OCTET_BASE; - break; - case 1: *--tobufp = (unsigned char)onechar; break; - } + case 1: *--tobufp = (unsigned char)onechar; + break; + } - return(numUTF8bytes); + return numUTF8bytes; } /* @@ -707,7 +718,7 @@ js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) char *options = JS_strdup(cx, oldoptions); int32 found = 0; - current = options; + current = options; for (;;) { comma = strchr(current, ','); if (comma) *comma = '\0'; @@ -735,22 +746,22 @@ 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; +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->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ + file->hasAutoflush = JS_FALSE; file->isNative = JS_FALSE; file->isPipe = JS_FALSE; - js_ResetBuffers(file); + js_ResetBuffers(file); } static JSBool @@ -764,9 +775,8 @@ js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ v[0] = STRING_TO_JSVAL(mask); v[1] = STRING_TO_JSVAL(type); - if (!file_open(cx, obj, 2, v, &rval)) { + if (!file_open(cx, obj, 2, v, &rval)) return JS_FALSE; - } return JS_TRUE; } @@ -787,18 +797,18 @@ js_BufferedRead(JSFile * f, char *buf, int32 len) } if (len>0) { - count+= (!f->isNative)? - PR_Read(f->handle, buf, len): - fread(buf, 1, len, f->nativehandle); + 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) +js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) { - unsigned char*aux; - int32 count, i; + unsigned char *aux; + int32 count = 0, i; jsint remainder; unsigned char utfbuf[3]; @@ -810,22 +820,24 @@ js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode) } switch (mode) { - case ASCII: + case ASCII: aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) { - return 0; - } + if (!aux) + return 0; + count = js_BufferedRead(file, aux, len); - if (count==-1) { - JS_free(cx, aux); - return 0; - } - for (i = 0;i0) { @@ -857,17 +868,22 @@ js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode) remainder--; } break; - case UCS2: - count = js_BufferedRead(file, (char*)buf, len*2)>>1; - if (count==-1) { + + case UCS2: + count = js_BufferedRead(file, (char*)buf, len*2) >> 1; + if (count == -1) return 0; - } + break; + + default: + /* Not reached. */ + JS_ASSERT(0); } - if(count==-1){ + if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "read", file->path); + JSFILEMSG_OP_FAILED, "read", file->path); } return count; @@ -876,16 +892,17 @@ js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode) static int32 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) { - int32 count, i; + int32 count = 0, i; jsint remainder; unsigned char utfbuf[3]; jschar tmp; switch (mode) { - case ASCII: + case ASCII: count = PR_Seek(file->handle, len, PR_SEEK_CUR); break; - case UTF8: + + case UTF8: remainder = 0; for (count = 0;count0) { @@ -917,15 +932,20 @@ js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) utfbuf[1] = utfbuf[2]; remainder--; } - break; - case UCS2: + break; + + case UCS2: count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; break; + + default: + /* Not reached. */ + JS_ASSERT(0); } - if(count==-1){ + if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "seek", file->path); + JSFILEMSG_OP_FAILED, "seek", file->path); } return count; @@ -935,29 +955,31 @@ static int32 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) { unsigned char *aux; - int32 count, i, j; + int32 count = 0, i, j; unsigned char *utfbuf; switch (mode) { - case ASCII: + case ASCII: aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) return 0; + if (!aux) + return 0; - for (i = 0; iisNative)? - PR_Write(file->handle, aux, len): - fwrite(aux, 1, len, file->nativehandle); + count = (!file->isNative) + ? 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: + + case UTF8: utfbuf = (unsigned char*)JS_malloc(cx, len*3); if (!utfbuf) return 0; i = 0; @@ -969,30 +991,36 @@ js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) } i+=j; } - j = (!file->isNative)? - PR_Write(file->handle, utfbuf, i): - fwrite(utfbuf, 1, i, file->nativehandle); + j = (!file->isNative) + ? 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; + break; - if (count==-1) { + case UCS2: + count = (!file->isNative) + ? PR_Write(file->handle, buf, len*2) >> 1 + : fwrite(buf, 1, len*2, file->nativehandle) >> 1; + + if (count == -1) return 0; - } break; + + default: + /* Not reached. */ + JS_ASSERT(0); } - if(count==-1){ + + if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "write", file->path); + JSFILEMSG_OP_FAILED, "write", file->path); } + return count; } @@ -1000,65 +1028,68 @@ js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) 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 */ + if (file->isNative) { + /* It doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } + + return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; } static JSBool js_canRead(JSContext *cx, JSFile *file) { - if(!file->isNative){ - if(file->isOpen&&!(file->mode&PR_RDONLY)) return JS_FALSE; - return (PR_Access(file->path, PR_ACCESS_READ_OK)==PR_SUCCESS); - }else{ - if(file->isPipe){ - /* pipe open for reading */ - return file->path[0]==PIPE_SYMBOL; - }else{ - return !strcmp(file->path, STDINPUT_NAME); - } + if (!file->isNative) { + if (file->isOpen && !(file->mode & PR_RDONLY)) + return JS_FALSE; + return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; + } + + if (file->isPipe) { + /* Is this pipe open for reading? */ + return file->path[0] == PIPE_SYMBOL; } + + return !strcmp(file->path, STDINPUT_NAME); } static JSBool js_canWrite(JSContext *cx, JSFile *file) { - if(!file->isNative){ - if(file->isOpen&&!(file->mode&PR_WRONLY)) return JS_FALSE; - return (PR_Access(file->path, PR_ACCESS_WRITE_OK)==PR_SUCCESS); - }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); - } + if (!file->isNative) { + if (file->isOpen && !(file->mode & PR_WRONLY)) + return JS_FALSE; + return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; } + + if(file->isPipe) { + /* Is this pipe open for writing? */ + return file->path[strlen(file->path)-1] == PIPE_SYMBOL; + } + + return !strcmp(file->path, STDOUTPUT_NAME) || + !strcmp(file->path, STDERROR_NAME); } static JSBool js_isFile(JSContext *cx, JSFile *file) { - if(!file->isNative){ + if (!file->isNative) { PRFileInfo info; - if ((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){ + 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); + 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; + } + + return info.type == PR_FILE_FILE; } + + /* This doesn't make sense for a pipe of stdstream. */ + return JS_FALSE; } static JSBool @@ -1067,21 +1098,23 @@ 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; + /* 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){ + 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); + 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; + } + + return info.type == PR_FILE_DIRECTORY; } + + /* This doesn't make sense for a pipe of stdstream. */ + return JS_FALSE; } static jsval @@ -1091,44 +1124,77 @@ js_size(JSContext *cx, JSFile *file) JSFILE_CHECK_NATIVE("size"); - if ((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){ + 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); + JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); + return JS_FALSE; + } + + return INT_TO_JSVAL(info.size); + out: return JSVAL_VOID; } -/* Return the parent object */ -static jsval -js_parent(JSContext *cx, JSFile *file) +/* + * Return the parent object + */ +static JSBool +js_parent(JSContext *cx, JSFile *file, jsval *resultp) { char *str; - /* since we only care about pipes and native files, return NULL */ - if(file->isNative) return JSVAL_VOID; + /* Since we only care about pipes and native files, return NULL. */ + if (file->isNative) { + *resultp = JSVAL_VOID; + return JS_TRUE; + } str = js_fileDirectoryName(cx, file->path); - /* 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); + if (!str) + return JS_FALSE; + + /* If the directory is equal to the original path, we're at the root. */ + if (!strcmp(file->path, str)) { + *resultp = JSVAL_NULL; + } else { + JSObject *obj = js_NewFileObject(cx, str); + if (!obj) { + JS_free(cx, str); + return JS_FALSE; + } + *resultp = OBJECT_TO_JSVAL(obj); } + + JS_free(cx, str); + return JS_TRUE; } -static jsval -js_name(JSContext *cx, JSFile *file){ - return file->isPipe? - JSVAL_VOID: - STRING_TO_JSVAL(JS_NewStringCopyZ(cx, js_fileBaseName(cx, file->path))); +static JSBool +js_name(JSContext *cx, JSFile *file, jsval *vp) +{ + char *name; + JSString *str; + + if (file->isPipe) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + name = js_fileBaseName(cx, file->path); + if (!name) + return JS_FALSE; + + str = JS_NewString(cx, name, strlen(name)); + if (!str) { + JS_free(cx, name); + return JS_FALSE; + } + + *vp = STRING_TO_JSVAL(str); + return JS_TRUE; } /* ------------------------------ File object methods ---------------------------- */ @@ -1141,26 +1207,28 @@ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) int32 mask, type; int len; + mode = NULL; + SECURITY_CHECK(cx, NULL, "open", file); /* A native file that is already open */ - if(file->isOpen && file->isNative){ + if(file->isOpen && file->isNative) { JS_ReportWarning(cx, "Native file %s is already open, proceeding", - file->path); + 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; + 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)){ + 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); + "trying to open it, proceeding", file->path); goto good; } @@ -1168,23 +1236,23 @@ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) len = strlen(file->path); /* Mode */ - if (argc>=1){ + if (argc >= 1) { strmode = JS_ValueToString(cx, argv[0]); - if (!strmode){ + if (!strmode) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[0]); + 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){ + } else { + if(file->path[0]==PIPE_SYMBOL) { /* pipe default mode */ mode = JS_strdup(cx, "read"); - }else - if(file->path[len-1]==PIPE_SYMBOL){ + } else if(file->path[len-1]==PIPE_SYMBOL) { /* pipe default mode */ mode = JS_strdup(cx, "write"); - }else{ + } else { /* non-destructive, permissive defaults. */ mode = JS_strdup(cx, "readWrite,append,create"); } @@ -1192,42 +1260,44 @@ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* 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; + /* TODO: this is pretty ugly, we walk thru the string too many times */ + mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; + mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; + mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; + mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; + mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; + mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; - if((mask&PR_RDWR)) mask|=(PR_RDONLY|PR_WRONLY); - if((mask&PR_RDONLY)&&(mask&PR_WRONLY)) mask|=PR_RDWR; + 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")); + file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); /* Type */ - if (argc>1) { + 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]); + JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, + argv[1]); goto out; } ctype = JS_GetStringBytes(strtype); - if(!strcmp(ctype, utfstring)) + if(!strcmp(ctype, utfstring)) { type = UTF8; - else - if (!strcmp(ctype, unicodestring)) + } else if (!strcmp(ctype, unicodestring)) { type = UCS2; - else{ - if(strcmp(ctype, asciistring)){ + } else { + if (strcmp(ctype, asciistring)) { JS_ReportWarning(cx, "File type %s is not supported, using " - "'text' instead, proceeding", ctype); + "'text' instead, proceeding", ctype); } type = ASCII; } - }else{ + } else { type = ASCII; } @@ -1235,52 +1305,56 @@ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) file->type = type; file->mode = mask; file->nativehandle = NULL; - file->hasRandomAccess = (type!=UTF8); + 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){ + * 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{ + JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); + goto out; + } else { + int i = 0; char pipemode[3]; SECURITY_CHECK(cx, NULL, "pipe_open", file); if(file->path[0] == PIPE_SYMBOL){ if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, - mode, file->path); + 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'; + pipemode[i++] = 'r'; +#ifndef XP_UNIX + pipemode[i++] = file->type==UTF8 ? 'b' : 't'; +#endif + pipemode[i++] = '\0'; file->nativehandle = POPEN(&file->path[1], pipemode); - }else - if(file->path[len-1] == PIPE_SYMBOL){ + } 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'; + pipemode[i++] = 'w'; +#ifndef XP_UNIX + pipemode[i++] = file->type==UTF8 ? 'b' : 't'; +#endif + pipemode[i++] = '\0'; file->nativehandle = POPEN(command, pipemode); - JS_free(cx, command); + JS_free(cx, command); } /* set the flags */ file->isNative = JS_TRUE; file->isPipe = JS_TRUE; file->hasRandomAccess = JS_FALSE; } - }else{ + } else { /* TODO: what about the permissions?? Java ignores the problem... */ file->handle = PR_Open(file->path, mask, 0644); } @@ -1290,21 +1364,22 @@ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) mode = NULL; /* Set the open flag and return result */ - if (file->handle==NULL && file->nativehandle==NULL){ - file->isOpen = JS_FALSE; + if (file->handle == NULL && file->nativehandle == NULL) { + file->isOpen = JS_FALSE; JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); + 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; + if(mode) + JS_free(cx, mode); return JS_FALSE; } @@ -1344,8 +1419,8 @@ file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) js_ResetAttributes(file); *rval = JSVAL_TRUE; return JS_TRUE; + out: - *rval = JSVAL_FALSE; return JS_FALSE; } @@ -1660,71 +1735,83 @@ 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; + jschar *buf = NULL, *tmp; + int32 offset, read; 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); + buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data); + if (!buf) + return JS_FALSE; + + room = MAX_LINE_LENGTH - 1; offset = 0; - /* XXX TEST ME!! TODO: yes, please do */ - for(;;) { - if (!js_FileRead(cx, file, &data, 1, file->type)) { - endofline = JS_FALSE; - goto loop; - } + for (;;) { + read = js_FileRead(cx, file, &data, 1, file->type); + if (read < 0) + goto out; + if (read == 0) + goto eof; + 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. */ + case '\r': + read = js_FileRead(cx, file, &data2, 1, file->type); + if (read < 0) + goto out; + + if (read == 1 && data2 != '\n') { + /* We read one char too far. Buffer it. */ file->charBuffer = data2; file->charBufferUsed = JS_TRUE; } - endofline = JS_TRUE; - goto loop; - default: + + /* Fall through. */ + case '\n': + goto done; + + 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; + tmp = JS_realloc(cx, buf, + (offset + MAX_LINE_LENGTH) * sizeof data); + if (!tmp) + goto out; + + room = MAX_LINE_LENGTH - 1; + buf = tmp; } - file->linebuffer->chars[offset++] = data; + + buf[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); + +eof: + if (offset == 0) { + *rval = JSVAL_NULL; return JS_TRUE; - }else{ - goto out; } + +done: + buf[offset] = 0; + tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data); + if (!tmp) + goto out; + + str = JS_NewUCString(cx, tmp, offset); + if (!str) + goto out; + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + out: - *rval = JSVAL_NULL; + if (buf) + JS_free(cx, buf); + return JS_FALSE; } @@ -1735,23 +1822,26 @@ file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *array; jsint len; jsval line; + JSBool lineok = JS_FALSE; SECURITY_CHECK(cx, NULL, "readAll", file); JSFILE_CHECK_READ; array = JS_NewArrayObject(cx, 0, NULL); + if (!array) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(array); + len = 0; - while(file_readln(cx, obj, 0, NULL, &line)){ - JS_SetElement(cx, array, len, &line); - len++; + lineok = file_readln(cx, obj, 0, NULL, &line); + while (lineok && !JSVAL_IS_NULL(line)) { + JS_SetElement(cx, array, len++, &line); + lineok = file_readln(cx, obj, 0, NULL, &line); } - *rval = OBJECT_TO_JSVAL(array); - return JS_TRUE; out: - *rval = JSVAL_FALSE; - return JS_FALSE; + return lineok; } static JSBool @@ -1976,18 +2066,18 @@ 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){ + 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); + if (file->path) + JS_free(cx, file->path); - JS_free(cx, file); - } + JS_free(cx, file); + } } /* @@ -1996,10 +2086,11 @@ file_finalize(JSContext *cx, JSObject *obj) static JSFile* file_init(JSContext *cx, JSObject *obj, char *bytes) { - JSFile *file; + JSFile *file; file = JS_malloc(cx, sizeof *file); - if (!file) return NULL; + if (!file) + return NULL; memset(file, 0 , sizeof *file); js_ResetAttributes(file); @@ -2008,11 +2099,12 @@ file_init(JSContext *cx, JSObject *obj, char *bytes) if (!JS_SetPrivate(cx, obj, file)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); + JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); JS_free(cx, file); return NULL; - }else - return file; + } + + return file; } /* Returns a JSObject. This function is globally visible */ @@ -2040,9 +2132,6 @@ js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, { 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){ @@ -2073,28 +2162,37 @@ js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, */ static JSBool file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { JSString *str; JSFile *file; - str = (argc==0)?JS_InternString(cx, ""):JS_ValueToString(cx, argv[0]); + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* Replace obj with a new File object. */ + obj = JS_NewObject(cx, &file_class, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + str = (argc == 0) + ? JS_InternString(cx, "") + : JS_ValueToString(cx, argv[0]); - if (!str){ + if (!str) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, argv[0]); - goto out; + JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, + argv[0]); + return JS_FALSE; } file = file_init(cx, obj, JS_GetStringBytes(str)); - if (!file) goto out; + if (!file) + return JS_FALSE; SECURITY_CHECK(cx, NULL, "constructor", file); return JS_TRUE; -out: - *rval = JSVAL_VOID; - return JS_FALSE; } /* -------------------- File methods and properties ------------------------- */ @@ -2114,8 +2212,8 @@ static JSFunctionSpec file_functions[] = { { "writeAll", file_writeAll, 0}, { "list", file_list, 0}, { "mkdir", file_mkdir, 0}, - { "toString", file_toString, 0}, - { "toURL", file_toURL, 0}, + { "toString", file_toString, 0}, + { "toURL", file_toURL, 0}, {0} }; @@ -2173,26 +2271,32 @@ static JSBool file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - char *str; + char *bytes; + JSString *str; jsint tiny; PRFileInfo info; - JSBool flag; - PRExplodedTime - expandedTime; + JSBool flag; + PRExplodedTime expandedTime; tiny = JSVAL_TO_INT(id); - if(!file) return JS_TRUE; + if (!file) + return JS_TRUE; switch (tiny) { case FILE_PARENT: SECURITY_CHECK(cx, NULL, "parent", file); - *vp = js_parent(cx, file); + if (!js_parent(cx, file, vp)) + return JS_FALSE; break; case FILE_PATH: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path)); + str = JS_NewStringCopyZ(cx, file->path); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); break; case FILE_NAME: - *vp = js_name(cx, file); + if (!js_name(cx, file, vp)) + return JS_FALSE; break; case FILE_ISDIR: SECURITY_CHECK(cx, NULL, "isDirectory", file); @@ -2265,47 +2369,47 @@ file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) case FILE_MODE: SECURITY_CHECK(cx, NULL, "mode", file); JSFILE_CHECK_OPEN("mode"); - str = (char*)JS_malloc(cx, MODE_SIZE); - str[0] = '\0'; + bytes = JS_malloc(cx, MODE_SIZE); + bytes[0] = '\0'; flag = JS_FALSE; if ((file->mode&PR_RDONLY)==PR_RDONLY) { - if (flag) strcat(str, ","); - strcat(str, "read"); + if (flag) strcat(bytes, ","); + strcat(bytes, "read"); flag = JS_TRUE; } if ((file->mode&PR_WRONLY)==PR_WRONLY) { - if (flag) strcat(str, ","); - strcat(str, "write"); + if (flag) strcat(bytes, ","); + strcat(bytes, "write"); flag = JS_TRUE; } if ((file->mode&PR_RDWR)==PR_RDWR) { - if (flag) strcat(str, ","); - strcat(str, "readWrite"); + if (flag) strcat(bytes, ","); + strcat(bytes, "readWrite"); flag = JS_TRUE; } if ((file->mode&PR_APPEND)==PR_APPEND) { - if (flag) strcat(str, ","); - strcat(str, "append"); + if (flag) strcat(bytes, ","); + strcat(bytes, "append"); flag = JS_TRUE; } if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { - if (flag) strcat(str, ","); - strcat(str, "create"); + if (flag) strcat(bytes, ","); + strcat(bytes, "create"); flag = JS_TRUE; } if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { - if (flag) strcat(str, ","); - strcat(str, "replace"); + if (flag) strcat(bytes, ","); + strcat(bytes, "replace"); flag = JS_TRUE; } if (file->hasAutoflush) { - if (flag) strcat(str, ","); - strcat(str, "hasAutoFlush"); + if (flag) strcat(bytes, ","); + strcat(bytes, "hasAutoFlush"); flag = JS_TRUE; } - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); - JS_free(cx, str); + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); + JS_free(cx, bytes); break; case FILE_CREATED: SECURITY_CHECK(cx, NULL, "creationTime", file); @@ -2415,33 +2519,40 @@ file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 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)); + + /* this is some other property -- try to use the dir["file"] syntax */ + if (js_isDirectory(cx, file)) { + PRDir *dir = NULL; + PRDirEntry *entry = NULL; + char *prop_name; + + str = JS_ValueToString(cx, id); + if (!str) + return JS_FALSE; + + prop_name = JS_GetStringBytes(str); /* no native files past this point */ dir = PR_OpenDir(file->path); if(!dir) { /* This is probably not a directory */ - JS_ReportWarning(cx, "Can't open directory %s", file->path); + 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); + while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { + if (!strcmp(entry->name, prop_name)){ + bytes = js_combinePath(cx, file->path, prop_name); + *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); + JS_free(cx, bytes); return JS_TRUE; - } - } - } + } + } + } } return JS_TRUE; + out: - *vp = JSVAL_VOID; return JS_FALSE; } @@ -2497,8 +2608,7 @@ file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; out: - *vp = JSVAL_VOID; - return JS_FALSE; + return JS_FALSE; } /* @@ -2507,52 +2617,54 @@ out: 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); + JSFile *file; + + file = JS_GetInstancePrivate(cx, obj, &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)){ + if (JSVAL_IS_OBJECT(*vp)) { + if (JS_InstanceOf(cx, obj, &file_class, NULL)) { /* Braindamaged rhs -- just return the old value */ - if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))){ + 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); + return JS_FALSE; + } else { chdir(file->path); return JS_TRUE; } - }else - goto out; - }else{ + } else { + return JS_FALSE; + } + } else { + JSObject *rhsObject; + char *path; + path = JS_GetStringBytes(JS_ValueToString(cx, *vp)); rhsObject = js_NewFileObject(cx, path); - if (!rhsObject) goto out; + if (!rhsObject) + return JS_FALSE; if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - }else{ + } 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 + 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) +js_InitFileClass(JSContext *cx, JSObject* obj) { JSObject *file, *ctor, *afile; jsval vp; @@ -2582,23 +2694,21 @@ js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams) 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 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 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); - /* 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)); diff --git a/src/dom/js/jsfile.h b/src/dom/js/jsfile.h index 47a8692d8..741c5bdd0 100644 --- a/src/dom/js/jsfile.h +++ b/src/dom/js/jsfile.h @@ -42,7 +42,7 @@ #if JS_HAS_FILE_OBJECT extern JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams); +js_InitFileClass(JSContext *cx, JSObject* obj); extern JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *bytes); diff --git a/src/dom/js/jsfile.msg b/src/dom/js/jsfile.msg index f03c51d42..137b35d87 100644 --- a/src/dom/js/jsfile.msg +++ b/src/dom/js/jsfile.msg @@ -55,6 +55,7 @@ MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_N MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") +MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") diff --git a/src/dom/js/jsfun.c b/src/dom/js/jsfun.c index bbb1e1b61..68372e129 100644 --- a/src/dom/js/jsfun.c +++ b/src/dom/js/jsfun.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -96,7 +97,8 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); return OBJ_GET_PROPERTY(cx, fp->callobj, - (jsid) cx->runtime->atomState.argumentsAtom, + ATOM_TO_JSID(cx->runtime->atomState + .argumentsAtom), vp); } argsobj = js_GetArgsObject(cx, fp); @@ -106,9 +108,6 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) 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) { @@ -119,7 +118,7 @@ MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) argsobj = fp->argsobj; (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - nbits = MAXARGS(fp); + nbits = fp->argc; JS_ASSERT(slot < nbits); if (JSVAL_IS_VOID(bmapval)) { if (nbits <= JSVAL_INT_BITS) { @@ -162,7 +161,7 @@ ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); if (JSVAL_IS_VOID(bmapval)) return JS_FALSE; - if (MAXARGS(fp) <= JSVAL_INT_BITS) { + if (fp->argc <= JSVAL_INT_BITS) { bmapint = JSVAL_TO_INT(bmapval); bitmap = (jsbitmap *) &bmapint; } else { @@ -182,7 +181,8 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); if (!OBJ_GET_PROPERTY(cx, fp->callobj, - (jsid) cx->runtime->atomState.argumentsAtom, + ATOM_TO_JSID(cx->runtime->atomState + .argumentsAtom), &val)) { return JS_FALSE; } @@ -199,15 +199,30 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, *objp = NULL; *vp = JSVAL_VOID; - if (JSVAL_IS_INT(id)) { - slot = (uintN) JSVAL_TO_INT(id); - if (slot < MAXARGS(fp)) { + if (JSID_IS_INT(id)) { + slot = (uintN) JSID_TO_INT(id); + if (slot < fp->argc) { if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = fp->argv[slot]; + } else { + /* + * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share + * storage between the formal parameter and arguments[k] for all + * k >= fp->argc && k < fp->fun->nargs. For example, in + * + * function f(x) { x = 42; return arguments[0]; } + * f(); + * + * the call to f should return undefined, not 42. If fp->argsobj + * is null at this point, as it would be in the example, return + * undefined in *vp. + */ + if (fp->argsobj) + return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); } } else { - if (id == (jsid) cx->runtime->atomState.lengthAtom) { + if (id == ATOM_TO_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); @@ -221,6 +236,10 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp) { JSObject *argsobj; + /* Skip eval and debugger frames. */ + while (fp->flags & JSFRAME_SPECIAL) + fp = fp->down; + /* Create an arguments object for fp only if it lacks one. */ argsobj = fp->argsobj; if (argsobj) @@ -262,7 +281,7 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp) (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) + if (fp->argc > JSVAL_INT_BITS) JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); } @@ -271,10 +290,14 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp) * 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); + ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), + &rval); + ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), + &rval); + ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), + &rval); + ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), + &rval); /* * Clear the private pointer to fp, which is about to go away (js_Invoke). @@ -308,7 +331,7 @@ args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; default: - if ((uintN)slot < MAXARGS(fp) && !MarkArgDeleted(cx, fp, slot)) + if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) return JS_FALSE; break; } @@ -342,7 +365,7 @@ args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; default: - if ((uintN)slot < MAXARGS(fp) && !ArgWasDeleted(cx, fp, slot)) + if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) *vp = fp->argv[slot]; break; } @@ -371,8 +394,11 @@ args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; default: - if ((uintN)slot < MAXARGS(fp) && !ArgWasDeleted(cx, fp, slot)) + if (fp->fun->interpreted && + (uintN)slot < fp->argc && + !ArgWasDeleted(cx, fp, slot)) { fp->argv[slot] = *vp; + } break; } return JS_TRUE; @@ -398,11 +424,12 @@ args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, if (JSVAL_IS_INT(id)) { slot = JSVAL_TO_INT(id); - if (slot < MAXARGS(fp) && !ArgWasDeleted(cx, fp, slot)) { + if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { /* XXX ECMA specs DontEnum, contrary to other array-like objects */ - if (!js_DefineProperty(cx, obj, (jsid) id, fp->argv[slot], + if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), + fp->argv[slot], args_getProperty, args_setProperty, - JSVERSION_IS_ECMA(cx->version) + JS_VERSION_IS_ECMA(cx) ? 0 : JSPROP_ENUMERATE, NULL)) { @@ -432,7 +459,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, } if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { - if (!js_DefineNativeProperty(cx, obj, (jsid) atom, value, + if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, args_getProperty, args_setProperty, 0, SPROP_HAS_SHORTID, tinyid, NULL)) { return JS_FALSE; @@ -450,7 +477,7 @@ args_enumerate(JSContext *cx, JSObject *obj) JSStackFrame *fp; JSObject *pobj; JSProperty *prop; - uintN slot, nargs; + uintN slot, argc; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); @@ -465,26 +492,26 @@ args_enumerate(JSContext *cx, JSObject *obj) * 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, + if (!js_LookupProperty(cx, obj, + ATOM_TO_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, + if (!js_LookupProperty(cx, obj, + ATOM_TO_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)) { + argc = fp->argc; + for (slot = 0; slot < argc; slot++) { + if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) return JS_FALSE; - } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } @@ -573,7 +600,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) * Get the arguments object to snapshot fp's actual argument values. */ if (fp->argsobj) { - argsid = (jsid) cx->runtime->atomState.argumentsAtom; + argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); ok &= js_GetProperty(cx, callobj, argsid, &aval); ok &= js_SetProperty(cx, callobj, argsid, &aval); ok &= js_PutArgsObject(cx, fp); @@ -695,11 +722,12 @@ static JSBool call_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; - JSObject *funobj; + JSObject *funobj, *pobj; JSScope *scope; JSScopeProperty *sprop, *cprop; JSPropertyOp getter; jsval *vec; + JSAtom *atom; JSProperty *prop; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); @@ -737,12 +765,28 @@ call_enumerate(JSContext *cx, JSObject *obj) else continue; - /* Trigger reflection in call_resolve by doing a lookup. */ - if (!js_LookupProperty(cx, obj, sprop->id, &obj, &prop)) + /* Trigger reflection by looking up the unhidden atom for sprop->id. */ + JS_ASSERT(JSID_IS_ATOM(sprop->id)); + atom = JSID_TO_ATOM(sprop->id); + JS_ASSERT(atom->flags & ATOM_HIDDEN); + atom = atom->entry.value; + + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; - JS_ASSERT(obj && prop); + + /* + * If we found the property in a different object, don't try sticking + * it into wrong slots vector. This can occur because we have a mutable + * __proto__ slot, and cloned function objects rely on their __proto__ + * to delegate to the object that contains the var and arg properties. + */ + if (!prop || pobj != obj) { + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + continue; + } cprop = (JSScopeProperty *)prop; - LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[sprop->shortid]); + LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); OBJ_DROP_PROPERTY(cx, obj, prop); } @@ -760,7 +804,6 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; - jsid propid; JSPropertyOp getter, setter; uintN attrs, slot, nslots, spflags; jsval *vp, value; @@ -777,27 +820,37 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; if (!funobj) return JS_TRUE; + JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun); 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)) + if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; - sprop = (JSScopeProperty *) prop; - if (sprop && OBJ_IS_NATIVE(obj2)) { - propid = sprop->id; + if (prop) { + if (!OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + sprop = (JSScopeProperty *) prop; 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) { + + /* Ensure we found an arg or var property for the same function. */ + if ((sprop->flags & SPROP_IS_HIDDEN) && + (obj2 == funobj || + (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) { if (getter == js_GetArgument) { vp = fp->argv; nslots = JS_MAX(fp->argc, fp->fun->nargs); getter = setter = NULL; } else { + JS_ASSERT(getter == js_GetLocalVariable); vp = fp->vars; nslots = fp->nvars; getter = js_GetCallVariable; @@ -812,7 +865,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, spflags = 0; shortid = 0; } - if (!js_DefineNativeProperty(cx, obj, propid, value, + if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, spflags, shortid, NULL)) { return JS_FALSE; @@ -820,6 +873,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, *objp = obj; } } + return JS_TRUE; } @@ -850,15 +904,25 @@ JSClass js_CallClass = { #endif /* JS_HAS_CALL_OBJECT */ -/* SHARED because fun_getProperty always computes a new value. */ -#define FUNCTION_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) +/* + * ECMA-262 specifies that length is a property of function object instances, + * but we can avoid that space cost by delegating to a prototype property that + * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes + * a fresh length value based on the arity of the individual function object's + * private data. + * + * The extensions below other than length, i.e., the ones not in ECMA-262, + * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility + * with ECMA we must allow a delegating object to override them. + */ +#define LENGTH_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}, + {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, + {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, + {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, + {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, + {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, {0,0,0,0,0} }; @@ -873,10 +937,34 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 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; + /* + * Loop because getter and setter can be delegated from another class, + * but loop only for ARGS_LENGTH because we must pretend that f.length + * is in each function instance f, per ECMA-262, instead of only in the + * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED + * to make it appear so). + * + * This code couples tightly to the attributes for the function_props[] + * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper. + * + * It's important to allow delegating objects, even though they inherit + * this getter (fun_getProperty), to override arguments, arity, caller, + * and name. If we didn't return early for slot != ARGS_LENGTH, we would + * clobber *vp with the native property value, instead of letting script + * override that value in delegating objects. + * + * Note how that clobbering is what simulates JSPROP_READONLY for all of + * the non-standard properties when the directly addressed object (obj) + * is a function object (i.e., when this loop does not iterate). + */ + while (!(fun = (JSFunction *) + JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { + if (slot != ARGS_LENGTH) + return JS_TRUE; + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) + return JS_TRUE; + } /* Find fun's top-most activation record. */ for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); @@ -908,7 +996,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) #endif /* !JS_HAS_ARGS_OBJECT */ case ARGS_LENGTH: - if (!JSVERSION_IS_ECMA(cx->version)) + if (!JS_VERSION_IS_ECMA(cx)) *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); else case FUN_ARITY: @@ -945,6 +1033,21 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } +static JSBool +fun_enumerate(JSContext *cx, JSObject *obj) +{ + jsid prototypeId; + JSObject *pobj; + JSProperty *prop; + + prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); + if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) + return JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + return JS_TRUE; +} + static JSBool fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) @@ -981,15 +1084,24 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * 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)) + if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), + &pval)) { return JS_FALSE; - if (JSVAL_IS_OBJECT(pval)) + } + if (!JSVAL_IS_PRIMITIVE(pval)) { + /* + * We are about to allocate a new object, so hack the newborn + * root until then to protect pval in case it is figuratively + * up in the air, with no strong refs protecting it. + */ + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(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. + * to find a prototype for that will recur back here _ad perniciem_. */ if (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom) return JS_TRUE; @@ -1048,9 +1160,10 @@ fun_finalize(JSContext *cx, JSObject *obj) JS_ATOMIC_DECREMENT(&fun->nrefs); if (fun->nrefs) return; - if (fun->interpreted) + + /* Null-check required since the parser sets interpreted very early. */ + if (fun->interpreted && fun->u.script) js_DestroyScript(cx, fun->u.script); - JS_free(cx, fun); } #if JS_HAS_XDR @@ -1070,6 +1183,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JSContext *cx; JSFunction *fun; JSString *atomstr; + JSTempValueRooter tvr; uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ char *propname; JSScopeProperty *sprop; @@ -1077,6 +1191,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JSAtom *atom; uintN i, n, dupflag; uint32 type; + JSBool ok; #ifdef DEBUG uintN nvars = 0, nargs = 0; #endif @@ -1106,12 +1221,16 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) atomstr = NULL; } + /* From here on, control flow must flow through label out. */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(fun->object), &tvr); + ok = JS_TRUE; + 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; + goto bad; } /* do arguments and local vars */ @@ -1131,7 +1250,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) n * sizeof(JSScopeProperty *)); if (!spvec) { JS_ReportOutOfMemory(cx); - return JS_FALSE; + goto bad; } } scope = OBJ_SCOPE(fun->object); @@ -1155,14 +1274,14 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) : 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)); + propname = JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(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; + goto bad; } } if (mark) @@ -1171,12 +1290,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JSPropertyOp getter, setter; for (i = n; i != 0; i--) { - uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT; + uintN attrs = JSPROP_PERMANENT; if (!JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || !JS_XDRCString(xdr, &propname)) { - return JS_FALSE; + goto bad; } JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || type == JSXDR_FUNCONST); @@ -1197,26 +1316,27 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) atom = js_Atomize(cx, propname, strlen(propname), 0); JS_free(cx, propname); if (!atom) - return JS_FALSE; + goto bad; /* Flag duplicate argument if atom is bound in fun->object. */ - dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), (jsid)atom) + dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), + ATOM_TO_JSID(atom)) ? SPROP_IS_DUPLICATE : 0; - if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), getter, setter, SPROP_INVALID_SLOT, attrs | JSPROP_SHARED, - SPROP_HAS_SHORTID | dupflag, + dupflag | SPROP_HAS_SHORTID, JSVAL_TO_INT(userid))) { - return JS_FALSE; + goto bad; } } } } if (!js_XDRScript(xdr, &fun->u.script, NULL)) - return JS_FALSE; + goto bad; if (xdr->mode == JSXDR_DECODE) { fun->interpreted = JS_TRUE; @@ -1228,13 +1348,19 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) /* XXX only if this was a top-level function! */ fun->atom = js_AtomizeString(cx, atomstr, 0); if (!fun->atom) - return JS_FALSE; + goto bad; } js_CallNewScriptHook(cx, fun->u.script, fun); } - return JS_TRUE; +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; + +bad: + ok = JS_FALSE; + goto out; } #else /* !JS_HAS_XDR */ @@ -1257,7 +1383,8 @@ fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) JSString *str; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &pval)) { return JS_FALSE; } @@ -1291,9 +1418,10 @@ fun_mark(JSContext *cx, JSObject *obj, void *arg) fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) { + JS_MarkGCThing(cx, fun, js_private_str, arg); if (fun->atom) GC_MARK_ATOM(cx, fun->atom, arg); - if (fun->interpreted) + if (fun->interpreted && fun->u.script) js_MarkScript(cx, fun->u.script, arg); } return 0; @@ -1313,12 +1441,12 @@ fun_reserveSlots(JSContext *cx, JSObject *obj) * 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_FRIEND_DATA(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_enumerate, (JSResolveOp)fun_resolve, fun_convert, fun_finalize, NULL, NULL, NULL, NULL, @@ -1349,6 +1477,7 @@ js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, &fval)) { return JS_FALSE; } + argv[-1] = fval; } if (!JSVAL_IS_FUNCTION(cx, fval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -1389,13 +1518,14 @@ fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } #endif -static const char js_call_str[] = "call"; +static const char 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; + JSString *str; void *mark; uintN i; JSStackFrame *fp; @@ -1406,16 +1536,19 @@ fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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))); + str = JS_ValueToString(cx, fval); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, call_str, + JS_GetStringBytes(str)); + } return JS_FALSE; } if (argc == 0) { - /* Call fun with its parent as the 'this' parameter if no args. */ - obj = OBJ_GET_PARENT(cx, obj); + /* Call fun with its global object as the 'this' param if no args. */ + obj = NULL; } else { /* Otherwise convert the first arg to 'this' and skip over it. */ if (!js_ValueToObject(cx, argv[0], &obj)) @@ -1454,6 +1587,7 @@ static JSBool fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, *sp, *oldsp; + JSString *str; JSObject *aobj; jsuint length; void *mark; @@ -1471,10 +1605,13 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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))); + str = JS_ValueToString(cx, fval); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, "apply", + JS_GetStringBytes(str)); + } return JS_FALSE; } @@ -1546,7 +1683,7 @@ static JSFunctionSpec function_methods[] = { {"apply", fun_apply, 2,0,0}, #endif #if JS_HAS_CALL_FUNCTION - {js_call_str, fun_call, 1,0,0}, + {call_str, fun_call, 1,0,0}, #endif {0,0,0,0,0} }; @@ -1562,7 +1699,7 @@ js_IsIdentifier(JSString *str) return JS_FALSE; s = JSSTRING_CHARS(str); c = *s; - if (!JS_ISIDENT_START(c)) + if (!JS_ISIDSTART(c)) return JS_FALSE; for (n--; n != 0; n--) { c = *++s; @@ -1589,7 +1726,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSTokenStream *ts; JSPrincipals *principals; jschar *collected_args, *cp; - size_t arg_length, args_length; + size_t arg_length, args_length, old_args_length; JSTokenType tt; JSBool ok; @@ -1622,7 +1759,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #endif fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, - JSVERSION_IS_ECMA(cx->version) + JS_VERSION_IS_ECMA(cx) ? cx->runtime->atomState.anonymousAtom : NULL); @@ -1648,6 +1785,10 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) principals = NULL; } + /* Belt-and-braces: check that the caller has access to parent. */ + if (!js_CheckPrincipalsAccess(cx, parent, principals, js_Function_str)) + return JS_FALSE; + n = argc ? argc - 1 : 0; if (n > 0) { /* @@ -1667,10 +1808,27 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!arg) return JS_FALSE; argv[i] = STRING_TO_JSVAL(arg); - args_length += JSSTRING_LENGTH(arg); + + /* + * Check for overflow. The < test works because the maximum + * JSString length fits in 2 fewer bits than size_t has. + */ + old_args_length = args_length; + args_length = old_args_length + JSSTRING_LENGTH(arg); + if (args_length < old_args_length) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + + /* Add 1 for each joining comma and check for overflow (two ways). */ + old_args_length = args_length; + args_length = old_args_length + n - 1; + if (args_length < old_args_length || + args_length >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; } - /* Add 1 for each joining comma. */ - args_length += n - 1; /* * Allocate a string to hold the concatenated arguments, including room @@ -1680,8 +1838,10 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, (args_length+1) * sizeof(jschar)); - if (!cp) + if (!cp) { + JS_ReportOutOfMemory(cx); return JS_FALSE; + } collected_args = cp; /* @@ -1724,8 +1884,10 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * 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)) + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &obj2, &prop)) { goto bad_formal; + } sprop = (JSScopeProperty *) prop; dupflag = 0; if (sprop) { @@ -1741,7 +1903,8 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ JS_ASSERT(sprop->getter == js_GetArgument); ok = name && - js_ReportCompileErrorNumber(cx, ts, NULL, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, @@ -1754,15 +1917,19 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto bad_formal; sprop = NULL; } - if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_SHARED, - SPROP_HAS_SHORTID | dupflag, + JSPROP_PERMANENT | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, fun->nargs)) { goto bad_formal; } + if (fun->nargs == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + goto bad; + } fun->nargs++; /* @@ -1818,6 +1985,7 @@ bad_formal: if (!(ts->flags & TSF_ERROR)) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); +bad: /* * Clean up the arguments string and tokenstream if we failed to parse * the arguments. @@ -1860,8 +2028,19 @@ bad: JSObject * js_InitCallClass(JSContext *cx, JSObject *obj) { - return JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, - call_props, NULL, NULL, NULL); + JSObject *proto; + + proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, + call_props, NULL, NULL, NULL); + if (!proto) + return NULL; + + /* + * Null Call.prototype's proto slot so that Object.prototype.* does not + * pollute the scope of heavyweight functions. + */ + OBJ_SET_PROTO(cx, proto, NULL); + return proto; } #endif @@ -1870,23 +2049,28 @@ 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; + JSTempValueRooter tvr; /* 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); + if (!funobj) return NULL; - } } + /* Protect fun from any potential last-ditch GCs. */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); + + /* + * Allocate fun after allocating funobj so slot allocation in js_NewObject + * does not wipe out fun from cx->newborn[GCX_PRIVATE]. + */ + fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction)); + if (!fun) + goto out; + /* Initialize all function members. */ fun->nrefs = 0; fun->object = NULL; @@ -1904,9 +2088,11 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, /* Link fun to funobj and vice versa. */ if (!js_LinkFunctionObject(cx, fun, funobj)) { cx->newborn[GCX_OBJECT] = NULL; - JS_free(cx, fun); - return NULL; + fun = NULL; } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); return fun; } @@ -1948,8 +2134,10 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, 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)) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(fun->object), + NULL, NULL, + attrs & ~JSFUN_FLAGS_MASK, NULL)) { return NULL; } return fun; @@ -1982,6 +2170,61 @@ js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) return (JSFunction *) JS_GetPrivate(cx, obj); } +JSObject * +js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) +{ + JSFunction *fun; + JSObject *funobj; + JSStackFrame *caller; + JSPrincipals *principals; + + if (JSVAL_IS_FUNCTION(cx, *vp)) + return JSVAL_TO_OBJECT(*vp); + + fun = js_ValueToFunction(cx, vp, flags); + if (!fun) + return NULL; + funobj = fun->object; + *vp = OBJECT_TO_JSVAL(funobj); + + caller = JS_GetScriptedCaller(cx, cx->fp); + if (caller) { + principals = caller->script->principals; + } else { + /* No scripted caller, don't allow access. */ + principals = NULL; + } + + /* + * FIXME: Reparameterize so we don't call js_AtomToPrintableString unless + * there is an error (bug 324694). + */ + if (!js_CheckPrincipalsAccess(cx, funobj, principals, + fun->atom + ? js_AtomToPrintableString(cx, fun->atom) + : js_anonymous_str)) { + return NULL; + } + return funobj; +} + +JSObject * +js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) +{ + JSObject *callable; + + callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp); + if (callable && + ((callable->map->ops == &js_ObjectOps) + ? OBJ_GET_CLASS(cx, callable)->call + : callable->map->ops->call)) { + *vp = OBJECT_TO_JSVAL(callable); + } else { + callable = js_ValueToFunctionObject(cx, vp, flags); + } + return callable; +} + void js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) { diff --git a/src/dom/js/jsfun.h b/src/dom/js/jsfun.h index eb79161a9..25aa10320 100644 --- a/src/dom/js/jsfun.h +++ b/src/dom/js/jsfun.h @@ -48,7 +48,7 @@ JS_BEGIN_EXTERN_C struct JSFunction { - jsrefcount nrefs; /* number of referencing objects */ + jsrefcount nrefs; /* number of referencing objects */ JSObject *object; /* back-pointer to GC'ed object header */ union { JSNative native; /* native method pointer or null */ @@ -78,7 +78,7 @@ 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) && \ + (!JSVAL_IS_PRIMITIVE(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) extern JSBool @@ -99,7 +99,7 @@ js_InitCallClass(JSContext *cx, JSObject *obj); extern JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom); + uintN flags, JSObject *parent, JSAtom *atom); extern JSObject * js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); @@ -109,7 +109,7 @@ js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); extern JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, - uintN nargs, uintN flags); + uintN nargs, uintN flags); /* * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the @@ -122,6 +122,12 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, extern JSFunction * js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); +extern JSObject * +js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags); + +extern JSObject * +js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags); + extern void js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); diff --git a/src/dom/js/jsgc.c b/src/dom/js/jsgc.c index 754f4ae6d..2383240c7 100644 --- a/src/dom/js/jsgc.c +++ b/src/dom/js/jsgc.c @@ -69,6 +69,10 @@ #include "jsscript.h" #include "jsstr.h" +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + /* * 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. @@ -87,14 +91,6 @@ #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. @@ -175,11 +171,26 @@ typedef struct JSGCPageInfo { #define FIRST_THING_PAGE(a) (((a)->base + GC_FLAGS_SIZE) & ~GC_PAGE_MASK) +/* + * Given a jsuword page pointer p and a thing size n, return the address of + * the first thing in p. We know that any n not a power of two packs from + * the end of the page leaving at least enough room for one JSGCPageInfo, but + * not for another thing, at the front of the page (JS_ASSERTs below insist + * on this). + * + * This works because all allocations are a multiple of sizeof(JSGCThing) == + * sizeof(JSGCPageInfo) in size. + */ +#define FIRST_THING(p,n) (((n) & ((n) - 1)) \ + ? (p) + (uint32)(GC_PAGE_SIZE % (n)) \ + : (p) + (n)) + static JSGCThing * -gc_new_arena(JSArenaPool *pool) +gc_new_arena(JSArenaPool *pool, size_t nbytes) { uint8 *flagp, *split, *pagep, *limit; JSArena *a; + jsuword p; JSGCThing *thing; JSGCPageInfo *pi; @@ -190,11 +201,13 @@ gc_new_arena(JSArenaPool *pool) 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); + p = FIRST_THING_PAGE(a); + split = pagep = (uint8 *) p; + a->avail = FIRST_THING(p, nbytes); + JS_ASSERT(a->avail >= p + sizeof(JSGCPageInfo)); thing = (JSGCThing *) a->avail; - a->avail += sizeof(JSGCThing); + JS_ArenaCountAllocation(pool, a->avail - p); + a->avail += nbytes; /* Initialize the JSGCPageInfo records at the start of every thing page. */ limit = pagep + GC_THINGS_SIZE; @@ -226,12 +239,62 @@ js_IsAboutToBeFinalized(JSContext *cx, void *thing) { uint8 flags = *js_GetGCThingFlags(thing); - return !(flags & (GCF_MARK | GCF_LOCKMASK | GCF_FINAL)); + return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL)); } typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing); -static GCFinalizeOp gc_finalizers[GCX_NTYPES]; +#ifndef DEBUG +# define js_FinalizeDouble NULL +#endif + +#if !JS_HAS_XML_SUPPORT +# define js_FinalizeXMLNamespace NULL +# define js_FinalizeXMLQName NULL +# define js_FinalizeXML NULL +#endif + +static GCFinalizeOp gc_finalizers[GCX_NTYPES] = { + (GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */ + (GCFinalizeOp) js_FinalizeString, /* GCX_STRING */ + (GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */ + (GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */ + NULL, /* GCX_PRIVATE */ + (GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */ + (GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */ + (GCFinalizeOp) js_FinalizeXML, /* GCX_XML */ + NULL, /* GCX_EXTERNAL_STRING */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +#ifdef GC_MARK_DEBUG +static const char newborn_external_string[] = "newborn external string"; + +static const char *gc_typenames[GCX_NTYPES] = { + "newborn object", + "newborn string", + "newborn double", + "newborn mutable string", + "newborn private", + "newborn Namespace", + "newborn QName", + "newborn XML", + newborn_external_string, + newborn_external_string, + newborn_external_string, + newborn_external_string, + newborn_external_string, + newborn_external_string, + newborn_external_string, + newborn_external_string +}; +#endif intN js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, @@ -261,6 +324,8 @@ js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) { + uintN i; + JS_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); @@ -268,50 +333,65 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) 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)); + for (i = 0; i < GC_NUM_FREELISTS; i++) + JS_InitArenaPool(&rt->gcArenaPool[i], "gc-arena", GC_ARENA_SIZE, 1); 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; + + /* + * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes + * for default backward API compatibility. + */ + rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; return JS_TRUE; } #ifdef JS_GCMETER -void +JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp) { + uintN i; + 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); + +#define UL(x) ((unsigned long)(x)) +#define ULSTAT(x) UL(rt->gcStats.x) + fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes)); + fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes)); + fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc)); + for (i = 0; i < GC_NUM_FREELISTS; i++) { + fprintf(fp, " GC freelist %u length: %lu\n", + i, ULSTAT(freelen[i])); + fprintf(fp, " recycles via GC freelist %u: %lu\n", + i, ULSTAT(recycle[i])); + } + fprintf(fp, "allocation retries after GC: %lu\n", ULSTAT(retry)); + fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); + fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); + fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); + fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); + fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); + fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); + fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); + fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); + fprintf(fp, " mark C stack overflows: %lu\n", ULSTAT(dswmark)); + fprintf(fp, " mark DSW recursion depth: %lu\n", ULSTAT(dswdepth)); + fprintf(fp, " maximum mark DSW recursion: %lu\n", ULSTAT(maxdswdepth)); + fprintf(fp, " mark DSW up-tree movement: %lu\n", ULSTAT(dswup)); + fprintf(fp, "DSW up-tree obj->slot steps: %lu\n", ULSTAT(dswupstep)); + fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); + fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); + fprintf(fp, " useless GC calls: %lu\n", ULSTAT(nopoke)); + fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); + fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); + fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); +#undef UL +#undef US + #ifdef JS_ARENAMETER JS_DumpArenaStats(fp); #endif @@ -337,13 +417,18 @@ js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) void js_FinishGC(JSRuntime *rt) { + uintN i; + #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif #ifdef JS_GCMETER js_DumpGCStats(rt, stdout); #endif - JS_FinishArenaPool(&rt->gcArenaPool); + for (i = 0; i < GC_NUM_FREELISTS; i++) { + JS_FinishArenaPool(&rt->gcArenaPool[i]); + rt->gcFreeList[i] = NULL; + } JS_ArenaFinish(); if (rt->gcRootsHash.ops) { @@ -376,7 +461,6 @@ js_FinishGC(JSRuntime *rt) JS_DHashTableDestroy(rt->gcLocksHash); rt->gcLocksHash = NULL; } - rt->gcFreeList = NULL; } JSBool @@ -451,21 +535,28 @@ js_RemoveRoot(JSRuntime *rt, void *rp) return JS_TRUE; } +#ifdef DEBUG_brendan +#define NGCHIST 64 + +static struct GCHist { + JSBool lastDitch; + JSGCThing *freeList; +} gchist[NGCHIST]; + +unsigned gchpos; +#endif + void * -js_AllocGCThing(JSContext *cx, uintN flags) +js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) { JSBool tried_gc; JSRuntime *rt; - JSGCThing *thing; + size_t nflags; + uintN i; + JSGCThing *thing, **flp; uint8 *flagp; JSLocalRootStack *lrs; - -#ifdef TOO_MUCH_GC - js_GC(cx, GC_KEEP_ATOMS); - tried_gc = JS_TRUE; -#else - tried_gc = JS_FALSE; -#endif + uint32 *bytesptr; rt = cx->runtime; JS_LOCK_GC(rt); @@ -475,17 +566,33 @@ js_AllocGCThing(JSContext *cx, uintN flags) JS_UNLOCK_GC(rt); return NULL; } + +#ifdef TOO_MUCH_GC +#ifdef WAY_TOO_MUCH_GC + rt->gcPoke = JS_TRUE; +#endif + js_GC(cx, GC_KEEP_ATOMS | GC_ALREADY_LOCKED); + tried_gc = JS_TRUE; +#else + tried_gc = JS_FALSE; +#endif + METER(rt->gcStats.alloc++); + nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); + nflags = nbytes / sizeof(JSGCThing); + i = GC_FREELIST_INDEX(nbytes); + flp = &rt->gcFreeList[i]; + retry: - thing = rt->gcFreeList; + thing = *flp; if (thing) { - rt->gcFreeList = thing->next; + *flp = thing->next; flagp = thing->flagp; - METER(rt->gcStats.freelen--); - METER(rt->gcStats.recycle++); + METER(rt->gcStats.freelen[i]--); + METER(rt->gcStats.recycle[i]++); } else { if (rt->gcBytes < rt->gcMaxBytes && - (tried_gc || rt->gcMallocBytes < rt->gcMaxBytes)) + (tried_gc || rt->gcMallocBytes < rt->gcMaxMallocBytes)) { /* * Inline form of JS_ARENA_ALLOCATE adapted to truncate the current @@ -493,25 +600,24 @@ retry: * GC_PAGE_SIZE-byte-aligned thing (which is actually not a thing, * it's a JSGCPageInfo record). */ - JSArenaPool *pool = &rt->gcArenaPool; + JSArenaPool *pool = &rt->gcArenaPool[i]; JSArena *a = pool->current; - size_t nb = sizeof(JSGCThing); jsuword p = a->avail; - jsuword q = p + nb; + jsuword q = p + nbytes; if (q > (a->limit & ~GC_PAGE_MASK)) { - thing = gc_new_arena(pool); + thing = gc_new_arena(pool, nbytes); } else { if ((p & GC_PAGE_MASK) == 0) { /* Beware, p points to a JSGCPageInfo record! */ - p = q; - q += nb; - JS_ArenaCountAllocation(pool, nb); + p = FIRST_THING(p, nbytes); + q = p + nbytes; + JS_ArenaCountAllocation(pool, p & GC_PAGE_MASK); } a->avail = q; thing = (JSGCThing *)p; } - JS_ArenaCountAllocation(pool, nb); + JS_ArenaCountAllocation(pool, nbytes); } /* @@ -548,8 +654,17 @@ retry: * 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) + if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { + /* + * When we fail for a thing allocated through the tail of + * the last arena, thing's flag byte is not initialized. So + * to prevent GC accessing the uninitialized flags during + * the finalization, we always mark the thing as final. See + * bug 337407. + */ + *flagp = GCF_FINAL; goto fail; + } } else { /* * No local root scope, so we're stuck with the old, fragile model of @@ -558,9 +673,12 @@ retry: cx->newborn[flags & GCF_TYPEMASK] = thing; } - /* We can't fail now, so update flags and rt->gcBytes. */ + /* We can't fail now, so update flags and rt->gc{,Private}Bytes. */ *flagp = (uint8)flags; - rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8); + bytesptr = ((flags & GCF_TYPEMASK) == GCX_PRIVATE) + ? &rt->gcPrivateBytes + : &rt->gcBytes; + *bytesptr += nbytes + nflags; /* * Clear thing before unlocking in case a GC run is about to scan it, @@ -568,6 +686,13 @@ retry: */ thing->next = NULL; thing->flagp = NULL; +#ifdef DEBUG_brendan + gchist[gchpos].lastDitch = tried_gc; + gchist[gchpos].freeList = *flp; + if (++gchpos == NGCHIST) + gchpos = 0; +#endif + METER(if (flags & GCF_LOCK) rt->gcStats.lockborn++); JS_UNLOCK_GC(rt); return thing; @@ -587,69 +712,85 @@ js_LockGCThing(JSContext *cx, void *thing) return ok; } +/* + * Deep GC-things can't be locked just by setting the GCF_LOCK bit, because + * their descendants must be marked by the GC. To find them during the mark + * phase, they are added to rt->gcLocksHash, which is created lazily. + * + * NB: we depend on the order of GC-thing type indexes here! + */ +#define GC_TYPE_IS_STRING(t) ((t) == GCX_STRING || \ + (t) >= GCX_EXTERNAL_STRING) +#define GC_TYPE_IS_XML(t) ((unsigned)((t) - GCX_NAMESPACE) <= \ + (unsigned)(GCX_XML - GCX_NAMESPACE)) +#define GC_TYPE_IS_DEEP(t) ((t) == GCX_OBJECT || GC_TYPE_IS_XML(t)) + +#define IS_DEEP_STRING(t,o) (GC_TYPE_IS_STRING(t) && \ + JSSTRING_IS_DEPENDENT((JSString *)(o))) + +#define GC_THING_IS_DEEP(t,o) (GC_TYPE_IS_DEEP(t) || IS_DEEP_STRING(t, o)) + JSBool js_LockGCThingRT(JSRuntime *rt, void *thing) { - uint8 *flagp, flags, lockbits; - JSBool ok; + JSBool ok, deep; + uint8 *flagp, flags, lock, type; JSGCLockHashEntry *lhe; + ok = JS_TRUE; if (!thing) - return JS_TRUE; + return ok; + 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 { + flags = *flagp; + lock = (flags & GCF_LOCK); + type = (flags & GCF_TYPEMASK); + deep = GC_THING_IS_DEEP(type, thing); + + /* + * Avoid adding a rt->gcLocksHash entry for shallow things until someone + * nests a lock -- then start such an entry with a count of 2, not 1. + */ + if (lock || deep) { + if (!rt->gcLocksHash) { + rt->gcLocksHash = + JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSGCLockHashEntry), + GC_ROOTS_SIZE); + if (!rt->gcLocksHash) { + ok = JS_FALSE; + goto done; + } + } else if (lock == 0) { #ifdef DEBUG - JSDHashEntryHdr *hdr = - JS_DHashTableOperate(rt->gcLocksHash, thing, - JS_DHASH_LOOKUP); - JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr)); + 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++; - } - } + } + + lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); + if (!lhe) { + ok = JS_FALSE; + goto done; + } + if (!lhe->thing) { + lhe->thing = thing; + lhe->count = deep ? 1 : 2; } else { - *flagp = (uint8)(flags + GCF_LOCK); + JS_ASSERT(lhe->count >= 1); + lhe->count++; } - } else { - METER(rt->gcStats.stuck++); } + *flagp = (uint8)(flags | GCF_LOCK); METER(rt->gcStats.lock++); ok = JS_TRUE; -error: +done: JS_UNLOCK_GC(rt); return ok; } @@ -657,41 +798,35 @@ error: JSBool js_UnlockGCThingRT(JSRuntime *rt, void *thing) { - uint8 *flagp, flags, lockbits; + uint8 *flagp, flags; JSGCLockHashEntry *lhe; if (!thing) return JS_TRUE; + flagp = js_GetGCThingFlags(thing); + JS_LOCK_GC(rt); 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); - } - } + if (flags & GCF_LOCK) { + if (!rt->gcLocksHash || + (lhe = (JSGCLockHashEntry *) + JS_DHashTableOperate(rt->gcLocksHash, thing, + JS_DHASH_LOOKUP), + JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) { + /* Shallow GC-thing with an implicit lock count of 1. */ + JS_ASSERT(!GC_THING_IS_DEEP(flags & GCF_TYPEMASK, thing)); } else { - *flagp = (uint8)(flags - GCF_LOCK); + /* Basis or nested unlock of a deep thing, or nested of shallow. */ + if (--lhe->count != 0) + goto out; + JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); } - } else { - METER(rt->gcStats.unstuck++); + *flagp = (uint8)(flags & ~GCF_LOCK); } rt->gcPoke = JS_TRUE; +out: METER(rt->gcStats.unlock++); JS_UNLOCK_GC(rt); return JS_TRUE; @@ -700,7 +835,6 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing) #ifdef GC_MARK_DEBUG #include -#include #include "jsprf.h" JS_FRIEND_DATA(FILE *) js_DumpGCHeap; @@ -771,9 +905,17 @@ gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) prev = prev->prev; } while (next) { - path = JS_sprintf_append(path, "%s(%s).", - next->name, - gc_object_class_name(next->thing)); + uint8 nextFlags = *js_GetGCThingFlags(next->thing); + if ((nextFlags & GCF_TYPEMASK) == GCX_OBJECT) { + path = JS_sprintf_append(path, "%s(%s @ 0x%08p).", + next->name, + gc_object_class_name(next->thing), + (JSObject*)next->thing); + } else { + path = JS_sprintf_append(path, "%s(%s).", + next->name, + gc_object_class_name(next->thing)); + } next = next->next; } if (!path) @@ -792,9 +934,36 @@ gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) fprintf(fp, "object %8p %s", privateThing, className); break; } +#if JS_HAS_XML_SUPPORT + case GCX_NAMESPACE: + { + JSXMLNamespace *ns = (JSXMLNamespace *)thing; + fprintf(fp, "namespace %s:%s", + JS_GetStringBytes(ns->prefix), JS_GetStringBytes(ns->uri)); + break; + } + case GCX_QNAME: + { + JSXMLQName *qn = (JSXMLQName *)thing; + fprintf(fp, "qname %s(%s):%s", + JS_GetStringBytes(qn->prefix), JS_GetStringBytes(qn->uri), + JS_GetStringBytes(qn->localName)); + break; + } + case GCX_XML: + { + extern const char *js_xml_class_str[]; + JSXML *xml = (JSXML *)thing; + fprintf(fp, "xml %8p %s", xml, js_xml_class_str[xml->xml_class]); + break; + } +#endif case GCX_DOUBLE: fprintf(fp, "double %g", *(jsdouble *)thing); break; + case GCX_PRIVATE: + fprintf(fp, "private %8p", (void *)thing); + break; default: fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); break; @@ -835,24 +1004,41 @@ js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg) #endif GC_MARK(cx, JSVAL_TO_GCTHING(key), name, arg); } + if (atom->flags & ATOM_HIDDEN) + js_MarkAtom(cx, atom->entry.value, 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; +/* + * These macros help avoid passing the GC_MARK_DEBUG-only |arg| parameter + * during recursive calls when GC_MARK_DEBUG is not defined. + */ #ifdef GC_MARK_DEBUG - JSScope *scope; - JSScopeProperty *sprop; +# define UNMARKED_GC_THING_FLAGS(thing, arg) \ + UnmarkedGCThingFlags(thing, arg) +# define NEXT_UNMARKED_GC_THING(vp, end, thingp, flagpp, arg) \ + NextUnmarkedGCThing(vp, end, thingp, flagpp, arg) +# define MARK_GC_THING(cx, thing, flagp, arg) \ + MarkGCThing(cx, thing, flagp, arg) +# define CALL_GC_THING_MARKER(marker, cx, thing, arg) \ + marker(cx, thing, arg) +#else +# define UNMARKED_GC_THING_FLAGS(thing, arg) \ + UnmarkedGCThingFlags(thing) +# define NEXT_UNMARKED_GC_THING(vp, end, thingp, flagpp, arg) \ + NextUnmarkedGCThing(vp, end, thingp, flagpp) +# define MARK_GC_THING(cx, thing, flagp, arg) \ + MarkGCThing(cx, thing, flagp) +# define CALL_GC_THING_MARKER(marker, cx, thing, arg) \ + marker(cx, thing, NULL) #endif +static uint8 * +UNMARKED_GC_THING_FLAGS(void *thing, void *arg) +{ + uint8 flags, *flagp; + if (!thing) - return; + return NULL; flagp = js_GetGCThingFlags(thing); flags = *flagp; @@ -863,85 +1049,194 @@ js_MarkGCThing(JSContext *cx, void *thing, void *arg) #endif if (flags & GCF_MARK) - return; + return NULL; + + return flagp; +} + +static jsval * +NEXT_UNMARKED_GC_THING(jsval *vp, jsval *end, void **thingp, uint8 **flagpp, + void *arg) +{ + jsval v; + void *thing; + uint8 *flagp; + + while (vp < end) { + v = *vp; + if (JSVAL_IS_GCTHING(v)) { + thing = JSVAL_TO_GCTHING(v); + flagp = UNMARKED_GC_THING_FLAGS(thing, arg); + if (flagp) { + *thingp = thing; + *flagpp = flagp; + return vp; + } + } + vp++; + } + return NULL; +} + +static void +DeutschSchorrWaite(JSContext *cx, void *thing, uint8 *flagp); + +static JSBool +MARK_GC_THING(JSContext *cx, void *thing, uint8 *flagp, void *arg) +{ + JSRuntime *rt; + JSObject *obj; + jsval v, *vp, *end; + JSString *str; + void *next_thing; + uint8 *next_flagp; +#ifdef JS_GCMETER + uint32 tailCallNesting; +#endif +#ifdef GC_MARK_DEBUG + JSScope *scope; + JSScopeProperty *sprop; + char name[32]; +#endif + int stackDummy; - *flagp |= GCF_MARK; rt = cx->runtime; + METER(tailCallNesting = 0); + METER(if (++rt->gcStats.cdepth > rt->gcStats.maxcdepth) + rt->gcStats.maxcdepth = rt->gcStats.cdepth); + +#ifndef GC_MARK_DEBUG + start: +#endif + JS_ASSERT(flagp); METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth) rt->gcStats.maxdepth = rt->gcStats.depth); + if (*flagp & GCF_MARK) { + /* + * This should happen only if recursive MARK_GC_THING marks flags + * already stored in the caller's *next_flagp. + */ + goto out; + } + + *flagp |= GCF_MARK; #ifdef GC_MARK_DEBUG if (js_DumpGCHeap) - gc_dump_thing(thing, flags, arg, js_DumpGCHeap); + gc_dump_thing(thing, *flagp, arg, js_DumpGCHeap); #endif - switch (flags & GCF_TYPEMASK) { + switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: + /* If obj->slots is null, obj must be a newborn. */ obj = (JSObject *) thing; vp = obj->slots; - if (!vp) { - /* If obj->slots is null, obj must be a newborn. */ - JS_ASSERT(!obj->map); + if (!vp) + goto out; + + /* Switch to Deutsch-Schorr-Waite if we exhaust our stack quota. */ + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + METER(rt->gcStats.dswmark++); + DeutschSchorrWaite(cx, thing, flagp); goto out; } - nslots = (obj->map->ops->mark) - ? obj->map->ops->mark(cx, obj, arg) - : JS_MIN(obj->map->freeslot, obj->map->nslots); + + /* Mark slots if they are small enough to be GC-allocated. */ + if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX) + GC_MARK(cx, vp - 1, "slots", arg); + + /* Set up local variables to loop over unmarked things. */ + end = vp + ((obj->map->ops->mark) + ? CALL_GC_THING_MARKER(obj->map->ops->mark, cx, obj, arg) + : JS_MIN(obj->map->freeslot, obj->map->nslots)); + + vp = NEXT_UNMARKED_GC_THING(vp, end, &thing, &flagp, arg); + if (!vp) + goto out; + v = *vp; + + /* + * Here, thing is the first value in obj->slots referring to an + * unmarked GC-thing. + */ #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)) { + for (;;) { + /* Check loop invariants. */ + JS_ASSERT(v == *vp && JSVAL_IS_GCTHING(v)); + JS_ASSERT(thing == JSVAL_TO_GCTHING(v)); + JS_ASSERT(flagp == js_GetGCThingFlags(thing)); + #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; - } + 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, js_proto_str); 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**"); - } + case JSSLOT_PARENT: + strcpy(name, js_parent_str); + 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); + } else { + strcpy(name, "**UNKNOWN OBJECT MAP ENTRY**"); } +#endif + + do { + vp = NEXT_UNMARKED_GC_THING(vp+1, end, &next_thing, &next_flagp, + arg); + if (!vp) { + /* + * Here thing came from the last unmarked GC-thing slot. + * We can eliminate tail recursion unless GC_MARK_DEBUG + * is defined. + */ +#ifdef GC_MARK_DEBUG + GC_MARK(cx, thing, name, arg); + goto out; +#else + METER(++tailCallNesting); + goto start; +#endif + } + } while (next_thing == thing); + v = *vp; + +#ifdef GC_MARK_DEBUG + GC_MARK(cx, thing, name, arg); +#else + MARK_GC_THING(cx, thing, flagp, arg); +#endif + thing = next_thing; + flagp = next_flagp; } break; @@ -954,13 +1249,206 @@ js_MarkGCThing(JSContext *cx, void *thing, void *arg) case GCX_MUTABLE_STRING: str = (JSString *)thing; - if (JSSTRING_IS_DEPENDENT(str)) - GC_MARK(cx, JSSTRDEP_BASE(str), "base", arg); + if (JSSTRING_IS_DEPENDENT(str)) { + thing = JSSTRDEP_BASE(str); + flagp = UNMARKED_GC_THING_FLAGS(thing, arg); + if (flagp) { +#ifdef GC_MARK_DEBUG + GC_MARK(cx, thing, "base", arg); + goto out; +#else + METER(++tailCallNesting); + goto start; +#endif + } + } + break; + +#if JS_HAS_XML_SUPPORT + case GCX_NAMESPACE: + CALL_GC_THING_MARKER(js_MarkXMLNamespace, cx, (JSXMLNamespace *)thing, + arg); + break; + + case GCX_QNAME: + CALL_GC_THING_MARKER(js_MarkXMLQName, cx, (JSXMLQName *)thing, arg); + break; + + case GCX_XML: + CALL_GC_THING_MARKER(js_MarkXML, cx, (JSXML *)thing, arg); break; +#endif } out: - METER(rt->gcStats.depth--); + METER(rt->gcStats.depth -= 1 + tailCallNesting); + METER(rt->gcStats.cdepth--); + return JS_TRUE; +} + +/* + * An invalid object reference that's distinct from JSVAL_TRUE and JSVAL_FALSE + * when tagged as a boolean. Used to indicate empty DSW mark stack. + * + * Reversed pointers that link the DSW mark stack through obj->slots entries + * are also tagged as booleans so we can find each pointer and unreverse it. + * Because no object pointer is <= 16, these values can be distinguished from + * JSVAL_EMPTY, JSVAL_TRUE, and JSVAL_FALSE. + */ +#define JSVAL_EMPTY (2 << JSVAL_TAGBITS) + +/* + * To optimize native objects to avoid O(n^2) explosion in pathological cases, + * we use a dswIndex member of JSScope to tell where in obj->slots to find the + * reversed pointer. Scrounging space in JSScope by packing existing members + * tighter yielded 16 bits of index, which we use directly if obj->slots has + * 64K or fewer slots. Otherwise we make scope->dswIndex a fixed-point 16-bit + * fraction of the number of slots. + */ +static JS_INLINE uint16 +EncodeDSWIndex(jsval *vp, jsval *slots) +{ + uint32 nslots, limit, index; + jsdouble d; + + nslots = slots[-1]; + limit = JS_BIT(16); + index = PTRDIFF(vp, slots, jsval); + JS_ASSERT(index < nslots); + if (nslots > limit) { + d = ((jsdouble)index / nslots) * limit; + JS_ASSERT(0 <= d && d < limit); + return (uint16) d; + } + return (uint16) index; +} + +static JS_INLINE uint32 +DecodeDSWIndex(uint16 dswIndex, jsval *slots) +{ + uint32 nslots, limit; + jsdouble d; + + nslots = slots[-1]; + limit = JS_BIT(16); + JS_ASSERT(dswIndex < nslots); + if (nslots > limit) { + d = ((jsdouble)dswIndex * nslots) / limit; + JS_ASSERT(0 <= d && d < nslots); + return (uint32) d; + } + return dswIndex; +} + +static void +DeutschSchorrWaite(JSContext *cx, void *thing, uint8 *flagp) +{ + jsval top, parent, v, *vp, *end; + JSObject *obj; + JSScope *scope; +#ifdef JS_GCMETER + JSRuntime *rt = cx->runtime; +#endif + + top = JSVAL_EMPTY; + +down: + METER(if (++rt->gcStats.dswdepth > rt->gcStats.maxdswdepth) + rt->gcStats.maxdswdepth = rt->gcStats.dswdepth); + obj = (JSObject *) thing; + parent = OBJECT_TO_JSVAL(obj); + + /* Precompute for quick testing to set and get scope->dswIndex. */ + scope = (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->object == obj) + ? OBJ_SCOPE(obj) + : NULL; + + /* Mark slots if they are small enough to be GC-allocated. */ + vp = obj->slots; + if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX) + GC_MARK(cx, vp - 1, "slots", NULL); + + end = vp + ((obj->map->ops->mark) + ? obj->map->ops->mark(cx, obj, NULL) + : JS_MIN(obj->map->freeslot, obj->map->nslots)); + + *flagp |= GCF_MARK; + + for (;;) { + while ((vp = NEXT_UNMARKED_GC_THING(vp, end, &thing, &flagp, NULL)) + != NULL) { + v = *vp; + JS_ASSERT(JSVAL_TO_GCTHING(v) == thing); + + if (JSVAL_IS_OBJECT(v)) { + *vp = JSVAL_SETTAG(top, JSVAL_BOOLEAN); + top = parent; + if (scope) + scope->dswIndex = EncodeDSWIndex(vp, obj->slots); + goto down; + } + + /* Handle string and double GC-things. */ + MARK_GC_THING(cx, thing, flagp, NULL); + } + + /* If we are back at the root (or we never left it), we're done. */ + METER(rt->gcStats.dswdepth--); + if (scope) + scope->dswIndex = 0; + if (top == JSVAL_EMPTY) + return; + + /* Time to go back up the spanning tree. */ + METER(rt->gcStats.dswup++); + obj = JSVAL_TO_OBJECT(top); + vp = obj->slots; + end = vp + vp[-1]; + + /* + * If obj is native and owns its own scope, we can minimize the cost + * of searching for the reversed pointer. + */ + scope = (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->object == obj) + ? OBJ_SCOPE(obj) + : NULL; + if (scope) + vp += DecodeDSWIndex(scope->dswIndex, vp); + + /* + * Alas, we must search for the reversed pointer. If we used the + * scope->dswIndex hint, we'll step over a few slots for objects with + * a few times 64K slots, etc. For more typical (that is, far fewer + * than 64K slots) native objects that own their own scopes, this loop + * won't iterate at all. The order of complexity for host objects and + * unmutated native objects is O(n^2), but n (4 or 5 in most cases) is + * low enough that we don't care. + * + * We cannot use a reversed pointer into obj->slots, because there + * is no way to find an object from an address within its slots. + */ + v = *vp; + while (v <= JSVAL_TRUE || !JSVAL_IS_BOOLEAN(v)) { + METER(rt->gcStats.dswupstep++); + JS_ASSERT(vp + 1 < end); + v = *++vp; + } + + *vp++ = parent; + parent = top; + top = JSVAL_CLRTAG(v); + } +} + +void +js_MarkGCThing(JSContext *cx, void *thing, void *arg) +{ + uint8 *flagp; + + flagp = UNMARKED_GC_THING_FLAGS(thing, arg); + if (!flagp) + return; + MARK_GC_THING(cx, thing, flagp, arg); } JS_STATIC_DLL_CALLBACK(JSDHashOperator) @@ -974,16 +1462,19 @@ gc_root_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { JSContext *cx = (JSContext *)arg; #ifdef DEBUG + uintN i; 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; + for (i = 0; i < GC_NUM_FREELISTS; i++) { + for (a = cx->runtime->gcArenaPool[i].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) { @@ -1044,10 +1535,13 @@ js_GC(JSContext *cx, uintN gcflags) JSStackFrame *fp, *chain; uintN i, depth, nslots, type; JSStackHeader *sh; + JSTempValueRooter *tvr; + size_t nbytes, nflags; JSArena *a, **ap; uint8 flags, *flagp, *split; JSGCThing *thing, *limit, **flp, **oflp; GCFinalizeOp finalizer; + uint32 *bytesptr; JSBool all_clear; #ifdef JS_THREADSAFE jsword currentThread; @@ -1082,7 +1576,7 @@ js_GC(JSContext *cx, uintN gcflags) if (!(gcflags & GC_ALREADY_LOCKED)) JS_LOCK_GC(rt); - /* Do nothing if no assignment has executed since the last GC. */ + /* Do nothing if no mutator has executed since the last GC. */ if (!rt->gcPoke) { METER(rt->gcStats.nopoke++); if (!(gcflags & GC_ALREADY_LOCKED)) @@ -1090,6 +1584,7 @@ js_GC(JSContext *cx, uintN gcflags) return; } METER(rt->gcStats.poke++); + rt->gcPoke = JS_FALSE; #ifdef JS_THREADSAFE /* Bump gcLevel and return rather than nest on this thread. */ @@ -1197,7 +1692,7 @@ js_GC(JSContext *cx, uintN gcflags) /* Drop atoms held by the property cache, and clear property weak links. */ js_DisablePropertyCache(cx); js_FlushPropertyCache(cx); -#ifdef DEBUG_brendan +#ifdef DEBUG_notme { extern void js_DumpScopeMeters(JSRuntime *rt); js_DumpScopeMeters(rt); } @@ -1213,7 +1708,10 @@ restart: 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); + js_MarkWatchPoints(cx); + js_MarkScriptFilenames(rt, gcflags); + js_MarkNativeIteratorStates(cx); + iter = NULL; while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { /* @@ -1256,8 +1754,11 @@ restart: GC_MARK(cx, fp->thisp, "this", NULL); if (fp->argv) { nslots = fp->argc; - if (fp->fun && fp->fun->nargs > nslots) - nslots = fp->fun->nargs; + if (fp->fun) { + if (fp->fun->nargs > nslots) + nslots = fp->fun->nargs; + nslots += fp->fun->extra; + } GC_MARK_JSVALS(cx, nslots, fp->argv, "arg"); } if (JSVAL_IS_GCTHING(fp->rval)) @@ -1267,6 +1768,9 @@ restart: GC_MARK(cx, fp->scopeChain, "scope chain", NULL); if (fp->sharpArray) GC_MARK(cx, fp->sharpArray, "sharp array", NULL); + + if (fp->xmlNamespace) + GC_MARK(cx, fp->xmlNamespace, "xmlNamespace", NULL); } while ((fp = fp->down) != NULL); } @@ -1276,15 +1780,15 @@ restart: /* 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); + for (i = 0; i < GCX_NTYPES; i++) + GC_MARK(cx, acx->newborn[i], gc_typenames[i], NULL); if (acx->lastAtom) GC_MARK_ATOM(cx, acx->lastAtom, NULL); + if (JSVAL_IS_GCTHING(acx->lastInternalResult)) { + thing = JSVAL_TO_GCTHING(acx->lastInternalResult); + if (thing) + GC_MARK(cx, thing, "lastInternalResult", NULL); + } #if JS_HAS_EXCEPTIONS if (acx->throwing && JSVAL_IS_GCTHING(acx->exception)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception", NULL); @@ -1302,6 +1806,22 @@ restart: if (acx->localRootStack) js_MarkLocalRoots(cx, acx->localRootStack); + for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { + if (tvr->count == -1) { + if (JSVAL_IS_GCTHING(tvr->u.value)) { + GC_MARK(cx, JSVAL_TO_GCTHING(tvr->u.value), + "tvr->u.value", NULL); + } + } else if (tvr->count == -2) { + tvr->u.marker(cx, tvr); + } else { + JS_ASSERT(tvr->count >= 0); + GC_MARK_JSVALS(cx, tvr->count, tvr->u.array, "tvr->u.array"); + } + } + + if (acx->sharpObjectMap.depth > 0) + js_GCMarkSharpMap(cx, &acx->sharpObjectMap); } #ifdef DUMP_CALL_TABLE js_DumpCallTable(cx); @@ -1312,114 +1832,142 @@ restart: /* * 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. + * + * Finalize smaller objects before larger, to guarantee finalization of + * GC-allocated obj->slots after obj. See FreeSlots in jsobj.c. */ 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); + for (i = 0; i < GC_NUM_FREELISTS; i++) { + nbytes = GC_FREELIST_NBYTES(i); + nflags = nbytes / sizeof(JSGCThing); + + for (a = rt->gcArenaPool[i].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 += nflags) { + if (((jsuword)thing & GC_PAGE_MASK) == 0) { + thing = (JSGCThing *) FIRST_THING((jsuword)thing, nbytes); + flagp = js_GetGCThingFlags(thing); } + flags = *flagp; + if (flags & GCF_MARK) { + *flagp &= ~GCF_MARK; + } else if (!(flags & (GCF_LOCK | 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; + /* 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); + bytesptr = (type == GCX_PRIVATE) + ? &rt->gcPrivateBytes + : &rt->gcBytes; + JS_ASSERT(*bytesptr >= nbytes + nflags); + *bytesptr -= nbytes + nflags; + } + flagp += nflags; + if (JS_UPTRDIFF(flagp, split) < nflags) + flagp += GC_THINGS_SIZE; } - if (++flagp == split) - flagp += GC_THINGS_SIZE; } } + /* + * Sweep script filenames after sweeping functions in the generic loop + * above. In this way when scripted function's finalizer destroys script + * triggering a call to rt->destroyScriptHook, the hook can still access + * script's filename. See bug 323267. + */ + js_SweepScriptFilenames(rt); + /* * 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++; + for (i = 0; i < GC_NUM_FREELISTS; i++) { + ap = &rt->gcArenaPool[i].first.next; + a = *ap; + if (!a) + continue; + + all_clear = JS_TRUE; + flp = oflp = &rt->gcFreeList[i]; + *flp = NULL; + METER(rt->gcStats.freelen[i] = 0); + + nbytes = GC_FREELIST_NBYTES(i); + nflags = nbytes / sizeof(JSGCThing); + do { + flagp = (uint8 *) a->base; + split = (uint8 *) FIRST_THING_PAGE(a); + limit = (JSGCThing *) a->avail; + for (thing = (JSGCThing *) split; thing < limit; thing += nflags) { + if (((jsuword)thing & GC_PAGE_MASK) == 0) { + thing = (JSGCThing *) FIRST_THING((jsuword)thing, nbytes); + flagp = js_GetGCThingFlags(thing); + } + if (*flagp != GCF_FINAL) { + all_clear = JS_FALSE; + } else { + thing->flagp = flagp; + *flp = thing; + flp = &thing->next; + METER(rt->gcStats.freelen[i]++); + } + flagp += nflags; + if (JS_UPTRDIFF(flagp, split) < nflags) + flagp += GC_THINGS_SIZE; } - if (*flagp != GCF_FINAL) { - all_clear = JS_FALSE; + + if (all_clear) { + JS_ARENA_DESTROY(&rt->gcArenaPool[i], a, ap); + flp = oflp; + METER(rt->gcStats.afree++); } else { - thing->flagp = flagp; - *flp = thing; - flp = &thing->next; - METER(rt->gcStats.freelen++); + ap = &a->next; + all_clear = JS_TRUE; + oflp = flp; } - if (++flagp == split) - flagp += GC_THINGS_SIZE; - } + } while ((a = *ap) != NULL); - 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; + /* Terminate the new freelist. */ + *flp = NULL; + } if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END); -#ifdef DEBUG_brendan +#ifdef DEBUG_notme { extern void DumpSrcNoteSizeHist(); DumpSrcNoteSizeHist(); + printf("GC HEAP SIZE %lu (%lu)\n", + (unsigned long)rt->gcBytes, (unsigned long)rt->gcPrivateBytes); } #endif -out: JS_LOCK_GC(rt); - if (rt->gcLevel > 1) { + if (rt->gcLevel > 1 || rt->gcPoke) { rt->gcLevel = 1; + rt->gcPoke = JS_FALSE; JS_UNLOCK_GC(rt); goto restart; } js_EnablePropertyCache(cx); rt->gcLevel = 0; rt->gcLastBytes = rt->gcBytes; - rt->gcPoke = rt->gcRunning = JS_FALSE; + rt->gcRunning = JS_FALSE; #ifdef JS_THREADSAFE /* If we were invoked during a request, pay back the temporary debit. */ diff --git a/src/dom/js/jsgc.h b/src/dom/js/jsgc.h index a4813d16d..efc13194e 100644 --- a/src/dom/js/jsgc.h +++ b/src/dom/js/jsgc.h @@ -54,16 +54,21 @@ JS_BEGIN_EXTERN_C #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_PRIVATE 4 /* private (unscanned) data */ +#define GCX_NAMESPACE 5 /* JSXMLNamespace */ +#define GCX_QNAME 6 /* JSXMLQName */ +#define GCX_XML 7 /* JSXML */ +#define GCX_EXTERNAL_STRING 8 /* JSString w/ external chars */ + +#define GCX_NTYPES_LOG2 4 /* 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_SYSTEM JS_BIT(GCX_NTYPES_LOG2 + 2) +#define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 3) /* lock bit shift */ #define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */ /* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */ @@ -119,8 +124,21 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name); extern JSBool js_RemoveRoot(JSRuntime *rt, void *rp); +/* + * The private JSGCThing struct, which describes a gcFreeList element. + */ +struct JSGCThing { + JSGCThing *next; + uint8 *flagp; +}; + +#define GC_NBYTES_MAX (10 * sizeof(JSGCThing)) +#define GC_NUM_FREELISTS (GC_NBYTES_MAX / sizeof(JSGCThing)) +#define GC_FREELIST_NBYTES(i) (((i) + 1) * sizeof(JSGCThing)) +#define GC_FREELIST_INDEX(n) (((n) / sizeof(JSGCThing)) - 1) + extern void * -js_AllocGCThing(JSContext *cx, uintN flags); +js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes); extern JSBool js_LockGCThing(JSContext *cx, void *thing); @@ -131,7 +149,7 @@ js_LockGCThingRT(JSRuntime *rt, void *thing); extern JSBool js_UnlockGCThingRT(JSRuntime *rt, void *thing); -extern JSBool +extern JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing); extern void @@ -169,6 +187,9 @@ struct GCMarkNode { js_MarkGCThing(cx_, thing_, &node_); \ JS_END_MACRO +extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; +JS_EXTERN_DATA(void *) js_LiveThingToFind; + #else /* !GC_MARK_DEBUG */ #define GC_MARK(cx, thing, name, prev) js_MarkGCThing(cx, thing, NULL) @@ -179,7 +200,7 @@ struct GCMarkNode { * 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 + * when the GC is called from js_NewGCThing, 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 @@ -197,21 +218,34 @@ js_ForceGC(JSContext *cx, uintN gcflags); extern void js_GC(JSContext *cx, uintN gcflags); +#ifdef DEBUG_notme +#define JS_GCMETER 1 +#endif + #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 freelen[GC_NUM_FREELISTS]; + /* gcFreeList lengths */ + uint32 recycle[GC_NUM_FREELISTS]; + /* number of things recycled through gcFreeList */ uint32 retry; /* allocation attempt retries after running the GC */ + uint32 retryhalt; /* allocation retries halted by the branch callback */ uint32 fail; /* allocation failures */ uint32 finalfail; /* finalizer calls allocator failures */ + uint32 lockborn; /* things born locked */ 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 depth; /* mark tail recursion depth */ + uint32 maxdepth; /* maximum mark tail recursion depth */ + uint32 cdepth; /* mark recursion depth of C functions */ + uint32 maxcdepth; /* maximum mark recursion depth of C functions */ + uint32 dswmark; /* mark C stack overflows => Deutsch-Schorr-Waite */ + uint32 dswdepth; /* DSW mark depth */ + uint32 maxdswdepth;/* maximum DSW mark depth */ + uint32 dswup; /* DSW moves up the mark spanning tree */ + uint32 dswupstep; /* steps in obj->slots to find DSW-reversed pointer */ 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 */ @@ -220,11 +254,19 @@ typedef struct JSGCStats { uint32 segslots; /* total stack segment jsval slots scanned */ } JSGCStats; -extern void +extern JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp); #endif /* JS_GCMETER */ +#ifdef DEBUG_notme +#define TOO_MUCH_GC 1 +#endif + +#ifdef WAY_TOO_MUCH_GC +#define TOO_MUCH_GC 1 +#endif + JS_END_EXTERN_C #endif /* jsgc_h___ */ diff --git a/src/dom/js/jshash.c b/src/dom/js/jshash.c index 954368450..0f22b5e39 100644 --- a/src/dom/js/jshash.c +++ b/src/dom/js/jshash.c @@ -114,7 +114,7 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash, ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); if (!ht) - return NULL; + return NULL; memset(ht, 0, sizeof *ht); ht->shift = JS_HASH_BITS - n; n = JS_BIT(n); @@ -211,7 +211,7 @@ JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, if (!ht->buckets) { ht->buckets = oldbuckets; return NULL; - } + } memset(ht->buckets, 0, nb); #ifdef HASHMETER ht->ngrows++; @@ -237,7 +237,7 @@ JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, /* Make a new key value entry */ he = ht->allocOps->allocEntry(ht->allocPriv, key); if (!he) - return NULL; + return NULL; he->keyHash = keyHash; he->key = key; he->value = value; diff --git a/src/dom/js/jsinterp.c b/src/dom/js/jsinterp.c index 6881e07dd..096610cf3 100644 --- a/src/dom/js/jsinterp.c +++ b/src/dom/js/jsinterp.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -37,11 +38,6 @@ * * ***** 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. */ @@ -75,6 +71,10 @@ #include "jsjit.h" #endif +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #ifdef DEBUG #define ASSERT_CACHE_IS_EMPTY(cache) \ JS_BEGIN_MACRO \ @@ -143,10 +143,6 @@ prop_iterator_finalize(JSContext *cx, JSObject *obj) &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 = { @@ -277,7 +273,7 @@ static JSClass prop_iterator_class = { #define VALUE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ - if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) { \ + if (!JSVAL_IS_PRIMITIVE(v)) { \ obj = JSVAL_TO_OBJECT(v); \ } else { \ SAVE_SP(fp); \ @@ -289,6 +285,13 @@ static JSClass prop_iterator_class = { } \ JS_END_MACRO +#define FETCH_OBJECT(cx, n, v, obj) \ + JS_BEGIN_MACRO \ + v = FETCH_OPND(n); \ + VALUE_TO_OBJECT(cx, v, obj); \ + STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ + JS_END_MACRO + #if JS_BUG_VOID_TOSTRING #define CHECK_VOID_TOSTRING(cx, v) \ if (JSVAL_IS_VOID(v)) { \ @@ -396,6 +399,12 @@ js_AllocStack(JSContext *cx, uintN nslots, void **markp) sp += 2; } + /* + * Store JSVAL_NULL using memset, to let compilers optimize as they see + * fit, in case a caller allocates and pushes GC-things one by one, which + * could nest a last-ditch GC that will scan this segment. + */ + memset(sp, 0, nslots * sizeof(jsval)); return sp; } @@ -424,126 +433,6 @@ js_FreeStack(JSContext *cx, void *mark) 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) { @@ -553,7 +442,7 @@ js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSBool js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - return SetFunctionSlot(cx, obj, js_SetArgument, id, *vp); + return JS_TRUE; } JSBool @@ -565,25 +454,12 @@ js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSBool js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - return SetFunctionSlot(cx, obj, js_SetLocalVariable, id, *vp); + return JS_TRUE; } -/* - * 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) +JSBool +js_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); @@ -612,13 +488,25 @@ ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp) */ JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || - !(parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2])))) { + !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; + jsid id; + jsval v; + uintN attrs; + + /* Walk up the parent chain. */ + thisp = JSVAL_TO_OBJECT(fp->argv[-2]); + id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); + for (;;) { + if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) + return JS_FALSE; + if (JSVAL_IS_VOID(v)) + v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); + if (JSVAL_IS_NULL(v)) + break; + thisp = JSVAL_TO_OBJECT(v); + } } } fp->thisp = thisp; @@ -977,8 +865,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) JSNative native; JSFunction *fun; JSScript *script; - uintN minargs, nvars; - intN nslots, nalloc, surplus; + uintN nslots, nvars, nalloc, surplus; JSInterpreterHook hook; void *hookData; @@ -1013,6 +900,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) */ if (JSVAL_IS_PRIMITIVE(v)) { #if JS_HAS_NO_SUCH_METHOD + jsid id; jsbytecode *pc; jsatomid atomIndex; JSAtom *atom; @@ -1036,14 +924,25 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) * any such defaulting of |this| to callee (v, *vp) ancestor. */ frame.argv = vp + 2; - ok = ComputeThis(cx, thisp, &frame); + ok = js_ComputeThis(cx, thisp, &frame); if (!ok) goto out2; thisp = frame.thisp; - ok = OBJ_GET_PROPERTY(cx, thisp, - (jsid)cx->runtime->atomState.noSuchMethodAtom, - &v); + id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); + if (OBJECT_IS_XML(cx, thisp)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) thisp->map->ops; + thisp = ops->getMethod(cx, thisp, id, &v); + if (!thisp) { + ok = JS_FALSE; + goto out2; + } + vp[1] = OBJECT_TO_JSVAL(thisp); + } else { + ok = OBJ_GET_PROPERTY(cx, thisp, id, &v); + } if (!ok) goto out2; if (JSVAL_IS_PRIMITIVE(v)) @@ -1053,6 +952,9 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) switch ((JSOp) *pc) { case JSOP_NAME: case JSOP_GETPROP: +#if JS_HAS_XML_SUPPORT + case JSOP_GETMETHOD: +#endif atomIndex = GET_ATOM_INDEX(pc); atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex); argsobj = js_NewArrayObject(cx, argc, vp + 2); @@ -1122,7 +1024,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) * 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 || + if (JS_VERSION_IS_1_2(cx) || ((ops == &js_ObjectOps) ? clasp->call : ops->call)) { ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); if (!ok) @@ -1138,7 +1040,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) } fun = NULL; script = NULL; - minargs = nvars = 0; + nslots = nvars = 0; /* Try a call or construct native object op. */ native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; @@ -1155,7 +1057,8 @@ have_fun: native = fun->u.native; script = NULL; } - minargs = fun->nargs + fun->extra; + nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; + nslots += fun->extra; nvars = fun->nvars; /* Handle bound method special case. */ @@ -1180,9 +1083,10 @@ have_fun: frame.sharpDepth = 0; frame.sharpArray = NULL; frame.dormantNext = NULL; + frame.xmlNamespace = NULL; /* Compute the 'this' parameter and store it in frame as frame.thisp. */ - ok = ComputeThis(cx, thisp, &frame); + ok = js_ComputeThis(cx, thisp, &frame); if (!ok) goto out2; @@ -1193,8 +1097,7 @@ have_fun: hook = cx->runtime->callHook; hookData = NULL; - /* Check for missing arguments expected by the function. */ - nslots = (intN)((argc < minargs) ? minargs - argc : 0); + /* Check for argument slots required by the function. */ if (nslots) { /* All arguments must be contiguous, so we may have to copy actuals. */ nalloc = nslots; @@ -1204,15 +1107,15 @@ have_fun: nalloc += 2 + argc; } else { /* Take advantage of surplus slots in the caller's frame depth. */ + JS_ASSERT((jsval *)mark >= sp); surplus = (jsval *)mark - sp; - JS_ASSERT(surplus >= 0); nalloc -= surplus; } /* Check whether we have enough space in the caller's frame. */ - if (nalloc > 0) { + if ((intN)nalloc > 0) { /* Need space for actuals plus missing formals minus surplus. */ - newsp = js_AllocRawStack(cx, (uintN)nalloc, NULL); + newsp = js_AllocRawStack(cx, nalloc, NULL); if (!newsp) { ok = JS_FALSE; goto out; @@ -1221,7 +1124,7 @@ have_fun: /* 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); + JS_ASSERT(2 + argc + nslots == nalloc); *newsp++ = vp[0]; *newsp++ = vp[1]; if (argc) @@ -1235,16 +1138,18 @@ have_fun: frame.vars += nslots; /* Push void to initialize missing args. */ - while (--nslots >= 0) + do { PUSH(JSVAL_VOID); + } while (--nslots != 0); } + JS_ASSERT(nslots == 0); /* 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 (nvars) { + JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); + surplus = (jsval *)cx->stackPool.current->avail - frame.vars; + if (surplus < nvars) { + newsp = js_AllocRawStack(cx, nvars, NULL); if (!newsp) { ok = JS_FALSE; goto out; @@ -1256,9 +1161,11 @@ have_fun: } /* Push void to initialize local variables. */ - while (--nslots >= 0) + do { PUSH(JSVAL_VOID); + } while (--nvars != 0); } + JS_ASSERT(nvars == 0); /* Store the current sp in frame before calling fun. */ SAVE_SP(&frame); @@ -1297,7 +1204,7 @@ have_fun: frame.scopeChain = funobj; #endif } - ok = js_Interpret(cx, &v); + ok = js_Interpret(cx, script->code, &v); } else { /* fun might be onerror trying to report a syntax error in itself. */ frame.scopeChain = NULL; @@ -1373,11 +1280,27 @@ js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, PUSH(OBJECT_TO_JSVAL(obj)); for (i = 0; i < argc; i++) PUSH(argv[i]); - fp->sp = sp; + SAVE_SP(fp); ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); if (ok) { RESTORE_SP(fp); + + /* + * Store *rval in the a scoped local root if a scope is open, else in + * the cx->lastInternalResult pigeon-hole GC root, solely so users of + * js_InternalInvoke and its direct and indirect (js_ValueToString for + * example) callers do not need to manage roots for local, temporary + * references to such results. + */ *rval = POP_OPND(); + if (JSVAL_IS_GCTHING(*rval)) { + if (cx->localRootStack) { + if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) + ok = JS_FALSE; + } else { + cx->lastInternalResult = *rval; + } + } } js_FreeStack(cx, mark); @@ -1478,6 +1401,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, frame.sharpDepth = 0; frame.flags = flags; frame.dormantNext = NULL; + frame.xmlNamespace = NULL; /* * Here we wrap the call to js_Interpret with code to (conditionally) @@ -1506,7 +1430,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, * 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); + ok = js_Interpret(cx, script->code, &frame.rval); *result = frame.rval; if (hookData) { @@ -1621,7 +1545,9 @@ ImportProperty(JSContext *cx, JSObject *obj, jsid id) ok = OBJ_SET_PROPERTY(cx, target, id, &value); } else { ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, - attrs & ~JSPROP_EXPORTED, + attrs & ~(JSPROP_EXPORTED | + JSPROP_GETTER | + JSPROP_SETTER), NULL); } if (prop) @@ -1697,7 +1623,7 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, : isFunction ? js_function_str : js_var_str; - name = js_AtomToPrintableString(cx, (JSAtom *)id); + name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); if (!name) goto bad; return JS_ReportErrorFlagsAndNumber(cx, report, @@ -1714,6 +1640,80 @@ bad: return JS_FALSE; } +JSBool +js_StrictlyEqual(jsval lval, jsval rval) +{ + jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); + jsdouble ld, rd; + + if (ltag == rtag) { + if (ltag == JSVAL_STRING) { + JSString *lstr = JSVAL_TO_STRING(lval), + *rstr = JSVAL_TO_STRING(rval); + return js_CompareStrings(lstr, rstr) == 0; + } + if (ltag == JSVAL_DOUBLE) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + return lval == rval; + } + if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = JSVAL_TO_INT(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { + ld = JSVAL_TO_INT(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + return lval == rval; +} + +static JSBool +InternStringElementId(JSContext *cx, jsval idval, jsid *idp) +{ + JSAtom *atom; + + atom = js_ValueToStringAtom(cx, idval); + if (!atom) + return JS_FALSE; + *idp = ATOM_TO_JSID(atom); + return JS_TRUE; +} + +static JSBool +InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) +{ + JS_ASSERT(!JSVAL_IS_INT(idval)); + +#if JS_HAS_XML_SUPPORT + if (JSVAL_IS_OBJECT(idval)) { + *idp = OBJECT_JSVAL_TO_JSID(idval); + return JS_TRUE; + } +#endif + + return InternStringElementId(cx, idval, idp); +} + +#if JS_HAS_XML_SUPPORT +#define CHECK_ELEMENT_ID(obj, id) \ + JS_BEGIN_MACRO \ + if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ + SAVE_SP(fp); \ + ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#else +#define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) +#endif + #ifndef MAX_INTERP_LEVEL #if defined(XP_OS2) #define MAX_INTERP_LEVEL 250 @@ -1725,7 +1725,7 @@ bad: #define MAX_INLINE_CALL_COUNT 1000 JSBool -js_Interpret(JSContext *cx, jsval *result) +js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) { JSRuntime *rt; JSStackFrame *fp; @@ -1739,9 +1739,10 @@ js_Interpret(JSContext *cx, jsval *result) jsint depth, len; jsval *sp, *newsp; void *mark; - jsbytecode *pc, *pc2, *endpc; + jsbytecode *endpc, *pc2; JSOp op, op2; const JSCodeSpec *cs; + jsatomid atomIndex; JSAtom *atom; uintN argc, slot, attrs; jsval *vp, lval, rval, ltmp, rtmp; @@ -1768,6 +1769,9 @@ js_Interpret(JSContext *cx, jsval *result) #endif #if JS_HAS_GETTER_SETTER JSPropertyOp getter, setter; +#endif +#if JS_HAS_XML_SUPPORT + JSBool foreach = JS_FALSE; #endif int stackDummy; @@ -1784,7 +1788,7 @@ js_Interpret(JSContext *cx, jsval *result) /* * Optimized Get and SetVersion for proper script language versioning. * - * If any native method or JSClass/JSObjectOps hook calls JS_SetVersion + * 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 @@ -1793,7 +1797,7 @@ js_Interpret(JSContext *cx, jsval *result) currentVersion = script->version; originalVersion = cx->version; if (currentVersion != originalVersion) - JS_SetVersion(cx, currentVersion); + js_SetVersion(cx, currentVersion); /* * Prepare to call a user-supplied branch handler, and abort the script @@ -1824,31 +1828,37 @@ js_Interpret(JSContext *cx, jsval *result) 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; + goto out2; } /* - * Allocate operand and pc stack slots for the script's worst-case depth. + * Allocate operand and pc stack slots for the script's worst-case depth, + * unless we're called to interpret a part of an already active script, a + * filtering predicate expression for example. */ - newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); - if (!newsp) { - ok = JS_FALSE; - goto out; + depth = (jsint) script->depth; + if (JS_LIKELY(!fp->spbase)) { + newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); + if (!newsp) { + ok = JS_FALSE; + goto out2; + } + sp = newsp + depth; + fp->spbase = sp; + SAVE_SP(fp); + } else { + sp = fp->sp; + JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); + newsp = fp->spbase - depth; + mark = NULL; } - sp = newsp + depth; - fp->spbase = sp; - SAVE_SP(fp); + endpc = script->code + script->length; while (pc < endpc) { fp->pc = pc; op = (JSOp) *pc; @@ -1870,7 +1880,7 @@ js_Interpret(JSContext *cx, jsval *result) SAVE_SP(fp); for (n = -nuses; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str != NULL) { + if (str) { fprintf(tracefp, "%s %s", (n == -nuses) ? " inputs:" : ",", JS_GetStringBytes(str)); @@ -1910,7 +1920,6 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_GROUP: - obj = NULL; break; case JSOP_PUSH: @@ -1940,11 +1949,13 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_ENTERWITH: - rval = FETCH_OPND(-1); - VALUE_TO_OBJECT(cx, rval, obj); - withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain); + FETCH_OBJECT(cx, -1, rval, obj); + SAVE_SP(fp); + withobj = js_NewWithObject(cx, obj, fp->scopeChain, + sp - fp->spbase - 1); if (!withobj) goto out; + rval = INT_TO_JSVAL(sp - fp->spbase); fp->scopeChain = withobj; STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); break; @@ -1958,6 +1969,7 @@ js_Interpret(JSContext *cx, jsval *result) rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); JS_ASSERT(JSVAL_IS_OBJECT(rval)); fp->scopeChain = JSVAL_TO_OBJECT(rval); + JS_SetPrivate(cx, withobj, NULL); break; case JSOP_SETRVAL: @@ -1983,6 +1995,17 @@ js_Interpret(JSContext *cx, jsval *result) LOAD_INTERRUPT_HANDLER(rt); } } + +#if JS_HAS_CALL_OBJECT + /* + * If frame has a call object, sync values and clear the back- + * pointer. This can happen for a lightweight function if it + * calls eval unexpectedly (in a way that is hidden from the + * compiler). See bug 325540. + */ + if (fp->callobj) + ok &= js_PutCallObject(cx, fp); +#endif #if JS_HAS_ARGS_OBJECT if (fp->argsobj) ok &= js_PutArgsObject(cx, fp); @@ -1992,7 +2015,7 @@ js_Interpret(JSContext *cx, jsval *result) if (cx->version == currentVersion) { currentVersion = ifp->callerVersion; if (currentVersion != cx->version) - JS_SetVersion(cx, currentVersion); + js_SetVersion(cx, currentVersion); } /* Store the return value in the caller's operand frame. */ @@ -2114,38 +2137,40 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_TOOBJECT: - SAVE_SP(fp); - ok = js_ValueToObject(cx, FETCH_OPND(-1), &obj); - if (!ok) - goto out; + rval = FETCH_OPND(-1); + if (!JSVAL_IS_PRIMITIVE(rval)) { + obj = JSVAL_TO_OBJECT(rval); + } else { + SAVE_SP(fp); + ok = js_ValueToObject(cx, rval, &obj); + if (!ok) + goto out; + } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); break; +/* + * If the index value at sp[n] is not an int that fits in a jsval, it could + * be an object (an XML QName, AttributeName, or AnyName), but only if we are + * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a + * string atom id. + */ #define FETCH_ELEMENT_ID(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; \ + jsval idval_ = FETCH_OPND(n); \ + if (JSVAL_IS_INT(idval_)) { \ + id = INT_JSVAL_TO_JSID(idval_); \ } else { \ SAVE_SP(fp); \ - atom = js_ValueToStringAtom(cx, (jsval)id); \ - if (!atom) { \ - ok = JS_FALSE; \ + ok = InternNonIntElementId(cx, idval_, &id); \ + if (!ok) \ 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: + SAVE_SP(fp); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { str = js_DecompileValueGenerator(cx, -1, rval, NULL); @@ -2157,12 +2182,13 @@ js_Interpret(JSContext *cx, jsval *result) ok = JS_FALSE; goto out; } - sp--; obj = JSVAL_TO_OBJECT(rval); - FETCH_ELEMENT_ID(-1, id); + FETCH_ELEMENT_ID(-2, id); + CHECK_ELEMENT_ID(obj, id); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; + sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); @@ -2176,13 +2202,13 @@ js_Interpret(JSContext *cx, jsval *result) */ lval = FETCH_OPND(-1); atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); i = -2; goto do_forinloop; case JSOP_FORNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); /* * ECMA 12.6.3 says to eval the LHS after looking for properties @@ -2248,14 +2274,31 @@ js_Interpret(JSContext *cx, jsval *result) vp = &sp[i - 1]; rval = *vp; + /* + * Save sp in fp now, before any OBJ_* call-outs that might nest + * an interpreter or GC activation on this context. + */ + SAVE_SP(fp); + /* 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); + /* + * Yes, create a new JSObject to hold the iterator state. + * Use NULL as the nominal parent in js_NewObject to ensure + * that we use the correct scope chain lookup to try to find the + * PropertyIterator constructor. + */ + propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL); if (!propobj) { ok = JS_FALSE; goto out; } + + /* + * Now that we've resolved the object, use the PARENT slot to + * store the object that we're iterating over. + */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; /* @@ -2275,20 +2318,27 @@ js_Interpret(JSContext *cx, jsval *result) */ *vp = OBJECT_TO_JSVAL(propobj); - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_INIT, &iter_state, + NULL, NULL) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, + NULL); + if (!ok) + goto out; /* * 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); @@ -2298,8 +2348,19 @@ js_Interpret(JSContext *cx, jsval *result) } enum_next_property: - /* Get the next jsid to be enumerated and store it in rval. */ - OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval); + { + jsid fid; + + /* Get the next jsid to be enumerated and store it in fid. */ + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_NEXT, &iter_state, + &fid, &rval) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &fid); propobj->slots[JSSLOT_ITER_STATE] = iter_state; /* No more jsids to iterate in obj? */ @@ -2309,10 +2370,22 @@ js_Interpret(JSContext *cx, jsval *result) if (!obj) { /* End of property list -- terminate loop. */ rval = JSVAL_FALSE; +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif goto end_forinloop; } - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0); + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_INIT, &iter_state, + NULL, NULL) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, + NULL); /* * Stash private iteration state into property iterator object. @@ -2333,29 +2406,76 @@ js_Interpret(JSContext *cx, jsval *result) 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); + /* Skip properties not owned by obj when looking from origobj. */ + ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop); if (!ok) goto out; - if (prop) { + if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); - /* Yes, don't enumerate again. Go to the next property. */ - if (obj2 != obj) + /* + * If the id was deleted, or found in a prototype or an unrelated + * object (specifically, not in an inner object for obj), skip it. + * This means that OBJ_LOOKUP_PROPERTY implementations must return + * an object either further on the prototype chain, or related by + * the JSExtendedClass.outerObject optional hook. + */ + if (!prop) + goto enum_next_property; + if (obj != obj2) { + cond = JS_FALSE; + clasp = OBJ_GET_CLASS(cx, obj2); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp; + + xclasp = (JSExtendedClass *) clasp; + cond = xclasp->outerObject && + xclasp->outerObject(cx, obj2) == obj; + } + if (!cond) 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; +#if JS_HAS_XML_SUPPORT + if (foreach) { + /* Clear the local foreach flag set by our prefix bytecode. */ + foreach = JS_FALSE; + + /* If obj is not XML, we must get rval given its fid. */ + if (!OBJECT_IS_XML(cx, obj)) { + ok = OBJ_GET_PROPERTY(cx, origobj, fid, &rval); + if (!ok) + goto out; + } + } else +#endif + { + /* Make rval a string for uniformity and compatibility. */ + if (JSID_IS_ATOM(fid)) { + rval = ATOM_KEY(JSID_TO_ATOM(fid)); + } +#if JS_HAS_XML_SUPPORT + else if (JSID_IS_OBJECT(fid)) { + str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(fid)); + if (!str) { + ok = JS_FALSE; + goto out; + } + + rval = STRING_TO_JSVAL(str); } +#endif + else if (!JS_VERSION_IS_1_2(cx)) { + str = js_NumberToString(cx, (jsdouble) JSID_TO_INT(fid)); + if (!str) { + ok = JS_FALSE; + goto out; + } - rval = STRING_TO_JSVAL(str); + rval = STRING_TO_JSVAL(str); + } else { + rval = INT_JSID_TO_JSVAL(fid); + } } switch (op) { @@ -2379,6 +2499,8 @@ js_Interpret(JSContext *cx, jsval *result) default: /* Convert lval to a non-null object containing id. */ VALUE_TO_OBJECT(cx, lval, obj); + if (i + 1 < 0) + STORE_OPND(i + 1, OBJECT_TO_JSVAL(obj)); /* Set the variable obj[id] to refer to rval. */ fp->flags |= JSFRAME_ASSIGNING; @@ -2396,6 +2518,7 @@ js_Interpret(JSContext *cx, jsval *result) sp += i + 1; PUSH_OPND(rval); break; + } case JSOP_DUP: JS_ASSERT(sp > fp->spbase); @@ -2413,9 +2536,8 @@ js_Interpret(JSContext *cx, jsval *result) #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); \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n, lval, obj); \ \ /* Get or set the property, set ok false if error, true if success. */\ SAVE_SP(fp); \ @@ -2426,16 +2548,31 @@ js_Interpret(JSContext *cx, jsval *result) #define ELEMENT_OP(n, call) \ JS_BEGIN_MACRO \ + /* Fetch the right part and resolve it to an internal id. */ \ FETCH_ELEMENT_ID(n, id); \ - PROPERTY_OP(n-1, call); \ + \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n - 1, lval, obj); \ + \ + /* Ensure that id has a type suitable for use with obj. */ \ + CHECK_ELEMENT_ID(obj, id); \ + \ + /* Get or set the element, set ok false if error, true if success. */ \ + SAVE_SP(fp); \ + call; \ + if (!ok) \ + goto out; \ 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. + * in case a getter or setter function is invoked. CACHED_GET and CACHED_SET + * use cx, obj, id, and rval from their caller's lexical environment. */ -#define CACHED_GET(call) \ +#define CACHED_GET(call) CACHED_GET_VP(call, &rval) + +#define CACHED_GET_VP(call,vp) \ JS_BEGIN_MACRO \ if (!OBJ_IS_NATIVE(obj)) { \ ok = call; \ @@ -2445,14 +2582,14 @@ js_Interpret(JSContext *cx, jsval *result) if (sprop) { \ JSScope *scope_ = OBJ_SCOPE(obj); \ slot = (uintN)sprop->slot; \ - rval = (slot != SPROP_INVALID_SLOT) \ - ? LOCKED_OBJ_GET_SLOT(obj, slot) \ - : JSVAL_VOID; \ + *(vp) = (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); \ + ok = SPROP_GET(cx, sprop, obj, obj, vp); \ JS_LOCK_SCOPE(cx, scope_); \ if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ - LOCKED_OBJ_SET_SLOT(obj, slot, rval); \ + LOCKED_OBJ_SET_SLOT(obj, slot, *(vp)); \ JS_UNLOCK_SCOPE(cx, scope_); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ @@ -2489,33 +2626,42 @@ js_Interpret(JSContext *cx, jsval *result) } \ JS_END_MACRO - case JSOP_SETCONST: +#define BEGIN_LITOPX_CASE(OP,PCOFF) \ + case OP: \ + atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ + do_##OP: \ + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + +#define END_LITOPX_CASE \ + break; \ + + BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) obj = fp->varobj; - atom = GET_ATOM(cx, script, pc); rval = FETCH_OPND(-1); - ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, rval, NULL, NULL, + SAVE_SP(fp); + ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, + NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); if (!ok) goto out; STORE_OPND(-1, rval); - break; + END_LITOPX_CASE - case JSOP_BINDNAME: - atom = GET_ATOM(cx, script, pc); + BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) SAVE_SP(fp); - obj = js_FindIdentifierBase(cx, (jsid)atom); + obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - break; + END_LITOPX_CASE case JSOP_SETNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); @@ -2526,6 +2672,7 @@ js_Interpret(JSContext *cx, jsval *result) goto out; sp--; STORE_OPND(-1, rval); + obj = NULL; break; #define INTEGER_OP(OP, EXTRA_CODE) \ @@ -2555,15 +2702,6 @@ js_Interpret(JSContext *cx, jsval *result) 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); \ @@ -2577,10 +2715,11 @@ js_Interpret(JSContext *cx, jsval *result) } 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); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ } \ } else { \ VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ + sp[-2] = lval; \ VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ str = JSVAL_TO_STRING(lval); \ @@ -2589,19 +2728,63 @@ js_Interpret(JSContext *cx, jsval *result) } else { \ VALUE_TO_NUMBER(cx, lval, d); \ VALUE_TO_NUMBER(cx, rval, d2); \ - cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ } \ } \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO +/* + * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies + * because they begin if/else chains, so callers must not put semicolons after + * the call expressions! + */ +#if JS_HAS_XML_SUPPORT +#define XML_EQUALITY_OP(OP) \ + if ((ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + OBJECT_IS_XML(cx, obj2)) || \ + (rtmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(rval)) && \ + OBJECT_IS_XML(cx, obj2))) { \ + JSXMLObjectOps *ops; \ + \ + ops = (JSXMLObjectOps *) obj2->map->ops; \ + if (obj2 == JSVAL_TO_OBJECT(rval)) \ + rval = lval; \ + SAVE_SP(fp); \ + ok = ops->equality(cx, obj2, rval, &cond); \ + if (!ok) \ + goto out; \ + cond = cond OP JS_TRUE; \ + } else + +#define XML_NAME_EQUALITY_OP(OP) \ + if (ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ + JSExtendedClass *xclasp; \ + \ + xclasp = (JSExtendedClass *) clasp; \ + SAVE_SP(fp); \ + ok = xclasp->equality(cx, obj2, rval, &cond); \ + if (!ok) \ + goto out; \ + cond = cond OP JS_TRUE; \ + } else +#else +#define XML_EQUALITY_OP(OP) /* nothing */ +#define XML_NAME_EQUALITY_OP(OP) /* nothing */ +#endif + #define EQUALITY_OP(OP, IFNAN) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ ltmp = JSVAL_TAG(lval); \ rtmp = JSVAL_TAG(rval); \ + XML_EQUALITY_OP(OP) \ if (ltmp == rtmp) { \ if (ltmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ @@ -2610,8 +2793,9 @@ js_Interpret(JSContext *cx, jsval *result) } else if (ltmp == JSVAL_DOUBLE) { \ d = *JSVAL_TO_DOUBLE(lval); \ d2 = *JSVAL_TO_DOUBLE(rval); \ - cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } else { \ + XML_NAME_EQUALITY_OP(OP) \ /* Handle all undefined (=>NaN) and int combinations. */ \ cond = lval OP rval; \ } \ @@ -2622,10 +2806,12 @@ js_Interpret(JSContext *cx, jsval *result) cond = 1 OP 0; \ } else { \ if (ltmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval); \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ + lval = sp[-2]; \ ltmp = JSVAL_TAG(lval); \ } else if (rtmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval); \ + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ + rval = sp[-1]; \ rtmp = JSVAL_TAG(rval); \ } \ if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ @@ -2635,7 +2821,7 @@ js_Interpret(JSContext *cx, jsval *result) } else { \ VALUE_TO_NUMBER(cx, lval, d); \ VALUE_TO_NUMBER(cx, rval, d2); \ - cond = COMPARE_DOUBLES(d, OP, d2, IFNAN); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } \ } \ } \ @@ -2652,52 +2838,26 @@ js_Interpret(JSContext *cx, jsval *result) break; #if !JS_BUG_FALLIBLE_EQOPS -#define NEW_EQUALITY_OP(OP, IFNAN) \ +#define NEW_EQUALITY_OP(OP) \ 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; \ - } \ - } \ + cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO case JSOP_NEW_EQ: - NEW_EQUALITY_OP(==, JS_FALSE); + NEW_EQUALITY_OP(==); break; case JSOP_NEW_NE: - NEW_EQUALITY_OP(!=, JS_TRUE); + NEW_EQUALITY_OP(!=); break; #if JS_HAS_SWITCH_STATEMENT case JSOP_CASE: - NEW_EQUALITY_OP(==, JS_FALSE); + NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMP_OFFSET(pc); @@ -2708,7 +2868,7 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_CASEX: - NEW_EQUALITY_OP(==, JS_FALSE); + NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMPX_OFFSET(pc); @@ -2768,32 +2928,52 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_ADD: rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, <mp); - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp); - if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) { +#if JS_HAS_XML_SUPPORT + if (!JSVAL_IS_PRIMITIVE(lval) && + (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && + VALUE_IS_XML(cx, rval)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj2->map->ops; 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; - } + ok = ops->concatenate(cx, obj2, rval, &rval); 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); + STORE_OPND(-1, rval); + break; + } +#endif + { + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); + lval = sp[-2]; + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); + rval = sp[-1]; + if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { + SAVE_SP(fp); + if (cond) { + str = JSVAL_TO_STRING(lval); + ok = (str2 = js_ValueToString(cx, rval)) != NULL; + } else { + str2 = JSVAL_TO_STRING(rval); + ok = (str = js_ValueToString(cx, lval)) != 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; @@ -2892,6 +3072,7 @@ js_Interpret(JSContext *cx, jsval *result) #if JS_HAS_INITIALIZERS do_new: #endif + SAVE_SP(fp); vp = sp - (2 + argc); JS_ASSERT(vp >= fp->spbase); @@ -2904,7 +3085,6 @@ js_Interpret(JSContext *cx, jsval *result) 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; @@ -2917,12 +3097,19 @@ js_Interpret(JSContext *cx, jsval *result) proto = parent = NULL; fun = NULL; } else { - /* Get the constructor prototype object for this function. */ + /* + * Get the constructor prototype object for this function. + * Use the nominal |this| parameter slot, vp[1], as a local + * root to protect this prototype, in case it has no other + * strong refs. + */ ok = OBJ_GET_PROPERTY(cx, obj2, - (jsid)rt->atomState.classPrototypeAtom, - &rval); + ATOM_TO_JSID(rt->atomState + .classPrototypeAtom), + &vp[1]); if (!ok) goto out; + rval = vp[1]; proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; parent = OBJ_GET_PARENT(cx, obj2); @@ -2940,7 +3127,6 @@ js_Interpret(JSContext *cx, jsval *result) /* 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); @@ -2953,17 +3139,14 @@ js_Interpret(JSContext *cx, jsval *result) /* Check the return value and update obj from it. */ rval = *vp; if (JSVAL_IS_PRIMITIVE(rval)) { - if (fun || !JSVERSION_IS_ECMA(cx->version)) { + if (fun || !JS_VERSION_IS_ECMA(cx)) { *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)); - } + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); ok = JS_FALSE; goto out; } @@ -2973,7 +3156,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_DELNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -2993,7 +3176,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_DELPROP: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); STORE_OPND(-1, rval); break; @@ -3005,11 +3188,11 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_TYPEOF: - rval = POP_OPND(); + rval = FETCH_OPND(-1); + SAVE_SP(fp); type = JS_TypeOfValue(cx, rval); atom = rt->atomState.typeAtoms[type]; - str = ATOM_TO_STRING(atom); - PUSH_OPND(STRING_TO_JSVAL(str)); + STORE_OPND(-1, ATOM_KEY(atom)); break; case JSOP_VOID: @@ -3022,7 +3205,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_NAMEINC: case JSOP_NAMEDEC: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -3033,6 +3216,7 @@ js_Interpret(JSContext *cx, jsval *result) OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); + i = 0; goto do_incop; case JSOP_INCPROP: @@ -3040,19 +3224,24 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_PROPINC: case JSOP_PROPDEC: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; - lval = POP_OPND(); + id = ATOM_TO_JSID(atom); + lval = FETCH_OPND(-1); + i = -1; goto do_incop; case JSOP_INCELEM: case JSOP_DECELEM: case JSOP_ELEMINC: case JSOP_ELEMDEC: - POP_ELEMENT_ID(id); - lval = POP_OPND(); + FETCH_ELEMENT_ID(-1, id); + lval = FETCH_OPND(-2); + i = -2; do_incop: VALUE_TO_OBJECT(cx, lval, obj); + if (i < 0) + STORE_OPND(i, OBJECT_TO_JSVAL(obj)); + CHECK_ELEMENT_ID(obj, id); /* The operand must contain a number. */ SAVE_SP(fp); @@ -3076,7 +3265,9 @@ js_Interpret(JSContext *cx, jsval *result) /* * 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. + * value goes in rval. Our caller must set vp to point at a GC-rooted jsval + * in which we home rtmp, to protect it from GC in case the unconverted rval + * is not a number. */ #define NONINT_INCREMENT_OP_MIDDLE() \ JS_BEGIN_MACRO \ @@ -3087,6 +3278,7 @@ js_Interpret(JSContext *cx, jsval *result) ok = js_NewNumberValue(cx, d, &rtmp); \ if (!ok) \ goto out; \ + *vp = rtmp; \ } \ (cs->format & JOF_INC) ? d++ : d--; \ ok = js_NewNumberValue(cx, d, &rval); \ @@ -3099,12 +3291,30 @@ js_Interpret(JSContext *cx, jsval *result) goto out; \ JS_END_MACRO + if (cs->format & JOF_POST) { + /* + * We must push early to protect the postfix increment + * or decrement result, if converted to a jsdouble from + * a non-number value, from GC nesting in the setter. + */ + vp = sp; + PUSH(JSVAL_VOID); + SAVE_SP(fp); + --i; + } +#ifdef __GNUC__ + else vp = NULL; /* suppress bogus gcc warnings */ +#endif + NONINT_INCREMENT_OP_MIDDLE(); } + fp->flags |= JSFRAME_ASSIGNING; CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + fp->flags &= ~JSFRAME_ASSIGNING; if (!ok) goto out; + sp += i; PUSH_OPND(rtmp); break; @@ -3187,15 +3397,17 @@ js_Interpret(JSContext *cx, jsval *result) #undef FAST_GLOBAL_INCREMENT_OP do_nonint_fast_global_incop: + vp = sp++; + SAVE_SP(fp); NONINT_INCREMENT_OP_MIDDLE(); OBJ_SET_SLOT(cx, obj, slot, rval); - PUSH_OPND(rtmp); + STORE_OPND(-1, rtmp); break; case JSOP_GETPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); STORE_OPND(-1, rval); break; @@ -3206,10 +3418,11 @@ js_Interpret(JSContext *cx, jsval *result) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); + obj = NULL; break; case JSOP_GETELEM: @@ -3223,13 +3436,14 @@ js_Interpret(JSContext *cx, jsval *result) ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp -= 2; STORE_OPND(-1, rval); + obj = NULL; 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); + FETCH_OBJECT(cx, -2, lval, obj); + CHECK_ELEMENT_ID(obj, id); rval = FETCH_OPND(-3); SAVE_SP(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); @@ -3322,7 +3536,7 @@ js_Interpret(JSContext *cx, jsval *result) newifp->mark = newmark; /* Compute the 'this' parameter now that argv is set. */ - ok = ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); + ok = js_ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); if (!ok) { js_FreeRawStack(cx, newmark); goto bad_inline_call; @@ -3352,7 +3566,7 @@ js_Interpret(JSContext *cx, jsval *result) if (cx->version == currentVersion) { currentVersion = script->version; if (currentVersion != cx->version) - JS_SetVersion(cx, currentVersion); + js_SetVersion(cx, currentVersion); } /* Push the frame and set interpreter registers. */ @@ -3426,7 +3640,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_NAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -3482,15 +3696,86 @@ js_Interpret(JSContext *cx, jsval *result) obj = NULL; break; + case JSOP_UINT24: + i = (jsint) GET_LITERAL_INDEX(pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); + break; + + case JSOP_LITERAL: + atomIndex = GET_LITERAL_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + PUSH_OPND(ATOM_KEY(atom)); + obj = NULL; + break; + + case JSOP_FINDNAME: + atomIndex = GET_LITERAL_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + SAVE_SP(fp); + obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + PUSH_OPND(ATOM_KEY(atom)); + break; + + case JSOP_LITOPX: + atomIndex = GET_LITERAL_INDEX(pc); + op = pc[1 + LITERAL_INDEX_LEN]; + switch (op) { + case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; + case JSOP_BINDNAME: goto do_JSOP_BINDNAME; + case JSOP_CLOSURE: goto do_JSOP_CLOSURE; + case JSOP_DEFCONST: goto do_JSOP_DEFCONST; + case JSOP_DEFFUN: goto do_JSOP_DEFFUN; + case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; + case JSOP_DEFVAR: goto do_JSOP_DEFVAR; +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; +#endif +#if JS_HAS_XML_SUPPORT + case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; + case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; +#endif + case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR; + case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; + case JSOP_NUMBER: goto do_JSOP_NUMBER; + case JSOP_OBJECT: goto do_JSOP_OBJECT; +#if JS_HAS_XML_SUPPORT + case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; + case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; +#endif + case JSOP_REGEXP: goto do_JSOP_REGEXP; + case JSOP_SETCONST: goto do_JSOP_SETCONST; + case JSOP_STRING: goto do_JSOP_STRING; +#if JS_HAS_XML_SUPPORT + case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; + case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; + case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; + case JSOP_XMLPI: goto do_JSOP_XMLPI; +#endif + default: JS_ASSERT(0); + } + /* NOTREACHED */ + break; + case JSOP_NUMBER: case JSOP_STRING: case JSOP_OBJECT: - atom = GET_ATOM(cx, script, pc); + atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_NUMBER: + do_JSOP_STRING: + do_JSOP_OBJECT: + atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; break; - case JSOP_REGEXP: + BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) { JSRegExp *re; JSObject *funobj; @@ -3519,7 +3804,6 @@ js_Interpret(JSContext *cx, jsval *result) * 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); @@ -3558,6 +3842,15 @@ js_Interpret(JSContext *cx, jsval *result) while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) obj2 = parent; + /* + * We must home sp here, because either js_CloneRegExpObject + * or JS_SetReservedSlot could nest a last-ditch GC. We home + * pc as well, in case js_CloneRegExpObject has to lookup the + * "RegExp" class in the global object, which could entail a + * JSNewResolveOp call. + */ + SAVE_SP(fp); + /* * If obj's parent is not obj2, we must clone obj so that it * has the right parent, and therefore, the right prototype. @@ -3602,8 +3895,8 @@ js_Interpret(JSContext *cx, jsval *result) PUSH_OPND(rval); obj = NULL; - break; } + END_LITOPX_CASE case JSOP_ZERO: PUSH_OPND(JSVAL_ZERO); @@ -3621,7 +3914,22 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_THIS: - PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp)); + obj = fp->thisp; + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp; + + xclasp = (JSExtendedClass *) clasp; + if (xclasp->outerObject) { + obj = xclasp->outerObject(cx, obj); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + } + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; break; @@ -3645,8 +3953,8 @@ js_Interpret(JSContext *cx, jsval *result) * 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) { + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) break; @@ -3728,8 +4036,8 @@ js_Interpret(JSContext *cx, jsval *result) * 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) { + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) break; @@ -3809,6 +4117,7 @@ js_Interpret(JSContext *cx, jsval *result) #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTALL: + SAVE_SP(fp); obj = fp->varobj; ida = JS_Enumerate(cx, obj); if (!ida) { @@ -3834,10 +4143,10 @@ js_Interpret(JSContext *cx, jsval *result) } break; - case JSOP_EXPORTNAME: - atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) + id = ATOM_TO_JSID(atom); obj = fp->varobj; + SAVE_SP(fp); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; @@ -3854,10 +4163,10 @@ js_Interpret(JSContext *cx, jsval *result) } if (!ok) goto out; - break; + END_LITOPX_CASE case JSOP_IMPORTALL: - id = (jsid)JSVAL_VOID; + id = (jsid) JSVAL_VOID; PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; break; @@ -3865,7 +4174,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_IMPORTPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; break; @@ -3911,7 +4220,7 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_ARGSUB: - id = (jsid) INT_TO_JSVAL(GET_ARGNO(pc)); + id = INT_TO_JSID(GET_ARGNO(pc)); SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) @@ -3933,7 +4242,7 @@ js_Interpret(JSContext *cx, jsval *result) #undef LAZY_ARGS_THISP case JSOP_ARGCNT: - id = (jsid) rt->atomState.lengthAtom; + id = ATOM_TO_JSID(rt->atomState.lengthAtom); SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) @@ -4000,7 +4309,7 @@ js_Interpret(JSContext *cx, jsval *result) * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) @@ -4011,14 +4320,15 @@ js_Interpret(JSContext *cx, jsval *result) GC_POKE(cx, obj->slots[slot]); OBJ_SET_SLOT(cx, obj, slot, rval); } + obj = NULL; break; case JSOP_DEFCONST: case JSOP_DEFVAR: - { - jsatomid atomIndex; - atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_DEFCONST: + do_JSOP_DEFVAR: atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = fp->varobj; attrs = JSPROP_ENUMERATE; @@ -4028,7 +4338,8 @@ js_Interpret(JSContext *cx, jsval *result) attrs |= JSPROP_READONLY; /* Lookup id in order to check for redeclaration problems. */ - id = (jsid)atom; + id = ATOM_TO_JSID(atom); + SAVE_SP(fp); ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); if (!ok) goto out; @@ -4049,7 +4360,7 @@ js_Interpret(JSContext *cx, jsval *result) * and has stub getter and setter, into a "fast global" accessed * by the JSOP_*GVAR opcodes. */ - if (script->numGlobalVars && + if (atomIndex < script->numGlobalVars && (attrs & JSPROP_PERMANENT) && obj2 == obj && OBJ_IS_NATIVE(obj)) { @@ -4069,18 +4380,16 @@ js_Interpret(JSContext *cx, jsval *result) OBJ_DROP_PROPERTY(cx, obj2, prop); break; - } - case JSOP_DEFFUN: + BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) { - 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; + id = ATOM_TO_JSID(fun->atom); /* * We must be at top-level (either outermost block that forms a @@ -4089,8 +4398,8 @@ js_Interpret(JSContext *cx, jsval *result) * 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. + * with statement, so we need to distinguish scope chain head from + * variables object. Hence the obj2 vs. parent distinction below. * First we make sure the function object we're defining has the * right scope chain. Then we define its name in fp->varobj. * @@ -4112,15 +4421,23 @@ js_Interpret(JSContext *cx, jsval *result) * 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); + obj2 = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + /* * ECMA requires functions defined when entering Global code to be * permanent, and functions defined when entering Eval code to be @@ -4136,8 +4453,10 @@ js_Interpret(JSContext *cx, jsval *result) * in the property itself, not in obj->slots. */ flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); - if (flags) + if (flags) { attrs |= flags | JSPROP_SHARED; + rval = JSVAL_VOID; + } /* * Check for a const property of the same name -- or any kind @@ -4146,22 +4465,26 @@ js_Interpret(JSContext *cx, jsval *result) * as well as multiple HTML script tags. */ parent = fp->varobj; + SAVE_SP(fp); ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); - if (!ok) - goto out; + if (ok) { + ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, + (flags & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (flags & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs, + &prop); + } - 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); + /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ + fp->scopeChain = obj2; if (!ok) goto out; + +#if 0 if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && script->numGlobalVars) { /* @@ -4172,12 +4495,13 @@ js_Interpret(JSContext *cx, jsval *result) sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } +#endif OBJ_DROP_PROPERTY(cx, parent, prop); - break; } + END_LITOPX_CASE #if JS_HAS_LEXICAL_CLOSURE - case JSOP_DEFLOCALFUN: + BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) /* * Define a local function (i.e., one nested at the top level of * another function), parented by the current scope chain, and @@ -4185,15 +4509,14 @@ js_Interpret(JSContext *cx, jsval *result) * 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); + slot = GET_VARNO(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); parent = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != parent) { + SAVE_SP(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; @@ -4201,16 +4524,16 @@ js_Interpret(JSContext *cx, jsval *result) } } fp->vars[slot] = OBJECT_TO_JSVAL(obj); - break; + END_LITOPX_CASE - case JSOP_ANONFUNOBJ: + BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) /* 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) { + SAVE_SP(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; @@ -4218,11 +4541,11 @@ js_Interpret(JSContext *cx, jsval *result) } } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - break; + obj = NULL; + END_LITOPX_CASE - case JSOP_NAMEDFUNOBJ: + BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ - atom = GET_ATOM(cx, script, pc); rval = ATOM_KEY(atom); JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); @@ -4235,8 +4558,8 @@ js_Interpret(JSContext *cx, jsval *result) * of the Function object clone. */ SAVE_SP(fp); - parent = js_ConstructObject(cx, &js_ObjectClass, NULL, - fp->scopeChain, 0, NULL); + obj2 = fp->scopeChain; + parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); if (!parent) { ok = JS_FALSE; goto out; @@ -4247,13 +4570,30 @@ js_Interpret(JSContext *cx, jsval *result) * 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]. + * + * Protect parent from GC after js_CloneFunctionObject calls into + * js_NewObject, which displaces the newborn object root in cx by + * allocating the clone, then runs a last-ditch GC while trying + * to allocate the clone's slots vector. Another, multi-threaded + * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS + * which may suspend the current request in ClaimScope, with the + * newborn displaced as in the first scenario. */ + fp->scopeChain = parent; obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); if (!obj) { ok = JS_FALSE; goto out; } + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + /* * 4. Create a property in the object Result(1). The property's * name is [fun->atom, the identifier parsed by the compiler], @@ -4261,10 +4601,11 @@ js_Interpret(JSContext *cx, jsval *result) */ fun = (JSFunction *) JS_GetPrivate(cx, obj); attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); - if (attrs) + if (attrs) { attrs |= JSPROP_SHARED; - ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, - attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), + rval = JSVAL_VOID; + } + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, (attrs & JSFUN_GETTER) ? (JSPropertyOp) obj : NULL, @@ -4275,6 +4616,9 @@ js_Interpret(JSContext *cx, jsval *result) JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); + + /* Restore fp->scopeChain now that obj is defined in parent. */ + fp->scopeChain = obj2; if (!ok) { cx->newborn[GCX_OBJECT] = NULL; goto out; @@ -4285,12 +4629,10 @@ js_Interpret(JSContext *cx, jsval *result) * 6. Return Result(3). */ PUSH_OPND(OBJECT_TO_JSVAL(obj)); - break; - - case JSOP_CLOSURE: - { - jsatomid atomIndex; + obj = NULL; + END_LITOPX_CASE + BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) /* * ECMA ed. 3 extension: a named function expression in a compound * statement (not at the top statement level of global code, or at @@ -4299,8 +4641,6 @@ js_Interpret(JSContext *cx, jsval *result) * 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); @@ -4311,15 +4651,24 @@ js_Interpret(JSContext *cx, jsval *result) * 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); + SAVE_SP(fp); + obj2 = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + /* * 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 @@ -4327,11 +4676,12 @@ js_Interpret(JSContext *cx, jsval *result) */ fun = (JSFunction *) JS_GetPrivate(cx, obj); attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); - if (attrs) + if (attrs) { attrs |= JSPROP_SHARED; + rval = JSVAL_VOID; + } parent = fp->varobj; - ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, - attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, (attrs & JSFUN_GETTER) ? (JSPropertyOp) obj : NULL, @@ -4341,10 +4691,15 @@ js_Interpret(JSContext *cx, jsval *result) attrs | JSPROP_ENUMERATE | JSPROP_PERMANENT, &prop); + + /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ + fp->scopeChain = obj2; if (!ok) { cx->newborn[GCX_OBJECT] = NULL; goto out; } + +#if 0 if (attrs == 0 && script->numGlobalVars) { /* * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals @@ -4354,9 +4709,9 @@ js_Interpret(JSContext *cx, jsval *result) sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } +#endif OBJ_DROP_PROPERTY(cx, parent, prop); - break; - } + END_LITOPX_CASE #endif /* JS_HAS_LEXICAL_CLOSURE */ #if JS_HAS_GETTER_SETTER @@ -4370,34 +4725,33 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_SETNAME: case JSOP_SETPROP: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); i = -1; - rval = FETCH_OPND(i); goto gs_pop_lval; case JSOP_SETELEM: rval = FETCH_OPND(-1); + FETCH_ELEMENT_ID(-2, id); i = -2; - FETCH_ELEMENT_ID(i, id); gs_pop_lval: - lval = FETCH_OPND(i-1); - VALUE_TO_OBJECT(cx, lval, obj); + FETCH_OBJECT(cx, i - 1, lval, obj); break; #if JS_HAS_INITIALIZERS case JSOP_INITPROP: JS_ASSERT(sp - fp->spbase >= 2); + rval = FETCH_OPND(-1); i = -1; - rval = FETCH_OPND(i); atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); goto gs_get_lval; case JSOP_INITELEM: JS_ASSERT(sp - fp->spbase >= 3); rval = FETCH_OPND(-1); + FETCH_ELEMENT_ID(-2, id); i = -2; - FETCH_ELEMENT_ID(i, id); gs_get_lval: lval = FETCH_OPND(i-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); @@ -4409,6 +4763,10 @@ js_Interpret(JSContext *cx, jsval *result) JS_ASSERT(0); } + /* Ensure that id has a type suitable for use with obj. */ + CHECK_ELEMENT_ID(obj, id); + + SAVE_SP(fp); if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, @@ -4448,6 +4806,7 @@ js_Interpret(JSContext *cx, jsval *result) if (!ok) goto out; + obj = NULL; sp += i; if (cs->ndefs) STORE_OPND(-1, rval); @@ -4478,7 +4837,7 @@ js_Interpret(JSContext *cx, jsval *result) /* Get the immediate property name into id. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); i = -1; goto do_init; @@ -4497,7 +4856,11 @@ js_Interpret(JSContext *cx, jsval *result) JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); + /* Ensure that id has a type suitable for use with obj. */ + CHECK_ELEMENT_ID(obj, id); + /* Set the property named by obj[id] to rval. */ + SAVE_SP(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; @@ -4506,6 +4869,7 @@ js_Interpret(JSContext *cx, jsval *result) #if JS_HAS_SHARP_VARS case JSOP_DEFSHARP: + SAVE_SP(fp); obj = fp->sharpArray; if (!obj) { obj = js_NewArrayObject(cx, 0, NULL); @@ -4516,7 +4880,7 @@ js_Interpret(JSContext *cx, jsval *result) fp->sharpArray = obj; } i = (jsint) GET_ATOM_INDEX(pc); - id = (jsid) INT_TO_JSVAL(i); + id = INT_TO_JSID(i); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { char numBuf[12]; @@ -4533,11 +4897,12 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); - id = (jsid) INT_TO_JSVAL(i); + id = INT_TO_JSID(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; } else { + SAVE_SP(fp); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; @@ -4566,15 +4931,34 @@ js_Interpret(JSContext *cx, jsval *result) i = (jsint) GET_ATOM_INDEX(pc); JS_ASSERT(i >= 0); sp = fp->spbase + i; + + obj = fp->scopeChain; + while (OBJ_GET_CLASS(cx, obj) == &js_WithClass && + JS_GetPrivate(cx, obj) == fp && + OBJ_BLOCK_DEPTH(cx, obj) >= i) { + obj = OBJ_GET_PARENT(cx, obj); + } + fp->scopeChain = obj; break; case JSOP_GOSUB: + JS_ASSERT(cx->exception != JSVAL_HOLE); + if (!cx->throwing) { + lval = JSVAL_HOLE; + } else { + lval = cx->exception; + cx->throwing = JS_FALSE; + } + PUSH(lval); i = PTRDIFF(pc, script->main, jsbytecode) + len; len = GET_JUMP_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); break; case JSOP_GOSUBX: + JS_ASSERT(cx->exception != JSVAL_HOLE); + lval = cx->throwing ? cx->exception : JSVAL_HOLE; + PUSH(lval); i = PTRDIFF(pc, script->main, jsbytecode) + len; len = GET_JUMPX_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); @@ -4583,6 +4967,19 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_RETSUB: rval = POP(); JS_ASSERT(JSVAL_IS_INT(rval)); + lval = POP(); + if (lval != JSVAL_HOLE) { + /* + * Exception was pending during finally, throw it *before* we + * adjust pc, because pc indexes into script->trynotes. This + * turns out not to be necessary, but it seems clearer. And + * it points out a FIXME: 350509, due to Igor Bukanov. + */ + cx->throwing = JS_TRUE; + cx->exception = lval; + ok = JS_FALSE; + goto out; + } i = JSVAL_TO_INT(rval); pc = script->main + i; len = 0; @@ -4590,6 +4987,12 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_EXCEPTION: PUSH(cx->exception); + cx->throwing = JS_FALSE; + break; + + case JSOP_THROWING: + JS_ASSERT(!cx->throwing); + cx->throwing = JS_TRUE; break; case JSOP_THROW: @@ -4599,32 +5002,48 @@ js_Interpret(JSContext *cx, jsval *result) /* let the code at out try to catch the exception. */ goto out; - case JSOP_INITCATCHVAR: - /* Pop the property's value into rval. */ + BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) + /* Load the value into rval, while keeping it live on stack. */ JS_ASSERT(sp - fp->spbase >= 2); - rval = POP_OPND(); + rval = FETCH_OPND(-1); /* Get the immediate catch variable name into id. */ - atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); /* Find the object being initialized at top of stack. */ - lval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); 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); + SAVE_SP(fp); + + /* + * It's possible for an evil script to substitute a random object + * for the new object. Check to make sure that we don't override a + * readonly property with the below OBJ_DEFINE_PROPERTY. + */ + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); if (!ok) goto out; - break; + if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT | + JSPROP_GETTER | JSPROP_SETTER))) { + /* 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; + } + + /* Now that we're done with rval, pop it. */ + sp--; + END_LITOPX_CASE #endif /* JS_HAS_EXCEPTIONS */ #if JS_HAS_INSTANCEOF case JSOP_INSTANCEOF: rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval)) { + if (JSVAL_IS_PRIMITIVE(rval) || + !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { SAVE_SP(fp); str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { @@ -4635,15 +5054,12 @@ js_Interpret(JSContext *cx, jsval *result) 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; - } + 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; @@ -4680,6 +5096,317 @@ js_Interpret(JSContext *cx, jsval *result) } #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case JSOP_DEFXMLNS: + rval = POP(); + SAVE_SP(fp); + ok = js_SetDefaultXMLNamespace(cx, rval); + if (!ok) + goto out; + break; + + case JSOP_ANYNAME: + SAVE_SP(fp); + ok = js_GetAnyName(cx, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) + PUSH_OPND(ATOM_KEY(atom)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) + rval = ATOM_KEY(atom); + lval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + case JSOP_QNAME: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + SAVE_SP(fp); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOATTRNAME: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_ToAttributeName(cx, &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_TOATTRVAL: + rval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_STRING(rval)); + SAVE_SP(fp); + str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_ADDATTRNAME: + case JSOP_ADDATTRVAL: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + str = JSVAL_TO_STRING(lval); + str2 = JSVAL_TO_STRING(rval); + SAVE_SP(fp); + str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_BINDXMLNAME: + lval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + PUSH_OPND(rval); + break; + + case JSOP_SETXMLNAME: + obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_SetXMLProperty(cx, obj, lval, &rval); + if (!ok) + goto out; + sp -= 2; + STORE_OPND(-1, rval); + obj = NULL; + break; + + case JSOP_XMLNAME: + lval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + ok = js_GetXMLProperty(cx, obj, rval, &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_DESCENDANTS: + case JSOP_DELDESC: + FETCH_OBJECT(cx, -2, lval, obj); + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_GetXMLDescendants(cx, obj, rval, &rval); + if (!ok) + goto out; + + if (op == JSOP_DELDESC) { + sp[-1] = rval; /* set local root */ + ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); + if (!ok) + goto out; + rval = JSVAL_TRUE; /* always succeed */ + } + + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_FILTER: + FETCH_OBJECT(cx, -1, lval, obj); + len = GET_JUMP_OFFSET(pc); + SAVE_SP(fp); + ok = js_FilterXMLList(cx, obj, pc + cs->length, &rval); + if (!ok) + goto out; + JS_ASSERT(fp->sp == sp); + STORE_OPND(-1, rval); + break; + + case JSOP_ENDFILTER: + *result = POP_OPND(); + goto out; + + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + break; + + case JSOP_TOXML: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ValueToXMLObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOXMLLIST: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ValueToXMLListObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_XMLTAGEXPR: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + str = js_ValueToString(cx, rval); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_XMLELTEXPR: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + if (VALUE_IS_XML(cx, rval)) { + str = js_ValueToXMLString(cx, rval); + } else { + str = js_ValueToString(cx, rval); + if (str) + str = js_EscapeElementValue(cx, str); + } + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) + SAVE_SP(fp); + obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) + str = ATOM_TO_STRING(atom); + rval = FETCH_OPND(-1); + str2 = JSVAL_TO_STRING(rval); + SAVE_SP(fp); + obj = js_NewXMLSpecialObject(cx, + JSXML_CLASS_PROCESSING_INSTRUCTION, + str, str2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) + /* Get an immediate atom naming the property. */ + id = ATOM_TO_JSID(atom); + FETCH_OBJECT(cx, -1, lval, obj); + SAVE_SP(fp); + + /* Special-case XML object method lookup, per ECMA-357. */ + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, &rval); + if (!obj) + ok = JS_FALSE; + } else { + CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); + } + if (!ok) + goto out; + STORE_OPND(-1, rval); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) + /* Get an immediate atom naming the property. */ + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); + FETCH_OBJECT(cx, -2, lval, obj); + SAVE_SP(fp); + + /* Special-case XML object method lookup, per ECMA-357. */ + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + ok = ops->setMethod(cx, obj, id, &rval); + } else { + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + } + if (!ok) + goto out; + --sp; + STORE_OPND(-1, rval); + obj = NULL; + END_LITOPX_CASE + + case JSOP_GETFUNNS: + ok = js_GetFunctionNamespace(cx, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_FOREACH: + foreach = JS_TRUE; + break; +#endif /* JS_HAS_XML_SUPPORT */ + default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", op); @@ -4701,6 +5428,8 @@ js_Interpret(JSContext *cx, jsval *result) ndefs = cs->ndefs; if (ndefs) { SAVE_SP(fp); + if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) + --ndefs; for (n = -ndefs; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { @@ -4724,45 +5453,73 @@ js_Interpret(JSContext *cx, jsval *result) out: #if JS_HAS_EXCEPTIONS - /* - * Has an exception been raised? - */ - if (!ok && cx->throwing) { + if (!ok) { /* - * Call debugger throw hook if set (XXX thread safety?). + * Has an exception been raised? Also insist that we are in the + * interpreter activation that pushed fp's operand stack, to avoid + * catching exceptions within XML filtering predicate expressions, + * such as the one from tests/e4x/Regress/regress-301596.js: + * + * try { + * .(@a == 1); + * throw 5; + * } catch (e) { + * } + * + * The inner interpreter activation executing the predicate bytecode + * will throw "reference to undefined XML name @a" (or 5, in older + * versions that followed the first edition of ECMA-357 and evaluated + * unbound identifiers to undefined), and the exception must not be + * caught until control unwinds to the outer interpreter activation. + * + * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, + * and the catch will move into the filtering predicate expression, + * leading to double catch execution if it rethrows. + * + * XXX This assumes the null mark case implies XML filtering predicate + * expression execution! + * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 */ - 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:; + if (cx->throwing && JS_LIKELY(mark != NULL)) { + /* + * 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); } - 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; + /* + * Look for a try block in script that can catch this exception. + */ + SCRIPT_FIND_CATCH_START(script, pc, pc); + if (pc) { + /* Don't clear cx->throwing to save cx->exception from GC. */ + len = 0; + ok = JS_TRUE; +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif + goto advance_pc; + } } +no_catch:; } -no_catch: #endif /* @@ -4770,19 +5527,29 @@ no_catch: * exception thrown under such a function was not caught by it. If so, go * to the inline code under JSOP_RETURN. */ - if (inlineCallCount) + if (inlineCallCount) { +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif 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 (JS_LIKELY(mark != NULL)) { + fp->sp = fp->spbase; + fp->spbase = NULL; + js_FreeRawStack(cx, mark); + } else { + SAVE_SP(fp); + } + +out2: if (cx->version == currentVersion && currentVersion != originalVersion) - JS_SetVersion(cx, originalVersion); + js_SetVersion(cx, originalVersion); cx->interpLevel--; return ok; diff --git a/src/dom/js/jsinterp.h b/src/dom/js/jsinterp.h index 81f16d760..edf70f86f 100644 --- a/src/dom/js/jsinterp.h +++ b/src/dom/js/jsinterp.h @@ -72,6 +72,7 @@ struct JSStackFrame { JSObject *sharpArray; /* scope for #n= initializer vars */ uint32 flags; /* frame flags -- see below */ JSStackFrame *dormantNext; /* next dormant frame chain */ + JSObject *xmlNamespace; /* null or default xml namespace in E4X */ }; typedef struct JSInlineFrame { @@ -93,7 +94,8 @@ typedef struct JSInlineFrame { #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 */ + references based on scope chain */ +#define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */ #define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ #define JSFRAME_OVERRIDE_BITS 8 @@ -254,9 +256,24 @@ extern size_t js_LogCallToSourceLimit; extern void js_DumpCallTable(JSContext *cx); #endif +/* + * 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. + */ +extern JSBool +js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp); + /* * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp - * is non-null). + * is non-null), and that the callee, |this| parameter, and actual arguments + * are already pushed on the stack under cx->fp->sp. */ extern JS_FRIEND_API(JSBool) js_Invoke(JSContext *cx, uintN argc, uintN flags); @@ -295,7 +312,10 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JSObject **objp, JSProperty **propp); extern JSBool -js_Interpret(JSContext *cx, jsval *result); +js_StrictlyEqual(jsval lval, jsval rval); + +extern JSBool +js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result); JS_END_EXTERN_C diff --git a/src/dom/js/jslibmath.h b/src/dom/js/jslibmath.h index 7cbcf767a..544d8f800 100644 --- a/src/dom/js/jslibmath.h +++ b/src/dom/js/jslibmath.h @@ -119,7 +119,7 @@ #define __P(p) () #endif -#if defined _WIN32 || defined SUNOS4 +#if (defined _WIN32 && !defined WINCE) || defined SUNOS4 #define fd_acos acos #define fd_asin asin @@ -268,7 +268,7 @@ 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)); diff --git a/src/dom/js/jslock.c b/src/dom/js/jslock.c index e0c87b1fc..968e98b84 100644 --- a/src/dom/js/jslock.c +++ b/src/dom/js/jslock.c @@ -1131,7 +1131,7 @@ js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) * state update. */ if (!oldscope) - return; + return; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, oldscope)); /* @@ -1141,7 +1141,7 @@ js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) * was actually locked. */ if (CX_THREAD_IS_RUNNING_GC(cx)) - return; + return; /* * Special case in js_LockObj and js_UnlockScope for locking the sealed diff --git a/src/dom/js/jslock.h b/src/dom/js/jslock.h index 9ece59c9b..0ab71927b 100644 --- a/src/dom/js/jslock.h +++ b/src/dom/js/jslock.h @@ -106,28 +106,9 @@ typedef struct JSFatLockTable { * 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) @@ -140,16 +121,14 @@ typedef struct JSFatLockTable { */ #define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ ? (void)0 \ - : (js_LockObj(cx, obj), \ - SET_OBJ_INFO(obj,__FILE__,__LINE__))) + : (js_LockObj(cx, obj))) #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_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ + : js_LockScope(cx, scope)) +#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) @@ -279,11 +258,4 @@ extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); #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 index 2589bb7f9..95353ba1a 100644 --- a/src/dom/js/jslocko.asm +++ b/src/dom/js/jslocko.asm @@ -1,39 +1,40 @@ -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 ***** - | +; -*- 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 diff --git a/src/dom/js/jslog2.c b/src/dom/js/jslog2.c index 113c276d0..9bfca4dd3 100644 --- a/src/dom/js/jslog2.c +++ b/src/dom/js/jslog2.c @@ -47,17 +47,17 @@ JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) JSIntn log2 = 0; if (n & (n-1)) - log2++; + log2++; if (n >> 16) - log2 += 16, n >>= 16; + log2 += 16, n >>= 16; if (n >> 8) - log2 += 8, n >>= 8; + log2 += 8, n >>= 8; if (n >> 4) - log2 += 4, n >>= 4; + log2 += 4, n >>= 4; if (n >> 2) - log2 += 2, n >>= 2; + log2 += 2, n >>= 2; if (n >> 1) - log2++; + log2++; return log2; } @@ -70,14 +70,14 @@ JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n) JSIntn log2 = 0; if (n >> 16) - log2 += 16, n >>= 16; + log2 += 16, n >>= 16; if (n >> 8) - log2 += 8, n >>= 8; + log2 += 8, n >>= 8; if (n >> 4) - log2 += 4, n >>= 4; + log2 += 4, n >>= 4; if (n >> 2) - log2 += 2, n >>= 2; + log2 += 2, n >>= 2; if (n >> 1) - log2++; + log2++; return log2; } diff --git a/src/dom/js/jslong.c b/src/dom/js/jslong.c index 76259e502..9a4a5b4d7 100644 --- a/src/dom/js/jslong.c +++ b/src/dom/js/jslong.c @@ -74,10 +74,10 @@ static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) 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; - } + 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; @@ -87,9 +87,9 @@ static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) if (r0 < m) { q0--, r0 += b; if (r0 >= b - && r0 < m) { - q0--, r0 += b; - } + && r0 < m) { + q0--, r0 += b; + } } *qp = (q1 << 16) | q0; *rp = r0 - m; @@ -101,17 +101,17 @@ static JSUint32 CountLeadingZeros(JSUint32 a) JSUint32 r = 32; if ((t = a >> 16) != 0) - r -= 16, a = t; + r -= 16, a = t; if ((t = a >> 8) != 0) - r -= 8, a = t; + r -= 8, a = t; if ((t = a >> 4) != 0) - r -= 4, a = t; + r -= 4, a = t; if ((t = a >> 2) != 0) - r -= 2, a = t; + r -= 2, a = t; if ((t = a >> 1) != 0) - r -= 1, a = t; + r -= 1, a = t; if (a & 1) - r--; + r--; return r; } @@ -125,157 +125,157 @@ JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint6 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; - } + 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 (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; + 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 index bde8bfbbf..059cf00bb 100644 --- a/src/dom/js/jslong.h +++ b/src/dom/js/jslong.h @@ -185,7 +185,7 @@ extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); /*********************************************************************** ** MACROS: JSLL_UDIVMOD ** DESCRIPTION: -** Produce both a quotient and a remainder given an unsigned +** 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 diff --git a/src/dom/js/jsmath.c b/src/dom/js/jsmath.c index 9c6fcec0e..19005eafa 100644 --- a/src/dom/js/jsmath.c +++ b/src/dom/js/jsmath.c @@ -56,28 +56,28 @@ #include "jsobj.h" #ifndef M_E -#define M_E 2.7182818284590452354 +#define M_E 2.7182818284590452354 #endif #ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 +#define M_LOG2E 1.4426950408889634074 #endif #ifndef M_LOG10E -#define M_LOG10E 0.43429448190325182765 +#define M_LOG10E 0.43429448190325182765 #endif #ifndef M_LN2 -#define M_LN2 0.69314718055994530942 +#define M_LN2 0.69314718055994530942 #endif #ifndef M_LN10 -#define M_LN10 2.30258509299404568402 +#define M_LN10 2.30258509299404568402 #endif #ifndef M_PI -#define M_PI 3.14159265358979323846 +#define M_PI 3.14159265358979323846 #endif #ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 +#define M_SQRT2 1.41421356237309504880 #endif #ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 +#define M_SQRT1_2 0.70710678118654752440 #endif static JSConstDoubleSpec math_constants[] = { @@ -129,10 +129,6 @@ math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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); } @@ -144,10 +140,6 @@ math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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); } @@ -158,9 +150,9 @@ 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; + return JS_FALSE; if (!js_ValueToNumber(cx, argv[1], &y)) - return JS_FALSE; + return JS_FALSE; z = fd_atan2(x, y); return js_NewNumberValue(cx, z, rval); } @@ -288,9 +280,19 @@ 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; + return JS_FALSE; if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; +#if !JS_USE_FDLIBM_MATH + /* + * Because C99 and ECMA specify different behavior for pow(), + * we need to wrap the libm call to make it ECMA compliant. + */ + if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } +#endif z = fd_pow(x, y); return js_NewNumberValue(cx, z, rval); } @@ -316,7 +318,7 @@ random_init(JSRuntime *rt) /* Do at most once. */ if (rt->rngInitialized) - return; + return; rt->rngInitialized = JS_TRUE; /* rt->rngMultiplier = 0x5DEECE66DL */ @@ -429,7 +431,7 @@ math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #if JS_HAS_TOSOURCE static JSBool math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) + jsval *rval) { *rval = ATOM_KEY(cx->runtime->atomState.MathAtom); return JS_TRUE; @@ -438,26 +440,26 @@ math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSFunctionSpec math_static_methods[] = { #if JS_HAS_TOSOURCE - {js_toSource_str, math_toSource, 0, 0, 0}, + {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}, + {"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} }; @@ -465,7 +467,7 @@ JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { JSObject *Math; - + Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0); if (!Math) return NULL; diff --git a/src/dom/js/jsmath.h b/src/dom/js/jsmath.h index 7a6b21657..b67dba1de 100644 --- a/src/dom/js/jsmath.h +++ b/src/dom/js/jsmath.h @@ -10,17 +10,17 @@ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. - * + * * The Original Code is Mozilla Communicator client code, released * 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"), diff --git a/src/dom/js/jsnum.c b/src/dom/js/jsnum.c index 9de1d72db..8a5596304 100644 --- a/src/dom/js/jsnum.c +++ b/src/dom/js/jsnum.c @@ -114,25 +114,25 @@ num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rva static JSBool num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSString *str; jsint radix; + JSString *str; 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 + } else { radix = 0; - + } if (radix != 0 && (radix < 2 || radix > 36)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; /* XXXbe js_strtointeger shouldn't require NUL termination */ bp = js_UndependString(cx, str); if (!bp) @@ -161,7 +161,7 @@ static JSFunctionSpec number_functions[] = { {0,0,0,0,0} }; -static JSClass number_class = { +JSClass js_NumberClass = { "Number", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, @@ -201,7 +201,7 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[64]; JSString *str; - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -211,7 +211,7 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_ReportOutOfMemory(cx); return JS_FALSE; } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", number_class.name, numStr); + JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; @@ -256,7 +256,7 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsint base; JSString *str; - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -385,7 +385,7 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, static JSBool num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; @@ -404,7 +404,7 @@ num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDTo 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)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -505,6 +505,7 @@ static jsdouble NaN; #if (defined XP_WIN || defined XP_OS2) && \ + !defined WINCE && \ !defined __MWERKS__ && \ (defined _M_IX86 || \ (defined __GNUC__ && !defined __MINGW32__)) @@ -537,27 +538,23 @@ js_InitRuntimeNumberState(JSContext *cx) 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)) + rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK); + if (!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)) { + rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK); + if (!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)) { + rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK); + if (!rt->jsNegativeInfinity) return JS_FALSE; - } u.s.hi = 0; u.s.lo = 1; @@ -605,7 +602,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, obj, number_functions)) return NULL; - proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1, + proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, NULL, number_methods, NULL, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; @@ -630,11 +627,11 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) } jsdouble * -js_NewDouble(JSContext *cx, jsdouble d) +js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag) { jsdouble *dp; - dp = (jsdouble *) js_AllocGCThing(cx, GCX_DOUBLE); + dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble)); if (!dp) return NULL; *dp = d; @@ -652,7 +649,7 @@ js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) { jsdouble *dp; - dp = js_NewDouble(cx, d); + dp = js_NewDouble(cx, d, 0); if (!dp) return JS_FALSE; *rval = DOUBLE_TO_JSVAL(dp); @@ -679,7 +676,7 @@ js_NumberToObject(JSContext *cx, jsdouble d) JSObject *obj; jsval v; - obj = js_NewObject(cx, &number_class, NULL, NULL); + obj = js_NewObject(cx, &js_NumberClass, NULL, NULL); if (!obj) return NULL; if (!js_NewNumberValue(cx, d, &v)) { diff --git a/src/dom/js/jsnum.h b/src/dom/js/jsnum.h index 28f40e6ec..cd99501e7 100644 --- a/src/dom/js/jsnum.h +++ b/src/dom/js/jsnum.h @@ -67,9 +67,9 @@ JS_BEGIN_EXTERN_C typedef union jsdpun { struct { #if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) - uint32 lo, hi; + uint32 lo, hi; #else - uint32 hi, lo; + uint32 hi, lo; #endif } s; jsdouble d; @@ -124,7 +124,7 @@ typedef union jsdpun { ((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_LO32(d) == 0) /* * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid @@ -134,7 +134,16 @@ typedef union jsdpun { */ #define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) \ && !JSDOUBLE_IS_NEGZERO(d) \ - && ((d) == (i = (jsint)(d)))) + && ((d) == (i = (jsint)(d)))) + +#if defined(XP_WIN) +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ + ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ + ? (IFNAN) \ + : (LVAL) OP (RVAL)) +#else +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) +#endif /* Initialize number constants and runtime state for the first context. */ extern JSBool @@ -144,6 +153,8 @@ extern void js_FinishRuntimeNumberState(JSContext *cx); /* Initialize the Number class, returning its prototype object. */ +extern JSClass js_NumberClass; + extern JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj); @@ -159,7 +170,7 @@ extern const char js_parseInt_str[]; /* GC-allocate a new JS number. */ extern jsdouble * -js_NewDouble(JSContext *cx, jsdouble d); +js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag); extern void js_FinalizeDouble(JSContext *cx, jsdouble *dp); diff --git a/src/dom/js/jsobj.c b/src/dom/js/jsobj.c index 847040c61..03a598094 100644 --- a/src/dom/js/jsobj.c +++ b/src/dom/js/jsobj.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -68,6 +69,10 @@ #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #ifdef JS_THREADSAFE #define NATIVE_DROP_PROPERTY js_DropProperty @@ -77,17 +82,9 @@ js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); #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, @@ -100,10 +97,6 @@ JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { js_GetRequiredSlot, js_SetRequiredSlot }; -#ifdef XP_MAC -#pragma export off -#endif - JSClass js_ObjectClass = { js_Object_str, 0, @@ -152,20 +145,39 @@ static JSBool obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { uint32 slot; + jsid propid; JSAccessMode mode; uintN attrs; + JSObject *pobj; + JSClass *clasp; + JSExtendedClass *xclasp; slot = (uint32) JSVAL_TO_INT(id); if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { - id = (jsid)cx->runtime->atomState.protoAtom; + propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); mode = JSACC_PROTO; } else { - id = (jsid)cx->runtime->atomState.parentAtom; + propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); mode = JSACC_PARENT; } - if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs)) + + /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ + if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) return JS_FALSE; - *vp = OBJ_GET_SLOT(cx, obj, slot); + + pobj = JSVAL_TO_OBJECT(*vp); + if (pobj) { + clasp = OBJ_GET_CLASS(cx, pobj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + xclasp = (JSExtendedClass *) clasp; + if (xclasp->outerObject) { + pobj = xclasp->outerObject(cx, pobj); + if (!pobj) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(pobj); + } + } + } return JS_TRUE; } @@ -174,6 +186,7 @@ obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSObject *pobj; uint32 slot; + jsid propid; uintN attrs; if (!JSVAL_IS_OBJECT(*vp)) @@ -184,7 +197,8 @@ obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 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)) + propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); + if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs)) return JS_FALSE; return js_SetProtoOrParent(cx, obj, slot, pobj); @@ -375,7 +389,7 @@ js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_object(const void *key) { - return (JSHashNumber)key >> JSVAL_TAGBITS; + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; } static JSHashEntry * @@ -396,6 +410,12 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) uintN attrs; #endif jsval val; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return NULL; + } map = &cx->sharpObjectMap; table = map->table; @@ -404,14 +424,25 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) he = *hep; if (!he) { sharpid = 0; - he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid); + he = JS_HashTableRawAdd(table, hep, hash, obj, + JS_UINT32_TO_PTR(sharpid)); if (!he) { JS_ReportOutOfMemory(cx); return NULL; } + + /* + * Increment map->depth to protect js_EnterSharpObject from reentering + * itself badly. Without this fix, if we reenter the basis case where + * map->depth == 0, when unwinding the inner call we will destroy the + * newly-created hash table and crash. + */ + ++map->depth; ida = JS_Enumerate(cx, obj); + --map->depth; if (!ida) return NULL; + ok = JS_TRUE; for (i = 0, length = ida->length; i < length; i++) { id = ida->vector[i]; @@ -419,29 +450,29 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) 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; + if (!prop) + continue; + 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); } - } else { - ok = OBJ_GET_PROPERTY(cx, obj, id, &val); + val = (jsval) ((JSScopeProperty*)prop)->setter; } + } else { + ok = OBJ_GET_PROPERTY(cx, obj, id, &val); } - OBJ_DROP_PROPERTY(cx, obj2, prop); } + OBJ_DROP_PROPERTY(cx, obj2, prop); #else ok = OBJ_GET_PROPERTY(cx, obj, id, &val); #endif @@ -458,10 +489,10 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) if (!ok) return NULL; } else { - sharpid = (jsatomid) he->value; + sharpid = JS_PTR_TO_UINT32(he->value); if (sharpid == 0) { sharpid = ++map->sharpgen << SHARP_ID_SHIFT; - he->value = (void *) sharpid; + he->value = JS_UINT32_TO_PTR(sharpid); } ida = NULL; } @@ -483,6 +514,12 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, char buf[20]; size_t len; + if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) && + cx->branchCallback && + !cx->branchCallback(cx, NULL)) { + return NULL; + } + /* Set to null in case we return an early error. */ *sp = NULL; map = &cx->sharpObjectMap; @@ -495,14 +532,16 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, return NULL; } map->table = table; + JS_KEEP_ATOMS(cx->runtime); } + /* From this point the control must flow either through out: or bad:. */ ida = NULL; if (map->depth == 0) { he = MarkSharpObjects(cx, obj, &ida); if (!he) goto bad; - JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0); + JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); if (!idap) { JS_DestroyIdArray(cx, ida); ida = NULL; @@ -525,20 +564,17 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, JS_ReportOutOfMemory(cx); goto bad; } - *sp = NULL; sharpid = 0; goto out; } } - sharpid = (jsatomid) he->value; - if (sharpid == 0) { - *sp = NULL; - } else { + sharpid = JS_PTR_TO_UINT32(he->value); + if (sharpid != 0) { len = JS_snprintf(buf, sizeof buf, "#%u%c", sharpid >> SHARP_ID_SHIFT, (sharpid & SHARP_BIT) ? '#' : '='); - *sp = js_InflateString(cx, buf, len); + *sp = js_InflateString(cx, buf, &len); if (!*sp) { if (ida) JS_DestroyIdArray(cx, ida); @@ -569,6 +605,7 @@ out: bad: /* Clean up the sharpObjectMap table on outermost error. */ if (map->depth == 0) { + JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; @@ -585,6 +622,7 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) map = &cx->sharpObjectMap; JS_ASSERT(map->depth > 0); if (--map->depth == 0) { + JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; @@ -598,7 +636,43 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) } } -#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */ +JS_STATIC_DLL_CALLBACK(intN) +gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) +{ + GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry", NULL); + return JS_DHASH_NEXT; +} + +void +js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map) +{ + JS_ASSERT(map->depth > 0); + JS_ASSERT(map->table); + + /* + * During recursive calls to MarkSharpObjects a non-native object or + * object with a custom getProperty method can potentially return an + * unrooted value or even cut from the object graph an argument of one of + * MarkSharpObjects recursive invocations. So we must protect map->table + * entries against GC. + * + * We can not simply use JSTempValueRooter to mark the obj argument of + * MarkSharpObjects during recursion as we have to protect *all* entries + * in JSSharpObjectMap including those that contains otherwise unreachable + * objects just allocated through custom getProperty. Otherwise newer + * allocations can re-use the address of an object stored in the hashtable + * confusing js_EnterSharpObject. So to address the problem we simply + * mark all objects from map->table. + * + * An alternative "proper" solution is to use JSTempValueRooter in + * MarkSharpObjects with code to remove during finalization entries + * with otherwise unreachable objects. But this is way too complex + * to justify spending efforts. + */ + JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx); +} + +#define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */ #if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE JSBool @@ -610,7 +684,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSIdArray *ida; jschar *chars, *ochars, *vsharp; const jschar *idstrchars, *vchars; - size_t nchars, idstrlength, gsoplength, vlength, vsharplength; + size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; char *comma; jsint i, j, length, valcnt; jsid id; @@ -619,7 +693,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSProperty *prop; uintN attrs; #endif - jsval val[2]; + jsval *val; JSString *gsop[2]; JSAtom *atom; JSString *idstr, *valstr, *str; @@ -634,7 +708,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * 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); + outermost = !JS_VERSION_IS_1_2(cx) && cx->sharpObjectMap.depth == 0; he = js_EnterSharpObject(cx, obj, &ida, &chars); if (!he) return JS_FALSE; @@ -729,6 +803,13 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, comma = NULL; + /* + * We have four local roots for cooked and raw value GC safety. Hoist the + * "argv + 2" out of the loop using the val local, which refers to the raw + * (unconverted, "uncooked") values. + */ + val = argv + 2; + 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]; @@ -789,14 +870,14 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, goto error; /* Convert id to a jsval and then to a string. */ - atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id; + atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; id = ID_TO_VALUE(id); idstr = js_ValueToString(cx, id); if (!idstr) { ok = JS_FALSE; goto error; } - argv[0] = STRING_TO_JSVAL(idstr); + *rval = STRING_TO_JSVAL(idstr); /* local root */ /* * If id is a string that's a reserved identifier, or else id is not @@ -805,13 +886,13 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, */ if (atom ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr)) - : JSVAL_TO_INT(id) < 0) { + : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { idstr = js_QuoteString(cx, idstr, (jschar)'\''); if (!idstr) { ok = JS_FALSE; goto error; } - argv[0] = STRING_TO_JSVAL(idstr); + *rval = STRING_TO_JSVAL(idstr); /* local root */ } idstrchars = JSSTRING_CHARS(idstr); idstrlength = JSSTRING_LENGTH(idstr); @@ -823,16 +904,19 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, ok = JS_FALSE; goto error; } - argv[1+j] = STRING_TO_JSVAL(valstr); + argv[j] = STRING_TO_JSVAL(valstr); /* local root */ vchars = JSSTRING_CHARS(valstr); vlength = JSSTRING_LENGTH(valstr); #ifndef OLD_GETTER_SETTER - /* Remove 'function ' from beginning of valstr. */ + /* + * Remove '(function ' from the beginning of valstr and ')' from the + * end so that we can put "get" in front of the function definition. + */ if (gsop[j]) { - int n = strlen(js_function_str) + 1; + int n = strlen(js_function_str) + 2; vchars += n; - vlength -= n; + vlength -= n + 1; } #endif @@ -860,14 +944,31 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } #endif +#define SAFE_ADD(n) \ + JS_BEGIN_MACRO \ + size_t n_ = (n); \ + curlen += n_; \ + if (curlen < n_) \ + goto overflow; \ + JS_END_MACRO + + curlen = nchars; + if (comma) + SAFE_ADD(2); + SAFE_ADD(idstrlength + 1); + if (gsop[j]) + SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); + SAFE_ADD(vsharplength); + SAFE_ADD(vlength); + SAFE_ADD((outermost ? 2 : 1) + 1); +#undef SAFE_ADD + + if (curlen > (size_t)-1 / sizeof(jschar)) + goto overflow; + /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = (jschar *) - 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)); + realloc((ochars = chars), curlen * sizeof(jschar)); if (!chars) { /* Save code space on error: let JS_free ignore null vsharp. */ JS_free(cx, vsharp); @@ -945,6 +1046,12 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } *rval = STRING_TO_JSVAL(str); return JS_TRUE; + + overflow: + JS_free(cx, vsharp); + free(chars); + chars = NULL; + goto error; } #endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */ @@ -958,7 +1065,7 @@ js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSString *str; #if JS_HAS_INITIALIZERS - if (cx->version == JSVERSION_1_2) + if (JS_VERSION_IS_1_2(cx)) return js_obj_toSource(cx, obj, argc, argv, rval); #endif @@ -986,6 +1093,20 @@ js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_TRUE; } +static JSBool +js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[-1]); + if (!str) + 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) { @@ -993,6 +1114,69 @@ obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +/* + * Check whether principals subsumes scopeobj's principals, and return true + * if so (or if scopeobj has no principals, for backward compatibility with + * the JS API, which does not require principals), and false otherwise. + */ +JSBool +js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, + JSPrincipals *principals, const char *caller) +{ + JSRuntime *rt; + JSPrincipals *scopePrincipals; + + rt = cx->runtime; + if (rt->findObjectPrincipals) { + scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); + if (!principals || !scopePrincipals || + !principals->subsume(principals, scopePrincipals)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, caller); + return JS_FALSE; + } + } + return JS_TRUE; +} + +JSObject * +js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) +{ + JSClass *clasp; + JSExtendedClass *xclasp; + JSObject *inner; + + if (!scopeobj) + goto bad; + + OBJ_TO_INNER_OBJECT(cx, scopeobj); + if (!scopeobj) + return NULL; + + inner = scopeobj; + + /* XXX This is an awful gross hack. */ + while (scopeobj) { + clasp = OBJ_GET_CLASS(cx, scopeobj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + xclasp = (JSExtendedClass*)clasp; + if (xclasp->innerObject && + xclasp->innerObject(cx, scopeobj) != scopeobj) { + goto bad; + } + } + + scopeobj = OBJ_GET_PARENT(cx, scopeobj); + } + + return inner; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, caller); + return NULL; +} + static JSBool obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -1007,14 +1191,15 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSBool ok; #if JS_HAS_EVAL_THIS_SCOPE JSObject *callerScopeChain = NULL, *callerVarObj = NULL; - JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE; + JSObject *setCallerScopeChain = NULL; + JSBool 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) && + if (JS_VERSION_IS_ECMA(cx) && indirectCall && !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, @@ -1029,6 +1214,14 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } + /* + * If the caller is a lightweight function and doesn't have a variables + * object, then we need to provide one for the compiler to stick any + * declared (var) variables into. + */ + if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) + return JS_FALSE; + #if JS_HAS_SCRIPT_OBJECT /* * Script.prototype.compile/exec and Object.prototype.eval all take an @@ -1048,14 +1241,21 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (indirectCall) { callerScopeChain = caller->scopeChain; if (obj != callerScopeChain) { - scopeobj = js_NewObject(cx, &js_WithClass, obj, - callerScopeChain); + if (!js_CheckPrincipalsAccess(cx, obj, + caller->script->principals, + js_eval_str)) { + return JS_FALSE; + } + + scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); if (!scopeobj) return JS_FALSE; /* Set fp->scopeChain too, for the compiler. */ caller->scopeChain = fp->scopeChain = scopeobj; - setCallerScopeChain = JS_TRUE; + + /* Remember scopeobj so we can null its private when done. */ + setCallerScopeChain = scopeobj; } callerVarObj = caller->varobj; @@ -1078,6 +1278,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #endif } + /* Ensure we compile this eval with the right object in the scope chain. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); + if (!scopeobj) + return JS_FALSE; + str = JSVAL_TO_STRING(argv[0]); if (caller) { file = caller->script->filename; @@ -1089,7 +1294,18 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) principals = NULL; } - fp->flags |= JSFRAME_EVAL; + /* + * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was + * invoked) between fp and its scripted caller, to help the compiler easily + * find the same caller whose scope and var obj we've set. + * + * XXX this nonsense could, and perhaps should, go away with a better way + * to pass params to the compiler than via the top-most frame. + */ + do { + fp->flags |= JSFRAME_EVAL; + } while ((fp = fp->down) != caller); + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), @@ -1109,14 +1325,25 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) scopeobj = caller->scopeChain; } #endif - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + + /* + * Belt-and-braces: check that the lesser of eval's principals and the + * caller's principals has access to scopeobj. + */ + ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, js_eval_str); + if (ok) + 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) + if (setCallerScopeChain) { caller->scopeChain = callerScopeChain; + JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); + JS_SetPrivate(cx, setCallerScopeChain, NULL); + } if (setCallerVarObj) caller->varobj = callerVarObj; #endif @@ -1129,13 +1356,37 @@ static JSBool obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, void *closure) { + JSObject *callable; + JSRuntime *rt; + JSStackFrame *caller; + JSPrincipals *subject, *watcher; JSResolvingKey key; JSResolvingEntry *entry; uint32 generation; - JSObject *funobj; jsval argv[3]; JSBool ok; + callable = (JSObject *) closure; + + rt = cx->runtime; + if (rt->findObjectPrincipals) { + /* Skip over any obj_watch_* frames between us and the real subject. */ + caller = JS_GetScriptedCaller(cx, cx->fp); + if (caller) { + /* + * Only call the watch handler if the watcher is allowed to watch + * the currently executing script. + */ + watcher = rt->findObjectPrincipals(cx, callable); + subject = JS_StackFramePrincipals(cx, caller); + + if (watcher && subject && !watcher->subsume(watcher, subject)) { + /* Silently don't call the watch handler. */ + return JS_TRUE; + } + } + } + /* Avoid recursion on (obj, id) already being watched on cx. */ key.obj = obj; key.id = id; @@ -1145,11 +1396,10 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, 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); + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); return ok; } @@ -1157,21 +1407,14 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, static JSBool obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSObject *funobj; - JSFunction *fun; + JSObject *callable; 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); + callable = js_ValueToCallableObject(cx, &argv[1], 0); + if (!callable) + return JS_FALSE; /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ userid = argv[0]; @@ -1182,7 +1425,7 @@ obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; if (attrs & JSPROP_READONLY) return JS_TRUE; - return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, funobj); + return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); } static JSBool @@ -1203,6 +1446,14 @@ obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty, + argc, argv, rval); +} + +JSBool +js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, + uintN argc, jsval *argv, jsval *rval) { jsid id; JSObject *obj2; @@ -1211,17 +1462,44 @@ obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + if (!lookup(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; + JSClass *clasp; + JSExtendedClass *xclasp; + + clasp = OBJ_GET_CLASS(cx, obj); + xclasp = (clasp->flags & JSCLASS_IS_EXTENDED) + ? (JSExtendedClass *)clasp + : NULL; + if (xclasp && xclasp->outerObject && + xclasp->outerObject(cx, obj2) == obj) { + *rval = JSVAL_TRUE; + } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) { + /* + * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a + * delegated property makes that property appear to be direct in + * all delegating instances of the same native class. This hack + * avoids bloating every function instance with its own 'length' + * (AKA 'arity') property. But it must not extend across class + * boundaries, to avoid making hasOwnProperty lie (bug 320854). + * + * It's not really a hack, of course: a permanent property can't + * be deleted, and JSPROP_SHARED means "don't allocate a slot in + * any instance, prototype or delegating". Without a slot, and + * without the ability to remove and recreate (with differences) + * the property, there is no way to tell whether it is directly + * owned, or indirectly delegated. + */ + sprop = (JSScopeProperty *)prop; + *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); + } else { + *rval = JSVAL_FALSE; + } } if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); @@ -1425,7 +1703,7 @@ static JSFunctionSpec object_methods[] = { {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_toLocaleString_str, js_obj_toLocaleString, 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 @@ -1474,49 +1752,12 @@ Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ 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 - ) + JSProperty **propp) { - JSObject *proto; - JSScopeProperty *sprop; - JSStackFrame *fp; - - proto = OBJ_GET_PROTO(cx, obj); + JSObject *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; + return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); } static JSBool @@ -1627,13 +1868,26 @@ with_getObjectOps(JSContext *cx, JSClass *clasp) JSClass js_WithClass = { "With", - 0, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), 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 }; +JSObject * +js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_WithClass, proto, parent); + if (!obj) + return NULL; + obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp); + OBJ_SET_BLOCK_DEPTH(cx, obj, depth); + return obj; +} + #if JS_HAS_OBJ_PROTO_PROP static JSBool With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -1650,7 +1904,7 @@ With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_WithClass, NULL, NULL); + obj = js_NewWithObject(cx, NULL, NULL, -1); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -1696,11 +1950,13 @@ js_InitObjectClass(JSContext *cx, JSObject *obj) #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, + if (!OBJ_GET_PROPERTY(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.evalAtom), &eval)) { return NULL; } - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom, + if (!OBJ_DEFINE_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.evalAtom), eval, NULL, NULL, 0, NULL)) { return NULL; } @@ -1757,28 +2013,87 @@ static JSBool GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, JSObject **protop); +static jsval * +AllocSlots(JSContext *cx, jsval *slots, uint32 nslots) +{ + size_t nbytes, obytes, minbytes; + uint32 i, oslots; + jsval *newslots; + + nbytes = (nslots + 1) * sizeof(jsval); + if (slots) { + oslots = slots[-1]; + obytes = (oslots + 1) * sizeof(jsval); + } else { + oslots = 0; + obytes = 0; + } + + if (nbytes <= GC_NBYTES_MAX) { + newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes); + } else { + newslots = (jsval *) + JS_realloc(cx, + (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1, + nbytes); + } + if (!newslots) + return NULL; + + if (obytes != 0) { + /* If either nbytes or obytes fit in a GC-thing, we must copy. */ + minbytes = JS_MIN(nbytes, obytes); + if (minbytes <= GC_NBYTES_MAX) + memcpy(newslots + 1, slots, minbytes - sizeof(jsval)); + + /* If nbytes are in a GC-thing but obytes aren't, free obytes. */ + if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX) + JS_free(cx, slots - 1); + + /* If we're extending an allocation, initialize free slots. */ + if (nslots > oslots) { + for (i = 1 + oslots; i <= nslots; i++) + newslots[i] = JSVAL_VOID; + } + } + + newslots[0] = nslots; + return ++newslots; +} + +static void +FreeSlots(JSContext *cx, jsval *slots) +{ + size_t nbytes; + + /* + * NB: We count on smaller GC-things being finalized before larger things + * that become garbage during the same GC. Without this assumption, we + * couldn't load slots[-1] here without possibly loading a gcFreeList link + * (see struct JSGCThing in jsgc.h). + */ + nbytes = (slots[-1] + 1) * sizeof(jsval); + if (nbytes > GC_NBYTES_MAX) + JS_free(cx, slots - 1); +} + JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - JSObject *obj, *ctor; + JSObject *obj; 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; + JSTempValueRooter tvr; /* Bootstrap the ur-object, and make it the default prototype object. */ if (!proto) { if (!GetClassPrototype(cx, parent, clasp->name, &proto)) - goto bad; + return NULL; if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto)) - goto bad; + return NULL; } /* Always call the class's getObjectOps hook if it has one. */ @@ -1786,6 +2101,23 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) ? clasp->getObjectOps(cx, clasp) : &js_ObjectOps; + /* + * Allocate a zeroed object from the GC heap. Do this *after* any other + * GC-thing allocations under GetClassPrototype or clasp->getObjectOps, + * to avoid displacing the newborn root for obj. + */ + obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); + if (!obj) + return NULL; + + /* + * Root obj to prevent it from being killed. + * AllocSlots can trigger a finalizer from a last-ditch GC calling + * JS_ClearNewbornRoots. There's also the possibilty of things + * happening under the objectHook call-out below. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); + /* * 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 @@ -1802,16 +2134,12 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) (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); - } + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + if (!parent) + parent = OBJ_GET_PARENT(cx, proto); /* Share the given prototype's map. */ obj->map = js_HoldObjectMap(cx, map); @@ -1830,14 +2158,12 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) } /* Allocate a slots vector, with a -1'st element telling its length. */ - newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval)); + newslots = AllocSlots(cx, NULL, nslots); 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); @@ -1857,15 +2183,18 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) JS_UNKEEP_ATOMS(cx->runtime); } +out: + JS_POP_TEMP_ROOT(cx, &tvr); + cx->newborn[GCX_OBJECT] = (JSGCThing *) obj; return obj; bad: - cx->newborn[GCX_OBJECT] = NULL; - return NULL; + obj = NULL; + goto out; } -static JSBool -FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) +JSBool +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) { JSAtom *atom; JSObject *obj, *pobj; @@ -1891,12 +2220,8 @@ FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) } 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 - )) { + if (!js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), + JSRESOLVE_CLASSNAME, &pobj, &prop)) { return JS_FALSE; } if (!prop) { @@ -1917,15 +2242,27 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv) { jsval cval, rval; + JSTempValueRooter argtvr, tvr; JSObject *obj, *ctor; - if (!FindConstructor(cx, parent, clasp->name, &cval)) + JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); + + if (!js_FindConstructor(cx, parent, clasp->name, &cval)) { + JS_POP_TEMP_ROOT(cx, &argtvr); return NULL; + } if (JSVAL_IS_PRIMITIVE(cval)) { js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); + JS_POP_TEMP_ROOT(cx, &argtvr); return NULL; } + /* + * Protect cval in case a crazy getter for .prototype uproots it. After + * this point, all control flow must exit through label out with obj set. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); + /* * If proto or parent are NULL, set them to Constructor.prototype and/or * Constructor.__parent__, just like JSOP_NEW does. @@ -1935,9 +2272,11 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, parent = OBJ_GET_PARENT(cx, ctor); if (!proto) { if (!OBJ_GET_PROPERTY(cx, ctor, - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &rval)) { - return NULL; + obj = NULL; + goto out; } if (JSVAL_IS_OBJECT(rval)) proto = JSVAL_TO_OBJECT(rval); @@ -1945,14 +2284,39 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, obj = js_NewObject(cx, clasp, proto, parent); if (!obj) - return NULL; + goto out; if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) goto bad; - return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj; + + if (JSVAL_IS_PRIMITIVE(rval)) + goto out; + obj = JSVAL_TO_OBJECT(rval); + + /* + * If the given class has both the JSCLASS_HAS_PRIVATE and the + * JSCLASS_CONSTRUCT_PROTOTYPE flags, then the class should have its private + * data set. If it doesn't, then it means the constructor was replaced, and + * we should throw a typerr. + */ + if (OBJ_GET_CLASS(cx, obj) != clasp || + (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | + JSCLASS_CONSTRUCT_PROTOTYPE)) && + !JS_GetPrivate(cx, obj))) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_WRONG_CONSTRUCTOR, clasp->name); + goto bad; + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + JS_POP_TEMP_ROOT(cx, &argtvr); + return obj; + bad: cx->newborn[GCX_OBJECT] = NULL; - return NULL; + obj = NULL; + goto out; } void @@ -1985,7 +2349,7 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) /* Drop map and free slots. */ js_DropObjectMap(cx, map, obj); obj->map = NULL; - JS_free(cx, obj->slots - 1); + FreeSlots(cx, obj->slots); obj->slots = NULL; } @@ -1996,7 +2360,7 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { JSObjectMap *map; JSClass *clasp; - uint32 nslots, i; + uint32 nslots; jsval *newslots; map = obj->map; @@ -2013,14 +2377,11 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); nslots += (nslots + 1) / 2; - newslots = (jsval *) - JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + newslots = AllocSlots(cx, obj->slots, nslots); 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; + map->nslots = nslots; + obj->slots = newslots; } #ifdef TOO_MUCH_GC @@ -2050,19 +2411,18 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) if (nslots < JS_INITIAL_NSLOTS) nslots = JS_INITIAL_NSLOTS; - newslots = (jsval *) - JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + newslots = AllocSlots(cx, obj->slots, nslots); if (!newslots) return; - newslots[0] = map->nslots = nslots; - obj->slots = newslots + 1; + map->nslots = nslots; + obj->slots = newslots; } } #if JS_BUG_EMPTY_INDEX_ZERO #define CHECK_FOR_EMPTY_INDEX(id) \ JS_BEGIN_MACRO \ - if (JSSTRING_LENGTH(_str) == 0) \ + if (JSSTRING_LENGTH(str_) == 0) \ id = JSVAL_ZERO; \ JS_END_MACRO #else @@ -2072,17 +2432,18 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) /* JSVAL_INT_MAX as a string */ #define JSVAL_INT_MAX_STRING "1073741823" -#define CHECK_FOR_FUNNY_INDEX(id) \ +#define CHECK_FOR_STRING_INDEX(id) \ JS_BEGIN_MACRO \ - if (!JSVAL_IS_INT(id)) { \ - JSAtom *atom_ = (JSAtom *)id; \ + if (JSID_IS_ATOM(id)) { \ + JSAtom *atom_ = JSID_TO_ATOM(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_); \ + if (JS7_ISDEC(*cp_)) { \ + size_t n_ = str_->length - negative_; \ + if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \ + id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \ } else { \ CHECK_FOR_EMPTY_INDEX(id); \ } \ @@ -2090,7 +2451,8 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) JS_END_MACRO static jsid -CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative) +CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, + JSBool negative) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; @@ -2104,17 +2466,66 @@ CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative) cp++; } } - if (*cp == 0 && + if (cp == end && (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); + id = INT_TO_JSID((jsint)index); } return id; } +static JSBool +HidePropertyName(JSContext *cx, jsid *idp) +{ + jsid id; + JSAtom *atom, *hidden; + + id = *idp; + JS_ASSERT(JSID_IS_ATOM(id)); + + atom = JSID_TO_ATOM(id); + JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); + JS_ASSERT(ATOM_IS_STRING(atom)); + + hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN); + if (!hidden) + return JS_FALSE; + + /* + * Link hidden to unhidden atom to optimize call_enumerate -- this means + * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom + * in jsgc.c). It overloads the entry.value member, which for unhidden + * atoms may point to keyword information. + */ + hidden->entry.value = atom; + *idp = ATOM_TO_JSID(hidden); + return JS_TRUE; +} + +JSScopeProperty * +js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + if (!HidePropertyName(cx, &id)) + return NULL; + + flags |= SPROP_IS_HIDDEN; + return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, + flags, shortid); +} + +JSBool +js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + return HidePropertyName(cx, &id) && + js_LookupProperty(cx, obj, id, objp, propp); +} + JSScopeProperty * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, @@ -2132,7 +2543,7 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ - CHECK_FOR_FUNNY_INDEX(id); + CHECK_FOR_STRING_INDEX(id); sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, flags, shortid); } @@ -2172,6 +2583,26 @@ js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, 0, 0, propp); } +/* + * Backward compatibility requires allowing addProperty hooks to mutate the + * nominal initial value of a slot-full property, while GC safety wants that + * value to be stored before the call-out through the hook. Optimize to do + * both while saving cycles for classes that stub their addProperty hook. + */ +#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ + JS_BEGIN_MACRO \ + if ((clasp)->addProperty != JS_PropertyStub) { \ + jsval nominal_ = *(vp); \ + if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ + cleanup; \ + } \ + if (*(vp) != nominal_) { \ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ + LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp)); \ + } \ + } \ + JS_END_MACRO + JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, @@ -2186,7 +2617,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ - CHECK_FOR_FUNNY_INDEX(id); + CHECK_FOR_STRING_INDEX(id); #if JS_HAS_GETTER_SETTER /* @@ -2257,15 +2688,15 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, 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; - } - + /* Store value before calling addProperty, in case the latter GC's. */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); + /* XXXbe called with lock held */ + ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, + js_RemoveScopeProperty(cx, scope, id); + goto bad); + #if JS_HAS_GETTER_SETTER out: #endif @@ -2294,6 +2725,8 @@ Detecting(JSContext *cx, jsbytecode *pc) JSOp op; JSAtom *atom; + if (!cx->fp) + return JS_FALSE; script = cx->fp->script; for (endpc = script->code + script->length; pc < endpc; pc++) { /* General case: a branch or equality op follows the access. */ @@ -2334,13 +2767,16 @@ Detecting(JSContext *cx, jsbytecode *pc) return JS_FALSE; } +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); +} + 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 **objp, JSProperty **propp) { JSObject *start, *obj2, *proto; JSScope *scope; @@ -2360,13 +2796,12 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ - CHECK_FOR_FUNNY_INDEX(id); + CHECK_FOR_STRING_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); @@ -2406,7 +2841,9 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (clasp->flags & JSCLASS_NEW_RESOLVE) { newresolve = (JSNewResolveOp)resolve; - if (cx->fp && (pc = cx->fp->pc)) { + if (!(flags & JSRESOLVE_CLASSNAME) && + cx->fp && + (pc = cx->fp->pc)) { cs = &js_CodeSpec[*pc]; format = cs->format; if ((format & JOF_MODEMASK) != JOF_NAME) @@ -2435,7 +2872,6 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, 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) { @@ -2463,7 +2899,10 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (scope->object == obj2) sprop = SCOPE_GET_PROPERTY(scope, id); } - if (obj2 != obj && !sprop) { + if (sprop) { + JS_ASSERT(obj2 == scope->object); + obj = obj2; + } else if (obj2 != obj) { JS_UNLOCK_OBJ(cx, obj2); JS_LOCK_OBJ(cx, obj); } @@ -2478,7 +2917,6 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, 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) @@ -2515,22 +2953,6 @@ out: 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) @@ -2632,7 +3054,7 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ - CHECK_FOR_FUNNY_INDEX(id); + CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &obj2, &prop)) return JS_FALSE; @@ -2641,7 +3063,7 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) #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) + default_val = (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ? JSVAL_NULL : JSVAL_VOID; #else @@ -2739,7 +3161,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ - CHECK_FOR_FUNNY_INDEX(id); + CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) return JS_FALSE; @@ -2777,7 +3199,7 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if ((attrs & JSPROP_READONLY) || (SCOPE_IS_SEALED(scope) && pobj == obj)) { JS_UNLOCK_SCOPE(cx, scope); - if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version)) + if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) return JS_TRUE; goto read_only_error; } @@ -2844,17 +3266,20 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 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. */ + /* + * Initialize the new property value (passed to setter) to undefined. + * Note that we store before calling addProperty, to match the order + * in js_DefineNativeProperty. + */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); + /* XXXbe called with obj locked */ + ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, + js_RemoveScopeProperty(cx, scope, id); + JS_UNLOCK_SCOPE(cx, scope); + return JS_FALSE); + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); } @@ -2954,9 +3379,7 @@ js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, } } sprop = (JSScopeProperty *)prop; - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, - *attrsp & - ~(JSPROP_GETTER | JSPROP_SETTER), 0, + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, sprop->getter, sprop->setter); if (noprop) OBJ_DROP_PROPERTY(cx, obj, prop); @@ -2975,13 +3398,13 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) JSScope *scope; JSBool ok; - *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID; + *rval = JS_VERSION_IS_ECMA(cx) ? 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); + CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &proto, &prop)) return JS_FALSE; @@ -3015,7 +3438,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) sprop = (JSScopeProperty *)prop; if (sprop->attrs & JSPROP_PERMANENT) { OBJ_DROP_PROPERTY(cx, obj, prop); - if (JSVERSION_IS_ECMA(cx->version)) { + if (JS_VERSION_IS_ECMA(cx)) { *rval = JSVAL_FALSE; return JS_TRUE; } @@ -3068,8 +3491,9 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) * method, and calling that method returned failure. */ if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, - &v)) + &v)) { return JS_FALSE; + } if (!JSVAL_IS_PRIMITIVE(v)) { if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) @@ -3080,7 +3504,7 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) * 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) { + if (!JSVAL_IS_PRIMITIVE(v) && JS_VERSION_IS_1_2(cx)) { char *bytes = JS_smprintf("[object %s]", OBJ_GET_CLASS(cx, obj)->name); if (!bytes) @@ -3106,7 +3530,7 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) goto out; } /* Don't convert to string (source object literal) for JS1.2. */ - if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN) + if (JS_VERSION_IS_1_2(cx) && hint == JSTYPE_BOOLEAN) goto out; if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) @@ -3146,27 +3570,33 @@ js_NewIdArray(JSContext *cx, jsint length) JSIdArray *ida; ida = (JSIdArray *) - JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval)); + 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) +js_SetIdArrayLength(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; + JSIdArray *rida; + + rida = (JSIdArray *) + JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); + if (!rida) + JS_DestroyIdArray(cx, ida); + else + rida->length = length; + return rida; } /* 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; +struct JSNativeIteratorState { + jsint next_index; /* index into jsid array */ + JSIdArray *ida; /* all property ids in enumeration */ + JSNativeIteratorState *next; /* double-linked list support */ + JSNativeIteratorState **prevp; +}; /* * This function is used to enumerate the properties of native JSObjects @@ -3177,7 +3607,8 @@ JSBool js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { - JSObject *proto_obj; + JSRuntime *rt; + JSObject *proto; JSClass *clasp; JSEnumerateOp enumerate; JSScopeProperty *sprop, *lastProp; @@ -3186,16 +3617,16 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, JSIdArray *ida; JSNativeIteratorState *state; + rt = cx->runtime; 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: + case JSENUMERATE_INIT: if (!enumerate(cx, obj)) - goto init_error; + return JS_FALSE; length = 0; /* @@ -3211,12 +3642,12 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, * 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)) { + proto = OBJ_GET_PROTO(cx, obj); + if (proto && scope == OBJ_SCOPE(proto)) { ida = js_NewIdArray(cx, 0); if (!ida) { JS_UNLOCK_OBJ(cx, obj); - goto init_error; + return JS_FALSE; } } else { /* Object has a private scope; Enumerate all props in scope. */ @@ -3236,7 +3667,7 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, ida = js_NewIdArray(cx, length); if (!ida) { JS_UNLOCK_OBJ(cx, obj); - goto init_error; + return JS_FALSE; } i = length; for (sprop = lastProp; sprop; sprop = sprop->parent) { @@ -3259,81 +3690,151 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, JS_malloc(cx, sizeof(JSNativeIteratorState)); if (!state) { JS_DestroyIdArray(cx, ida); - goto init_error; + return JS_FALSE; } state->ida = ida; state->next_index = 0; + + JS_LOCK_RUNTIME(rt); + state->next = rt->nativeIteratorStates; + if (state->next) + state->next->prevp = &state->next; + state->prevp = &rt->nativeIteratorStates; + *state->prevp = state; + JS_UNLOCK_RUNTIME(rt); + *statep = PRIVATE_TO_JSVAL(state); if (idp) *idp = INT_TO_JSVAL(length); - return JS_TRUE; + break; - case JSENUMERATE_NEXT: + 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; + break; } + /* FALL THROUGH */ - /* Fall through ... */ - - case JSENUMERATE_DESTROY: + case JSENUMERATE_DESTROY: state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); + + JS_LOCK_RUNTIME(rt); + JS_ASSERT(rt->nativeIteratorStates); + JS_ASSERT(*state->prevp == state); + if (state->next) { + JS_ASSERT(state->next->prevp == &state->next); + state->next->prevp = state->prevp; + } + *state->prevp = state->next; + JS_UNLOCK_RUNTIME(rt); + JS_DestroyIdArray(cx, state->ida); JS_free(cx, state); *statep = JSVAL_NULL; - return JS_TRUE; - - default: - JS_ASSERT(0); - return JS_FALSE; + break; } + return JS_TRUE; +} -init_error: - *statep = JSVAL_NULL; - return JS_FALSE; +void +js_MarkNativeIteratorStates(JSContext *cx) +{ + JSNativeIteratorState *state; + jsid *cursor, *end, id; + + state = cx->runtime->nativeIteratorStates; + if (!state) + return; + + do { + JS_ASSERT(*state->prevp == state); + cursor = state->ida->vector; + end = cursor + state->ida->length; + for (; cursor != end; ++cursor) { + id = *cursor; + if (JSID_IS_ATOM(id)) { + GC_MARK_ATOM(cx, JSID_TO_ATOM(id), NULL); + } else if (JSID_IS_OBJECT(id)) { + GC_MARK(cx, JSID_TO_OBJECT(id), "ida->vector[i]", NULL); + } + } + } while ((state = state->next) != NULL); } + JSBool js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) { + JSBool writing; JSObject *pobj; JSProperty *prop; - JSScopeProperty *sprop; JSClass *clasp; - JSBool ok; + JSScopeProperty *sprop; + JSCheckAccessOp check; + + writing = (mode & JSACC_WRITE) != 0; + switch (mode & JSACC_TYPEMASK) { + case JSACC_PROTO: + pobj = obj; + if (!writing) + *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO); + *attrsp = JSPROP_PERMANENT; + break; - 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)) { + case JSACC_PARENT: + JS_ASSERT(!writing); + pobj = obj; + *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT); + *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; + break; + + default: + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + if (!writing) + *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; + *attrsp = sprop->attrs; + if (!writing) { + *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) + ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) + : JSVAL_VOID; + } 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; + + /* + * If obj's class has a stub (null) checkAccess hook, use the per-runtime + * checkObjectAccess callback, if configured. + * + * We don't want to require all classes to supply a checkAccess hook; we + * need that hook only for certain classes used when precompiling scripts + * and functions ("brutal sharing"). But for general safety of built-in + * magic properties such as __proto__ and __parent__, we route all access + * checks, even for classes that stub out checkAccess, through the global + * checkObjectAccess hook. This covers precompilation-based sharing and + * (possibly unintended) runtime sharing across trust boundaries. + */ + clasp = OBJ_GET_CLASS(cx, pobj); + check = clasp->checkAccess; + if (!check) + check = cx->runtime->checkObjectAccess; + return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); } #ifdef JS_THREADSAFE @@ -3382,7 +3883,8 @@ GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.ExecutionContextAtom, + ATOM_TO_JSID(cx->runtime->atomState + .ExecutionContextAtom), &xcval)) { return JS_FALSE; } @@ -3391,7 +3893,7 @@ GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) return JS_FALSE; } if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), - (jsid)cx->runtime->atomState.currentAtom, + ATOM_TO_JSID(cx->runtime->atomState.currentAtom), rval)) { return JS_FALSE; } @@ -3413,7 +3915,7 @@ js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, - (jsid)cx->runtime->atomState.callAtom, + ATOM_TO_JSID(cx->runtime->atomState.callAtom), &fval)) { return JS_FALSE; } @@ -3455,7 +3957,8 @@ js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, - (jsid)cx->runtime->atomState.constructAtom, + ATOM_TO_JSID(cx->runtime->atomState + .constructAtom), &cval)) { return JS_FALSE; } @@ -3485,6 +3988,7 @@ JSBool js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSClass *clasp; + JSString *str; clasp = OBJ_GET_CLASS(cx, obj); if (clasp->hasInstance) @@ -3494,7 +3998,8 @@ js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) jsval fval, rval; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.hasInstanceAtom, + ATOM_TO_JSID(cx->runtime->atomState + .hasInstanceAtom), &fval)) { return JS_FALSE; } @@ -3504,8 +4009,14 @@ js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) } } #endif - *bp = JS_FALSE; - return JS_TRUE; + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, + OBJECT_TO_JSVAL(obj), NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INSTANCEOF_RHS, + JS_GetStringBytes(str)); + } + return JS_FALSE; } JSBool @@ -3539,20 +4050,72 @@ GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, jsval v; JSObject *ctor; - if (!FindConstructor(cx, scope, name, &v)) + if (!js_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, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &v)) { return JS_FALSE; } + if (!JSVAL_IS_PRIMITIVE(v)) { + /* + * Set the newborn root in case v is otherwise unreferenced. + * It's ok to overwrite newborn roots here, since the getter + * called just above could have. Unlike the common GC rooting + * model, our callers do not have to protect protop thanks to + * this newborn root, since they all immediately create a new + * instance that delegates to this object, or just query the + * prototype for its class. + */ + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); + } } *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; return JS_TRUE; } +/* + * For shared precompilation of function objects, we support cloning on entry + * to an execution context in which the function declaration or expression + * should be processed as if it were not precompiled, where the precompiled + * function's scope chain does not match the execution context's. The cloned + * function object carries its execution-context scope in its parent slot; it + * links to the precompiled function (the "clone-parent") via its proto slot. + * + * Note that this prototype-based delegation leaves an unchecked access path + * from the clone to the clone-parent's 'constructor' property. If the clone + * lives in a less privileged or shared scope than the clone-parent, this is + * a security hole, a sharing hazard, or both. Therefore we check all such + * accesses with the following getter/setter pair, which we use when defining + * 'constructor' in f.prototype for all function objects f. + */ +static JSBool +CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSAtom *atom; + uintN attrs; + + atom = cx->runtime->atomState.constructorAtom; + JS_ASSERT(id == ATOM_KEY(atom)); + return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, + vp, &attrs); +} + +static JSBool +CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSAtom *atom; + uintN attrs; + + atom = cx->runtime->atomState.constructorAtom; + JS_ASSERT(id == ATOM_KEY(atom)); + return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, + vp, &attrs); +} + JSBool js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs) @@ -3564,8 +4127,10 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, * DontDelete. */ if (!OBJ_DEFINE_PROPERTY(cx, ctor, - (jsid)cx->runtime->atomState.classPrototypeAtom, - OBJECT_TO_JSVAL(proto), NULL, NULL, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), + OBJECT_TO_JSVAL(proto), + JS_PropertyStub, JS_PropertyStub, attrs, NULL)) { return JS_FALSE; } @@ -3575,8 +4140,10 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, * 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, + ATOM_TO_JSID(cx->runtime->atomState + .constructorAtom), + OBJECT_TO_JSVAL(ctor), + CheckCtorGetAccess, CheckCtorSetAccess, 0, NULL); } @@ -3649,8 +4216,15 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, uintN argc, jsval *argv, jsval *rval) { JSErrorReporter older; + jsid id; jsval fval; JSBool ok; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return JS_FALSE; + } /* * Report failure only if an appropriate method was found, and calling it @@ -3658,16 +4232,24 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, * 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; + id = ATOM_TO_JSID(atom); + fval = JSVAL_VOID; +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, &fval); + ok = (obj != NULL); + } else +#endif + { + ok = OBJ_GET_PROPERTY(cx, obj, id, &fval); } + if (!ok) + JS_ClearPendingException(cx); + ok = JSVAL_IS_PRIMITIVE(fval) || + js_InternalCall(cx, obj, fval, argc, argv, rval); JS_SetErrorReporter(cx, older); return ok; } @@ -3713,7 +4295,7 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) if (xdr->mode != JSXDR_ENCODE) { if (classDef) { - ok = js_GetClassPrototype(cx, className, &proto); + ok = GetClassPrototype(cx, NULL, className, &proto); if (!ok) goto out; clasp = OBJ_GET_CLASS(cx, proto); @@ -3834,14 +4416,16 @@ js_Mark(JSContext *cx, JSObject *obj, void *arg) 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 (JSID_IS_ATOM(sprop->id)) + GC_MARK_ATOM(cx, JSID_TO_ATOM(sprop->id), arg); + else if (JSID_IS_OBJECT(sprop->id)) + GC_MARK(cx, JSID_TO_OBJECT(sprop->id), "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; + JSAtom *atom = JSID_TO_ATOM(sprop->id); const char *id = (atom && ATOM_IS_STRING(atom)) ? JS_GetStringBytes(ATOM_TO_STRING(atom)) : "unknown"; @@ -3941,7 +4525,7 @@ JSBool js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { JSScope *scope; - uint32 nslots, i; + uint32 nslots; JSClass *clasp; jsval *newslots; @@ -3966,18 +4550,14 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) nslots += clasp->reserveSlots(cx, obj); JS_ASSERT(slot < nslots); - newslots = (jsval *) - JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval)); + newslots = AllocSlots(cx, obj->slots, nslots); 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; + obj->slots = newslots; } /* Whether or not we grew nslots, we may need to advance freeslot. */ diff --git a/src/dom/js/jsobj.h b/src/dom/js/jsobj.h index 9ac5857e0..a2400e982 100644 --- a/src/dom/js/jsobj.h +++ b/src/dom/js/jsobj.h @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -61,13 +62,8 @@ struct JSObjectMap { }; /* 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) \ @@ -105,6 +101,16 @@ struct JSObjectMap { ? (obj)->map->ops->setRequiredSlot(cx, obj, slot, v) \ : JS_TRUE) +#define OBJ_TO_INNER_OBJECT(cx,obj) \ + JS_BEGIN_MACRO \ + JSClass *clasp_ = OBJ_GET_CLASS(cx, obj); \ + if (clasp_->flags & JSCLASS_IS_EXTENDED) { \ + JSExtendedClass *xclasp_ = (JSExtendedClass*)clasp_; \ + if (xclasp_->innerObject) \ + obj = xclasp_->innerObject(cx, obj); \ + } \ + JS_END_MACRO + /* * 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, @@ -231,6 +237,24 @@ extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; extern JSClass js_ObjectClass; extern JSClass js_WithClass; +extern JSObject * +js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); + +/* + * A With object is like a Block object, in that both have one reserved slot + * telling the stack depth of the relevant slots (the slot whose value is the + * object named in the with statement, the slots containing the block's local + * variables); and both have a private slot referring to the JSStackFrame in + * whose activation they were created (or null if the with or block object + * outlives the frame). + */ +#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) + +#define OBJ_BLOCK_DEPTH(cx,obj) \ + JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH)) +#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ + OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)) + struct JSSharpObjectMap { jsrefcount depth; jsatomid sharpgen; @@ -240,26 +264,37 @@ struct JSSharpObjectMap { #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)) +#define IS_SHARP(he) (JS_PTR_TO_UINT32((he)->value) & SHARP_BIT) +#define MAKE_SHARP(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|SHARP_BIT)) +#define IS_BUSY(he) (JS_PTR_TO_UINT32((he)->value) & BUSY_BIT) +#define MAKE_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|BUSY_BIT)) +#define CLEAR_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)&~BUSY_BIT)) extern JSHashEntry * js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp); + jschar **sp); extern void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); +/* + * Mark objects stored in map if GC happens between js_EnterSharpObject + * and js_LeaveSharpObject. GC calls this when map->depth > 0. + */ +extern void +js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map); + extern JSBool js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); + jsval *rval); extern JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); + jsval *rval); + +extern JSBool +js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, + uintN argc, jsval *argv, jsval *rval); extern JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj); @@ -277,11 +312,11 @@ extern const char js_lookupSetter_str[]; extern void js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp); + JSClass *clasp); extern JSObjectMap * js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); + JSClass *clasp, JSObject *obj); extern void js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); @@ -295,6 +330,9 @@ js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); extern JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); +extern JSBool +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp); + extern JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv); @@ -308,6 +346,21 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); extern void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); +/* + * Native property add and lookup variants that hide id in the hidden atom + * subspace, so as to avoid collisions between internal properties such as + * formal arguments and local variables in function objects, and externally + * set properties with the same ids. + */ +extern JSScopeProperty * +js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +extern JSBool +js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); + /* * Find or create a property named by id in obj's scope, with the given getter * and setter, slot, attributes, and other members. @@ -336,8 +389,8 @@ js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, */ extern JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp); + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp); extern JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, @@ -351,33 +404,20 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, * *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 + JSProperty **propp); /* * 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 - ); + JSObject **objp, JSProperty **propp); extern JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp); + JSProperty **propp); extern JSObject * js_FindIdentifierBase(JSContext *cx, jsid id); @@ -393,11 +433,11 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); + uintN *attrsp); extern JSBool js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); + uintN *attrsp); extern JSBool js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); @@ -408,23 +448,29 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); extern JSIdArray * js_NewIdArray(JSContext *cx, jsint length); +/* + * Unlike realloc(3), this function frees ida on failure. + */ extern JSIdArray * -js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length); +js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length); extern JSBool js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp); + jsval *statep, jsid *idp); + +extern void +js_MarkNativeIteratorStates(JSContext *cx); extern JSBool js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp); + 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); + jsval *rval); extern JSBool js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); @@ -440,7 +486,7 @@ js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop); extern JSBool js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs); + uintN attrs); extern JSBool js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); @@ -453,7 +499,7 @@ 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); + uintN argc, jsval *argv, jsval *rval); extern JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp); @@ -470,6 +516,12 @@ js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); extern JSBool js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); +extern JSObject * +js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller); + +extern JSBool +js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, + JSPrincipals *principals, const char *caller); JS_END_EXTERN_C #endif /* jsobj_h___ */ diff --git a/src/dom/js/jsopcode.c b/src/dom/js/jsopcode.c index e751103a0..80c7eb4dd 100644 --- a/src/dom/js/jsopcode.c +++ b/src/dom/js/jsopcode.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -82,6 +83,7 @@ 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_default_str[] = "default"; const char *js_incop_str[] = {"++", "--"}; @@ -111,7 +113,7 @@ 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); @@ -120,7 +122,7 @@ GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) #ifdef DEBUG -JS_FRIEND_API(void) +JS_FRIEND_API(JSBool) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) { jsbytecode *pc, *end; @@ -135,9 +137,10 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) PTRDIFF(pc, script->code, jsbytecode), lines, fp); if (!len) - return; + return JS_FALSE; pc += len; } + return JS_TRUE; } JS_FRIEND_API(uintN) @@ -150,7 +153,6 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, uint32 type; JSAtom *atom; JSString *str; - char *cstr; op = (JSOp)*pc; if (op >= JSOP_LIMIT) { @@ -189,11 +191,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, 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); + fprintf(fp, " %s", JS_GetStringBytes(str)); break; case JOF_UINT16: @@ -249,12 +247,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, 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); + fprintf(fp, "\n\t%s: %d", JS_GetStringBytes(str), off); npairs--; } len = 1 + pc2 - pc; @@ -271,21 +264,59 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, break; #if JS_HAS_LEXICAL_CLOSURE - case JOF_DEFLOCALVAR: + case JOF_INDEXCONST: 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); + fprintf(fp, " %s", JS_GetStringBytes(str)); break; #endif + case JOF_UINT24: + if (op == JSOP_FINDNAME) { + /* Special case to avoid a JOF_FINDNAME just for this op. */ + atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + fprintf(fp, " %s", JS_GetStringBytes(str)); + break; + } + + JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL); + fprintf(fp, " %u", GET_LITERAL_INDEX(pc)); + break; + + case JOF_LITOPX: + atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + + /* + * Bytecode: JSOP_LITOPX op [ if JSOP_DEFLOCALFUN]. + * Advance pc to point at op. + */ + pc += 1 + LITERAL_INDEX_LEN; + op = *pc; + cs = &js_CodeSpec[op]; + fprintf(fp, " %s op %s", JS_GetStringBytes(str), cs->name); +#if JS_HAS_LEXICAL_CLOSURE + if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST) + fprintf(fp, " %u", GET_VARNO(pc)); +#endif + + /* + * Set len to advance pc to skip op and any other immediates (namely, + * if JSOP_DEFLOCALFUN). + */ + JS_ASSERT(cs->length > ATOM_INDEX_LEN); + len = cs->length - ATOM_INDEX_LEN; + break; + default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format); @@ -365,7 +396,7 @@ Sprint(Sprinter *sp, const char *format, ...) ptrdiff_t offset; va_start(ap, format); - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ + bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ va_end(ap); if (!bp) { JS_ReportOutOfMemory(sp->context); @@ -432,10 +463,36 @@ QuoteString(Sprinter *sp, JSString *str, jschar quote) break; /* Use js_EscapeMap, \u, or \x only if necessary. */ - if ((u = js_strchr(js_EscapeMap, c)) != NULL) + if ((u = js_strchr(js_EscapeMap, c)) != NULL) { ok = Sprint(sp, "\\%c", (char)u[1]) >= 0; - else + } else { +#ifdef JS_C_STRINGS_ARE_UTF8 + /* If this is a surrogate pair, make sure to print the pair. */ + if (c >= 0xD800 && c <= 0xDBFF) { + jschar buffer[3]; + buffer[0] = c; + buffer[1] = *++t; + buffer[2] = 0; + if (t == z) { + char numbuf[10]; + JS_snprintf(numbuf, sizeof numbuf, "0x%x", c); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_BAD_SURROGATE_CHAR, + numbuf); + ok = JS_FALSE; + break; + } + ok = Sprint(sp, "%hs", buffer) >= 0; + } else { + /* Print as UTF-8 string. */ + ok = Sprint(sp, "%hc", c) >= 0; + } +#else + /* Use \uXXXX or \xXX if the string can't be displayed as UTF-8. */ ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0; +#endif + } if (!ok) return NULL; } @@ -443,6 +500,13 @@ QuoteString(Sprinter *sp, JSString *str, jschar quote) /* Sprint the closing quote and return the quoted string. */ if (quote && Sprint(sp, "%c", (char)quote) < 0) return NULL; + + /* + * If we haven't Sprint'd anything yet, Sprint an empty string so that + * the OFF2STR below gives a valid result. + */ + if (off == sp->offset && Sprint(sp, "") < 0) + return NULL; return OFF2STR(sp, off); } @@ -545,7 +609,7 @@ js_printf(JSPrinter *jp, const char *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') { + if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') { fp = JS_strdup(jp->sprinter.context, format); if (!fp) return -1; @@ -554,7 +618,7 @@ js_printf(JSPrinter *jp, const char *format, ...) } /* Allocate temp space, convert format, and put. */ - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ + bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ if (fp) { JS_free(jp->sprinter.context, fp); format = NULL; @@ -590,7 +654,7 @@ typedef struct SprintStack { } SprintStack; /* Gap between stacked strings to allow for insertion of parens and commas. */ -#define PAREN_SLOP (2 + 1) +#define PAREN_SLOP (2 + 1) /* * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, @@ -701,7 +765,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, diff = table[0].offset - defaultOffset; if (diff > 0) { jp->indent += 2; - js_printf(jp, "\tdefault:\n"); + js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; if (!Decompile(ss, pc + defaultOffset, diff)) return JS_FALSE; @@ -751,7 +815,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_FALSE; } rval = QuoteString(&ss->sprinter, str, - JSVAL_IS_STRING(key) ? (jschar)'"' : 0); + (jschar)(JSVAL_IS_STRING(key) ? '"' : 0)); if (!rval) return JS_FALSE; RETRACT(&ss->sprinter, rval); @@ -768,7 +832,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, off = defaultOffset; } jp->indent -= 2; - js_printf(jp, "\tdefault:\n"); + js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; } if (!Decompile(ss, pc + off, off2 - off)) @@ -779,7 +843,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, if (defaultOffset == switchLength) { jp->indent += 2; - js_printf(jp, "\tdefault:;\n"); + js_printf(jp, "\t%s:;\n", js_default_str); jp->indent -= 2; } js_printf(jp, "\t}\n"); @@ -800,9 +864,9 @@ GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) if (sprop->getter != getter) continue; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT(!JSVAL_IS_INT(sprop->id)); + JS_ASSERT(JSID_IS_ATOM(sprop->id)); if ((uintN) sprop->shortid == slot) - return (JSAtom *) sprop->id; + return JSID_TO_ATOM(sprop->id); } obj = scope->object; if (!obj) @@ -818,20 +882,13 @@ GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) 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; + return "var "; + if (SN_TYPE(sn) == SRC_CONST) + return "const "; } - if (!kw) - return ""; - JS_snprintf(buf, sizeof buf, "%s ", kw); - return buf; + return ""; } static JSBool @@ -847,21 +904,28 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) const char *lval, *rval, *xval, *fmt; jsint i, argc; char **argv; + jsatomid atomIndex; JSAtom *atom; JSObject *obj; JSFunction *fun; JSString *str; JSBool ok; +#if JS_HAS_XML_SUPPORT + JSBool foreach, inXML, quoteAttr; +#else +#define inXML JS_FALSE +#endif jsval val; + int stackDummy; 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 +#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 @@ -871,13 +935,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (!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. + * Given an atom already fetched from jp->script's atom map, quote/escape its + * string appropriately into rval, and select fmt from the quoted and unquoted + * alternatives. */ -#define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ +#define GET_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; \ @@ -890,15 +954,33 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) return JS_FALSE; \ JS_END_MACRO +/* + * Get atom from jp->script's atom map, quote/escape its string appropriately + * into rval, and select fmt from the quoted and unquoted alternatives. + */ +#define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ + JS_BEGIN_MACRO \ + atom = GET_ATOM(cx, jp->script, pc); \ + GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \ + JS_END_MACRO + cx = ss->sprinter.context; + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + return JS_FALSE; + } + jp = ss->printer; endpc = pc + nb; forelem_tail = forelem_done = NULL; tail = -1; - todo = -2; /* NB: different from Sprint() error return. */ + todo = -2; /* NB: different from Sprint() error return. */ op = JSOP_NOP; sn = NULL; rval = NULL; +#if JS_HAS_XML_SUPPORT + foreach = inXML = quoteAttr = JS_FALSE; +#endif while (pc < endpc) { lastop = op; @@ -926,9 +1008,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { /* Print only the right operand of the assignment-op. */ todo = SprintPut(&ss->sprinter, rval, strlen(rval)); - } else { + } else if (!inXML) { todo = Sprint(&ss->sprinter, "%s %s %s", lval, cs->token, rval); + } else { + /* In XML, just concatenate the two operands. */ + JS_ASSERT(op == JSOP_ADD); + todo = Sprint(&ss->sprinter, "%s%s", lval, rval); } break; @@ -938,12 +1024,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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; @@ -953,6 +1033,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } } else { switch (op) { +#define BEGIN_LITOPX_CASE(OP) \ + case OP: \ + atomIndex = GET_ATOM_INDEX(pc); \ + do_##OP: \ + atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); + +#define END_LITOPX_CASE \ + break; + case JSOP_NOP: /* * Check for a do-while loop, a for-loop with an empty @@ -1054,14 +1143,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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); + if (*pc == JSOP_LITOPX) { + atomIndex = GET_LITERAL_INDEX(pc); + pc += 1 + LITERAL_INDEX_LEN; + LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR); + ++pc; + } else { + LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR); + atomIndex = GET_ATOM_INDEX(pc); + pc += js_CodeSpec[JSOP_INITCATCHVAR].length; + } + atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); 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; @@ -1093,12 +1190,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (!jp2) return JS_FALSE; jp2->scope = jp->scope; - if (js_DecompileFunction(jp2, fun)) { + js_puts(jp2, "\n"); + ok = js_DecompileFunction(jp2, fun); + if (ok) { + js_puts(jp2, "\n"); str = js_GetPrinterOutput(jp2); if (str) js_printf(jp, "%s\n", JS_GetStringBytes(str)); + else + ok = JS_FALSE; } js_DestroyPrinter(jp2); + if (!ok) + return JS_FALSE; + break; default:; @@ -1122,6 +1227,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_PUSH: case JSOP_PUSHOBJ: case JSOP_BINDNAME: + do_JSOP_BINDNAME: todo = Sprint(&ss->sprinter, ""); break; @@ -1133,7 +1239,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; { - static const char finally_cookie[] = "/*FINALLY*/"; + static const char exception_cookie[] = "/*EXCEPTION*/"; + static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; case JSOP_FINALLY: jp->indent -= 4; @@ -1145,12 +1252,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * 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); + todo = Sprint(&ss->sprinter, exception_cookie); + if (todo < 0 || !PushOff(ss, todo, op)) + return JS_FALSE; + todo = Sprint(&ss->sprinter, retsub_pc_cookie); break; case JSOP_RETSUB: rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0); + LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0); + lval = POP_STR(); + LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); todo = -2; break; } @@ -1285,6 +1397,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; #if JS_HAS_EXCEPTIONS + case JSOP_THROWING: + todo = -2; + break; + case JSOP_THROW: sn = js_GetSrcNote(jp->script, pc); todo = -2; @@ -1489,15 +1605,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) tail = js_GetSrcNoteOffset(sn2, 0); do_forinbody: - js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval); +#if JS_HAS_XML_SUPPORT + if (foreach) { + foreach = JS_FALSE; + js_printf(jp, "\tfor %s (%s%s", + js_each_str, VarPrefix(sn), lval); + } else +#endif + 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); + } else if (xval && *xval) { + js_printf(jp, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? ".%s" + : "[%s]", + xval); } rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); js_printf(jp, " in %s) {\n", rval); @@ -1559,6 +1686,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) forelem_done = NULL; goto do_forinbody; +#if JS_HAS_GETTER_SETTER + case JSOP_GETTER: + case JSOP_SETTER: + todo = -2; + break; +#endif + case JSOP_DUP2: rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]); todo = SprintPut(&ss->sprinter, rval, strlen(rval)); @@ -1585,7 +1719,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_SETCONST: case JSOP_SETNAME: case JSOP_SETGVAR: - atom = GET_ATOM(cx, jp->script, pc); + atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_SETCONST: + atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); + do_setname: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) @@ -1593,6 +1731,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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) { @@ -1694,10 +1833,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_DELELEM: xval = POP_STR(); lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s[%s]", + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s %s.%s" + : "%s %s[%s]", js_delete_str, lval, xval); break; +#if JS_HAS_XML_SUPPORT + case JSOP_DELDESC: + xval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s..%s", + js_delete_str, lval, xval); + break; +#endif + case JSOP_TYPEOF: case JSOP_VOID: rval = POP_STR(); @@ -1743,9 +1894,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s%s.%s" + : "%s%s[%s]", + js_incop_str[!(cs->format & JOF_INC)], + lval, xval); + } else { + todo = Sprint(&ss->sprinter, "%s%s", + js_incop_str[!(cs->format & JOF_INC)], lval); + } break; case JSOP_ARGINC: @@ -1787,9 +1946,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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)]); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s%s" + : "%s[%s]%s", + lval, xval, + js_incop_str[!(cs->format & JOF_INC)]); + } else { + todo = Sprint(&ss->sprinter, "%s%s", + lval, js_incop_str[!(cs->format & JOF_INC)]); + } break; case JSOP_GETPROP2: @@ -1798,13 +1965,41 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* FALL THROUGH */ case JSOP_GETPROP: - GET_ATOM_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval); + atom = GET_ATOM(cx, jp->script, pc); + + do_getprop: + GET_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval); + + do_getprop_lval: lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, lval, rval); break; +#if JS_HAS_XML_SUPPORT + BEGIN_LITOPX_CASE(JSOP_GETMETHOD) + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_PCBASE) + goto do_getprop; + GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval); + goto do_getprop_lval; + + BEGIN_LITOPX_CASE(JSOP_SETMETHOD) + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_PCBASE) + goto do_setprop; + GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s", + "%s.function::%s %s= %s", + xval); + goto do_setprop_rval; +#endif + case JSOP_SETPROP: - GET_ATOM_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); + atom = GET_ATOM(cx, jp->script, pc); + + do_setprop: + GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); + + do_setprop_rval: rval = POP_STR(); lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc - 1); @@ -1825,10 +2020,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = POP_STR(); op = JSOP_GETELEM; lval = POP_STR(); - if (*xval == '\0') + if (*xval == '\0') { todo = Sprint(&ss->sprinter, "%s", lval); - else - todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval); + } else { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s" + : "%s[%s]", + lval, xval); + } break; case JSOP_SETELEM: @@ -1840,7 +2040,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (*xval == '\0') goto do_setlval; sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, "%s[%s] %s= %s", + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s %s= %s" + : "%s[%s] %s= %s", lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) ? js_CodeSpec[lastop].token @@ -1883,11 +2086,62 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_UINT16: i = (jsint) GET_ATOM_INDEX(pc); + goto do_sprint_int; + + case JSOP_UINT24: + i = (jsint) GET_LITERAL_INDEX(pc); + do_sprint_int: todo = Sprint(&ss->sprinter, "%u", (unsigned) i); break; - case JSOP_NUMBER: - atom = GET_ATOM(cx, jp->script, pc); + case JSOP_LITERAL: + atomIndex = GET_LITERAL_INDEX(pc); + goto do_JSOP_STRING; + + case JSOP_FINDNAME: + atomIndex = GET_LITERAL_INDEX(pc); + todo = Sprint(&ss->sprinter, ""); + if (todo < 0 || !PushOff(ss, todo, op)) + return JS_FALSE; + atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); + goto do_name; + + case JSOP_LITOPX: + atomIndex = GET_LITERAL_INDEX(pc); + op = pc[1 + LITERAL_INDEX_LEN]; + switch (op) { + case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; + case JSOP_BINDNAME: goto do_JSOP_BINDNAME; + case JSOP_CLOSURE: goto do_JSOP_CLOSURE; +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; +#endif +#if JS_HAS_XML_SUPPORT + case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; + case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; +#endif + case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; + case JSOP_NUMBER: goto do_JSOP_NUMBER; + case JSOP_OBJECT: goto do_JSOP_OBJECT; +#if JS_HAS_XML_SUPPORT + case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; + case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; +#endif + case JSOP_REGEXP: goto do_JSOP_REGEXP; + case JSOP_SETCONST: goto do_JSOP_SETCONST; + case JSOP_STRING: goto do_JSOP_STRING; +#if JS_HAS_XML_SUPPORT + case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; + case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; + case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; + case JSOP_XMLPI: goto do_JSOP_XMLPI; +#endif + default: JS_ASSERT(0); + } + /* NOTREACHED */ + break; + + BEGIN_LITOPX_CASE(JSOP_NUMBER) val = ATOM_KEY(atom); if (JSVAL_IS_INT(val)) { long ival = (long)JSVAL_TO_INT(val); @@ -1902,22 +2156,27 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } todo = Sprint(&ss->sprinter, numStr); } - break; + END_LITOPX_CASE - case JSOP_STRING: - atom = GET_ATOM(cx, jp->script, pc); + BEGIN_LITOPX_CASE(JSOP_STRING) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar)'"'); + inXML ? 0 : (jschar)'"'); if (!rval) return JS_FALSE; todo = STR2OFF(&ss->sprinter, rval); - break; + END_LITOPX_CASE case JSOP_OBJECT: case JSOP_REGEXP: case JSOP_ANONFUNOBJ: case JSOP_NAMEDFUNOBJ: - atom = GET_ATOM(cx, jp->script, pc); + atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_OBJECT: + do_JSOP_REGEXP: + do_JSOP_ANONFUNOBJ: + do_JSOP_NAMEDFUNOBJ: + atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); if (op == JSOP_OBJECT || op == JSOP_REGEXP) { if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, &val)) { @@ -1946,7 +2205,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) jsbytecode *pc2; ptrdiff_t jmplen, off, off2; jsint j, n, low, high; - TableEntry *table; + TableEntry *table, pivot; sn = js_GetSrcNote(jp->script, pc); JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); @@ -1989,7 +2248,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } pc2 += jmplen; } - js_HeapSort(table, (size_t) j, sizeof(TableEntry), + js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry), CompareOffsets, NULL); } @@ -2149,19 +2408,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) lval, (op == JSOP_NEW_EQ) ? '=' : '!', #if JS_HAS_TRIPLE_EQOPS - JSVERSION_IS_ECMA(cx->version) ? "==" : + JS_VERSION_IS_ECMA(cx) ? "==" : #endif "=", rval); break; -#endif /* !JS_BUG_FALLIBLE_EQOPS */ +#endif #if JS_HAS_LEXICAL_CLOSURE - case JSOP_CLOSURE: - atom = GET_ATOM(cx, jp->script, pc); + BEGIN_LITOPX_CASE(JSOP_CLOSURE) JS_ASSERT(ATOM_IS_OBJECT(atom)); + todo = -2; goto do_function; -#endif /* JS_HAS_LEXICAL_CLOSURE */ + END_LITOPX_CASE +#endif #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTALL: @@ -2169,15 +2429,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; - case JSOP_EXPORTNAME: - atom = GET_ATOM(cx, jp->script, pc); + BEGIN_LITOPX_CASE(JSOP_EXPORTNAME) 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; + END_LITOPX_CASE case JSOP_IMPORTALL: lval = POP_STR(); @@ -2186,6 +2445,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_IMPORTPROP: + do_importprop: GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n", rval); lval = POP_STR(); @@ -2196,6 +2456,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_IMPORTELEM: xval = POP_STR(); op = JSOP_GETELEM; + if (js_CodeSpec[lastop].format & JOF_XMLNAME) + goto do_importprop; lval = POP_STR(); js_printf(jp, "\timport %s[%s]\n", lval, xval); todo = -2; @@ -2246,10 +2508,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 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 : '\''); + (jschar) + (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); if (!xval) return JS_FALSE; rval = POP_STR(); @@ -2268,13 +2530,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval); #else if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { - todo = Sprint(&ss->sprinter, "%s%s%s %s%s", + rval += strlen(js_function_str) + 1; + 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); + strlen(rval) - 1, + rval); } else { todo = Sprint(&ss->sprinter, "%s%s%s:%s", lval, @@ -2319,9 +2583,166 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + inXML = op == JSOP_STARTXML; + todo = -2; + break; + + case JSOP_DEFXMLNS: + rval = POP_STR(); + js_printf(jp, "\t%s %s %s = %s;\n", + js_default_str, js_xml_str, js_namespace_str, rval); + todo = -2; + break; + + case JSOP_ANYNAME: + todo = SprintPut(&ss->sprinter, "*", 1); + break; + + BEGIN_LITOPX_CASE(JSOP_QNAMEPART) + goto do_name; + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_QNAMECONST) + rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!rval) + return JS_FALSE; + RETRACT(&ss->sprinter, rval); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); + END_LITOPX_CASE + + case JSOP_QNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval); + break; + + case JSOP_TOATTRNAME: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "@%s", rval); + break; + + case JSOP_TOATTRVAL: + todo = -2; + break; + + case JSOP_ADDATTRNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s", lval, rval); + /* This gets reset by all XML tag expressions. */ + quoteAttr = JS_TRUE; + break; + + case JSOP_ADDATTRVAL: + rval = POP_STR(); + lval = POP_STR(); + if (quoteAttr) + todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); + else + todo = Sprint(&ss->sprinter, "%s=%s", lval, rval); + break; + + case JSOP_BINDXMLNAME: + /* Leave the name stacked and push a dummy string. */ + todo = Sprint(&ss->sprinter, ""); + break; + + case JSOP_SETXMLNAME: + /* Pop the r.h.s., the dummy string, and the name. */ + rval = POP_STR(); + (void) PopOff(ss, op); + lval = POP_STR(); + goto do_setlval; + + case JSOP_XMLELTEXPR: + case JSOP_XMLTAGEXPR: + todo = Sprint(&ss->sprinter, "{%s}", POP_STR()); + inXML = JS_TRUE; + /* If we're an attribute value, we shouldn't quote this. */ + quoteAttr = JS_FALSE; + break; + + case JSOP_TOXMLLIST: + todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); + inXML = JS_FALSE; + break; + + case JSOP_FOREACH: + foreach = JS_TRUE; + todo = -2; + break; + + case JSOP_TOXML: + inXML = JS_FALSE; + /* FALL THROUGH */ + + case JSOP_XMLNAME: + case JSOP_FILTER: + /* Conversion and prefix ops do nothing in the decompiler. */ + todo = -2; + break; + + case JSOP_ENDFILTER: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval); + break; + + case JSOP_DESCENDANTS: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); + break; + + BEGIN_LITOPX_CASE(JSOP_XMLOBJECT) + atom = GET_ATOM(cx, jp->script, pc); + todo = Sprint(&ss->sprinter, "", + ATOM_TO_OBJECT(atom)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCDATA) + todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0)) + return JS_FALSE; + SprintPut(&ss->sprinter, "]]>", 3); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT) + todo = SprintPut(&ss->sprinter, "", 3); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLPI) + rval = JS_strdup(cx, POP_STR()); + if (!rval) + return JS_FALSE; + todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && + SprintPut(&ss->sprinter, " ", 1) >= 0 && + SprintPut(&ss->sprinter, rval, strlen(rval)); + JS_free(cx, (char *)rval); + if (!ok) + return JS_FALSE; + SprintPut(&ss->sprinter, "?>", 2); + END_LITOPX_CASE + + case JSOP_GETFUNNS: + todo = SprintPut(&ss->sprinter, js_function_str, 8); + break; +#endif /* JS_HAS_XML_SUPPORT */ + default: todo = -2; break; + +#undef BEGIN_LITOPX_CASE +#undef END_LITOPX_CASE } } @@ -2339,10 +2760,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* * Undefine local macros. */ +#undef inXML #undef DECOMPILE_CODE #undef POP_STR #undef LOCAL_ASSERT #undef ATOM_IS_IDENTIFIER +#undef GET_QUOTE_AND_FMT #undef GET_ATOM_QUOTE_AND_FMT return JS_TRUE; @@ -2443,7 +2866,6 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) * an expression by parenthesizing. */ if (jp->pretty) { - js_puts(jp, "\n"); js_printf(jp, "\t"); } else { if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) @@ -2460,6 +2882,8 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) js_puts(jp, "("); if (fun->interpreted && fun->object) { + size_t paramsize; + /* * Print the parameters. * @@ -2473,20 +2897,21 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) cx = jp->sprinter.context; nargs = fun->nargs; mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, - nargs * sizeof(JSAtom *)); + paramsize = nargs * sizeof(JSAtom *); + JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize); if (!params) { JS_ReportOutOfMemory(cx); return JS_FALSE; } + memset(params, 0, paramsize); 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; + JS_ASSERT((uint16) sprop->shortid < nargs); + JS_ASSERT(JSID_IS_ATOM(sprop->id)); + params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); } for (i = 0; i < nargs; i++) { if (i > 0) @@ -2519,9 +2944,7 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) jp->indent -= 4; js_printf(jp, "\t}"); - if (jp->pretty) { - js_puts(jp, "\n"); - } else { + if (!jp->pretty) { if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) js_puts(jp, ")"); } @@ -2538,14 +2961,15 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSScript *script; JSOp op; const JSCodeSpec *cs; - uint32 format, mode; + uint32 format, mode, type; intN depth; jssrcnote *sn; uintN len, off; JSPrinter *jp; JSString *name; - fp = cx->fp; + for (fp = cx->fp; fp && !fp->script; fp = fp->down) + continue; if (!fp) goto do_fallback; @@ -2657,8 +3081,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, begin = pc; } else { sn = js_GetSrcNote(script, pc); - if (!sn || SN_TYPE(sn) != SRC_PCBASE) + if (!sn || (SN_TYPE(sn) != SRC_PCBASE && SN_TYPE(sn) != SRC_PCDELTA)) { + if (cs->token) + return JS_NewStringCopyZ(cx, cs->token); goto do_fallback; + } begin = pc - js_GetSrcNoteOffset(sn, 0); } end = pc + cs->length; @@ -2670,7 +3097,17 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, return NULL; memcpy(tmp, begin, len * sizeof(jsbytecode)); if (mode == JOF_NAME) { - tmp[0] = JSOP_NAME; + /* + * JOF_NAME does not imply JOF_CONST, so we must check for the + * QARG and QVAR format types and translate those to JSOP_GETARG + * or JSOP_GETVAR appropriately, instead of JSOP_NAME. + */ + type = format & JOF_TYPEMASK; + tmp[0] = (type == JOF_QARG) + ? JSOP_GETARG + : (type == JOF_QVAR) + ? JSOP_GETVAR + : JSOP_NAME; } else { /* * We must replace the faulting pc's bytecode with a corresponding @@ -2719,8 +3156,8 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, } if (js_DecompileCode(jp, script, begin, len)) name = js_GetPrinterOutput(jp); + js_DestroyPrinter(jp); } - js_DestroyPrinter(jp); if (tmp) JS_free(cx, tmp); return name; diff --git a/src/dom/js/jsopcode.h b/src/dom/js/jsopcode.h index 01a6d46ac..2a488f6eb 100644 --- a/src/dom/js/jsopcode.h +++ b/src/dom/js/jsopcode.h @@ -70,10 +70,14 @@ typedef enum JSOp { #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_INDEXCONST 8 /* arg or var index + constant pool index */ #define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ #define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ #define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ +#define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ +#define JOF_LITOPX 13 /* JOF_UINT24 followed by op being extended, + where op if JOF_CONST has no unsigned 16- + bit immediate operand */ #define JOF_TYPEMASK 0x000f /* mask for above immediate types */ #define JOF_NAME 0x0010 /* name operation */ #define JOF_PROP 0x0020 /* obj.prop operation */ @@ -93,6 +97,7 @@ typedef enum JSOp { #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_XMLNAME 0x20000 /* XML name: *, a::b, @a, @a::b, etc. */ #define JOF_TYPE_IS_EXTENDED_JUMP(t) \ ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) @@ -107,7 +112,7 @@ typedef enum JSOp { #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)) + (pc)[2] = JUMP_OFFSET_LO(off)) #define JUMP_OFFSET_MIN ((int16)0x8000) #define JUMP_OFFSET_MAX ((int16)0x7fff) @@ -124,7 +129,7 @@ typedef enum JSOp { */ #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)) + (pc)[2] = JUMP_OFFSET_LO(i)) #define SPANDEP_INDEX_MAX ((uint16)0xfffe) #define SPANDEP_INDEX_HUGE ((uint16)0xffff) @@ -143,16 +148,36 @@ typedef enum JSOp { #define JUMPX_OFFSET_MIN ((int32)0x80000000) #define JUMPX_OFFSET_MAX ((int32)0x7fffffff) -/* A literal is indexed by a per-script atom map. */ +/* + * A literal is indexed by a per-script atom map. Most scripts have relatively + * few literals, so the standard JOF_CONST format specifies a fixed 16 bits of + * immediate operand index. A script with more than 64K literals must push all + * high-indexed literals on the stack using JSOP_LITERAL, then use JOF_ELEM ops + * instead of JOF_PROP, etc. + */ #define ATOM_INDEX_LEN 2 -#define ATOM_INDEX_HI(index) ((jsbytecode)((index) >> 8)) -#define ATOM_INDEX_LO(index) ((jsbytecode)(index)) +#define ATOM_INDEX_HI(i) ((jsbytecode)((i) >> 8)) +#define ATOM_INDEX_LO(i) ((jsbytecode)(i)) #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 SET_ATOM_INDEX(pc,i) ((pc)[1] = ATOM_INDEX_HI(i), \ + (pc)[2] = ATOM_INDEX_LO(i)) #define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ - GET_ATOM_INDEX(pc)) -#define ATOM_INDEX_LIMIT_LOG2 16 + GET_ATOM_INDEX(pc)) + +/* A full atom index for JSOP_LITERAL uses 24 bits of immediate operand. */ +#define LITERAL_INDEX_LEN 3 +#define LITERAL_INDEX_HI(i) ((jsbytecode)((i) >> 16)) +#define LITERAL_INDEX_MID(i) ((jsbytecode)((i) >> 8)) +#define LITERAL_INDEX_LO(i) ((jsbytecode)(i)) +#define GET_LITERAL_INDEX(pc) ((jsatomid)(((pc)[1] << 16) | \ + ((pc)[2] << 8) | \ + (pc)[3])) +#define SET_LITERAL_INDEX(pc,i) ((pc)[1] = LITERAL_INDEX_HI(i), \ + (pc)[2] = LITERAL_INDEX_MID(i), \ + (pc)[3] = LITERAL_INDEX_LO(i)) + +/* Atom index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ +#define ATOM_INDEX_LIMIT_LOG2 23 #define ATOM_INDEX_LIMIT ((uint32)1 << ATOM_INDEX_LIMIT_LOG2) /* Actual argument count operand format helpers. */ @@ -192,6 +217,7 @@ 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 char js_default_str[]; extern const JSCodeSpec js_CodeSpec[]; extern uintN js_NumCodeSpecs; extern const jschar js_EscapeMap[]; @@ -230,12 +256,12 @@ js_puts(JSPrinter *jp, const char *s); */ #include -extern JS_FRIEND_API(void) +extern JS_FRIEND_API(JSBool) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); extern JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, - JSBool lines, FILE *fp); + JSBool lines, FILE *fp); #endif /* DEBUG */ /* @@ -265,7 +291,7 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun); */ extern JSString * js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, - JSString *fallback); + JSString *fallback); #define JSDVG_IGNORE_STACK 0 #define JSDVG_SEARCH_STACK 1 diff --git a/src/dom/js/jsopcode.tbl b/src/dom/js/jsopcode.tbl index c4996f349..6e649413e 100644 --- a/src/dom/js/jsopcode.tbl +++ b/src/dom/js/jsopcode.tbl @@ -62,8 +62,8 @@ * prec Operator precedence, zero if not an operator * format Bytecode plus immediate operand encoding format * - * This file is best viewed with 116 columns: -01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345 + * This file is best viewed with 128 columns: +12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 */ /* legend: op val name image len use def prec format */ @@ -147,8 +147,8 @@ OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 0, JOF_JUMP|J 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) +OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING) +OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING) /* New, infallible/transitive identity ops. */ OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) @@ -188,8 +188,8 @@ 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_INITPROP, 91, "initprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP|JOF_DETECTING) +OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_DETECTING) OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) @@ -225,8 +225,8 @@ OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE|J 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) +OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) +OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 0, 0, 0, JOF_BYTE) /* More exception handling ops. */ OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) @@ -256,15 +256,15 @@ OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 1, JOF_BYTE | * 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) +OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) /* * Prolog bytecodes for defining function, var, and const names. */ OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_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) +OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) +OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|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) @@ -272,8 +272,11 @@ 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) +/* + * Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA + * Edition 3 catch variables. + */ +OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL, 3, 1, 0, 0, JOF_CONST) /* 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) @@ -309,7 +312,7 @@ OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 12, JOF_BYTE) * 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) +OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXCONST|JOF_DECLARING) /* Extended jumps. */ OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) @@ -317,16 +320,18 @@ OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 0, JOF_JUMPX| OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 0, JOF_JUMPX|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_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 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) +OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING) +OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING) /* 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 cx->throwing where cx->exception was already set, to trigger rethrow. */ +OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 0, 0, 0, JOF_BYTE) /* Set and get return value pseudo-register in stack frame. */ OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE) @@ -340,5 +345,52 @@ OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 10, JOF_CONST| 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. */ +/* Regular expression literal requiring special "fork on exec" handling. */ OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 12, JOF_CONST) + +/* XML (ECMA-357, a.k.a. "E4X") support. */ +OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 12, JOF_CONST|JOF_XMLNAME) +OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 12, JOF_CONST|JOF_XMLNAME) +OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 8, JOF_BYTE) +OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 8, JOF_BYTE) +OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 0, JOF_BYTE|JOF_XMLNAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 1, JOF_BYTE|JOF_XMLNAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 11, JOF_BYTE) +OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 11, JOF_JUMP) +OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_XMLOBJECT, 180,"xmlobject", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 12, JOF_CONST) +OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 11, JOF_CONST|JOF_PROP) +OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEL) + +/* + * Opcodes for extended literal addressing, using unsigned 24-bit immediate + * operands to hold integer operands (JSOP_UINT24), extended atom indexes in + * script->atomMap (JSOP_LITERAL, JSOP_FINDNAME), and ops prefixed by such + * atom index immediates (JSOP_LITOPX). See jsemit.c, EmitAtomIndexOp. + */ +OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 12, JOF_UINT24) +OPDEF(JSOP_LITERAL, 189,"literal", NULL, 4, 0, 1, 12, JOF_UINT24) +OPDEF(JSOP_FINDNAME, 190,"findname", NULL, 4, 0, 2, 0, JOF_UINT24) +OPDEF(JSOP_LITOPX, 191,"litopx", NULL, 5, 0, 0, 12, JOF_LITOPX) + +/* + * Opcodes to help the decompiler deal with XML. + */ +OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 1, JOF_CONST|JOF_PROP) diff --git a/src/dom/js/jsosdep.h b/src/dom/js/jsosdep.h index c93eee2f1..e9c4a5534 100644 --- a/src/dom/js/jsosdep.h +++ b/src/dom/js/jsosdep.h @@ -52,21 +52,6 @@ #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 diff --git a/src/dom/js/jsotypes.h b/src/dom/js/jsotypes.h index ad8b5203e..ede1221b8 100644 --- a/src/dom/js/jsotypes.h +++ b/src/dom/js/jsotypes.h @@ -75,7 +75,7 @@ typedef JSUintn uint; typedef JSUintn uintn; typedef JSUint64 uint64; -#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2) +#if !defined(_WIN32) && !defined(XP_OS2) typedef JSUint32 uint32; #else typedef unsigned long uint32; @@ -102,7 +102,7 @@ typedef JSInt64 int64; #ifdef HPUX #include #else -#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2) +#if !defined(_WIN32) && !defined(XP_OS2) typedef JSInt32 int32; #else typedef long int32; @@ -112,14 +112,14 @@ typedef JSInt8 int8; #endif /* HPUX */ #endif /* AIX && HAVE_SYS_INTTYPES_H */ -#endif /* XP_BEOS */ +#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 +#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 @@ -199,13 +199,4 @@ typedef JSFloat64 float64; #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 index 6b3f7600c..1438f35a2 100644 --- a/src/dom/js/jsparse.c +++ b/src/dom/js/jsparse.c @@ -48,9 +48,7 @@ * 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. + * This parser attempts no error recovery. */ #include "jsstddef.h" #include @@ -76,11 +74,15 @@ #include "jsscript.h" #include "jsstr.h" +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + /* * JS parsers, from lowest to highest precedence. * - * Each parser takes a context and a token stream, and emits bytecode using - * a code generator. + * Each parser takes a context, a token stream, and a tree context struct. + * Each returns a parse node tree or null on error. */ typedef JSParseNode * @@ -121,7 +123,8 @@ static JSParser PrimaryExpr; #define MUST_MATCH_TOKEN(tt, errno) \ JS_BEGIN_MACRO \ if (js_GetToken(cx, ts) != tt) { \ - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \ + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ + errno); \ return NULL; \ } \ JS_END_MACRO @@ -130,7 +133,7 @@ static JSParser PrimaryExpr; JS_BEGIN_MACRO \ int stackDummy; \ if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \ + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ JSMSG_OVER_RECURSED); \ return NULL; \ } \ @@ -142,17 +145,21 @@ static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif -static void +static JSParseNode * RecycleTree(JSParseNode *pn, JSTreeContext *tc) { + JSParseNode *next; + if (!pn) - return; + return NULL; JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ + next = pn->pn_next; pn->pn_next = tc->nodeList; tc->nodeList = pn; #ifdef METER_PARSENODES recyclednodes++; #endif + return next; } static JSParseNode * @@ -202,6 +209,13 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) break; } } +#ifdef METER_PARSENODES + if (pn) { + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; + } +#endif return pn; } @@ -209,23 +223,23 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) * Allocate a JSParseNode from cx's temporary arena. */ static JSParseNode * -NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity, +NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, JSTreeContext *tc) { JSParseNode *pn; + JSToken *tp; pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; - pn->pn_type = tok->type; - pn->pn_pos = tok->pos; + tp = &CURRENT_TOKEN(ts); + pn->pn_type = tp->type; + pn->pn_pos = tp->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; +#if JS_HAS_XML_SUPPORT + pn->pn_ts = ts; #endif return pn; } @@ -252,7 +266,6 @@ NewBinary(JSContext *cx, JSTokenType tt, 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; @@ -302,10 +315,8 @@ NewBinary(JSContext *cx, JSTokenType tt, pn->pn_left = left; pn->pn_right = right; pn->pn_next = NULL; -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; +#if JS_HAS_XML_SUPPORT + pn->pn_ts = NULL; #endif return pn; } @@ -332,7 +343,7 @@ CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) return TOK_NAME; (void) js_GetToken(cx, ts); if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_GETTER_OR_SETTER, (op == JSOP_GETTER) ? js_getter_str @@ -342,7 +353,8 @@ CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) CURRENT_TOKEN(ts).t_op = op; name = js_AtomToPrintableString(cx, atom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, @@ -377,13 +389,17 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) frame.varobj = chain; } frame.down = fp; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } 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 + * GC attempted within js_NewGCThing), or * - run for any reason on another thread if this thread is suspended on * an object lock before it finishes generating bytecode into a script * protected from the GC by a root or a stack frame reference. @@ -393,7 +409,7 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) pn = Statements(cx, ts, &tc); if (pn) { if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else { @@ -438,6 +454,10 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, frame.varobj = chain; } frame.down = fp; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } cx->fp = &frame; } flags = cx->fp->flags; @@ -453,7 +473,7 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, if (!pn) { ok = JS_FALSE; } else if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); ok = JS_FALSE; } else { @@ -548,6 +568,9 @@ HasFinalReturn(JSParseNode *pn) case TOK_RETURN: return ENDS_IN_RETURN; + case TOK_COLON: + return HasFinalReturn(pn->pn_expr); + #if JS_HAS_EXCEPTIONS case TOK_THROW: return ENDS_IN_RETURN; @@ -588,12 +611,14 @@ ReportNoReturnValue(JSContext *cx, JSTokenStream *ts) fun = cx->fp->fun; if (fun->atom) { char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom)); - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, name); } else { - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_ANON_NO_RETURN_VALUE); @@ -624,7 +649,8 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; - frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO); + if (fp) + frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; cx->fp = &frame; } @@ -640,7 +666,7 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, } cx->fp = fp; - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); return pn; } @@ -718,8 +744,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda) { JSOp op, prevop; - JSParseNode *pn, *body; - JSAtom *funAtom, *argAtom; + JSParseNode *pn, *body, *result; + JSAtom *funAtom, *objAtom, *argAtom; JSStackFrame *fp; JSObject *varobj, *pobj; JSAtomListElement *ale; @@ -733,12 +759,19 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_GETTER_SETTER op = CURRENT_TOKEN(ts).t_op; #endif - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc); + pn = NewParseNode(cx, 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; +#if !JS_HAS_LEXICAL_CLOSURE + if (!funAtom && !lambda) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } +#endif /* Find the nearest variable-declaring scope and use it as our parent. */ fp = cx->fp; @@ -755,11 +788,12 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { const char *name = js_AtomToPrintableString(cx, funAtom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, (prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | + ? JSREPORT_TS | + JSREPORT_WARNING | JSREPORT_STRICT - : JSREPORT_ERROR, + : JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) @@ -786,32 +820,49 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * 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 + * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a * JSOP_GETVAR bytecode). */ if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) { + JSScopeProperty *sprop; + /* - * Define a property on the outer function so that LookupArgOrVar + * Define a property on the outer function so that BindNameToSlot * 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)) + if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_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)) { + sprop = NULL; + if (!prop || + pobj != varobj || + (sprop = (JSScopeProperty *)prop, + sprop->getter != js_GetLocalVariable)) { + uintN sflags; + + /* + * Use SPROP_IS_DUPLICATE if there is a formal argument of the + * same name, so the decompiler can find the parameter name. + */ + sflags = (sprop && sprop->getter == js_GetArgument) + ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID + : SPROP_HAS_SHORTID; + if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), + js_GetLocalVariable, + js_SetLocalVariable, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + sflags, fp->fun->nvars)) { + return NULL; + } + if (fp->fun->nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); return NULL; } fp->fun->nvars++; @@ -829,6 +880,27 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; #endif + + /* + * Set interpreted early so js_EmitTree can test it to decide whether to + * eliminate useless expressions. + */ + fun->interpreted = JS_TRUE; + + /* + * Atomize fun->object early to protect against a last-ditch GC under + * js_LookupHiddenProperty. + * + * 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. + */ + objAtom = js_AtomizeObject(cx, fun->object, 0); + if (!objAtom) + return NULL; + /* 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)) { @@ -836,8 +908,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 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)) { + if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + &pobj, &prop)) { return NULL; } dupflag = 0; @@ -854,7 +926,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * mapped by an entry in scope. */ ok = name && - js_ReportCompileErrorNumber(cx, ts, NULL, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, @@ -867,15 +940,19 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; prop = NULL; } - if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_SHARED, - SPROP_HAS_SHORTID | dupflag, + JSPROP_PERMANENT | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, fun->nargs)) { return NULL; } + if (fun->nargs == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + return NULL; + } fun->nargs++; } while (js_MatchToken(cx, ts, TOK_COMMA)); @@ -918,14 +995,27 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } #endif + result = pn; #if JS_HAS_LEXICAL_CLOSURE - if (lambda || !funAtom) { + if (lambda) { + /* + * ECMA ed. 3 standard: function expression, possibly anonymous. + */ + op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; + } else if (!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). + * If this anonymous function definition is *not* embedded within a + * larger expression, we treat it as an expression statement, not as + * a function declaration -- and not as a syntax error (as ECMA-262 + * Edition 3 would have it). Backward compatibility trumps all. */ - op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; + result = NewParseNode(cx, ts, PN_UNARY, tc); + if (!result) + return NULL; + result->pn_type = TOK_SEMI; + result->pn_pos = pn->pn_pos; + result->pn_kid = pn; + op = JSOP_ANONFUNOBJ; } else if (tc->topStmt) { /* * ECMA ed. 3 extension: a function expression statement not at the @@ -938,23 +1028,13 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #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_funAtom = objAtom; pn->pn_op = op; pn->pn_body = body; - pn->pn_flags = funtc.flags & TCF_FUN_FLAGS; + pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS); pn->pn_tryCount = funtc.tryCount; TREE_CONTEXT_FINISH(&funtc); - return pn; + return result; } static JSParseNode * @@ -984,7 +1064,7 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) CHECK_RECURSION(); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); @@ -993,8 +1073,11 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { ts->flags &= ~TSF_OPERAND; pn2 = Statement(cx, ts, tc); - if (!pn2) + if (!pn2) { + if (ts->flags & TSF_EOF) + ts->flags |= TSF_UNEXPECTED_EOF; return NULL; + } ts->flags |= TSF_OPERAND; /* If compiling top-level statements, emit as we go to save space. */ @@ -1058,9 +1141,11 @@ Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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, + JSBool rewrite = !JS_VERSION_IS_ECMA(cx); + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, rewrite ? "\nAssuming equality test" @@ -1116,11 +1201,11 @@ MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) static JSParseNode * ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - JSParseNode *pn, *pn2, *pn3; + JSParseNode *pn, *pn2; JSTokenType tt; MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn = NewParseNode(cx, ts, PN_NAME, tc); if (!pn) return NULL; pn->pn_op = JSOP_NAME; @@ -1136,7 +1221,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) goto bad_import; if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; if (js_MatchToken(cx, ts, TOK_STAR)) { @@ -1153,21 +1238,12 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + /* Make a TOK_LB binary node. */ + pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), 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; @@ -1193,7 +1269,8 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn; bad_import: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT); + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_IMPORT); return NULL; } #endif /* JS_HAS_EXPORT_IMPORT */ @@ -1225,19 +1302,19 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) switch (tt) { #if JS_HAS_EXPORT_IMPORT case TOK_EXPORT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, 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); + pn2 = NewParseNode(cx, 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); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; @@ -1253,7 +1330,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_IMPORT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); @@ -1269,11 +1346,15 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_EXPORT_IMPORT */ case TOK_FUNCTION: +#if JS_HAS_XML_SUPPORT + if (js_PeekToken(cx, ts) == TOK_DBLCOLON) + goto expression; +#endif 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); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; pn1 = Condition(cx, ts, tc); @@ -1305,7 +1386,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSParseNode *pn5; JSBool seenDefault = JS_FALSE; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); @@ -1319,7 +1400,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; PN_INIT_LIST(pn2); @@ -1330,7 +1411,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) switch (tt) { case TOK_DEFAULT: if (seenDefault) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS); return NULL; } @@ -1338,7 +1420,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* fall through */ case TOK_CASE: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn3 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn3) return NULL; if (tt == TOK_DEFAULT) { @@ -1350,7 +1432,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } PN_APPEND(pn2, pn3); if (pn2->pn_count == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_CASES); return NULL; } @@ -1360,19 +1443,22 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | 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); + pn4 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn4) return NULL; pn4->pn_type = TOK_LC; PN_INIT_LIST(pn4); + ts->flags |= TSF_OPERAND; while ((tt = js_PeekToken(cx, ts)) != TOK_RC && tt != TOK_CASE && tt != TOK_DEFAULT) { + ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; pn5 = Statement(cx, ts, tc); @@ -1380,7 +1466,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; pn4->pn_pos.end = pn5->pn_pos.end; PN_APPEND(pn4, pn5); + ts->flags |= TSF_OPERAND; } + ts->flags &= ~TSF_OPERAND; /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ if (pn4->pn_head) @@ -1399,7 +1487,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_WHILE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); @@ -1417,7 +1505,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #if JS_HAS_DO_WHILE_LOOP case TOK_DO: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); @@ -1432,7 +1520,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; - if (cx->version != JSVERSION_ECMA_3) { + if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) { /* * All legacy and extended versions must do automatic semicolon * insertion after do-while. See the testcase and discussion in @@ -1446,16 +1534,31 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); +#if JS_HAS_XML_SUPPORT + pn->pn_op = JSOP_NOP; + if (js_MatchToken(cx, ts, TOK_NAME)) { + if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) + pn->pn_op = JSOP_FOREACH; + else + js_UngetToken(ts); + } +#endif + 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) { +#if JS_HAS_XML_SUPPORT + if (pn->pn_op == JSOP_FOREACH) + goto bad_for_each; +#endif + /* No initializer -- set first kid of left sub-node to null. */ pn1 = NULL; } else { @@ -1495,12 +1598,22 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) stmtInfo.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ + while (pn1->pn_type == TOK_RP) + pn1 = pn1->pn_kid; 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 && +#if JS_HAS_LVALUE_RETURN + pn1->pn_type != TOK_LP && +#endif +#if JS_HAS_XML_SUPPORT + (pn1->pn_type != TOK_UNARYOP || + pn1->pn_op != JSOP_XMLNAME) && +#endif pn1->pn_type != TOK_LB)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE); return NULL; } @@ -1515,6 +1628,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn1->pn_extra |= PNX_POPVAR; } else { pn2 = pn1; +#if JS_HAS_LVALUE_RETURN + if (pn2->pn_type == TOK_LP) + pn2->pn_op = JSOP_SETCALL; +#endif +#if JS_HAS_XML_SUPPORT + if (pn2->pn_type == TOK_UNARYOP) + pn2->pn_op = JSOP_BINDXMLNAME; +#endif } /* Beware 'for (arguments in ...)' with or without a 'var'. */ @@ -1529,6 +1650,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; pn->pn_left = pn2; } else { +#if JS_HAS_XML_SUPPORT + if (pn->pn_op == JSOP_FOREACH) + goto bad_for_each; +#endif + /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); ts->flags |= TSF_OPERAND; @@ -1556,7 +1682,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } /* Build the RESERVED node to use as the left kid of pn. */ - pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn4) return NULL; pn4->pn_type = TOK_RESERVED; @@ -1580,6 +1706,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = pn2->pn_pos.end; return pn; +#if JS_HAS_XML_SUPPORT + bad_for_each: + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_FOR_EACH_LOOP); + return NULL; +#endif + #if JS_HAS_EXCEPTIONS case TOK_TRY: { JSParseNode *catchtail = NULL; @@ -1601,7 +1735,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * * finally nodes are unary (just the finally expression) */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); pn->pn_op = JSOP_NOP; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); @@ -1616,7 +1750,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL); return NULL; } @@ -1628,7 +1763,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * (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); + pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn2) return NULL; @@ -1638,7 +1773,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) */ 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); + pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; @@ -1686,7 +1821,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_kid3 = NULL; } if (!pn->pn_kid2 && !pn->pn_kid3) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY); return NULL; } @@ -1695,7 +1830,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } case TOK_THROW: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; @@ -1706,7 +1841,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } @@ -1721,19 +1856,19 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ case TOK_CATCH: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY); return NULL; case TOK_FINALLY: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY); return NULL; #endif /* JS_HAS_EXCEPTIONS */ case TOK_BREAK: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) @@ -1743,7 +1878,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (label) { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND); return NULL; } @@ -1753,7 +1889,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } else { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOUGH_BREAK); return NULL; } @@ -1766,7 +1903,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_CONTINUE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) @@ -1776,14 +1913,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (label) { for (stmt2 = NULL; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | 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, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; @@ -1797,7 +1936,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } else { for (; ; stmt = stmt->down) { if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; } @@ -1810,14 +1950,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); @@ -1849,11 +1982,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_RETURN: if (!(tc->flags & TCF_IN_FUNCTION)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_RETURN); return NULL; } - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; @@ -1899,7 +2032,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_EOL: case TOK_SEMI: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; @@ -1908,7 +2041,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #if JS_HAS_DEBUGGER_KEYWORD case TOK_DEBUGGER: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_type = TOK_DEBUGGER; @@ -1916,10 +2049,38 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case TOK_DEFAULT: + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + if (!js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || + !js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || + !js_MatchToken(cx, ts, TOK_ASSIGN) || + CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_DEFAULT_XML_NAMESPACE); + return NULL; + } + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_op = JSOP_DEFXMLNS; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + tc->flags |= TCF_HAS_DEFXMLNS; + break; +#endif + case TOK_ERROR: return NULL; default: +#if JS_HAS_XML_SUPPORT + expression: +#endif js_UngetToken(ts); pn2 = Expr(cx, ts, tc); if (!pn2) @@ -1927,14 +2088,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (js_PeekToken(cx, ts) == TOK_COLON) { if (pn2->pn_type != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | 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, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL); return NULL; } @@ -1956,7 +2119,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn2; } - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; @@ -1971,7 +2134,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT); return NULL; } @@ -2006,11 +2169,10 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * 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); + pn = NewParseNode(cx, 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); /* @@ -2053,17 +2215,18 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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) { + if (JS_HAS_STRICT_OPTION(cx) + ? pn->pn_op != JSOP_DEFVAR || prevop != JSOP_DEFVAR + : pn->pn_op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { const char *name = js_AtomToPrintableString(cx, atom); if (!name || - !js_ReportCompileErrorNumber(cx, ts, NULL, + !js_ReportCompileErrorNumber(cx, ts, (pn->pn_op != JSOP_DEFCONST && prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | + ? JSREPORT_TS | + JSREPORT_WARNING | JSREPORT_STRICT - : JSREPORT_ERROR, + : JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) @@ -2084,7 +2247,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } ALE_SET_JSOP(ale, pn->pn_op); - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; @@ -2092,15 +2255,20 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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; + ? JSPROP_PERMANENT | JSPROP_READONLY + : JSPROP_PERMANENT; PN_APPEND(pn, pn2); if (!fun) { - prop = NULL; /* don't lookup global variables at compile time */ + /* Don't lookup global variables at compile time. */ + prop = NULL; + } else if (OBJ_IS_NATIVE(obj)) { + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &pobj, &prop)) { + return NULL; + } } else { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return NULL; } if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { @@ -2110,14 +2278,16 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!name) { ok = JS_FALSE; } else if (pn->pn_op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_PARAM, name); ok = JS_FALSE; } else { currentGetter = js_GetArgument; currentSetter = js_SetArgument; - ok = js_ReportCompileErrorNumber(cx, ts, NULL, + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_VAR_HIDES_ARG, @@ -2129,7 +2299,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (clasp == &js_FunctionClass) { JS_ASSERT(sprop->getter == js_GetLocalVariable); JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - sprop->shortid < fun->nvars); + (uint16) sprop->shortid < fun->nvars); } else if (clasp == &js_CallClass) { if (sprop->getter == js_GetCallVariable) { /* @@ -2138,7 +2308,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * that the slot number we have is in range. */ JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - sprop->shortid < fun->nvars); + (uint16) sprop->shortid < fun->nvars); } else { /* * A variable introduced through another eval: @@ -2182,12 +2352,17 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) atom != cx->runtime->atomState.argumentsAtom && fp->scopeChain == obj && !js_InWithStatement(tc)) { - if (!js_AddNativeProperty(cx, obj, (jsid)atom, + if (!js_AddHiddenProperty(cx, obj, ATOM_TO_JSID(atom), currentGetter, currentSetter, SPROP_INVALID_SLOT, pn2->pn_attrs | JSPROP_SHARED, SPROP_HAS_SHORTID, fun->nvars)) { - ok = JS_FALSE; + return NULL; + } + if (fun->nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); + return NULL; } fun->nvars++; } @@ -2195,7 +2370,8 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (js_MatchToken(cx, ts, TOK_ASSIGN)) { if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_VAR_INIT); ok = JS_FALSE; } else { @@ -2229,7 +2405,7 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = AssignExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; pn2->pn_pos.begin = pn->pn_pos.begin; @@ -2282,7 +2458,9 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) tc->flags |= TCF_FUN_HEAVYWEIGHT; break; case TOK_DOT: - pn2->pn_op = JSOP_SETPROP; + pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD) + ? JSOP_SETMETHOD + : JSOP_SETPROP; break; case TOK_LB: pn2->pn_op = JSOP_SETELEM; @@ -2291,9 +2469,17 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_LP: pn2->pn_op = JSOP_SETCALL; break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (pn2->pn_op == JSOP_XMLNAME) { + pn2->pn_op = JSOP_SETXMLNAME; + break; + } + /* FALL THROUGH */ #endif default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); return NULL; } @@ -2312,7 +2498,7 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = OrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { pn1 = pn; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc); + pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; #if JS_HAS_IN_OPERATOR @@ -2519,9 +2705,12 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, kid->pn_type != TOK_DOT && #if JS_HAS_LVALUE_RETURN (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && +#endif +#if JS_HAS_XML_SUPPORT + (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && #endif kid->pn_type != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_OPERAND, name); return NULL; } @@ -2558,7 +2747,15 @@ SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_LVALUE_RETURN case TOK_LP: + JS_ASSERT(kid->pn_op == JSOP_CALL); kid->pn_op = JSOP_SETCALL; + /* FALL THROUGH */ +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (kid->pn_op == JSOP_XMLNAME) + kid->pn_op = JSOP_SETXMLNAME; + /* FALL THROUGH */ #endif case TOK_LB: op = (tt == TOK_INC) @@ -2580,6 +2777,8 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSTokenType tt; JSParseNode *pn, *pn2; + CHECK_RECURSION(); + ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; @@ -2588,7 +2787,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_UNARYOP: case TOK_PLUS: case TOK_MINUS: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ @@ -2602,7 +2801,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_INC: case TOK_DEC: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_TRUE); @@ -2614,7 +2813,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_DELETE: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = UnaryExpr(cx, ts, tc); @@ -2645,7 +2844,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + pn2 = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn2) return NULL; if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) @@ -2677,7 +2876,7 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } while (js_MatchToken(cx, ts, TOK_COMMA)); if (js_GetToken(cx, ts) != TOK_RP) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS); return JS_FALSE; } @@ -2701,7 +2900,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (tt == TOK_NEW) { (void) js_GetToken(cx, ts); - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_FALSE); @@ -2723,21 +2922,102 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn = PrimaryExpr(cx, ts, tc); if (!pn) return NULL; + + if (pn->pn_type == TOK_ANYNAME || + pn->pn_type == TOK_AT || + pn->pn_type == TOK_DBLCOLON) { + pn2 = NewOrRecycledNode(cx, tc); + if (!pn2) + return NULL; + pn2->pn_type = TOK_UNARYOP; + pn2->pn_pos = pn->pn_pos; + pn2->pn_op = JSOP_XMLNAME; + pn2->pn_arity = PN_UNARY; + pn2->pn_kid = pn; + pn2->pn_next = NULL; +#if JS_HAS_XML_SUPPORT + pn2->pn_ts = ts; +#endif + pn = pn2; + } } while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; +#if JS_HAS_XML_SUPPORT + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME || + (tt == TOK_DBLCOLON && + pn3->pn_arity == PN_NAME && + pn3->pn_expr->pn_type == TOK_FUNCTION)) { + pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + RecycleTree(pn3, tc); + } else { + if (TOKEN_TYPE_IS_XML(tt)) { + pn2->pn_type = TOK_LB; + pn2->pn_op = JSOP_GETELEM; + } else if (tt == TOK_RP) { + JSParseNode *group = pn3; + + /* Recycle the useless TOK_RP/JSOP_GROUP node. */ + pn3 = group->pn_kid; + group->pn_kid = NULL; + RecycleTree(group, tc); + pn2->pn_type = TOK_FILTER; + pn2->pn_op = JSOP_FILTER; + } else { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_arity = PN_BINARY; + pn2->pn_left = pn; + pn2->pn_right = pn3; + } +#else 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; +#endif + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#if JS_HAS_XML_SUPPORT + } else if (tt == TOK_DBLDOT) { + pn2 = NewParseNode(cx, ts, PN_BINARY, tc); + if (!pn2) + return NULL; + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME) { + pn3->pn_type = TOK_STRING; + pn3->pn_arity = PN_NULLARY; + pn3->pn_op = JSOP_STRING; + } else if (!TOKEN_TYPE_IS_XML(tt)) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_op = JSOP_DESCENDANTS; + pn2->pn_left = pn; + pn2->pn_right = pn3; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#endif } else if (tt == TOK_LB) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; pn3 = Expr(cx, ts, tc); @@ -2761,7 +3041,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2->pn_right = pn3; } } else if (allowCallSyntax && tt == TOK_LP) { - pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; @@ -2797,70 +3077,774 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } static JSParseNode * -PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +BracketedExpr(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; + uintN oldflags; + JSParseNode *pn; - defsharp = NULL; - notsharp = JS_FALSE; - again: +#if JS_HAS_IN_OPERATOR /* - * 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. + * 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 + pn = Expr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); #endif + return pn; +} - CHECK_RECURSION(); +#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; +static JSParseNode * +EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); - if (tt == TOK_ERROR) - return NULL; - } -#endif + pn = BracketedExpr(cx, ts, tc); + if (!pn) + return NULL; - switch (tt) { -#if JS_HAS_LEXICAL_CLOSURE - case TOK_FUNCTION: - pn = FunctionExpr(cx, ts, tc); - if (!pn) - return NULL; - break; -#endif + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); + return pn; +} -#if JS_HAS_INITIALIZERS - case TOK_LB: - { - JSBool matched; - jsuint atomIndex; +/* + * From the ECMA-357 grammar in 11.1.1 and 11.1.2: + * + * AttributeIdentifier: + * @ PropertySelector + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * + * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: + * + * AttributeIdentifier: + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * PropertySelector + * + * Since PrimaryExpression: Identifier in ECMA-262 and we want the semantics + * for that rule to result in a name node, but extend the grammar to include + * PrimaryExpression: QualifiedIdentifier, we factor further: + * + * QualifiedIdentifier: + * PropertySelector QualifiedSuffix + * + * QualifiedSuffix: + * :: PropertySelector + * :: [ Expression ] + * /nothing/ + * + * And use this production instead of PrimaryExpression: QualifiedIdentifier: + * + * PrimaryExpression: + * Identifier QualifiedSuffix + * + * We hoists the :: match into callers of QualifiedSuffix, in order to tweak + * PropertySelector vs. Identifier pn_arity, pn_op, and other members. + */ +static JSParseNode * +PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_RB; - pn->pn_extra = 0; + pn = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn) + return NULL; + if (pn->pn_type == TOK_STAR) { + pn->pn_type = TOK_ANYNAME; + pn->pn_op = JSOP_ANYNAME; + pn->pn_atom = cx->runtime->atomState.starAtom; + } else { + JS_ASSERT(pn->pn_type == TOK_NAME); + pn->pn_op = JSOP_QNAMEPART; + pn->pn_arity = PN_NAME; + pn->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn->pn_expr = NULL; + pn->pn_slot = -1; + pn->pn_attrs = 0; + } + return pn; +} -#if JS_HAS_SHARP_VARS - if (defsharp) { - PN_INIT_LIST_1(pn, defsharp); - defsharp = NULL; - } else +static JSParseNode * +QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, + JSTreeContext *tc) +{ + JSParseNode *pn2, *pn3; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); + pn2 = NewParseNode(cx, ts, PN_NAME, tc); + if (!pn2) + return NULL; + + /* Left operand of :: must be evaluated if it is an identifier. */ + if (pn->pn_op == JSOP_QNAMEPART) + pn->pn_op = JSOP_NAME; + + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ + pn2->pn_op = JSOP_QNAMECONST; + pn2->pn_atom = (tt == TOK_STAR) + ? cx->runtime->atomState.starAtom + : CURRENT_TOKEN(ts).t_atom; + pn2->pn_expr = pn; + pn2->pn_slot = -1; + pn2->pn_attrs = 0; + return pn2; + } + + if (tt != TOK_LB) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + pn3 = EndBracketedExpr(cx, ts, tc); + if (!pn3) + return NULL; + + pn2->pn_op = JSOP_QNAME; + pn2->pn_arity = PN_BINARY; + pn2->pn_left = pn; + pn2->pn_right = pn3; + return pn2; +} + +static JSParseNode * +QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = PropertySelector(cx, ts, tc); + if (!pn) + return NULL; + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) + pn = QualifiedSuffix(cx, ts, pn, tc); + return pn; +} + +static JSParseNode * +AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_op = JSOP_TOATTRNAME; + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + pn2 = QualifiedIdentifier(cx, ts, tc); + } else if (tt == TOK_LB) { + pn2 = EndBracketedExpr(cx, ts, tc); + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_kid = pn2; + return pn; +} + +/* + * Make a TOK_LC unary node whose pn_kid is an expression. + */ +static JSParseNode * +XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + uintN oldflags; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); + pn = NewParseNode(cx, ts, PN_UNARY, tc); + if (!pn) + return NULL; + + /* + * Turn off XML tag mode, but don't restore it after parsing this braced + * expression. Instead, simply restore ts's old flags. This is required + * because XMLExpr is called both from within a tag, and from within text + * contained in an element, but outside of any start, end, or point tag. + */ + oldflags = ts->flags; + ts->flags = oldflags & ~TSF_XMLTAGMODE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); + ts->flags = oldflags; + pn->pn_kid = pn2; + pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR; + return pn; +} + +/* + * Make a terminal node for oneof TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, + * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting + * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole + * child of a container tag. + */ +static JSParseNode * +XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + JSToken *tp; + + pn = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn) + return NULL; + tp = &CURRENT_TOKEN(ts); + pn->pn_op = tp->t_op; + pn->pn_atom = tp->t_atom; + if (tp->type == TOK_XMLPI) + pn->pn_atom2 = tp->t_atom2; + return pn; +} + +/* + * Parse the productions: + * + * XMLNameExpr: + * XMLName XMLNameExpr? + * { Expr } XMLNameExpr? + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces + * a list of names and/or expressions, a single expression, or a single name. + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type + * will be TOK_LC. + */ +static JSParseNode * +XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2, *list; + JSTokenType tt; + + pn = list = NULL; + do { + tt = CURRENT_TOKEN(ts).type; + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_TRUE, tc); + if (!pn2) + return NULL; + } else { + JS_ASSERT(tt == TOK_XMLNAME); + pn2 = XMLAtomNode(cx, ts, tc); + if (!pn2) + return NULL; + } + + if (!pn) { + pn = pn2; + } else { + if (!list) { + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLNAME; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + list->pn_extra = PNX_CANTFOLD; + pn = list; + } + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); + + js_UngetToken(ts); + return pn; +} + +/* + * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded + * at compile time into a JSXML tree. + */ +#define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ + ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ + : (pn)->pn_type != TOK_LC) + +/* + * Parse the productions: + * + * XMLTagContent: + * XMLNameExpr + * XMLTagContent S XMLNameExpr S? = S? XMLAttr + * XMLTagContent S XMLNameExpr S? = S? { Expr } + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent + * produces a list of name and attribute values and/or braced expressions, a + * single expression, or a single name. + * + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where + * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is + * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and + * we parsed exactly one expression. + */ +static JSParseNode * +XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSTokenType tagtype, JSAtom **namep) +{ + JSParseNode *pn, *pn2, *list; + JSTokenType tt; + + pn = XMLNameExpr(cx, ts, tc); + if (!pn) + return NULL; + *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; + list = NULL; + + while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { + tt = js_GetToken(cx, ts); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_UngetToken(ts); + break; + } + + pn2 = XMLNameExpr(cx, ts, tc); + if (!pn2) + return NULL; + if (!list) { + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = tagtype; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + pn = list; + } + PN_APPEND(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLATTR) { + pn2 = XMLAtomNode(cx, ts, tc); + } else if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_TRUE, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_ATTR_VALUE); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + return pn; +} + +#define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ + JS_BEGIN_MACRO \ + if ((tt) <= TOK_EOF) { \ + if ((tt) == TOK_EOF) { \ + js_ReportCompileErrorNumber(cx, ts, \ + JSREPORT_TS | JSREPORT_ERROR, \ + JSMSG_END_OF_XML_SOURCE); \ + } \ + return result; \ + } \ + JS_END_MACRO + +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList); + +/* + * Consume XML element tag content, including the TOK_XMLETAGO (flags &= ~TSF_XMLTAGMODE; + for (;;) { + ts->flags |= TSF_XMLTEXTMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_XMLTEXTMODE; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + + JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT); + textAtom = CURRENT_TOKEN(ts).t_atom; + if (textAtom) { + /* Non-zero-length XML text scanned. */ + pn2 = XMLAtomNode(cx, ts, tc); + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + ts->flags |= TSF_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + if (tt == TOK_XMLETAGO) + break; + + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, JS_FALSE, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else if (tt == TOK_XMLSTAGO) { + pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); + if (pn2) { + pn2->pn_extra &= ~PNX_XMLROOT; + pn->pn_extra |= pn2->pn_extra; + } + } else { + JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || + tt == TOK_XMLPI); + pn2 = XMLAtomNode(cx, ts, tc); + } + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); + ts->flags |= TSF_XMLTAGMODE; + return JS_TRUE; +} + +/* + * Return a PN_LIST node containing an XML or XMLList Initialiser. + */ +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList) +{ + JSParseNode *pn, *pn2, *list; + JSBool hadSpace; + JSTokenType tt; + JSAtom *startAtom, *endAtom; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); + pn = NewParseNode(cx, ts, PN_LIST, tc); + if (!pn) + return NULL; + + ts->flags |= TSF_XMLTAGMODE; + hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + if (tt == TOK_ERROR) + return NULL; + + if (tt == TOK_XMLNAME || tt == TOK_LC) { + /* + * XMLElement. Append the tag and its contents, if any, to pn. + */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); + if (!pn2) + return NULL; + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLPTAGC) { + /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ + if (pn2->pn_type == TOK_XMLSTAGO) { + PN_INIT_LIST(pn); + RecycleTree(pn, tc); + pn = pn2; + } else { + JS_ASSERT(pn2->pn_type == TOK_XMLNAME || + pn2->pn_type == TOK_LC); + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + } + pn->pn_type = TOK_XMLPTAGC; + pn->pn_extra |= PNX_XMLROOT; + } else { + /* We had better have a tag-close (>) at this point. */ + if (tt != TOK_XMLTAGC) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + + /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ + if (pn2->pn_type != TOK_XMLSTAGO) { + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn2 = pn; + pn = NewParseNode(cx, ts, PN_LIST, tc); + if (!pn) + return NULL; + } + + /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ + pn->pn_type = TOK_XMLELEM; + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_XMLROOT; + + /* Get element contents and delimiting end-tag-open sequence. */ + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + + /* Parse end tag; check mismatch at compile-time if we can. */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); + if (!pn2) + return NULL; + if (pn2->pn_type == TOK_XMLETAGO) { + /* Oops, end tag has attributes! */ + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + if (endAtom && startAtom && endAtom != startAtom) { + /* End vs. start tag name mismatch: point to the tag name. */ + ++pn2->pn_pos.begin.index; + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_XML_TAG_NAME_MISMATCH); + return NULL; + } + + /* Make a TOK_XMLETAGO list with pn2 as its single child. */ + JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); + list = NewParseNode(cx, ts, PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLETAGO; + PN_INIT_LIST_1(list, pn2); + PN_APPEND(pn, list); + if (!XML_FOLDABLE(pn2)) { + list->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_CANTFOLD; + } + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); + } + + /* Set pn_op now that pn has been updated to its final value. */ + pn->pn_op = JSOP_TOXML; + } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { + /* XMLList Initialiser. */ + pn->pn_type = TOK_XMLLIST; + pn->pn_op = JSOP_TOXMLLIST; + PN_INIT_LIST(pn); + pn->pn_extra |= PNX_XMLROOT; + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); + } else { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_NAME_SYNTAX); + return NULL; + } + + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + ts->flags &= ~TSF_XMLTAGMODE; + return pn; +} + +static JSParseNode * +XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList) +{ + uint32 oldopts; + JSParseNode *pn; + + /* + * Force XML support to be enabled so that comments and CDATA literals + * are recognized, instead of ). + */ + oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML); + pn = XMLElementOrList(cx, ts, tc, allowList); + JS_SetOptions(cx, oldopts); + return pn; +} + +JS_FRIEND_API(JSParseNode *) +js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSBool allowList) +{ + JSStackFrame *fp, frame; + JSParseNode *pn; + JSTreeContext tc; + JSTokenType tt; + + /* + * 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; + if (fp) { + frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | + JSFRAME_SCRIPT_OBJECT); + } + cx->fp = &frame; + } + + JS_KEEP_ATOMS(cx->runtime); + TREE_CONTEXT_INIT(&tc); + + /* Set XML-only mode to turn off special treatment of {expr} in XML. */ + ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + + if (tt != TOK_XMLSTAGO) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); + pn = NULL; + } else { + pn = XMLElementOrListRoot(cx, ts, &tc, allowList); + } + + ts->flags &= ~TSF_XMLONLYMODE; + TREE_CONTEXT_FINISH(&tc); + JS_UNKEEP_ATOMS(cx->runtime); + cx->fp = fp; + return pn; +} + +#endif /* JS_HAS_XMLSUPPORT */ + +static JSParseNode * +PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode *pn, *pn2, *pn3; +#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 || JS_HAS_XML_SUPPORT + case TOK_FUNCTION: +#if JS_HAS_XML_SUPPORT + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { + pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); + if (!pn2) + return NULL; + pn2->pn_type = TOK_FUNCTION; + pn = QualifiedSuffix(cx, ts, pn2, tc); + if (!pn) + return NULL; + break; + } +#endif + 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, ts, PN_LIST, tc); + if (!pn) + return NULL; + pn->pn_type = TOK_RB; + +#if JS_HAS_SHARP_VARS + if (defsharp) { + PN_INIT_LIST_1(pn, defsharp); + defsharp = NULL; + } else #endif PN_INIT_LIST(pn); @@ -2868,7 +3852,14 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) matched = js_MatchToken(cx, ts, TOK_RB); ts->flags &= ~TSF_OPERAND; if (!matched) { - for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) { + for (atomIndex = 0; ; atomIndex++) { + if (atomIndex == ATOM_INDEX_LIMIT) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_ARRAY_INIT_TOO_BIG); + return NULL; + } + ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; @@ -2880,7 +3871,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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); + pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); } else { pn2 = AssignExpr(cx, ts, tc); } @@ -2902,7 +3893,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } case TOK_LC: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_type = TOK_RC; @@ -2922,7 +3913,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) tt = js_GetToken(cx, ts); switch (tt) { case TOK_NUMBER: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; break; @@ -2936,8 +3927,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) ? JSOP_GETTER : JSOP_SETTER; if (js_MatchToken(cx, ts, TOK_NAME)) { - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, - tc); + pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; @@ -2954,12 +3944,13 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* else fall thru ... */ #endif case TOK_STRING: - pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; break; case TOK_RC: - if (!js_ReportCompileErrorNumber(cx, ts, NULL, + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_TRAILING_COMMA)) { @@ -2967,7 +3958,8 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } goto end_obj_init; default: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_PROP_ID); return NULL; } @@ -2981,7 +3973,8 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } #endif if (tt != TOK_COLON) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_COLON_AFTER_ID); return NULL; } @@ -3006,7 +3999,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_DEFSHARP: if (defsharp) goto badsharp; - defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + defsharp = NewParseNode(cx, ts, PN_UNARY, tc); if (!defsharp) return NULL; defsharp->pn_kid = NULL; @@ -3015,7 +4008,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_USESHARP: /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; @@ -3025,26 +4018,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_INITIALIZERS */ case TOK_LP: - { -#if JS_HAS_IN_OPERATOR - uintN oldflags; -#endif - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + pn = NewParseNode(cx, 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 + pn2 = BracketedExpr(cx, ts, tc); if (!pn2) return NULL; @@ -3053,26 +4030,68 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_kid = pn2; break; - } + +#if JS_HAS_XML_SUPPORT + case TOK_STAR: + pn = QualifiedIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_AT: + pn = AttributeIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_XMLSTAGO: + pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE); + if (!pn) + return NULL; + notsharp = JS_TRUE; /* XXXbe could be sharp? */ + break; +#endif /* JS_HAS_XML_SUPPORT */ case TOK_STRING: #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; -#endif /* FALL THROUGH */ +#endif + +#if JS_HAS_XML_SUPPORT + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: +#endif case TOK_NAME: case TOK_OBJECT: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, 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 JS_HAS_XML_SUPPORT + if (tt == TOK_XMLPI) + pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; + else +#endif + pn->pn_op = CURRENT_TOKEN(ts).t_op; if (tt == TOK_NAME) { pn->pn_arity = PN_NAME; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; +#if JS_HAS_XML_SUPPORT + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { + pn = QualifiedSuffix(cx, ts, pn, tc); + if (!pn) + return NULL; + break; + } +#endif + /* Unqualified __parent__ and __proto__ uses require activations. */ if (pn->pn_atom == cx->runtime->atomState.parentAtom || pn->pn_atom == cx->runtime->atomState.protoAtom) { @@ -3102,7 +4121,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_NUMBER: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_dval = CURRENT_TOKEN(ts).t_dval; @@ -3112,7 +4131,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; case TOK_PRIMARY: - pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_op = CURRENT_TOKEN(ts).t_op; @@ -3125,21 +4144,12 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) 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, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } @@ -3148,7 +4158,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (defsharp) { if (notsharp) { badsharp: - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); return NULL; } @@ -3329,6 +4339,157 @@ FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, return JS_TRUE; } +#if JS_HAS_XML_SUPPORT + +static JSBool +FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode **pnp, *pn1, *pn2; + JSString *accum, *str; + uint32 i, j; + + JS_ASSERT(pn->pn_arity == PN_LIST); + tt = pn->pn_type; + pnp = &pn->pn_head; + pn1 = *pnp; + accum = NULL; + if ((pn->pn_extra & PNX_CANTFOLD) == 0) { + if (tt == TOK_XMLETAGO) + accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom); + else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) + accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom); + } + + for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) { + /* The parser already rejected end-tags with attributes. */ + JS_ASSERT(tt != TOK_XMLETAGO || i == 0); + switch (pn2->pn_type) { + case TOK_XMLATTR: + if (!accum) + goto cantfold; + /* FALL THROUGH */ + case TOK_XMLNAME: + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_STRING: + if (pn2->pn_arity == PN_LIST) + goto cantfold; + str = ATOM_TO_STRING(pn2->pn_atom); + break; + + case TOK_XMLCDATA: + str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom)); + if (!str) + return JS_FALSE; + break; + + case TOK_XMLCOMMENT: + str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom)); + if (!str) + return JS_FALSE; + break; + + case TOK_XMLPI: + str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom), + ATOM_TO_STRING(pn2->pn_atom2)); + if (!str) + return JS_FALSE; + break; + + cantfold: + default: + JS_ASSERT(*pnp == pn1); + if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && + (i & 1) ^ (j & 1)) { +#ifdef DEBUG_brendanXXX + printf("1: %d, %d => %s\n", + i, j, accum ? JS_GetStringBytes(accum) : "NULL"); +#endif + } else if (accum && pn1 != pn2) { + while (pn1->pn_next != pn2) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_arity = PN_NULLARY; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + JS_ASSERT(pnp != &pn1->pn_next); + *pnp = pn1; + } + pnp = &pn2->pn_next; + pn1 = *pnp; + accum = NULL; + continue; + } + + if (accum) { + str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0) + ? js_AddAttributePart(cx, i & 1, accum, str) + : js_ConcatStrings(cx, accum, str); + if (!str) + return JS_FALSE; +#ifdef DEBUG_brendanXXX + printf("2: %d, %d => %s (%u)\n", + i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); +#endif + ++j; + } + accum = str; + } + + if (accum) { + str = NULL; + if ((pn->pn_extra & PNX_CANTFOLD) == 0) { + if (tt == TOK_XMLPTAGC) + str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); + else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) + str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); + } + if (str) { + accum = js_ConcatStrings(cx, accum, str); + if (!accum) + return JS_FALSE; + } + + JS_ASSERT(*pnp == pn1); + while (pn1->pn_next) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_arity = PN_NULLARY; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + JS_ASSERT(pnp != &pn1->pn_next); + *pnp = pn1; + } + + if (pn1 && pn->pn_count == 1) { + /* + * Only one node under pn, and it has been folded: move pn1 onto pn + * unless pn is an XML root (in which case we need it to tell the code + * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an + * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid + * extra "<" and "/>" bracketing at runtime. + */ + if (!(pn->pn_extra & PNX_XMLROOT)) { + PN_MOVE_NODE(pn, pn1); + } else if (tt == TOK_XMLPTAGC) { + pn->pn_type = TOK_XMLELEM; + pn->pn_op = JSOP_TOXML; + } + } + return JS_TRUE; +} + +#endif /* JS_HAS_XML_SUPPORT */ + JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { @@ -3342,11 +4503,61 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) switch (pn->pn_arity) { case PN_FUNC: + { + uint16 oldflags = tc->flags; + + tc->flags = (uint16) pn->pn_flags; if (!js_FoldConstants(cx, pn->pn_body, tc)) return JS_FALSE; + tc->flags = oldflags; break; + } case PN_LIST: +#if 0 /* JS_HAS_XML_SUPPORT */ + switch (pn->pn_type) { + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + /* + * Try to fold this XML parse tree once, from the top down, into + * a JSXML tree with just one object wrapping the tree root. + * + * Certain subtrees could be folded similarly, but we'd have to + * ensure that none used namespace prefixes declared elsewhere in + * its super-tree, and we would have to convert each XML object + * created at runtime for such sub-trees back into a string, and + * concatenate and re-parse anyway. + */ + if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT && + !(tc->flags & TCF_HAS_DEFXMLNS)) { + JSObject *obj; + JSAtom *atom; + + obj = js_ParseNodeToXMLObject(cx, pn); + if (!obj) + return JS_FALSE; + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) + return JS_FALSE; + pn->pn_op = JSOP_XMLOBJECT; + pn->pn_arity = PN_NULLARY; + pn->pn_atom = atom; + return JS_TRUE; + } + + /* + * Can't fold from parse node to XML tree -- try folding strings + * as much as possible, and folding XML sub-trees bottom up to + * minimize string concatenation and ToXML/ToXMLList operations + * at runtime. + */ + break; + + default:; + } +#endif + /* 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)) @@ -3485,13 +4696,11 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } /* Fill the buffer, advancing chars and recycling kids as we go. */ - for (pn2 = pn1; pn2; pn2 = pn3) { + for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { 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; @@ -3628,6 +4837,42 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } break; +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); + if (!FoldXMLConstants(cx, pn, tc)) + return JS_FALSE; + } + break; + + case TOK_AT: + if (pn1->pn_type == TOK_XMLNAME) { + jsval v; + JSAtom *atom; + + v = ATOM_KEY(pn1->pn_atom); + if (!js_ToAttributeName(cx, &v)) + return JS_FALSE; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0); + if (!atom) + return JS_FALSE; + + pn->pn_type = TOK_XMLNAME; + pn->pn_op = JSOP_OBJECT; + pn->pn_arity = PN_NULLARY; + pn->pn_atom = atom; + RecycleTree(pn1, tc); + } + break; +#endif /* JS_HAS_XML_SUPPORT */ + default:; } diff --git a/src/dom/js/jsparse.h b/src/dom/js/jsparse.h index 9ce12b391..24b27bdc1 100644 --- a/src/dom/js/jsparse.h +++ b/src/dom/js/jsparse.h @@ -42,6 +42,7 @@ /* * JS parser definitions. */ +#include "jsconfig.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsscan.h" @@ -155,8 +156,8 @@ JS_BEGIN_EXTERN_C * 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_DOT, name pn_expr: MEMBER expr to left of . + * TOK_DBLDOT 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 @@ -182,6 +183,58 @@ JS_BEGIN_EXTERN_C * 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 + * TOK_ANYNAME nullary pn_op: JSOP_ANYNAME + * pn_atom: cx->runtime->atomState.starAtom + * TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr + * TOK_DBLCOLON binary pn_op: JSOP_QNAME + * pn_left: TOK_ANYNAME or TOK_NAME node + * pn_right: TOK_STRING "*" node, or expr within [] + * name pn_op: JSOP_QNAMECONST + * pn_expr: TOK_ANYNAME or TOK_NAME left operand + * pn_atom: name on right of :: + * TOK_XMLELEM list XML element node + * pn_head: start tag, content1, ... contentN, end tag + * pn_count: 2 + N where N is number of content nodes + * N may be > x.length() if {expr} embedded + * TOK_XMLLIST list XML list node + * pn_head: content1, ... contentN + * TOK_XMLSTAGO, list XML start, end, and point tag contents + * TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ... + * TOK_XMLPTAGO + * TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded + * TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr} + * TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING + * TOK_XMLCDATA, + * TOK_XMLCOMMENT + * TOK_XMLPI nullary pn_atom: XML processing instruction target + * pn_atom2: XML PI content, or null if no content + * TOK_XMLTEXT nullary pn_atom: marked-up text, or null if empty string + * TOK_LC unary {expr} in XML tag or content; pn_kid is expr + * + * So an XML tag with no {expr} and three attributes is a list with the form: + * + * (tagname attrname1 attrvalue1 attrname2 attrvalue2 attrname2 attrvalue3) + * + * An XML tag with embedded expressions like so: + * + * + * + * would have the form: + * + * ((name1 {expr1}) (name2 {expr2} name3) {expr3}) + * + * where () bracket a list with elements separated by spaces, and {expr} is a + * TOK_LC unary node with expr as its kid. + * + * Thus, the attribute name/value pairs occupy successive odd and even list + * locations, where pn_head is the TOK_XMLNAME node at list location 0. The + * parser builds the same sort of structures for elements: + * + * Hi there!How are you?{x + y} + * + * translates to: + * + * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) */ typedef enum JSParseNodeArity { PN_FUNC = -3, @@ -194,11 +247,11 @@ typedef enum JSParseNodeArity { } JSParseNodeArity; struct JSParseNode { - JSTokenType pn_type; + uint16 pn_type; + uint8 pn_op; + int8 pn_arity; 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 */ @@ -232,9 +285,16 @@ struct JSParseNode { jsint slot; /* -1 or arg or local var slot */ uintN attrs; /* attributes if local var or const */ } name; + struct { + JSAtom *atom; /* first atom in pair */ + JSAtom *atom2; /* second atom in pair or null */ + } apair; jsdouble dval; /* aligned numeric literal value */ } pn_u; JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ +#if JS_HAS_XML_SUPPORT + JSTokenStream *pn_ts; /* token stream for XML error reports */ +#endif }; #define pn_funAtom pn_u.func.funAtom @@ -258,6 +318,7 @@ struct JSParseNode { #define pn_slot pn_u.name.slot #define pn_attrs pn_u.name.attrs #define pn_dval pn_u.dval +#define pn_atom2 pn_u.apair.atom2 /* PN_LIST pn_extra flags. */ #define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ @@ -266,6 +327,7 @@ struct JSParseNode { #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 */ +#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ /* * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off @@ -304,7 +366,7 @@ struct JSParseNode { JS_BEGIN_MACRO \ (list)->pn_head = NULL; \ (list)->pn_tail = &(list)->pn_head; \ - (list)->pn_count = 0; \ + (list)->pn_count = (list)->pn_extra = 0; \ JS_END_MACRO #define PN_INIT_LIST_1(list, pn) \ @@ -312,6 +374,7 @@ struct JSParseNode { (list)->pn_head = (pn); \ (list)->pn_tail = &(pn)->pn_next; \ (list)->pn_count = 1; \ + (list)->pn_extra = 0; \ JS_END_MACRO #define PN_APPEND(list, pn) \ @@ -340,6 +403,12 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); extern JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); +#if JS_HAS_XML_SUPPORT +JS_FRIEND_API(JSParseNode *) +js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSBool allowList); +#endif + JS_END_EXTERN_C #endif /* jsparse_h___ */ diff --git a/src/dom/js/jsprf.c b/src/dom/js/jsprf.c index 591313ef7..ba7c0f03b 100644 --- a/src/dom/js/jsprf.c +++ b/src/dom/js/jsprf.c @@ -49,6 +49,8 @@ #include "jsprf.h" #include "jslong.h" #include "jsutil.h" /* Added by JSIFY */ +#include "jspubtd.h" +#include "jsstr.h" /* ** Note: on some platforms va_list is defined as an array, @@ -105,6 +107,7 @@ struct NumArgState{ #define TYPE_STRING 8 #define TYPE_DOUBLE 9 #define TYPE_INTSTR 10 +#define TYPE_WSTRING 11 #define TYPE_UNKNOWN 20 #define FLAG_LEFT 0x1 @@ -395,6 +398,27 @@ static int cvt_s(SprintfState *ss, const char *s, int width, int prec, return fill2(ss, s ? s : "(null)", slen, width, flags); } +static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, + int flags) +{ + int result; + /* + * Supply NULL as the JSContext; errors are not reported, + * and malloc() is used to allocate the buffer buffer. + */ + if (ws) { + int slen = js_strlen(ws); + char *s = js_DeflateString(NULL, ws, slen); + if (!s) + return -1; /* JSStuffFunc error indicator. */ + result = cvt_s(ss, s, width, prec, flags); + free(s); + } else { + result = cvt_s(ss, NULL, width, prec, flags); + } + return result; +} + /* ** BuildArgArray stands for Numbered Argument list Sprintf ** for example, @@ -578,7 +602,7 @@ static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, break; case 's': - nas[ cn ].type = TYPE_STRING; + nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING; break; case 'n': @@ -635,6 +659,8 @@ static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, case TYPE_STRING: (void)va_arg( ap, char* ); break; + case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break; + case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break; case TYPE_DOUBLE: (void)va_arg( ap, double ); break; @@ -662,11 +688,13 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) int flags, width, prec, radix, type; union { char ch; + jschar wch; int i; long l; JSInt64 ll; double d; const char *s; + const jschar* ws; int *ip; } u; const char *fmt0; @@ -678,7 +706,10 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; char pattern[20]; const char *dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ - +#ifdef JS_C_STRINGS_ARE_UTF8 + char utf8buf[6]; + int utf8len; +#endif /* ** build an argument array, IF the fmt is numbered argument @@ -905,7 +936,6 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) break; case 'c': - u.ch = va_arg(ap, int); if ((flags & FLAG_LEFT) == 0) { while (width-- > 1) { rv = (*ss->stuff)(ss, " ", 1); @@ -914,7 +944,20 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) } } } - rv = (*ss->stuff)(ss, &u.ch, 1); + switch (type) { + case TYPE_INT16: + /* Treat %hc as %c if JS_C_STRINGS_ARE_UTF8 is undefined. */ +#ifdef JS_C_STRINGS_ARE_UTF8 + u.wch = va_arg(ap, int); + utf8len = js_OneUcs4ToUtf8Char (utf8buf, u.wch); + rv = (*ss->stuff)(ss, utf8buf, utf8len); + break; +#endif + case TYPE_INTN: + u.ch = va_arg(ap, int); + rv = (*ss->stuff)(ss, &u.ch, 1); + break; + } if (rv < 0) { return rv; } @@ -953,8 +996,17 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) #endif case 's': - u.s = va_arg(ap, const char*); - rv = cvt_s(ss, u.s, width, prec, flags); + if(type == TYPE_INT16) { + /* + * This would do a simple string/byte conversion + * if JS_C_STRINGS_ARE_UTF8 is not defined. + */ + u.ws = va_arg(ap, const jschar*); + rv = cvt_ws(ss, u.ws, width, prec, flags); + } else { + u.s = va_arg(ap, const char*); + rv = cvt_s(ss, u.s, width, prec, flags); + } if (rv < 0) { return rv; } diff --git a/src/dom/js/jsprf.h b/src/dom/js/jsprf.h index e8cc46c0c..0eb910f27 100644 --- a/src/dom/js/jsprf.h +++ b/src/dom/js/jsprf.h @@ -41,19 +41,21 @@ /* ** 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 +** %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 +** %hs - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) +** %c - character +** %hc - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) +** %p - pointer (deals with machine dependent pointer size) +** %f - float +** %g - float */ #include "jstypes.h" #include diff --git a/src/dom/js/jsprvtd.h b/src/dom/js/jsprvtd.h index f5f1e77f6..ed12f1bde 100644 --- a/src/dom/js/jsprvtd.h +++ b/src/dom/js/jsprvtd.h @@ -56,6 +56,32 @@ #include "jspubtd.h" +/* Internal identifier (jsid) macros. */ +#define JSID_ATOM 0x0 +#define JSID_INT 0x1 +#define JSID_OBJECT 0x2 +#define JSID_TAGMASK 0x3 +#define JSID_TAG(id) ((id) & JSID_TAGMASK) +#define JSID_SETTAG(id,t) ((id) | (t)) +#define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) + +#define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) +#define JSID_TO_ATOM(id) ((JSAtom *)(id)) +#define ATOM_TO_JSID(atom) ((jsid)(atom)) +#define ATOM_JSID_TO_JSVAL(id) ATOM_KEY(JSID_TO_ATOM(id)) + +#define JSID_IS_INT(id) ((id) & JSID_INT) +#define JSID_TO_INT(id) ((jsint)(id) >> 1) +#define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) +#define INT_JSID_TO_JSVAL(id) (id) +#define INT_JSVAL_TO_JSID(v) (v) + +#define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) +#define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) +#define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) +#define OBJECT_JSID_TO_JSVAL(id) OBJECT_TO_JSVAL(JSID_CLRTAG(id)) +#define OBJECT_JSVAL_TO_JSID(v) OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)) + /* Scalar typedefs. */ typedef uint8 jsbytecode; typedef uint8 jssrcnote; @@ -92,7 +118,13 @@ typedef struct JSScopeOps JSScopeOps; typedef struct JSScopeProperty JSScopeProperty; typedef struct JSStackFrame JSStackFrame; typedef struct JSStackHeader JSStackHeader; +typedef struct JSStringBuffer JSStringBuffer; typedef struct JSSubString JSSubString; +typedef struct JSXML JSXML; +typedef struct JSXMLNamespace JSXMLNamespace; +typedef struct JSXMLQName JSXMLQName; +typedef struct JSXMLArray JSXMLArray; +typedef struct JSXMLArrayCursor JSXMLArrayCursor; /* "Friend" types used by jscntxt.h and jsdbgapi.h. */ typedef enum JSTrapStatus { @@ -104,8 +136,8 @@ typedef enum JSTrapStatus { } JSTrapStatus; typedef JSTrapStatus -(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, - jsval *rval, void *closure); +(* 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, @@ -115,56 +147,53 @@ typedef JSBool typedef void (* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, const char *filename, /* URL of script */ - uintN lineno, /* line script starts */ + uintN lineno, /* first line */ JSScript *script, JSFunction *fun, void *callerdata); /* called just before script destruction */ typedef void -(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, - JSScript *script, +(* 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, +(* 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. -*/ - + * 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 *not* to + * 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, +(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, void *closure); typedef JSBool diff --git a/src/dom/js/jspubtd.h b/src/dom/js/jspubtd.h index 5f19a15ce..e9da92783 100644 --- a/src/dom/js/jspubtd.h +++ b/src/dom/js/jspubtd.h @@ -69,6 +69,7 @@ typedef enum JSVersion { JSVERSION_1_4 = 140, JSVERSION_ECMA_3 = 148, JSVERSION_1_5 = 150, + JSVERSION_1_6 = 160, JSVERSION_DEFAULT = 0, JSVERSION_UNKNOWN = -1 } JSVersion; @@ -84,6 +85,8 @@ typedef enum JSType { JSTYPE_STRING, /* string */ JSTYPE_NUMBER, /* number */ JSTYPE_BOOLEAN, /* boolean */ + JSTYPE_NULL, /* null */ + JSTYPE_XML, /* xml object */ JSTYPE_LIMIT } JSType; @@ -112,6 +115,7 @@ typedef enum JSIterateOp { /* Struct typedefs. */ typedef struct JSClass JSClass; +typedef struct JSExtendedClass JSExtendedClass; typedef struct JSConstDoubleSpec JSConstDoubleSpec; typedef struct JSContext JSContext; typedef struct JSErrorReport JSErrorReport; @@ -123,11 +127,12 @@ typedef struct JSPropertySpec JSPropertySpec; typedef struct JSObject JSObject; typedef struct JSObjectMap JSObjectMap; typedef struct JSObjectOps JSObjectOps; +typedef struct JSXMLObjectOps JSXMLObjectOps; typedef struct JSRuntime JSRuntime; -typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ +typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ typedef struct JSScript JSScript; typedef struct JSString JSString; -typedef struct JSXDRState JSXDRState; +typedef struct JSXDRState JSXDRState; typedef struct JSExceptionState JSExceptionState; typedef struct JSLocaleCallbacks JSLocaleCallbacks; @@ -203,6 +208,9 @@ typedef JSBool * * 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 + * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence + * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode + * JSRESOLVE_CLASSNAME class name used when constructing * * 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, @@ -263,12 +271,17 @@ typedef void * 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. + * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to + * finalize the object. * * 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. + * + * Further extension to preserve API compatibility: if this function returns + * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls + * extended hooks needed for E4X. */ typedef JSObjectOps * (* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); @@ -387,11 +400,7 @@ typedef void */ 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 - ); + JSObject **objp, JSProperty **propp); /* * Define obj[id], a direct property of obj named id, having the given initial @@ -491,6 +500,27 @@ typedef JSBool (* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, uint32 slot, jsval v); +typedef JSObject * +(* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +typedef JSBool +(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, + jsval *vp); + /* Typedef for native functions called by the JS VM. */ typedef JSBool @@ -517,13 +547,19 @@ typedef void JSErrorReport *report); typedef struct JSErrorFormatString { + /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ const char *format; - uintN argCount; + + /* The number of arguments to expand in the formatted error message. */ + uint16 argCount; + + /* One of the JSExnType constants above. */ + int16 exnType; } JSErrorFormatString; typedef const JSErrorFormatString * (* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, - const uintN errorNumber); + const uintN errorNumber); #ifdef va_start #define JS_ARGUMENT_FORMATTER_DEFINED 1 diff --git a/src/dom/js/jsregexp.c b/src/dom/js/jsregexp.c index 4d909d4e2..e9400eb09 100644 --- a/src/dom/js/jsregexp.c +++ b/src/dom/js/jsregexp.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -62,10 +63,6 @@ #include "jsscan.h" #include "jsstr.h" -#ifdef XP_MAC -#include -#endif - #if JS_HAS_REGEXPS /* Note : contiguity of 'simple opcodes' is important for SimpleMatch() */ @@ -137,22 +134,22 @@ struct RENode { union { void *kid2; /* second operand */ jsint num; /* could be a number */ - uint16 parenIndex; /* or a parenthesis index */ + size_t parenIndex; /* or a parenthesis index */ struct { /* or a quantifier range */ - uint16 min; - uint16 max; - JSBool greedy; + uintN min; + uintN max; + JSPackedBool greedy; } range; struct { /* or a character class */ - uint16 startIndex; - uint16 kidlen; /* length of string at kid, in jschars */ + size_t startIndex; + size_t kidlen; /* length of string at kid, in jschars */ + size_t index; /* index into class list */ uint16 bmsize; /* bitmap size, based on max char code */ - uint16 index; /* index into class list */ - JSBool sense; + JSPackedBool sense; } ucclass; struct { /* or a literal sequence */ jschar chr; /* of one character */ - uint16 length; /* or many (via the kid) */ + size_t length; /* or many (via the kid) */ } flat; struct { RENode *kid2; /* second operand from ALT */ @@ -175,29 +172,118 @@ typedef struct CompilerState { 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 parenCount; + size_t classCount; /* number of [] encountered */ + size_t treeDepth; /* maximum depth of parse tree */ size_t progLength; /* estimated bytecode length */ RENode *result; + size_t classBitmapsMem; /* memory to hold all class bitmaps */ struct { const jschar *start; /* small cache of class strings */ - uint16 length; /* since they're often the same */ - uint16 index; + size_t length; /* since they're often the same */ + size_t index; } classCache[CLASS_CACHE_SIZE]; + uint16 flags; } CompilerState; +typedef struct EmitStateStackEntry { + jsbytecode *altHead; /* start of REOP_ALT* opcode */ + jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ + jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ + jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ + RENode *continueNode; /* original REOP_ALT* node being stacked */ + jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ + JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to + avoid 16-bit unsigned offset overflow */ +} EmitStateStackEntry; + +/* + * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, + * the getters and setters take the pc of the offset, not of the opcode before + * the offset. + */ +#define ARG_LEN 2 +#define GET_ARG(pc) ((uint16)(((pc)[0] << 8) | (pc)[1])) +#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ + (pc)[1] = (jsbytecode) (arg)) + +#define OFFSET_LEN ARG_LEN +#define OFFSET_MAX (JS_BIT(ARG_LEN * 8) - 1) +#define GET_OFFSET(pc) GET_ARG(pc) + +/* + * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. + * For sanity, we limit it to 2^24 bytes. + */ +#define TREE_DEPTH_MAX (JS_BIT(24) / sizeof(EmitStateStackEntry)) + +/* + * The maximum memory that can be allocated for class bitmaps. + * For sanity, we limit it to 2^24 bytes. + */ +#define CLASS_BITMAPS_MEM_LIMIT JS_BIT(24) + +/* + * Functions to get size and write/read bytecode that represent small indexes + * compactly. + * Each byte in the code represent 7-bit chunk of the index. 8th bit when set + * indicates that the following byte brings more bits to the index. Otherwise + * this is the last byte in the index bytecode representing highest index bits. + */ +static size_t +GetCompactIndexWidth(size_t index) +{ + size_t width; + + for (width = 1; (index >>= 7) != 0; ++width) { } + return width; +} + +static jsbytecode * +WriteCompactIndex(jsbytecode *pc, size_t index) +{ + size_t next; + + while ((next = index >> 7) != 0) { + *pc++ = (jsbytecode)(index | 0x80); + index = next; + } + *pc++ = (jsbytecode)index; + return pc; +} + +static jsbytecode * +ReadCompactIndex(jsbytecode *pc, size_t *result) +{ + size_t nextByte; + + nextByte = *pc++; + if ((nextByte & 0x80) == 0) { + /* + * Short-circuit the most common case when compact index <= 127. + */ + *result = nextByte; + } else { + size_t shift = 7; + *result = 0x7F & nextByte; + do { + nextByte = *pc++; + *result |= (nextByte & 0x7F) << shift; + shift += 7; + } while ((nextByte & 0x80) != 0); + } + return pc; +} + typedef struct RECapture { - int32 index; /* start of contents, -1 for empty */ - uint16 length; /* length of capture */ + ptrdiff_t index; /* start of contents, -1 for empty */ + size_t 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. - */ + allocated at end of this struct */ } REMatchState; struct REBackTrackData; @@ -205,12 +291,12 @@ 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 */ + ptrdiff_t index; /* progress in text */ + size_t parenSoFar; /* highest indexed paren started */ union { struct { - uint16 min; /* current quantifier limits */ - uint16 max; + uintN min; /* current quantifier limits */ + uintN max; } quantifier; struct { size_t top; /* backtrack stack state */ @@ -224,15 +310,15 @@ typedef struct REBackTrackData { 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 */ + size_t parenIndex; /* start index of saved paren contents */ + size_t parenCount; /* # of saved paren contents */ + size_t saveStateStackTop; /* number of parent states */ /* saved parent states follow */ /* saved paren contents follow */ } REBackTrackData; -#define INITIAL_STATESTACK (100) -#define INITIAL_BACKTRACK (8000) +#define INITIAL_STATESTACK 100 +#define INITIAL_BACKTRACK 8000 typedef struct REGlobalData { JSContext *cx; @@ -240,25 +326,23 @@ typedef struct REGlobalData { 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 */ + const jschar *cpbegin; /* text base address */ + const jschar *cpend; /* text limit address */ REProgState *stateStack; /* stack of state of current parents */ - uint16 stateStackTop; - uint16 stateStackLimit; + size_t stateStackTop; + size_t 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 - */ - + JSArenaPool pool; /* It's faster to use one malloc'd pool + 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 @@ -332,7 +416,7 @@ isASCIIHexDigit(jschar c, uintN *digit) typedef struct { REOp op; const jschar *errPos; - uint16 parenIndex; + size_t parenIndex; } REOpData; @@ -341,7 +425,8 @@ typedef struct { * operand in the penultimate slot. Update progLength and treeDepth. */ static JSBool -ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN operandSP) +ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, + intN operandSP) { RENode *result; @@ -353,11 +438,19 @@ ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN op result->kid = operandStack[operandSP - 2]; result->u.kid2 = operandStack[operandSP - 1]; operandStack[operandSP - 2] = result; + + if (state->treeDepth == TREE_DEPTH_MAX) { + js_ReportCompileErrorNumber(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + return JS_FALSE; + } + ++state->treeDepth; + /* - * 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 + * 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) { @@ -393,25 +486,29 @@ ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN op JUMP, ... ENDALT */ state->progLength += 13; } - else + 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); + js_ReportCompileErrorNumberUC(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_MISSING_PAREN, opData->errPos); return JS_FALSE; + default:; } return JS_TRUE; @@ -422,6 +519,7 @@ ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN op */ static JSBool ParseTerm(CompilerState *state); static JSBool ParseQuantifier(CompilerState *state); +static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues); /* * Top-down regular expression grammar, based closely on Perl4. @@ -434,7 +532,7 @@ static JSBool ParseQuantifier(CompilerState *state); static JSBool ParseRegExp(CompilerState *state) { - uint16 parenIndex; + size_t parenIndex; RENode *operand; REOpData *operatorStack; RENode **operandStack; @@ -461,7 +559,7 @@ ParseRegExp(CompilerState *state) if (!operandStack) goto out; - while (JS_TRUE) { + for (;;) { parenIndex = state->parenCount; if (state->cp == state->cpend) { /* @@ -477,8 +575,7 @@ ParseRegExp(CompilerState *state) } } else { switch (*state->cp) { - /* balance '(' */ - case '(': /* balance ')' */ + case '(': ++state->cp; if (state->cp + 1 < state->cpend && *state->cp == '?' && @@ -504,26 +601,30 @@ ParseRegExp(CompilerState *state) } else { op = REOP_LPAREN; /* LPAREN, , ... RPAREN, */ - state->progLength += 6; + state->progLength + += 2 * (1 + GetCompactIndexWidth(parenIndex)); state->parenCount++; if (state->parenCount == 65535) { js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, + JSREPORT_TS | + JSREPORT_ERROR, JSMSG_TOO_MANY_PARENS); goto out; } } goto pushOperator; + case ')': - /* If there's not a stacked open parenthesis, throw - * a syntax error. + /* + * If there's no stacked open parenthesis, throw syntax error. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, + JSREPORT_TS | + JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } @@ -534,13 +635,15 @@ ParseRegExp(CompilerState *state) break; } } - /* fall thru... */ + /* FALL THROUGH */ + 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; @@ -548,9 +651,9 @@ ParseRegExp(CompilerState *state) pushOperand: if (operandSP == operandStackSize) { operandStackSize += operandStackSize; - operandStack = - (RENode **)JS_realloc(state->context, operandStack, - sizeof(RENode *) * operandStackSize); + operandStack = (RENode **) + JS_realloc(state->context, operandStack, + sizeof(RENode *) * operandStackSize); if (!operandStack) goto out; } @@ -558,7 +661,8 @@ pushOperand: break; } } - /* At the end; process remaining operators */ + + /* At the end; process remaining operators. */ restartOperator: if (state->cp == state->cpend) { while (operatorSP) { @@ -573,6 +677,7 @@ restartOperator: result = JS_TRUE; goto out; } + switch (*state->cp) { case '|': /* Process any stacked 'concat' operators */ @@ -581,22 +686,23 @@ restartOperator: operatorStack[operatorSP - 1].op == REOP_CONCAT) { --operatorSP; if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) + 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. + /* + * If there's no stacked open parenthesis, throw syntax error. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } @@ -608,8 +714,9 @@ restartOperator: } } ++state->cp; - /* process everything on the stack until the open */ - while (JS_TRUE) { + + /* Process everything on the stack until the open parenthesis. */ + for (;;) { JS_ASSERT(operatorSP); --operatorSP; switch (operatorStack[operatorSP].op) { @@ -624,8 +731,17 @@ restartOperator: JS_ASSERT(operandSP); operand->kid = operandStack[operandSP - 1]; operandStack[operandSP - 1] = operand; + if (state->treeDepth == TREE_DEPTH_MAX) { + js_ReportCompileErrorNumber(state->context, + state->tokenStream, + JSREPORT_TS | + JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + goto out; + } ++state->treeDepth; - /* fall thru... */ + /* FALL THROUGH */ + case REOP_LPARENNON: state->result = operandStack[operandSP - 1]; if (!ParseQuantifier(state)) @@ -641,24 +757,42 @@ restartOperator: } } break; + + case '{': + { + const jschar *errp = state->cp; + + if (ParseMinMaxQuantifier(state, JS_TRUE) < 0) { + /* + * This didn't even scan correctly as a quantifier, so we should + * treat it as flat. + */ + op = REOP_CONCAT; + goto pushOperator; + } + + state->cp = errp; + /* FALL THROUGH */ + } + case '+': case '*': case '?': - case '{': - js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp); + js_ReportCompileErrorNumberUC(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp); result = JS_FALSE; goto out; + default: - /* Anything else is the start of the next term */ + /* 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); + operatorStack = (REOpData *) + JS_realloc(state->context, operatorStack, + sizeof(REOpData) * operatorStackSize); if (!operatorStack) goto out; } @@ -712,6 +846,7 @@ FindParenCount(CompilerState *state) temp.classCount = 0; temp.progLength = 0; temp.treeDepth = 0; + temp.classBitmapsMem = 0; for (i = 0; i < CLASS_CACHE_SIZE; i++) temp.classCache[i].start = NULL; @@ -998,7 +1133,7 @@ ParseTerm(CompilerState *state) if (state->cp >= state->cpend) { /* a trailing '\' is an error */ js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_TRAILING_SLASH); return JS_FALSE; } @@ -1019,31 +1154,28 @@ ParseTerm(CompilerState *state) 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 { + /* Give a strict warning. See also the note below. */ + if (!js_ReportCompileErrorNumber(state->context, + state->tokenStream, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_INVALID_BACKREF)) { + return JS_FALSE; + } 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; + 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) @@ -1066,13 +1198,10 @@ ParseTerm(CompilerState *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; - } + /* Give a strict mode warning. */ if (!js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, + JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, (c >= '8') @@ -1080,14 +1209,30 @@ ParseTerm(CompilerState *state) : JSMSG_BAD_BACKREF)) { return JS_FALSE; } - num = 0x10000; + + /* + * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax + * error here. However, for compatibility with IE, we treat the + * whole backref as flat if the first character in it is not a + * valid octal character, and as an octal escape otherwise. + */ + state->cp = termStart; + if (c >= '8') { + /* Treat this as flat. termStart - 1 is the \. */ + c = '\\'; + goto asFlat; + } + + /* Treat this as an octal escape. */ + goto doOctal; } 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; + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.parenIndex); break; /* Control escape */ case 'f': @@ -1129,8 +1274,8 @@ lexHex: c = *state->cp++; if (!isASCIIHexDigit(c, &digit)) { /* - * back off to accepting the original - * 'u' or 'x' as a literal + * Back off to accepting the original 'u' or 'x' as a + * literal. */ state->cp -= i + 2; n = *state->cp++; @@ -1181,20 +1326,23 @@ doSimple: return JS_FALSE; termStart = state->cp; state->result->u.ucclass.startIndex = termStart - state->cpbegin; - while (JS_TRUE) { + for (;;) { if (state->cp == state->cpend) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, - JSMSG_UNTERM_CLASS, termStart); + js_ReportCompileErrorNumberUC(state->context, state->tokenStream, + JSREPORT_TS | 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; - } + if (state->cp != state->cpend) + state->cp++; + continue; + } + if (*state->cp == ']') { + state->result->u.ucclass.kidlen = state->cp - termStart; + break; } state->cp++; } @@ -1209,8 +1357,8 @@ doSimple: state->result->u.ucclass.kidlen) { for (n = 0; ; n++) { if (n == state->classCache[i].length) { - state->result->u.ucclass.index = - state->classCache[i].index; + state->result->u.ucclass.index + = state->classCache[i].index; goto claim; } if (state->classCache[i].start[n] != termStart[n]) @@ -1219,6 +1367,7 @@ doSimple: } } state->result->u.ucclass.index = state->classCount++; + claim: /* * Call CalculateBitmapSize now as we want any errors it finds @@ -1226,20 +1375,50 @@ doSimple: */ if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) return JS_FALSE; - state->progLength += 3; /* CLASS, */ + /* + * Update classBitmapsMem with number of bytes to hold bmsize bits, + * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 + * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. + */ + n = (state->result->u.ucclass.bmsize >> 3) + 1; + if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { + js_ReportCompileErrorNumber(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + return JS_FALSE; + } + state->classBitmapsMem += n; + /* CLASS, */ + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); break; case '.': state->result = NewRENode(state, REOP_DOT); goto doSimple; + + case '{': + { + const jschar *errp = state->cp--; + intN err; + + err = ParseMinMaxQuantifier(state, JS_TRUE); + state->cp = errp; + + if (err < 0) + goto asFlat; + + /* FALL THROUGH */ + } case '*': case '+': case '?': - js_ReportCompileErrorNumber(state->context, state->tokenStream, - NULL, JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp - 1); + js_ReportCompileErrorNumberUC(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp - 1); return JS_FALSE; default: +asFlat: state->result = NewRENode(state, REOP_FLAT); if (!state->result) return JS_FALSE; @@ -1264,7 +1443,7 @@ ParseQuantifier(CompilerState *state) if (!state->result) return JS_FALSE; state->result->u.range.min = 1; - state->result->u.range.max = (uint16)-1; + state->result->u.range.max = (uintN)-1; /* , ... */ state->progLength += 4; goto quantifier; @@ -1273,7 +1452,7 @@ ParseQuantifier(CompilerState *state) if (!state->result) return JS_FALSE; state->result->u.range.min = 0; - state->result->u.range.max = (uint16)-1; + state->result->u.range.max = (uintN)-1; /* , ... */ state->progLength += 4; goto quantifier; @@ -1287,109 +1466,131 @@ ParseQuantifier(CompilerState *state) 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; + { + intN err; + const jschar *errp = state->cp; + + err = ParseMinMaxQuantifier(state, JS_FALSE); + if (err == 0) + goto quantifier; + if (err == -1) return JS_TRUE; -quantError: - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - NULL, JSREPORT_ERROR, - err, errp); - return JS_FALSE; - } + + js_ReportCompileErrorNumberUC(state->context, + state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + err, errp); + return JS_FALSE; + } + default:; } } return JS_TRUE; quantifier: + if (state->treeDepth == TREE_DEPTH_MAX) { + js_ReportCompileErrorNumber(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + return JS_FALSE; + } + ++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 + } 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 +static intN +ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues) +{ + uintN min, max; + jschar c; + const jschar *errp = state->cp++; -/* - * Recursively generate bytecode for the tree rooted at t. Iteratively. - */ + c = *state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + min = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; -typedef struct { - RENode *nextAlt; - jsbytecode *nextAltFixup, *nextTermFixup, *endTermFixup; - RENode *continueNode; - REOp continueOp; -} EmitStateStackEntry; + if (!ignoreValues && min == OVERFLOW_VALUE) + return JSMSG_MIN_TOO_BIG; + if (c == ',') { + c = *++state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + max = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + if (!ignoreValues && max == OVERFLOW_VALUE) + return JSMSG_MAX_TOO_BIG; + if (!ignoreValues && min > max) + return JSMSG_OUT_OF_ORDER; + } 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, , , ... + * where is written as compact(max+1) to make + * (uintN)-1 sentinel to occupy 1 byte, not width_of(max)+1. + */ + state->progLength += (1 + GetCompactIndexWidth(min) + + GetCompactIndexWidth(max + 1) + +3); + return 0; + } + } + + state->cp = errp; + return -1; +} + +static JSBool +SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) +{ + ptrdiff_t offset = target - jump; + + /* Check that target really points forward. */ + JS_ASSERT(offset >= 2); + if ((size_t)offset > OFFSET_MAX) + return JS_FALSE; + + jump[0] = JUMP_OFFSET_HI(offset); + jump[1] = JUMP_OFFSET_LO(offset); + return JS_TRUE; +} + +/* + * Generate bytecode for the tree rooted at t using an explicit stack instead + * of recursion. + */ static jsbytecode * -EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, +EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, jsbytecode *pc, RENode *t) { - ptrdiff_t diff; + EmitStateStackEntry *emitStateSP, *emitStateStack; RECharSet *charSet; - EmitStateStackEntry *emitStateSP, *emitStateStack = NULL; REOp op; - if (treeDepth) { + if (treeDepth == 0) { + emitStateStack = NULL; + } else { emitStateStack = (EmitStateStackEntry *)JS_malloc(state->context, sizeof(EmitStateStackEntry) * @@ -1400,7 +1601,7 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, emitStateSP = emitStateStack; op = t->op; - while (JS_TRUE) { + for (;;) { *pc++ = op; switch (op) { case REOP_EMPTY: @@ -1410,6 +1611,7 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, case REOP_ALTPREREQ2: case REOP_ALTPREREQ: JS_ASSERT(emitStateSP); + emitStateSP->altHead = pc - 1; emitStateSP->endTermFixup = pc; pc += OFFSET_LEN; SET_ARG(pc, t->u.altprereq.ch1); @@ -1417,58 +1619,148 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, SET_ARG(pc, t->u.altprereq.ch2); pc += ARG_LEN; - emitStateSP->nextAltFixup = pc; /* address of next alternate */ + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = JS_FALSE; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_JUMP: - emitStateSP->nextTermFixup = pc; /* address of following term */ + emitStateSP->nextTermFixup = pc; /* offset to following term */ pc += OFFSET_LEN; - diff = pc - emitStateSP->nextAltFixup; - CHECK_OFFSET(diff); - SET_OFFSET(emitStateSP->nextAltFixup, diff); + if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) + goto jump_too_big; emitStateSP->continueOp = REOP_ENDALT; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->u.kid2; + JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->u.kid2; op = t->op; continue; case REOP_ENDALT: - diff = pc - emitStateSP->nextTermFixup; - CHECK_OFFSET(diff); - SET_OFFSET(emitStateSP->nextTermFixup, diff); + /* + * If we already patched emitStateSP->nextTermFixup to jump to + * a nearer jump, to avoid 16-bit immediate offset overflow, we + * are done here. + */ + if (emitStateSP->jumpToJumpFlag) + break; + + /* + * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. + * REOP_ENDALT is executed only on successful match of the last + * alternate in a group. + */ + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; if (t->op != REOP_ALT) { - diff = pc - emitStateSP->endTermFixup; - CHECK_OFFSET(diff); - SET_OFFSET(emitStateSP->endTermFixup, diff); + if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) + goto jump_too_big; + } + + /* + * If the program is bigger than the REOP_JUMP offset range, then + * we must check for alternates before this one that are part of + * the same group, and fix up their jump offsets to target jumps + * close enough to fit in a 16-bit unsigned offset immediate. + */ + if ((size_t)(pc - re->program) > OFFSET_MAX && + emitStateSP > emitStateStack) { + EmitStateStackEntry *esp, *esp2; + jsbytecode *alt, *jump; + ptrdiff_t span, header; + + esp2 = emitStateSP; + alt = esp2->altHead; + for (esp = esp2 - 1; esp >= emitStateStack; --esp) { + if (esp->continueOp == REOP_ENDALT && + !esp->jumpToJumpFlag && + esp->nextTermFixup + OFFSET_LEN == alt && + (size_t)(pc - ((esp->continueNode->op != REOP_ALT) + ? esp->endTermFixup + : esp->nextTermFixup)) > OFFSET_MAX) { + alt = esp->altHead; + jump = esp->nextTermFixup; + + /* + * The span must be 1 less than the distance from + * jump offset to jump offset, so we actually jump + * to a REOP_JUMP bytecode, not to its offset! + */ + for (;;) { + JS_ASSERT(jump < esp2->nextTermFixup); + span = esp2->nextTermFixup - jump - 1; + if ((size_t)span <= OFFSET_MAX) + break; + do { + if (--esp2 == esp) + goto jump_too_big; + } while (esp2->continueOp != REOP_ENDALT); + } + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + + if (esp->continueNode->op != REOP_ALT) { + /* + * We must patch the offset at esp->endTermFixup + * as well, for the REOP_ALTPREREQ{,2} opcodes. + * If we're unlucky and endTermFixup is more than + * OFFSET_MAX bytes from its target, we cheat by + * jumping 6 bytes to the jump whose offset is at + * esp->nextTermFixup, which has the same target. + */ + jump = esp->endTermFixup; + header = esp->nextTermFixup - jump; + span += header; + if ((size_t)span > OFFSET_MAX) + span = header; + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + } + + esp->jumpToJumpFlag = JS_TRUE; + } + } } break; case REOP_ALT: JS_ASSERT(emitStateSP); - emitStateSP->nextAltFixup = pc; /* address of pointer to next alternate */ + emitStateSP->altHead = pc - 1; + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = JS_FALSE; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; + JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; op = t->op; continue; case REOP_FLAT: /* - * Consecutize FLAT's if possible. + * Coalesce FLATs if possible and if it would not increase bytecode + * beyond preallocated limit. The latter happens only when bytecode + * size for coalesced string with offset p and length 2 exceeds 6 + * bytes preallocated for 2 single char nodes, i.e. when + * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or + * GetCompactIndexWidth(p) > 4. + * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more + * nodes strictly decreases bytecode size, the check has to be + * done only for the first coalescing. */ - if (t->kid) { + if (t->kid && + GetCompactIndexWidth((jschar *)t->kid - state->cpbegin) <= 4) + { while (t->next && t->next->op == REOP_FLAT && (jschar*)t->kid + t->u.flat.length == @@ -1477,26 +1769,17 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, 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; + if (t->kid && t->u.flat.length > 1) { + pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; + pc = WriteCompactIndex(pc, (jschar *)t->kid - state->cpbegin); + pc = WriteCompactIndex(pc, t->u.flat.length); } else if (t->u.flat.chr < 256) { - if (state->flags & JSREG_FOLD) - pc[-1] = REOP_FLAT1i; - else - pc[-1] = REOP_FLAT1; + pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; *pc++ = (jsbytecode) t->u.flat.chr; } else { - if (state->flags & JSREG_FOLD) - pc[-1] = REOP_UCFLAT1i; - else - pc[-1] = REOP_UCFLAT1; + pc[-1] = (state->flags & JSREG_FOLD) + ? REOP_UCFLAT1i + : REOP_UCFLAT1; SET_ARG(pc, t->u.flat.chr); pc += ARG_LEN; } @@ -1504,24 +1787,23 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, case REOP_LPAREN: JS_ASSERT(emitStateSP); - SET_ARG(pc, t->u.parenIndex); - pc += ARG_LEN; + pc = WriteCompactIndex(pc, t->u.parenIndex); emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_RPAREN; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; + case REOP_RPAREN: - SET_ARG(pc, t->u.parenIndex); - pc += ARG_LEN; + pc = WriteCompactIndex(pc, t->u.parenIndex); break; case REOP_BACKREF: - SET_ARG(pc, t->u.parenIndex); - pc += ARG_LEN; + pc = WriteCompactIndex(pc, t->u.parenIndex); break; + case REOP_ASSERT: JS_ASSERT(emitStateSP); emitStateSP->nextTermFixup = pc; @@ -1529,16 +1811,17 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ASSERTTEST; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + JS_ASSERT((size_t)(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); + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; break; + case REOP_ASSERT_NOT: JS_ASSERT(emitStateSP); emitStateSP->nextTermFixup = pc; @@ -1546,45 +1829,48 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ASSERTNOTTEST; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + JS_ASSERT((size_t)(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) { + if (t->u.range.min == 0 && t->u.range.max == (uintN)-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) { + } else if (t->u.range.min == 1 && t->u.range.max == (uintN) -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; + pc = WriteCompactIndex(pc, t->u.range.min); + /* + * Write max + 1 to avoid using size_t(max) + 1 bytes + * for (uintN)-1 sentinel. + */ + pc = WriteCompactIndex(pc, t->u.range.max + 1); } emitStateSP->nextTermFixup = pc; pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ENDCHILD; ++emitStateSP; - JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth); + JS_ASSERT((size_t)(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); + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; break; + case REOP_CLASS: if (!t->u.ucclass.sense) pc[-1] = REOP_NCLASS; - SET_ARG(pc, t->u.ucclass.index); - pc += ARG_LEN; + pc = WriteCompactIndex(pc, t->u.ucclass.index); charSet = &re->classList[t->u.ucclass.index]; charSet->converted = JS_FALSE; charSet->length = t->u.ucclass.bmsize; @@ -1592,23 +1878,34 @@ EmitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth, charSet->u.src.length = t->u.ucclass.kidlen; charSet->sense = t->u.ucclass.sense; break; + default: break; } + t = t->next; - if (!t) { + if (t) { + op = t->op; + } else { if (emitStateSP == emitStateStack) break; --emitStateSP; t = emitStateSP->continueNode; op = emitStateSP->continueOp; } - else - op = t->op; } + + cleanup: if (emitStateStack) JS_free(state->context, emitStateStack); return pc; + + jump_too_big: + js_ReportCompileErrorNumber(state->context, state->tokenStream, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + pc = NULL; + goto cleanup; } @@ -1626,37 +1923,40 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts, re = NULL; mark = JS_ARENA_MARK(&cx->tempPool); + len = JSSTRING_LENGTH(str); state.context = cx; state.tokenStream = ts; state.cpbegin = state.cp = JSSTRING_CHARS(str); - state.cpend = state.cp + JSSTRING_LENGTH(str); + state.cpend = state.cp + len; state.flags = flags; state.parenCount = 0; state.classCount = 0; state.progLength = 0; state.treeDepth = 0; + state.classBitmapsMem = 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->u.flat.length = len; state.result->kid = (void *) state.cpbegin; - state.progLength += 5; + /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ + state.progLength += 1 + GetCompactIndexWidth(0) + + GetCompactIndexWidth(len); } else { if (!ParseRegExp(&state)) goto out; } - resize = sizeof *re + state.progLength + 1; - re = (JSRegExp *) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword))); + resize = offsetof(JSRegExp, program) + state.progLength + 1; + re = (JSRegExp *) JS_malloc(cx, resize); if (!re) goto out; re->nrefs = 1; + JS_ASSERT(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); re->classCount = state.classCount; if (re->classCount) { re->classList = (RECharSet *) @@ -1676,7 +1976,19 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts, goto out; } *endPC++ = REOP_END; - JS_ASSERT(endPC <= (re->program + (state.progLength + 1))); + /* + * Check whether size was overestimated and shrink using realloc. + * This is safe since no pointers to newly parsed regexp or its parts + * besides re exist here. + */ + if ((size_t)(endPC - re->program) != state.progLength + 1) { + JSRegExp *tmp; + JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); + resize = offsetof(JSRegExp, program) + (endPC - re->program); + tmp = (JSRegExp *) JS_realloc(cx, re, resize); + if (tmp) + re = tmp; + } re->flags = flags; re->cloneIndex = 0; @@ -1714,7 +2026,8 @@ js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, default: charBuf[0] = (char)s[i]; charBuf[1] = '\0'; - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FLAG, charBuf); return NULL; } @@ -1723,10 +2036,6 @@ js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, 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 @@ -1736,9 +2045,9 @@ js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, static REBackTrackData * PushBackTrackState(REGlobalData *gData, REOp op, jsbytecode *target, REMatchState *x, const jschar *cp, - uintN parenIndex, intN parenCount) + size_t parenIndex, size_t parenCount) { - intN i; + size_t i; REBackTrackData *result = (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); @@ -1775,15 +2084,13 @@ PushBackTrackState(REGlobalData *gData, REOp op, memcpy(result + 1, gData->stateStack, sizeof(REProgState) * result->saveStateStackTop); - /* FIXME: parenCount should be uintN */ - JS_ASSERT(parenCount >= 0); - if (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++) + for (i = 0; i != parenCount; i++) x->parens[parenIndex + i].index = -1; } @@ -1797,12 +2104,12 @@ PushBackTrackState(REGlobalData *gData, REOp op, #if 0 static REMatchState * FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - intN length) + size_t length) { - intN i; - if (x->cp + length > gData->cpend) + size_t i; + if (length > gData->cpend - x->cp) return NULL; - for (i = 0; i < length; i++) { + for (i = 0; i != length; i++) { if (matchChars[i] != x->cp[i]) return NULL; } @@ -1813,12 +2120,13 @@ FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, static REMatchState * FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - intN length) + size_t length) { - intN i; - if (x->cp + length > gData->cpend) + size_t i; + JS_ASSERT(gData->cpend >= x->cp); + if (length > (size_t)(gData->cpend - x->cp)) return NULL; - for (i = 0; i < length; i++) { + for (i = 0; i != length; i++) { if (upcase(matchChars[i]) != upcase(x->cp[i])) return NULL; } @@ -1850,20 +2158,20 @@ FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, * 10. Call c(y) and return its result. */ static REMatchState * -BackrefMatcher(REGlobalData *gData, REMatchState *x, uintN parenIndex) +BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) { - uintN len; - uintN i; + size_t len, i; const jschar *parenContent; - RECapture *s = &x->parens[parenIndex]; - if (s->index == -1) + RECapture *cap = &x->parens[parenIndex]; + + if (cap->index == -1) return x; - len = s->length; + len = cap->length; if (x->cp + len > gData->cpend) return NULL; - parenContent = &gData->cpbegin[s->index]; + parenContent = &gData->cpbegin[cap->index]; if (gData->regexp->flags & JSREG_FOLD) { for (i = 0; i < len; i++) { if (upcase(parenContent[i]) != upcase(x->cp[i])) @@ -1918,10 +2226,7 @@ AddCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2) 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; - + const jschar *src, *end; JSBool inRange = JS_FALSE; jschar rangeStart = 0; uintN byteLength, n; @@ -1929,7 +2234,21 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet) intN nDigits, i; JS_ASSERT(!charSet->converted); + /* + * Assert that startIndex and length points to chars inside [] inside + * source string. + */ + JS_ASSERT(1 <= charSet->u.src.startIndex); + JS_ASSERT(charSet->u.src.startIndex + < JSSTRING_LENGTH(gData->regexp->source)); + JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(gData->regexp->source) + - 1 - charSet->u.src.startIndex); + charSet->converted = JS_TRUE; + src = JSSTRING_CHARS(gData->regexp->source) + charSet->u.src.startIndex; + end = src + charSet->u.src.length; + JS_ASSERT(src[-1] == '['); + JS_ASSERT(end[0] == ']'); byteLength = (charSet->length >> 3) + 1; charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); @@ -1943,10 +2262,9 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet) if (*src == '^') { JS_ASSERT(charSet->sense == JS_FALSE); ++src; - } - else + } else { JS_ASSERT(charSet->sense == JS_TRUE); - + } while (src != end) { switch (*src) { @@ -2015,7 +2333,6 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet) * 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; @@ -2125,7 +2442,7 @@ js_DestroyRegExp(JSContext *cx, JSRegExp *re) static JSBool ReallocStateStack(REGlobalData *gData) { - uint16 limit = gData->stateStackLimit; + size_t limit = gData->stateStackLimit; size_t sz = sizeof(REProgState) * limit; JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, &gData->pool, sz, sz); @@ -2157,8 +2474,8 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, { REMatchState *result = NULL; jschar matchCh; - uintN parenIndex; - intN offset, length, index; + size_t parenIndex; + size_t offset, length, index; jsbytecode *pc = *startpc; /* pc has already been incremented past op */ jschar *source; const jschar *startcp = x->cp; @@ -2243,18 +2560,19 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, } break; case REOP_BACKREF: - parenIndex = GET_ARG(pc); - pc += ARG_LEN; + pc = ReadCompactIndex(pc, &parenIndex); + JS_ASSERT(parenIndex < gData->regexp->parenCount); 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++) { + pc = ReadCompactIndex(pc, &offset); + JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + JS_ASSERT(1 <= length); + JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); + if (length <= (size_t)(gData->cpend - x->cp)) { + source = JSSTRING_CHARS(gData->regexp->source) + offset; + for (index = 0; index != length; index++) { if (source[index] != x->cp[index]) return NULL; } @@ -2270,10 +2588,11 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, } break; case REOP_FLATi: - offset = GET_ARG(pc); - pc += ARG_LEN; - length = GET_ARG(pc); - pc += ARG_LEN; + pc = ReadCompactIndex(pc, &offset); + JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + JS_ASSERT(1 <= length); + JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); source = JSSTRING_CHARS(gData->regexp->source); result = FlatNIMatcher(gData, x, source + offset, length); break; @@ -2301,8 +2620,8 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, } break; case REOP_CLASS: - index = GET_ARG(pc); - pc += ARG_LEN; + pc = ReadCompactIndex(pc, &index); + JS_ASSERT(index < gData->regexp->classCount); if (x->cp != gData->cpend) { charSet = &gData->regexp->classList[index]; JS_ASSERT(charSet->converted); @@ -2317,8 +2636,8 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, } break; case REOP_NCLASS: - index = GET_ARG(pc); - pc += ARG_LEN; + pc = ReadCompactIndex(pc, &index); + JS_ASSERT(index < gData->regexp->classCount); if (x->cp != gData->cpend) { charSet = &gData->regexp->classList[index]; JS_ASSERT(charSet->converted); @@ -2351,14 +2670,13 @@ 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; + size_t parenIndex, k; + size_t parenSoFar = 0; jschar matchCh1, matchCh2; RECharSet *charSet; @@ -2390,7 +2708,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) return NULL; } - while (JS_TRUE) { + for (;;) { if (REOP_IS_SIMPLE(op)) { result = SimpleMatch(gData, x, op, &pc, JS_TRUE); } else { @@ -2469,8 +2787,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) */ case REOP_JUMP: --gData->stateStackTop; - offset = GET_OFFSET(pc); - pc += offset; + pc += GET_OFFSET(pc); op = (REOp) *pc++; continue; @@ -2483,18 +2800,27 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) continue; case REOP_LPAREN: - parenIndex = GET_ARG(pc); + pc = ReadCompactIndex(pc, &parenIndex); + JS_ASSERT(parenIndex < gData->regexp->parenCount); 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; + pc = ReadCompactIndex(pc, &parenIndex); + JS_ASSERT(parenIndex < gData->regexp->parenCount); cap = &x->parens[parenIndex]; + + /* + * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346090 + * This wallpaper prevents a case where we somehow took a step + * backward in input while minimally-matching an empty string. + */ + if (x->cp < gData->cpbegin + cap->index) + cap->index = -1; cap->length = x->cp - (gData->cpbegin + cap->index); op = (REOp) *pc++; continue; @@ -2519,6 +2845,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) return NULL; } continue; + case REOP_ASSERT_NOT: nextpc = pc + GET_OFFSET(pc); pc += ARG_LEN; @@ -2540,6 +2867,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) nextpc, x, x->cp, 0, 0)) return NULL; continue; + case REOP_ASSERTTEST: --gData->stateStackTop; --curState; @@ -2551,6 +2879,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) if (result) result = x; break; + case REOP_ASSERTNOTTEST: --gData->stateStackTop; --curState; @@ -2569,21 +2898,24 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) case REOP_STAR: curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uint16)-1; + curState->u.quantifier.max = (uintN)-1; goto quantcommon; case REOP_PLUS: curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uint16)-1; + curState->u.quantifier.max = (uintN)-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; + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* max is k - 1 to use one byte for (uintN)-1 sentinel. */ + curState->u.quantifier.max = k - 1; + JS_ASSERT(curState->u.quantifier.min + <= curState->u.quantifier.max); quantcommon: if (curState->u.quantifier.max == 0) { pc = pc + GET_OFFSET(pc); @@ -2643,7 +2975,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) } if (curState->u.quantifier.min != 0) curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uint16) -1) + if (curState->u.quantifier.max != (uintN) -1) curState->u.quantifier.max--; if (curState->u.quantifier.max == 0) goto repeatDone; @@ -2683,21 +3015,24 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) case REOP_MINIMALSTAR: curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uint16)-1; + curState->u.quantifier.max = (uintN)-1; goto minimalquantcommon; case REOP_MINIMALPLUS: curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uint16)-1; + curState->u.quantifier.max = (uintN)-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; + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* See REOP_QUANT comments about k - 1. */ + curState->u.quantifier.max = k - 1; + JS_ASSERT(curState->u.quantifier.min + <= curState->u.quantifier.max); minimalquantcommon: curState->index = x->cp - gData->cpbegin; curState->parenSoFar = parenSoFar; @@ -2706,7 +3041,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) curState->continue_op = REOP_MINIMALREPEAT; curState->continue_pc = pc; /* step over */ - pc += ARG_LEN; + pc += OFFSET_LEN; op = (REOp) *pc++; } else { if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, @@ -2727,7 +3062,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) /* * Non-greedy failure - try to consume another child. */ - if (curState->u.quantifier.max == (uint16) -1 || + if (curState->u.quantifier.max == (uintN) -1 || curState->u.quantifier.max > 0) { curState->index = x->cp - gData->cpbegin; curState->continue_op = REOP_MINIMALREPEAT; @@ -2750,7 +3085,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) } if (curState->u.quantifier.min != 0) curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uint16) -1) + if (curState->u.quantifier.max != (uintN) -1) curState->u.quantifier.max--; if (curState->u.quantifier.min != 0) { curState->continue_op = REOP_MINIMALREPEAT; @@ -2783,6 +3118,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) } break_switch:; } + /* * If the match failed and there's a backtrack option, take it. * Otherwise this is a complete and utter failure. @@ -2886,8 +3222,8 @@ InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) JS_ARENA_ALLOCATE_CAST(result, REMatchState *, &gData->pool, - sizeof(REMatchState) - + (re->parenCount - 1) * sizeof(RECapture)); + offsetof(REMatchState, parens) + + re->parenCount * sizeof(RECapture)); if (!result) return NULL; @@ -2952,7 +3288,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, goto out; } cp = result->cp; - i = PTRDIFF(cp, gData.cpbegin, jschar); + i = cp - gData.cpbegin; *indexp = i; matchlen = i - (start + gData.skipped); ep = cp; @@ -2998,7 +3334,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, ok = JS_FALSE; goto out; } - DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0)); + DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0)); } res = &cx->regExpStatics; @@ -3027,8 +3363,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, } else if (morenum >= res->moreLength) { res->moreLength += 10; morepar = (JSSubString*) - JS_realloc(cx, morepar, res->moreLength * - sizeof(JSSubString)); + JS_realloc(cx, morepar, + res->moreLength * sizeof(JSSubString)); } if (!morepar) { cx->newborn[GCX_OBJECT] = NULL; @@ -3048,7 +3384,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, if (test) continue; if (parsub->index == -1) { - ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, NULL); } else { @@ -3060,7 +3396,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, ok = JS_FALSE; goto out; } - ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), STRING_TO_JSVAL(parstr), NULL, NULL, JSPROP_ENUMERATE, NULL); } @@ -3084,20 +3420,20 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, * order (so they come after the elements). */ DEFVAL(INT_TO_JSVAL(start + gData.skipped), - (jsid)cx->runtime->atomState.indexAtom); + ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); DEFVAL(STRING_TO_JSVAL(str), - (jsid)cx->runtime->atomState.inputAtom); + ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); } #undef DEFVAL res->lastMatch.chars = cp; res->lastMatch.length = matchlen; - if (cx->version == JSVERSION_1_2) { + if (JS_VERSION_IS_1_2(cx)) { /* * 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: + * pseudo-context. On "hi there bye", the following would result: * * Language while(/ /g){print("$`");} s/ /$`/g * perl4.036 "hi", "there" "hihitherehi therebye" @@ -3151,7 +3487,7 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSRegExp *re; if (!JSVAL_IS_INT(id)) - return JS_TRUE; + return JS_TRUE; slot = JSVAL_TO_INT(id); if (slot == REGEXP_LAST_INDEX) return JS_GetReservedSlot(cx, obj, 0, vp); @@ -3159,20 +3495,20 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *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; - } + 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; @@ -3187,7 +3523,7 @@ regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) ok = JS_TRUE; if (!JSVAL_IS_INT(id)) - return ok; + return ok; slot = JSVAL_TO_INT(id); if (slot == REGEXP_LAST_INDEX) { if (!js_ValueToNumber(cx, *vp, &lastIndex)) @@ -3433,10 +3769,14 @@ regexp_mark(JSContext *cx, JSObject *obj, void *arg) 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 + 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}; @@ -3633,7 +3973,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv); if (!ok) - return JS_FALSE; + return JS_FALSE; JS_LOCK_OBJ(cx, obj); re = (JSRegExp *) JS_GetPrivate(cx, obj); if (!re) { @@ -3724,7 +4064,7 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * TypeError.) See 10.15.3.1. */ if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && - JSVAL_IS_OBJECT(argv[0]) && + !JSVAL_IS_PRIMITIVE(argv[0]) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) { *rval = argv[0]; return JS_TRUE; @@ -3734,6 +4074,12 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); if (!obj) return JS_FALSE; + + /* + * regexp_compile does not use rval to root its temporaries + * so we can use it to root obj. + */ + *rval = OBJECT_TO_JSVAL(obj); } return regexp_compile(cx, obj, argc, argv, rval); } @@ -3776,6 +4122,7 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, JSString *str; JSObject *obj; JSRegExp *re; + JSTempValueRooter tvr; str = js_NewStringCopyN(cx, chars, length, 0); if (!str) @@ -3783,11 +4130,13 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); if (!re) return NULL; + JS_PUSH_SINGLE_TEMP_ROOT(cx, STRING_TO_JSVAL(str), &tvr); 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; + obj = NULL; } + JS_POP_TEMP_ROOT(cx, &tvr); return obj; } diff --git a/src/dom/js/jsregexp.h b/src/dom/js/jsregexp.h index 998352df5..50789832d 100644 --- a/src/dom/js/jsregexp.h +++ b/src/dom/js/jsregexp.h @@ -65,9 +65,9 @@ struct JSRegExpStatics { /* * 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 + * 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 + * original regexp source of the beginning of the class contents. The first * use of the class converts the source representation into a bitmap. * */ @@ -78,8 +78,8 @@ typedef struct RECharSet { union { uint8 *bits; struct { - uint16 startIndex; - uint16 length; + size_t startIndex; + size_t length; } src; } u; } RECharSet; @@ -102,8 +102,8 @@ struct JSRegExp { 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 */ + size_t parenCount; /* number of parenthesized submatches */ + size_t classCount; /* count [...] bitmaps */ RECharSet *classList; /* list of [...] bitmaps */ JSString *source; /* locked source string, sans // */ jsbytecode program[1]; /* regular expression bytecode */ @@ -117,6 +117,9 @@ extern JSRegExp * js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, JSString *str, JSString *opt, JSBool flat); +#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) +#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) + extern void js_DestroyRegExp(JSContext *cx, JSRegExp *re); @@ -127,7 +130,7 @@ js_DestroyRegExp(JSContext *cx, JSRegExp *re); */ extern JSBool js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, jsval *rval); + JSBool test, jsval *rval); /* * These two add and remove GC roots, respectively, so their calls must be diff --git a/src/dom/js/jsscan.c b/src/dom/js/jsscan.c index 995c1e660..c043799dc 100644 --- a/src/dom/js/jsscan.c +++ b/src/dom/js/jsscan.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set sw=4 ts=8 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -66,10 +67,18 @@ #include "jsopcode.h" #include "jsregexp.h" #include "jsscan.h" +#include "jsscript.h" + +#if JS_HAS_XML_SUPPORT +#include "jsparse.h" +#include "jsxml.h" +#endif #define RESERVE_JAVA_KEYWORDS #define RESERVE_ECMA_KEYWORDS +#define MAX_KEYWORD_LENGTH 12 + static struct keyword { const char *name; JSTokenType tokentype; /* JSTokenType */ @@ -79,7 +88,7 @@ static struct keyword { {"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_default_str, 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}, @@ -96,7 +105,7 @@ static struct keyword { {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_var_str, 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}, @@ -171,10 +180,13 @@ JSBool js_InitScanner(JSContext *cx) { struct keyword *kw; + size_t length; JSAtom *atom; for (kw = keywords; kw->name; kw++) { - atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED); + length = strlen(kw->name); + JS_ASSERT(length <= MAX_KEYWORD_LENGTH); + atom = js_Atomize(cx, kw->name, length, ATOM_PINNED); if (!atom) return JS_FALSE; ATOM_SET_KEYWORD(atom, kw); @@ -209,6 +221,42 @@ js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, return ts; } +#define TBMIN 64 + +static JSBool +GrowTokenBuf(JSStringBuffer *sb, size_t newlength) +{ + JSContext *cx; + jschar *base; + ptrdiff_t offset, length; + size_t tbsize; + JSArenaPool *pool; + + cx = sb->data; + base = sb->base; + offset = PTRDIFF(sb->ptr, base, jschar); + pool = &cx->tempPool; + if (!base) { + tbsize = TBMIN * sizeof(jschar); + length = TBMIN - 1; + JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); + } else { + length = PTRDIFF(sb->limit, base, jschar); + tbsize = (length + 1) * sizeof(jschar); + length += length + 1; + JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); + } + if (!base) { + JS_ReportOutOfMemory(cx); + sb->base = STRING_BUFFER_ERROR_BASE; + return JS_FALSE; + } + sb->base = base; + sb->limit = base + length; + sb->ptr = base + offset; + return JS_TRUE; +} + JS_FRIEND_API(JSTokenStream *) js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) { @@ -227,6 +275,8 @@ js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) ts->userbuf.base = (jschar *)base; ts->userbuf.limit = (jschar *)base + length; ts->userbuf.ptr = (jschar *)base; + ts->tokenbuf.grow = GrowTokenBuf; + ts->tokenbuf.data = cx; ts->listener = cx->runtime->sourceHandler; ts->listenerData = cx->runtime->sourceHandlerData; return ts; @@ -272,8 +322,8 @@ js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) return !ts->file || fclose(ts->file) == 0; } -static int -my_fgets(char *buf, int size, FILE *file) +JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file) { int n, i, c; JSBool crflag; @@ -323,7 +373,7 @@ GetChar(JSTokenStream *ts) /* 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); + len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); if (len <= 0) { ts->flags |= TSF_EOF; return EOF; @@ -479,6 +529,11 @@ PeekChar(JSTokenStream *ts) return c; } +/* + * Peek n chars ahead into ts. Return true if n chars were read, false if + * there weren't enough characters in the input stream. This function cannot + * be used to peek into or past a newline. + */ static JSBool PeekChars(JSTokenStream *ts, intN n, jschar *cp) { @@ -489,6 +544,10 @@ PeekChars(JSTokenStream *ts, intN n, jschar *cp) c = GetChar(ts); if (c == EOF) break; + if (c == '\n') { + UngetChar(ts, c); + break; + } cp[i] = (jschar)c; } for (j = i - 1; j >= 0; j--) @@ -515,37 +574,52 @@ MatchChar(JSTokenStream *ts, int32 expect) return JS_FALSE; } -JSBool -js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, - JSCodeGenerator *cg, uintN flags, - const uintN errorNumber, ...) +static JSBool +ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, JSErrorReport *report, + JSBool charArgs, va_list ap) { - va_list ap; - JSErrorReporter onError; - JSErrorReport report; - jschar *tokenptr; JSString *linestr = NULL; + JSTokenStream *ts = NULL; + JSCodeGenerator *cg = NULL; +#if JS_HAS_XML_SUPPORT + JSParseNode *pn = NULL; +#endif + JSErrorReporter onError; + JSTokenPos *tp; + JSStackFrame *fp; + uintN index; 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; + 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)) { + errorNumber, &message, report, &warning, + charArgs, ap)) { return JS_FALSE; } - va_end(ap); js_AddRoot(cx, &linestr, "error line buffer"); + switch (flags & JSREPORT_HANDLE) { + case JSREPORT_TS: + ts = handle; + break; + case JSREPORT_CG: + cg = handle; + break; +#if JS_HAS_XML_SUPPORT + case JSREPORT_PN: + pn = handle; + ts = pn->pn_ts; + break; +#endif + } + JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); onError = cx->errorReporter; if (onError) { @@ -554,30 +628,70 @@ js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, * 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); - } + do { + if (ts) { + report->filename = ts->filename; +#if JS_HAS_XML_SUPPORT + if (pn) { + report->lineno = pn->pn_pos.begin.lineno; + if (report->lineno != ts->lineno) + break; + } +#endif + report->lineno = ts->lineno; + linestr = js_NewStringCopyN(cx, ts->linebuf.base, + PTRDIFF(ts->linebuf.limit, + ts->linebuf.base, + jschar), + 0); + report->linebuf = linestr + ? JS_GetStringBytes(linestr) + : NULL; + tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos; +#if JS_HAS_XML_SUPPORT + if (pn) + tp = &pn->pn_pos; +#endif + /* + * FIXME: What should instead happen here is that we should + * find error-tokens in userbuf, if !ts->file. That will + * allow us to deliver a more helpful error message, which + * includes all or part of the bad string or bad token. The + * code here yields something that looks truncated. + * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970 + */ + index = 0; + if (tp->begin.lineno == tp->end.lineno) { + if (tp->begin.index < ts->linepos) + break; + + index = tp->begin.index - ts->linepos; + } + + report->tokenptr = linestr ? report->linebuf + index : NULL; + report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; + report->uctokenptr = linestr ? report->uclinebuf + index : NULL; + break; + } + + if (cg) { + report->filename = cg->filename; + report->lineno = CG_CURRENT_LINE(cg); + break; + } + + /* + * 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; + } + } + } while (0); #if JS_HAS_ERROR_EXCEPTIONS /* @@ -598,54 +712,352 @@ js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, */ /* - * Only try to raise an exception if there isn't one already set - - * otherwise the exception will describe only the last compile error, + * Try to raise an exception only if there isn't one already set -- + * otherwise the exception will describe the last compile-time error, * which is likely spurious. */ - if (!(ts && (ts->flags & TSF_ERROR))) - if (js_ErrorToException(cx, message, &report)) + 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. + * Suppress any compile-time 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) + if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags)) onError = NULL; #endif - if (cx->runtime->debugErrorHook && onError) { + if (onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; - /* test local in case debugErrorHook changed on another thread */ - if (hook && !hook(cx, message, &report, + + /* + * If debugErrorHook is present then we give it a chance to veto + * sending the error on to the regular error reporter. + */ + if (hook && !hook(cx, message, report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) - (*onError)(cx, message, &report); + (*onError)(cx, message, report); } + if (message) JS_free(cx, message); + 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; +} + +JSBool +js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...) +{ + va_list ap; + JSErrorReport report; + JSBool warning; + + if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) + return JS_TRUE; + + va_start(ap, errorNumber); + warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, + &report, JS_TRUE, ap); + va_end(ap); + + /* + * We have to do this here because js_ReportCompileErrorNumberUC doesn't + * need to do this. + */ 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); + return warning; +} + +JSBool +js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...) +{ + va_list ap; + JSErrorReport report; + JSBool warning; + + if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) + return JS_TRUE; + + va_start(ap, errorNumber); + warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, + &report, JS_FALSE, ap); + va_end(ap); + + if (report.messageArgs) + JS_free(cx, (void *)report.messageArgs); - if (ts && !JSREPORT_IS_WARNING(flags)) { - /* Set the error flag to suppress spurious reports. */ - ts->flags |= TSF_ERROR; - } return warning; } +static JSBool +GrowStringBuffer(JSStringBuffer *sb, size_t newlength) +{ + ptrdiff_t offset; + jschar *bp; + + offset = PTRDIFF(sb->ptr, sb->base, jschar); + JS_ASSERT(offset >= 0); + newlength += offset + 1; + if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar)) + bp = realloc(sb->base, newlength * sizeof(jschar)); + else + bp = NULL; + if (!bp) { + free(sb->base); + sb->base = STRING_BUFFER_ERROR_BASE; + return JS_FALSE; + } + sb->base = bp; + sb->ptr = bp + offset; + sb->limit = bp + newlength - 1; + return JS_TRUE; +} + +static void +FreeStringBuffer(JSStringBuffer *sb) +{ + JS_ASSERT(STRING_BUFFER_OK(sb)); + if (sb->base) + free(sb->base); +} + +void +js_InitStringBuffer(JSStringBuffer *sb) +{ + sb->base = sb->limit = sb->ptr = NULL; + sb->data = NULL; + sb->grow = GrowStringBuffer; + sb->free = FreeStringBuffer; +} + +void +js_FinishStringBuffer(JSStringBuffer *sb) +{ + sb->free(sb); +} + +#define ENSURE_STRING_BUFFER(sb,n) \ + ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n)) + +static void +FastAppendChar(JSStringBuffer *sb, jschar c) +{ + if (!STRING_BUFFER_OK(sb)) + return; + if (!ENSURE_STRING_BUFFER(sb, 1)) + return; + *sb->ptr++ = c; +} + +void +js_AppendChar(JSStringBuffer *sb, jschar c) +{ + jschar *bp; + + if (!STRING_BUFFER_OK(sb)) + return; + if (!ENSURE_STRING_BUFFER(sb, 1)) + return; + bp = sb->ptr; + *bp++ = c; + *bp = 0; + sb->ptr = bp; +} + +#if JS_HAS_XML_SUPPORT + +void +js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count) +{ + jschar *bp; + + if (!STRING_BUFFER_OK(sb) || count == 0) + return; + if (!ENSURE_STRING_BUFFER(sb, count)) + return; + for (bp = sb->ptr; count; --count) + *bp++ = c; + *bp = 0; + sb->ptr = bp; +} + +void +js_AppendCString(JSStringBuffer *sb, const char *asciiz) +{ + size_t length; + jschar *bp; + + if (!STRING_BUFFER_OK(sb) || *asciiz == '\0') + return; + length = strlen(asciiz); + if (!ENSURE_STRING_BUFFER(sb, length)) + return; + for (bp = sb->ptr; length; --length) + *bp++ = (jschar) *asciiz++; + *bp = 0; + sb->ptr = bp; +} + +void +js_AppendJSString(JSStringBuffer *sb, JSString *str) +{ + size_t length; + jschar *bp; + + if (!STRING_BUFFER_OK(sb)) + return; + length = JSSTRING_LENGTH(str); + if (length == 0 || !ENSURE_STRING_BUFFER(sb, length)) + return; + bp = sb->ptr; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + *bp = 0; + sb->ptr = bp; +} + +static JSBool +GetXMLEntity(JSContext *cx, JSTokenStream *ts) +{ + ptrdiff_t offset, length, i; + int32 c, d; + JSBool ispair; + jschar *bp, digit; + char *bytes; + JSErrNum msg; + + /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */ + offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar); + FastAppendChar(&ts->tokenbuf, '&'); + while ((c = GetChar(ts)) != ';') { + if (c == EOF || c == '\n') { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_END_OF_XML_ENTITY); + return JS_FALSE; + } + FastAppendChar(&ts->tokenbuf, (jschar) c); + } + + /* Let length be the number of jschars after the '&', including the ';'. */ + length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset; + bp = ts->tokenbuf.base + offset; + c = d = 0; + ispair = JS_FALSE; + if (length > 2 && bp[1] == '#') { + /* Match a well-formed XML Character Reference. */ + i = 2; + if (length > 3 && JS_TOLOWER(bp[i]) == 'x') { + if (length > 9) /* at most 6 hex digits allowed */ + goto badncr; + while (++i < length) { + digit = bp[i]; + if (!JS7_ISHEX(digit)) + goto badncr; + c = (c << 4) + JS7_UNHEX(digit); + } + } else { + while (i < length) { + digit = bp[i++]; + if (!JS7_ISDEC(digit)) + goto badncr; + c = (c * 10) + JS7_UNDEC(digit); + if (c < 0) + goto badncr; + } + } + + if (0x10000 <= c && c <= 0x10FFFF) { + /* Form a surrogate pair (c, d) -- c is the high surrogate. */ + d = 0xDC00 + (c & 0x3FF); + c = 0xD7C0 + (c >> 10); + ispair = JS_TRUE; + } else { + /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */ + if (c != 0x9 && c != 0xA && c != 0xD && + !(0x20 <= c && c <= 0xD7FF) && + !(0xE000 <= c && c <= 0xFFFD)) { + goto badncr; + } + } + } else { + /* Try to match one of the five XML 1.0 predefined entities. */ + switch (length) { + case 3: + if (bp[2] == 't') { + if (bp[1] == 'l') + c = '<'; + else if (bp[1] == 'g') + c = '>'; + } + break; + case 4: + if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p') + c = '&'; + break; + case 5: + if (bp[3] == 'o') { + if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's') + c = '\''; + else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't') + c = '"'; + } + break; + } + if (c == 0) { + msg = JSMSG_UNKNOWN_XML_ENTITY; + goto bad; + } + } + + /* If we matched, retract ts->tokenbuf and store the entity's value. */ + *bp++ = (jschar) c; + if (ispair) + *bp++ = (jschar) d; + *bp = 0; + ts->tokenbuf.ptr = bp; + return JS_TRUE; + +badncr: + msg = JSMSG_BAD_XML_NCR; +bad: + /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */ + bytes = js_DeflateString(cx, bp + 1, + PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1); + if (bytes) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + msg, bytes); + JS_free(cx, bytes); + } + return JS_FALSE; +} + +#endif /* JS_HAS_XML_SUPPORT */ + JSTokenType js_PeekToken(JSContext *cx, JSTokenStream *ts) { @@ -666,55 +1078,15 @@ js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) JSTokenType tt; JS_ASSERT(ts->lookahead == 0 || - ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)); + ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos) || + ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type + == TOK_EOL); 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. @@ -741,14 +1113,16 @@ GetUnicodeEscape(JSTokenStream *ts) } static JSToken * -NewToken(JSTokenStream *ts) +NewToken(JSTokenStream *ts, ptrdiff_t adjust) { 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->ptr = ts->linebuf.ptr + adjust; + tp->pos.begin.index = ts->linepos + + PTRDIFF(tp->ptr, ts->linebuf.base, jschar) - + ts->ungetpos; tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno; return tp; } @@ -763,20 +1137,21 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) 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_LENGTH() PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) +#define TOKENBUF_OK() STRING_BUFFER_OK(&ts->tokenbuf) +#define TOKENBUF_TO_ATOM() (TOKENBUF_OK() \ + ? js_AtomizeChars(cx, \ + TOKENBUF_BASE(), \ + TOKENBUF_LENGTH(), \ + 0) \ + : NULL) +#define ADD_TO_TOKENBUF(c) FastAppendChar(&ts->tokenbuf, (jschar) (c)) + +/* The following 4 macros should only be used when TOKENBUF_OK() is true. */ #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 +#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i) +#define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0) /* If there was a fatal error, keep returning TOK_ERROR. */ if (ts->flags & TSF_ERROR) @@ -784,6 +1159,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) /* Check for a pushed-back token resulting from mismatching lookahead. */ while (ts->lookahead != 0) { + JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); ts->lookahead--; ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tt = CURRENT_TOKEN(ts).type; @@ -791,6 +1167,163 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) return tt; } +#if JS_HAS_XML_SUPPORT + if (ts->flags & TSF_XMLTEXTMODE) { + tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */ + tp = NewToken(ts, 0); + INIT_TOKENBUF(); + qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{'; + + while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) { + if (c == '&' && qc == '<') { + if (!GetXMLEntity(cx, ts)) + goto error; + tt = TOK_XMLTEXT; + continue; + } + + if (!JS_ISXMLSPACE(c)) + tt = TOK_XMLTEXT; + ADD_TO_TOKENBUF(c); + } + UngetChar(ts, c); + + if (TOKENBUF_LENGTH() == 0) { + atom = NULL; + } else { + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + } + tp->pos.end.lineno = (uint16)ts->lineno; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + goto out; + } + + if (ts->flags & TSF_XMLTAGMODE) { + tp = NewToken(ts, 0); + c = GetChar(ts); + if (JS_ISXMLSPACE(c)) { + do { + c = GetChar(ts); + } while (JS_ISXMLSPACE(c)); + UngetChar(ts, c); + tt = TOK_XMLSPACE; + goto out; + } + + if (c == EOF) { + tt = TOK_EOF; + goto out; + } + + INIT_TOKENBUF(); + if (JS_ISXMLNSSTART(c)) { + JSBool sawColon = JS_FALSE; + + ADD_TO_TOKENBUF(c); + while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) { + if (c == ':') { + int nextc; + + if (sawColon || + (nextc = PeekChar(ts), + ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') && + !JS_ISXMLNAME(nextc))) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_ERROR, + JSMSG_BAD_XML_QNAME); + goto error; + } + sawColon = JS_TRUE; + } + + ADD_TO_TOKENBUF(c); + } + + UngetChar(ts, c); + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + tt = TOK_XMLNAME; + goto out; + } + + switch (c) { + case '{': + if (ts->flags & TSF_XMLONLYMODE) + goto bad_xml_char; + tt = TOK_LC; + goto out; + + case '=': + tt = TOK_ASSIGN; + goto out; + + case '"': + case '\'': + qc = c; + while ((c = GetChar(ts)) != qc) { + if (c == EOF) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_UNTERMINATED_STRING); + goto error; + } + + /* + * XML attribute values are double-quoted when pretty-printed, + * so escape " if it is expressed directly in a single-quoted + * attribute value. + */ + if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) { + JS_ASSERT(qc == '\''); + js_AppendCString(&ts->tokenbuf, js_quot_entity_str); + continue; + } + + if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) { + if (!GetXMLEntity(cx, ts)) + goto error; + continue; + } + + 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_XMLATTR; + goto out; + + case '>': + tt = TOK_XMLTAGC; + goto out; + + case '/': + if (MatchChar(ts, '>')) { + tt = TOK_XMLPTAGC; + goto out; + } + /* FALL THROUGH */ + + bad_xml_char: + default: + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_XML_CHARACTER); + goto error; + } + /* NOTREACHED */ + } +#endif /* JS_HAS_XML_SUPPORT */ + retry: do { c = GetChar(ts); @@ -801,20 +1334,17 @@ retry: } } while (JS_ISSPACE(c)); - tp = NewToken(ts); + tp = NewToken(ts, -1); if (c == EOF) { tt = TOK_EOF; goto out; } - if (c != '-' && c != '\n') - ts->flags |= TSF_DIRTYLINE; - hadUnicodeEscape = JS_FALSE; - if (JS_ISIDENT_START(c) || + if (JS_ISIDSTART(c) || (c == '\\' && (c = GetUnicodeEscape(ts), - hadUnicodeEscape = JS_ISIDENT_START(c)))) { + hadUnicodeEscape = JS_ISIDSTART(c)))) { INIT_TOKENBUF(); for (;;) { ADD_TO_TOKENBUF(c); @@ -835,11 +1365,28 @@ retry: 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; + struct keyword *kw; + + JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); + kw = ATOM_KEYWORD(atom); + if (kw->tokentype == TOK_RESERVED) { + char buf[MAX_KEYWORD_LENGTH + 1]; + size_t buflen = sizeof(buf) - 1; + if (!js_DeflateStringToBuffer(cx, TOKENBUF_BASE(), TOKENBUF_LENGTH(), + buf, &buflen)) + goto error; + buf [buflen] = 0; + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_RESERVED_ID, buf)) { + goto error; + } + } else if (JS_VERSION_IS_ECMA(cx) || + kw->version <= (cx->version & JSVERSION_MASK)) { tt = kw->tokentype; + tp->t_op = (JSOp) kw->op; goto out; } } @@ -880,7 +1427,8 @@ retry: * not always be so permissive, so we warn about it. */ if (radix == 8 && c >= '8') { - if (!js_ReportCompileErrorNumber(cx, ts, NULL, + if (!js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_WARNING, JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) { @@ -908,7 +1456,8 @@ retry: c = GetChar(ts); } if (!JS7_ISDEC(c)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_MISSING_EXPONENT); goto error; } @@ -923,15 +1472,19 @@ retry: UngetChar(ts, c); ADD_TO_TOKENBUF(0); + if (!TOKENBUF_OK()) + goto error; if (radix == 10) { if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) { - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | 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, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_OUT_OF_MEMORY); goto error; } @@ -947,7 +1500,8 @@ retry: while ((c = GetChar(ts)) != qc) { if (c == '\n' || c == EOF) { UngetChar(ts, c); - js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNTERMINATED_STRING); goto error; } @@ -998,7 +1552,7 @@ retry: c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); SkipChars(ts, 2); } - } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) { + } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) { /* ECMA follows C by removing escaped newlines. */ continue; } @@ -1018,25 +1572,33 @@ retry: } 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 '\n': tt = TOK_EOL; goto eol_out; + 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; +#if JS_HAS_XML_SUPPORT + if (MatchChar(ts, c)) + tt = TOK_DBLDOT; + else +#endif + tt = TOK_DOT; break; case ':': +#if JS_HAS_XML_SUPPORT + if (MatchChar(ts, c)) { + tt = TOK_DBLCOLON; + break; + } +#endif /* * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an * object initializer, likewise for setter. @@ -1104,12 +1666,156 @@ retry: } break; +#if JS_HAS_XML_SUPPORT + case '@': + tt = TOK_AT; + break; +#endif + case '<': +#if JS_HAS_XML_SUPPORT + /* + * After much testing, it's clear that Postel's advice to protocol + * designers ("be liberal in what you accept, and conservative in what + * you send") invites a natural-law repercussion for JS as "protocol": + * + * "If you are liberal in what you accept, others will utterly fail to + * be conservative in what they send." + * + * Which means you will get within every //-style comment unless we have to. So we set + * TSF_IN_HTML_COMMENT when a either on a clean line, or + * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment. + * + * This still works as before given a malformed comment hiding hack such as: + * + * + * + * It does not cope with malformed comment hiding hacks where --> is hidden + * by C-style comments, or on a dirty line. Such cases are already broken. + */ +#define TSF_IN_HTML_COMMENT 0x2000 /* Unicode separators that are treated as line terminators, in addition to \n, \r */ #define LINE_SEPARATOR 0x2028 @@ -213,6 +297,9 @@ js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); extern JS_FRIEND_API(JSBool) js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); +extern JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file); + /* * Initialize the scanner, installing JS keywords into cx's global scope. */ @@ -231,9 +318,18 @@ js_MapKeywords(void (*mapfun)(const char *)); * Return true for a warning, false for an error. */ extern JSBool -js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, - JSCodeGenerator *cg, uintN flags, - const uintN errorNumber, ...); +js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +extern JSBool +js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +/* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ +#define JSREPORT_HANDLE 0x300 +#define JSREPORT_TS 0x000 +#define JSREPORT_CG 0x100 +#define JSREPORT_PN 0x200 /* * Look ahead one token and return its type. diff --git a/src/dom/js/jsscope.c b/src/dom/js/jsscope.c index e3603b18f..4f9512387 100644 --- a/src/dom/js/jsscope.c +++ b/src/dom/js/jsscope.c @@ -98,7 +98,7 @@ InitMinimalScope(JSScope *scope) } static JSBool -CreateScopeTable(JSScope *scope) +CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) { int sizeLog2; JSScopeProperty *sprop, **spp; @@ -120,8 +120,14 @@ CreateScopeTable(JSScope *scope) scope->table = (JSScopeProperty **) calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); - if (!scope->table) + if (!scope->table) { + if (report) + JS_ReportOutOfMemory(cx); return JS_FALSE; + } + + /* Racy update after calloc, to help keep the GC self-scheduled well. */ + cx->runtime->gcMallocBytes += JS_BIT(sizeLog2) * sizeof(JSScopeProperty *); scope->hashShift = JS_DHASH_BITS - sizeLog2; for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { @@ -144,6 +150,7 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, js_InitObjectMap(&scope->map, nrefs, ops, clasp); scope->object = obj; scope->flags = 0; + scope->dswIndex = 0; InitMinimalScope(scope); #ifdef JS_THREADSAFE @@ -347,6 +354,9 @@ ChangeScope(JSContext *cx, JSScope *scope, int change) oldtable = scope->table; scope->table = table; + /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ + cx->runtime->gcMallocBytes += nbytes; + /* Copy only live entries, leaving removed and free ones behind. */ for (oldspp = oldtable; oldsize != 0; oldspp++) { sprop = SPROP_FETCH(oldspp); @@ -528,7 +538,7 @@ DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) /* NB: Called with the runtime lock held. */ static JSBool InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, - JSScopeProperty *child) + JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) { JSPropertyTreeEntry *entry; JSScopeProperty **childp, *kids, *sprop; @@ -594,9 +604,13 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, chunkp = &chunk->next; } while ((chunk = *chunkp) != NULL); - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + } *chunkp = chunk; childp = &chunk->kids[0]; } else { @@ -776,7 +790,7 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, if (!parent) { entry->child = sprop; } else { - if (!InsertPropertyTreeChild(rt, parent, sprop)) + if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) goto out_of_memory; } @@ -967,10 +981,8 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, * delete code is simple-minded that way! */ if (!scope->table) { - if (!CreateScopeTable(scope)) { - JS_ReportOutOfMemory(cx); + if (!CreateScopeTable(cx, scope, JS_TRUE)) return NULL; - } spp = js_SearchScope(scope, id, JS_TRUE); sprop = overwriting = SPROP_FETCH(spp); } @@ -1166,7 +1178,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, * entry count just reached the threshold. */ if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) - (void) CreateScopeTable(scope); + (void) CreateScopeTable(cx, scope, JS_FALSE); } METER(adds); @@ -1308,10 +1320,8 @@ js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) /* 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); + if (!CreateScopeTable(cx, scope, JS_TRUE)) return JS_FALSE; - } spp = js_SearchScope(scope, id, JS_FALSE); stored = *spp; sprop = SPROP_CLEAR_COLLISION(stored); @@ -1445,10 +1455,12 @@ DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) 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)), + JSID_IS_ATOM(sprop->id) + ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) + : JSID_IS_OBJECT(sprop->id) + ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(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); @@ -1546,27 +1558,78 @@ js_SweepScopeProperties(JSRuntime *rt) /* 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. */ + /* + * Take care to reparent all sprop's kids to their grandparent. + * InsertPropertyTreeChild can potentially fail for two reasons: + * + * 1. If parent is null, insertion into the root property hash + * table may fail. We are forced to leave the kid out of the + * table (as can already happen with duplicates) but ensure + * that the kid's parent pointer is set to null. + * + * 2. If parent is non-null, allocation of a new KidsChunk can + * fail. To prevent this from happening, we allow sprops's own + * chunks to be reused by the grandparent, which removes the + * need for InsertPropertyTreeChild to malloc a new KidsChunk. + * + * We also require the grandparent to have either no kids or else + * chunky kids. A single non-chunky kid would force a new chunk to + * be malloced in some cases (if sprop had a single non-chunky + * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that + * RemovePropertyTreeChild never converts a single entry chunky + * kid back to a non-chunky kid, so we are assured of correct + * behaviour. + */ kids = sprop->kids; if (kids) { sprop->kids = NULL; parent = sprop->parent; + /* Validate that grandparent has no kids or chunky kids. */ + JS_ASSERT(!parent || !parent->kids || + KIDS_IS_CHUNKY(parent->kids)); if (KIDS_IS_CHUNKY(kids)) { chunk = KIDS_TO_CHUNK(kids); do { + nextChunk = chunk->next; + chunk->next = NULL; for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { kid = chunk->kids[i]; if (!kid) break; JS_ASSERT(kid->parent == sprop); - InsertPropertyTreeChild(rt, parent, kid); + + /* + * Clear a space in the kids array for possible + * re-use by InsertPropertyTreeChild. + */ + chunk->kids[i] = NULL; + if (!InsertPropertyTreeChild(rt, parent, kid, + chunk)) { + /* + * This can happen only if we failed to add an + * entry to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + if (!chunk->kids[0]) { + /* The chunk wasn't reused so we can free it */ + DestroyPropTreeKidsChunk(rt, chunk); } - nextChunk = chunk->next; - DestroyPropTreeKidsChunk(rt, chunk); } while ((chunk = nextChunk) != NULL); } else { kid = kids; - InsertPropertyTreeChild(rt, parent, kid); + if (!InsertPropertyTreeChild(rt, parent, kid, NULL)) { + /* + * The removal of sprop should have left a free space + * for kid to be inserted into parent, unless the root + * hash table was shrunk. In this case we allow for + * failure only when parent is null. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } } } diff --git a/src/dom/js/jsscope.h b/src/dom/js/jsscope.h index 8ac7ef073..0f8a837c0 100644 --- a/src/dom/js/jsscope.h +++ b/src/dom/js/jsscope.h @@ -99,7 +99,7 @@ * 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 @@ -201,8 +201,9 @@ struct JSScope { JSObjectMap map; /* base class state */ JSObject *object; /* object that owns this scope */ - uint16 flags; /* flags, see below */ - int16 hashShift; /* multiplicative hash shift */ + uint8 flags; /* flags, see below */ + int8 hashShift; /* multiplicative hash shift */ + uint16 dswIndex; /* Deutsch-Schorr-Waite scaled index */ uint32 entryCount; /* number of entries in table */ uint32 removedCount; /* removed entry sentinels in table */ JSScopeProperty **table; /* table of ptrs to shared tree nodes */ @@ -290,6 +291,8 @@ struct JSScopeProperty { #define SPROP_IS_DUPLICATE 0x02 #define SPROP_IS_ALIAS 0x04 #define SPROP_HAS_SHORTID 0x08 +#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, + e.g., function arg or var */ /* * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather @@ -345,10 +348,12 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, 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) +#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ + JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ + (jsval)(id)) +#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ + JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ + (jsatomid) JSID_TO_INT(id)) extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/src/dom/js/jsscript.c b/src/dom/js/jsscript.c index 7e5aefc30..6fc92de93 100644 --- a/src/dom/js/jsscript.c +++ b/src/dom/js/jsscript.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -63,6 +64,9 @@ #if JS_HAS_SCRIPT_OBJECT +static const char js_script_exec[] = "Script.prototype.exec"; +static const char js_script_compile[] = "Script.prototype.compile"; + #if JS_HAS_TOSOURCE static JSBool script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -157,8 +161,8 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSScript *oldscript, *script; - JSString *str; JSStackFrame *fp, *caller; + JSString *str; JSObject *scopeobj; const char *file; uintN line; @@ -172,10 +176,23 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (argc == 0) goto out; + /* XXX thread safety was completely neglected in this function... */ + oldscript = (JSScript *) JS_GetPrivate(cx, obj); + if (oldscript) { + for (fp = cx->fp; fp; fp = fp->down) { + if (fp->script == oldscript) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_SELF_MODIFYING_SCRIPT); + return JS_FALSE; + } + } + } + /* Otherwise, the first arg is the script source to compile. */ str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ fp = cx->fp; @@ -201,6 +218,11 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, principals = NULL; } + /* Ensure we compile this script with the right (inner) principals. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); + if (!scopeobj) + return JS_FALSE; + /* * Compile the new script using the caller's scope chain, a la eval(). * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's @@ -209,6 +231,7 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * tested in jsemit.c and jsscan.c to optimize based on identity of run- * and compile-time scope. */ + fp->flags |= JSFRAME_SCRIPT_OBJECT; script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), @@ -217,7 +240,6 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 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; @@ -238,6 +260,7 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSScript *script; JSObject *scopeobj, *parent; JSStackFrame *fp, *caller; + JSPrincipals *principals; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; @@ -297,6 +320,15 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } } + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); + if (!scopeobj) + return JS_FALSE; + + /* Belt-and-braces: check that this script object has access to scopeobj. */ + principals = script->principals; + if (!js_CheckPrincipalsAccess(cx, scopeobj, principals, js_script_exec)) + return JS_FALSE; + return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); } @@ -704,6 +736,7 @@ script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); /* create new XDR */ xdr = JS_XDRNewMem(cx, JSXDR_DECODE); @@ -839,6 +872,12 @@ Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); if (!obj) return JS_FALSE; + + /* + * script_compile does not use rval to root its temporaries + * so we can use it to root obj. + */ + *rval = OBJECT_TO_JSVAL(obj); } return script_compile(cx, obj, argc, argv, rval); } @@ -899,7 +938,8 @@ 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 */ + uint32 flags; /* user-defined filename prefix flags */ + JSPackedBool mark; /* GC mark flag */ char filename[3]; /* two or more bytes, NUL-terminated */ } ScriptFilenameEntry; @@ -919,45 +959,42 @@ js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) free(he); } -static JSHashAllocOps table_alloc_ops = { +static JSHashAllocOps sftbl_alloc_ops = { js_alloc_table_space, js_free_table_space, js_alloc_sftbl_entry, js_free_sftbl_entry }; JSBool -js_InitRuntimeScriptState(JSContext *cx) +js_InitRuntimeScriptState(JSRuntime *rt) { - 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; - } + JS_ASSERT(!rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; #endif + JS_ASSERT(!rt->scriptFilenameTable); + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &sftbl_alloc_ops, NULL); if (!rt->scriptFilenameTable) { - 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; - } + js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ + return JS_FALSE; } + JS_INIT_CLIST(&rt->scriptFilenamePrefixes); return JS_TRUE; } +typedef struct ScriptFilenamePrefix { + JSCList links; /* circular list linkage for easy deletion */ + const char *name; /* pointer to pinned ScriptFilenameEntry string */ + size_t length; /* prefix string length, precomputed */ + uint32 flags; /* user-defined flags to inherit from this prefix */ +} ScriptFilenamePrefix; + void -js_FinishRuntimeScriptState(JSContext *cx) +js_FinishRuntimeScriptState(JSRuntime *rt) { - JSRuntime *rt = cx->runtime; - if (rt->scriptFilenameTable) { JS_HashTableDestroy(rt->scriptFilenameTable); rt->scriptFilenameTable = NULL; @@ -970,20 +1007,34 @@ js_FinishRuntimeScriptState(JSContext *cx) #endif } +void +js_FreeRuntimeScriptState(JSRuntime *rt) +{ + ScriptFilenamePrefix *sfp; + + while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { + sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; + JS_REMOVE_LINK(&sfp->links); + free(sfp); + } + js_FinishRuntimeScriptState(rt); +} + #ifdef DEBUG_brendan size_t sftbl_savings = 0; #endif -const char * -js_SaveScriptFilename(JSContext *cx, const char *filename) +static ScriptFilenameEntry * +SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) { - JSRuntime *rt = cx->runtime; JSHashTable *table; JSHashNumber hash; JSHashEntry **hep; ScriptFilenameEntry *sfe; + size_t length; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); table = rt->scriptFilenameTable; hash = JS_HashString(filename); hep = JS_HashTableRawLookup(table, hash, filename); @@ -992,40 +1043,174 @@ js_SaveScriptFilename(JSContext *cx, const char *filename) 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); + if (!sfe) + return NULL; + sfe->key = strcpy(sfe->filename, filename); + sfe->flags = 0; + sfe->mark = JS_FALSE; + } + + /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ + if (flags != 0) { + /* Search in case filename was saved already; we must be idempotent. */ + sfp = NULL; + length = strlen(filename); + for (head = link = &rt->scriptFilenamePrefixes; + link->next != head; + link = link->next) { + /* Lag link behind sfp to insert in non-increasing length order. */ + sfp = (ScriptFilenamePrefix *) link->next; + if (!strcmp(sfp->name, filename)) + break; + if (sfp->length <= length) { + sfp = NULL; + break; + } + sfp = NULL; } + + if (!sfp) { + /* No such prefix: add one now. */ + sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); + if (!sfp) + return NULL; + JS_INSERT_AFTER(&sfp->links, link); + sfp->name = sfe->filename; + sfp->length = length; + sfp->flags = 0; + } + + /* + * Accumulate flags in both sfe and sfp: sfe for later access from the + * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer + * filename entries can inherit by prefix. + */ + sfe->flags |= flags; + sfp->flags |= flags; } - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + + return sfe; +} + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSRuntime *rt; + ScriptFilenameEntry *sfe; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + rt = cx->runtime; + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, 0); if (!sfe) { + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); JS_ReportOutOfMemory(cx); return NULL; } + + /* + * Try to inherit flags by prefix. We assume there won't be more than a + * few (dozen! ;-) prefixes, so linear search is tolerable. + * XXXbe every time I've assumed that in the JS engine, I've been wrong! + */ + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + if (!strncmp(sfp->name, filename, sfp->length)) { + sfe->flags |= sfp->flags; + break; + } + } + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); return sfe->filename; } +const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) +{ + ScriptFilenameEntry *sfe; + + /* This may be called very early, via the jsdbgapi.h entry point. */ + if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) + return NULL; + + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, flags); + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + if (!sfe) + return NULL; + + return sfe->filename; +} + +/* + * Back up from a saved filename by its offset within its hash table entry. + */ +#define FILENAME_TO_SFE(fn) \ + ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) + +/* + * The sfe->key member, redundant given sfe->filename but required by the old + * jshash.c code, here gives us a useful sanity check. This assertion will + * very likely botch if someone tries to mark a string that wasn't allocated + * as an sfe->filename. + */ +#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) + +uint32 +js_GetScriptFilenameFlags(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + return sfe->flags; +} + void js_MarkScriptFilename(const char *filename) { ScriptFilenameEntry *sfe; - /* - * 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 = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); sfe->mark = JS_TRUE; } +JS_STATIC_DLL_CALLBACK(intN) +js_script_filename_marker(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + sfe->mark = JS_TRUE; + return HT_ENUMERATE_NEXT; +} + +void +js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags) +{ + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + if (gcflags & GC_KEEP_ATOMS) { + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_marker, + rt); + } + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + js_MarkScriptFilename(sfp->name); + } +} + JS_STATIC_DLL_CALLBACK(intN) js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) { @@ -1043,7 +1228,7 @@ js_SweepScriptFilenames(JSRuntime *rt) JS_HashTableEnumerateEntries(rt->scriptFilenameTable, js_script_filename_sweeper, rt); -#ifdef DEBUG_brendan +#ifdef DEBUG_notme printf("script filename table savings so far: %u\n", sftbl_savings); #endif } @@ -1142,8 +1327,8 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) } } -void -js_DestroyScript(JSContext *cx, JSScript *script) +JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script) { JSRuntime *rt; JSDestroyScriptHook hook; @@ -1152,6 +1337,12 @@ js_DestroyScript(JSContext *cx, JSScript *script) hook = rt->destroyScriptHook; if (hook) hook(cx, script, rt->destroyScriptHookData); +} + +void +js_DestroyScript(JSContext *cx, JSScript *script) +{ + js_CallDestroyScriptHook(cx, script); JS_ClearScriptTraps(cx, script); js_FreeAtomMap(cx, &script->atomMap); @@ -1240,19 +1431,31 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) return lineno; } +/* The line number limit is the same as the jssrcnote offset limit. */ +#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) + jsbytecode * js_LineNumberToPC(JSScript *script, uintN target) { - ptrdiff_t offset; - uintN lineno; + ptrdiff_t offset, best; + uintN lineno, bestdiff, diff; jssrcnote *sn; JSSrcNoteType type; offset = 0; + best = -1; lineno = script->lineno; + bestdiff = SN_LINE_LIMIT; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - if (lineno >= target) - break; + if (lineno == target) + goto out; + if (lineno > target) { + diff = lineno - target; + if (diff < bestdiff) { + bestdiff = diff; + best = offset; + } + } offset += SN_DELTA(sn); type = (JSSrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { @@ -1261,10 +1464,13 @@ js_LineNumberToPC(JSScript *script, uintN target) lineno++; } } + if (best >= 0) + offset = best; +out: return script->code + offset; } -uintN +JS_FRIEND_API(uintN) js_GetScriptLineExtent(JSScript *script) { uintN lineno; diff --git a/src/dom/js/jsscript.h b/src/dom/js/jsscript.h index 6284917af..77f59ed75 100644 --- a/src/dom/js/jsscript.h +++ b/src/dom/js/jsscript.h @@ -50,8 +50,8 @@ 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 + * All fields except length are code offsets relative to the main entry point + * 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 { @@ -102,18 +102,45 @@ extern JS_FRIEND_DATA(JSClass) js_ScriptClass; extern JSObject * js_InitScriptClass(JSContext *cx, JSObject *obj); +/* + * On first new context in rt, initialize script runtime state, specifically + * the script filename table and its lock. + */ extern JSBool -js_InitRuntimeScriptState(JSContext *cx); +js_InitRuntimeScriptState(JSRuntime *rt); +/* + * On last context destroy for rt, if script filenames are all GC'd, free the + * script filename table and its lock. + */ +extern void +js_FinishRuntimeScriptState(JSRuntime *rt); + +/* + * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any + * script filename table entries that have not been GC'd, the latter using + * js_FinishRuntimeScriptState. + * + * This allows script filename prefixes to outlive any context in rt. + */ extern void -js_FinishRuntimeScriptState(JSContext *cx); +js_FreeRuntimeScriptState(JSRuntime *rt); extern const char * js_SaveScriptFilename(JSContext *cx, const char *filename); +extern const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); + +extern uint32 +js_GetScriptFilenameFlags(const char *filename); + extern void js_MarkScriptFilename(const char *filename); +extern void +js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags); + extern void js_SweepScriptFilenames(JSRuntime *rt); @@ -143,6 +170,9 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); extern JS_FRIEND_API(void) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); +extern JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script); + extern void js_DestroyScript(JSContext *cx, JSScript *script); @@ -159,7 +189,7 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern jsbytecode * js_LineNumberToPC(JSScript *script, uintN lineno); -extern uintN +extern JS_FRIEND_API(uintN) js_GetScriptLineExtent(JSScript *script); /* diff --git a/src/dom/js/jsstddef.h b/src/dom/js/jsstddef.h index 0d87b0c0c..addaa88ff 100644 --- a/src/dom/js/jsstddef.h +++ b/src/dom/js/jsstddef.h @@ -74,7 +74,7 @@ typedef long ptrdiff_t; #else /*WIN16*/ #define PTRDIFF(p1, p2, type) \ - ((p1) - (p2)) + ((p1) - (p2)) #endif diff --git a/src/dom/js/jsstr.c b/src/dom/js/jsstr.c index e143ab8df..5e0bafd3b 100644 --- a/src/dom/js/jsstr.c +++ b/src/dom/js/jsstr.c @@ -1,4 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -167,7 +168,7 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) 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) @@ -266,9 +267,6 @@ 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); @@ -360,6 +358,20 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval } else { newlength += 5; /* The character will be encoded as %uXXXX */ } + + /* + * This overflow test works because newlength is incremented by at + * most 5 on each iteration. + */ + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + + if (newlength >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; } newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); @@ -512,9 +524,16 @@ str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_INT(id)) return JS_TRUE; + + /* + * Call js_ValueToString because getters and setters can be invoked on + * objects of different class, unlike enumerate, resolve, and the other + * class hooks. + */ 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)); @@ -529,15 +548,21 @@ str_enumerate(JSContext *cx, JSObject *obj) JSString *str, *str1; size_t i, length; + /* Avoid infinite recursion via js_obj_toSource (see bug 271477). */ + if (JS_VERSION_IS_1_2(cx)) + return JS_TRUE; + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) - return JS_FALSE; + return JS_TRUE; + cx->newborn[GCX_STRING] = (JSGCThing *) str; + 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), + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; @@ -547,36 +572,40 @@ str_enumerate(JSContext *cx, JSObject *obj) } static JSBool -str_resolve(JSContext *cx, JSObject *obj, jsval id) +str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) { JSString *str, *str1; jsint slot; - if (!JSVAL_IS_INT(id)) + if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) return JS_TRUE; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) - return JS_FALSE; + return JS_TRUE; + cx->newborn[GCX_STRING] = (JSGCThing *) str; + 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), + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; } + *objp = obj; } return JS_TRUE; } -static JSClass string_class = { +JSClass js_StringClass = { js_String_str, - JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, - str_enumerate, str_resolve, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, + str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; @@ -594,6 +623,8 @@ str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + str = js_QuoteString(cx, str, '"'); if (!str) return JS_FALSE; @@ -610,7 +641,7 @@ str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[16]; jschar *s, *t; - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) @@ -618,7 +649,7 @@ str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *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); + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); s = JSSTRING_CHARS(str); k = JSSTRING_LENGTH(str); n = j + k + 2; @@ -648,7 +679,7 @@ str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) @@ -660,7 +691,7 @@ str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; @@ -703,7 +734,7 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, else if (end > length) end = length; if (end < begin) { - if (cx->version != JSVERSION_1_2) { + if (!JS_VERSION_IS_1_2(cx)) { /* XXX emulate old JDK1.0 java.lang.String.substring. */ jsdouble tmp = begin; begin = end; @@ -734,6 +765,8 @@ str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + n = JSSTRING_LENGTH(str); news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!news) @@ -765,6 +798,7 @@ str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); return cx->localeCallbacks->localeToLowerCase(cx, str, rval); } return str_toLowerCase(cx, obj, 0, argv, rval); @@ -781,6 +815,8 @@ str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + n = JSSTRING_LENGTH(str); news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!news) @@ -812,6 +848,7 @@ str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); return cx->localeCallbacks->localeToUpperCase(cx, str, rval); } return str_toUpperCase(cx, obj, 0, argv, rval); @@ -834,8 +871,10 @@ str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, thatStr = js_ValueToString(cx, argv[0]); if (!thatStr) return JS_FALSE; - if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { + argv[0] = STRING_TO_JSVAL(thatStr); return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); + } *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); } return JS_TRUE; @@ -1033,8 +1072,8 @@ str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, d = js_DoubleToInteger(d); if (d < 0) i = 0; - else if (d > textlen - patlen) - i = textlen - patlen; + else if (d > textlen) + i = textlen; else i = (jsint)d; } @@ -1126,7 +1165,9 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; reobj = NULL; } + /* From here on, all control flow must reach the matching DROP. */ data->regexp = re; + HOLD_REGEXP(cx, re); if (re->flags & JSREG_GLOB) data->flags |= GLOBAL_REGEXP; @@ -1142,23 +1183,23 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 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) + if (ok) { + 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; - index++; + ok = glob(cx, count, data); + if (!ok) + break; + if (cx->regExpStatics.lastMatch.length == 0) { + if (index == length) + break; + index++; + } } } } else { @@ -1171,25 +1212,35 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * 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; + JSStackFrame *fp = cx->fp->down; + + /* Skip Function.prototype.call and .apply frames. */ + while (fp && !fp->pc) { + JS_ASSERT(!fp->script); + fp = fp->down; + } + + /* Assume a full array result is required, then prove otherwise. */ + test = JS_FALSE; + if (fp) { + JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); + JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); + switch (fp->pc[3]) { + case JSOP_POP: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_IFEQX: + case JSOP_IFNEX: + test = JS_TRUE; + break; + default:; + } } } ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); } + DROP_REGEXP(cx, re); if (reobj) { /* Tell our caller that it doesn't need to destroy data->regexp. */ data->flags &= ~KEEP_REGEXP; @@ -1198,6 +1249,7 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, data->regexp = NULL; js_DestroyRegExp(cx, re); } + return ok; } @@ -1228,7 +1280,7 @@ match_glob(JSContext *cx, jsint count, GlobData *data) if (!matchstr) return JS_FALSE; v = STRING_TO_JSVAL(matchstr); - return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v); + return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); } static JSBool @@ -1271,8 +1323,10 @@ typedef struct ReplaceData { } ReplaceData; static JSSubString * -interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) +interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, + size_t *skip) { + JSVersion version; JSRegExpStatics *res; jschar dc, *cp; uintN num, tmp; @@ -1284,23 +1338,28 @@ interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) * 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) { + version = cx->version & JSVERSION_MASK; + if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) { if (dp > JSSTRING_CHARS(rdata->repstr) && dp[-1] == '\\') return NULL; } + /* If there is only a dollar, bail now */ + if (dp + 1 >= ep) + return NULL; + /* Interpret all Perl match-induced dollar variables. */ res = &cx->regExpStatics; dc = dp[1]; if (JS7_ISDEC(dc)) { - if (cx->version != JSVERSION_DEFAULT && cx->version <= JSVERSION_1_4) { + if (version != JSVERSION_DEFAULT && 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)) { + while (++cp < ep && (dc = *cp, JS7_ISDEC(dc))) { tmp = 10 * num + JS7_UNDEC(dc); if (tmp < num) break; @@ -1310,9 +1369,9 @@ interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) num = JS7_UNDEC(dc); if (num > res->parenCount) return NULL; + cp = dp + 2; - dc = *cp; - if ((dc != 0) && JS7_ISDEC(dc)) { + if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { tmp = 10 * num + JS7_UNDEC(dc); if (tmp <= res->parenCount) { cp++; @@ -1339,7 +1398,7 @@ interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip) case '+': return &res->lastParen; case '`': - if (cx->version == JSVERSION_1_2) { + if (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, @@ -1378,11 +1437,13 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) 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. + * Save the regExpStatics from the current regexp, since they may be + * clobbered by a RegExp usage in the lambda function. Note that all + * members of JSRegExpStatics are JSSubStrings, so not GC roots, save + * input, which is rooted otherwise via argv[-1] in str_replace. */ - JSSubString saveRightContext = cx->regExpStatics.rightContext; + JSRegExpStatics save = cx->regExpStatics; + JSBool freeMoreParens = JS_FALSE; /* * In the lambda case, not only do we find the replacement string's @@ -1425,6 +1486,14 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) for (j = 0; i < m; i++, j++) PUSH_REGEXP_STATIC(moreParens[j]); + /* + * We need to clear moreParens in the top-of-stack cx->regExpStatics + * to it won't be possibly realloc'ed, leaving the bottom-of-stack + * moreParens pointing to freed memory. + */ + cx->regExpStatics.moreParens = NULL; + freeMoreParens = JS_TRUE; + #undef PUSH_REGEXP_STATIC /* Make sure to push undefined for any unmatched parens. */ @@ -1460,7 +1529,9 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) lambda_out: js_FreeStack(cx, mark); - cx->regExpStatics.rightContext = saveRightContext; + if (freeMoreParens) + JS_free(cx, cx->regExpStatics.moreParens); + cx->regExpStatics = save; return ok; } #endif /* JS_HAS_REPLACE_LAMBDA */ @@ -1469,7 +1540,7 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) 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); + sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { replen += sub->length - skip; dp += skip; @@ -1497,7 +1568,7 @@ do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) js_strncpy(chars, cp, len); chars += len; cp = dp; - sub = interpret_dollar(cx, dp, rdata, &skip); + sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { len = sub->length; js_strncpy(chars, sub->chars, len); @@ -1556,6 +1627,7 @@ str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *lambda; JSString *repstr, *str; ReplaceData rdata; + JSVersion version; JSBool ok; jschar *chars; size_t leftlen, rightlen, length; @@ -1579,7 +1651,8 @@ str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * 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) + version = cx->version & JSVERSION_MASK; + if (version == JSVERSION_DEFAULT || version > JSVERSION_1_4) rdata.base.flags |= FORCE_FLAT; rdata.base.optarg = 2; @@ -1694,7 +1767,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, */ chars = JSSTRING_CHARS(str); length = JSSTRING_LENGTH(str); - if (cx->version == JSVERSION_1_2 && + if (JS_VERSION_IS_1_2(cx) && !re && *sep->chars == ' ' && sep->chars[1] == 0) { /* Skip leading whitespace if at front of str. */ @@ -1757,7 +1830,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * sep->length to our return value. */ if ((size_t)i == length) { - if (cx->version == JSVERSION_1_2) { + if (JS_VERSION_IS_1_2(cx)) { sep->length = 1; return i; } @@ -1766,6 +1839,14 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, i++; goto again; } + if ((size_t)i == length) { + /* + * If there was a trivial zero-length match at the end of the + * split, then we shouldn't output the matched string at the end + * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. + */ + sep->chars = NULL; + } } JS_ASSERT((size_t)i >= sep->length); return i - sep->length; @@ -1777,7 +1858,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * string into a non-empty array (an array of length 1 that contains the * empty string). */ - if (!JSVERSION_IS_ECMA(cx->version) && length == 0) + if (!JS_VERSION_IS_ECMA(cx) && length == 0) return -1; /* @@ -1790,7 +1871,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * to include an additional null string at the end of the substring list. */ if (sep->length == 0) { - if (cx->version == JSVERSION_1_2) { + if (JS_VERSION_IS_1_2(cx)) { if ((size_t)i == length) { sep->length = 1; return i; @@ -1922,7 +2003,7 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } #endif i = j + sep->length; - if (!JSVERSION_IS_ECMA(cx->version)) { + if (!JS_VERSION_IS_ECMA(cx)) { /* * Deviate from ECMA to imitate Perl, which omits a final * split unless a limit argument is given and big enough. @@ -1947,6 +2028,7 @@ str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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)) @@ -2072,7 +2154,7 @@ str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ static JSBool tagify(JSContext *cx, JSObject *obj, jsval *argv, - const char *begin, const jschar *param, const char *end, + const char *begin, JSString *param, const char *end, jsval *rval) { JSString *str; @@ -2092,12 +2174,17 @@ tagify(JSContext *cx, JSObject *obj, jsval *argv, taglen = 1 + beglen + 1; /* '' */ parlen = 0; /* Avoid warning. */ if (param) { - parlen = js_strlen(param); + parlen = JSSTRING_LENGTH(param); taglen += 2 + parlen + 1; /* '="param"' */ } endlen = strlen(end); taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ + if (taglen >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); if (!tagbuf) return JS_FALSE; @@ -2109,7 +2196,7 @@ tagify(JSContext *cx, JSObject *obj, jsval *argv, if (param) { tagbuf[j++] = '='; tagbuf[j++] = '"'; - js_strncpy(&tagbuf[j], param, parlen); + js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); j += parlen; tagbuf[j++] = '"'; } @@ -2144,7 +2231,7 @@ tagify_value(JSContext *cx, JSObject *obj, jsval *argv, if (!param) return JS_FALSE; argv[0] = STRING_TO_JSVAL(param); - return tagify(cx, obj, argv, begin, JSSTRING_CHARS(param), end, rval); + return tagify(cx, obj, argv, begin, param, end, rval); } static JSBool @@ -2229,39 +2316,39 @@ str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE - {"quote", str_quote, 0,0,0}, + {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE,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}, + {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE,0}, + {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE,0}, + {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE,0}, + {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE,0}, + {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE,0}, + {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, + {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, + {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE,0}, + {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE,0}, + {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE,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}, + {"match", str_match, 1,JSFUN_GENERIC_NATIVE,2}, + {"search", str_search, 1,JSFUN_GENERIC_NATIVE,0}, + {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE,0}, + {"split", str_split, 2,JSFUN_GENERIC_NATIVE,0}, #endif #if JS_HAS_PERL_SUBSTR - {"substr", str_substr, 2,0,0}, + {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE,0}, #endif /* Python-esque sequence methods. */ #if JS_HAS_SEQUENCE_OPS - {"concat", str_concat, 0,0,0}, - {"slice", str_slice, 0,0,0}, + {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE,0}, + {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE,0}, #endif /* HTML string methods. */ @@ -2293,6 +2380,7 @@ String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); } else { str = cx->runtime->emptyString; } @@ -2380,6 +2468,7 @@ js_InitRuntimeStringState(JSContext *cx) { JSRuntime *rt; JSString *empty; + JSAtom *atom; rt = cx->runtime; JS_ASSERT(!rt->emptyString); @@ -2390,10 +2479,12 @@ js_InitRuntimeStringState(JSContext *cx) return JS_FALSE; /* Atomize it for scripts that use '' + x to convert x to string. */ - if (!js_AtomizeString(cx, empty, ATOM_PINNED)) + atom = js_AtomizeString(cx, empty, ATOM_PINNED); + if (!atom) return JS_FALSE; rt->emptyString = empty; + rt->atomState.emptyAtom = atom; return JS_TRUE; } @@ -2415,7 +2506,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, obj, string_functions)) return NULL; - proto = JS_InitClass(cx, obj, NULL, &string_class, String, 1, + proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, string_props, string_methods, NULL, string_static_methods); if (!proto) @@ -2435,7 +2526,7 @@ js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) return NULL; } - str = (JSString *) js_AllocGCThing(cx, gcflag | GCX_STRING); + str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); if (!str) return NULL; str->length = length; @@ -2462,13 +2553,17 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, if (length == 0) return cx->runtime->emptyString; + if (start == 0 && length == JSSTRING_LENGTH(base)) + return base; + 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); + ds = (JSDependentString *) + js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); if (!ds) return NULL; if (start == 0) { @@ -2574,7 +2669,7 @@ js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_string_pointer(const void *key) { - return (JSHashNumber)key >> JSVAL_TAGBITS; + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; } void @@ -2635,13 +2730,31 @@ js_StringToObject(JSContext *cx, JSString *str) { JSObject *obj; - obj = js_NewObject(cx, &string_class, NULL, NULL); + obj = js_NewObject(cx, &js_StringClass, NULL, NULL); if (!obj) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return obj; } +JS_FRIEND_API(const char *) +js_ValueToPrintableString(JSContext *cx, jsval v) +{ + JSString *str; + const char *bytes; + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + str = js_QuoteString(cx, str, 0); + if (!str) + return NULL; + bytes = js_GetStringBytes(str); + if (!bytes) + JS_ReportOutOfMemory(cx); + return bytes; +} + JSString * js_ValueToString(JSContext *cx, jsval v) { @@ -2672,6 +2785,9 @@ js_ValueToString(JSContext *cx, jsval v) JSString * js_ValueToSource(JSContext *cx, jsval v) { + JSTempValueRooter tvr; + JSString *str; + if (JSVAL_IS_STRING(v)) return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); if (JSVAL_IS_PRIMITIVE(v)) { @@ -2682,14 +2798,19 @@ js_ValueToSource(JSContext *cx, jsval v) return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); } + return js_ValueToString(cx, v); + } + + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), + cx->runtime->atomState.toSourceAtom, + 0, NULL, &tvr.u.value)) { + str = NULL; } else { - if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), - cx->runtime->atomState.toSourceAtom, - 0, NULL, &v)) { - return NULL; - } + str = js_ValueToString(cx, tvr.u.value); } - return js_ValueToString(cx, v); + JS_POP_TEMP_ROOT(cx, &tvr); + return str; } JSHashNumber @@ -2764,32 +2885,257 @@ js_SkipWhiteSpace(const jschar *s) return s; } -#define INFLATE_STRING_BODY \ - for (i = 0; i < length; i++) \ - chars[i] = (unsigned char) bytes[i]; \ - chars[i] = 0; +#ifdef JS_C_STRINGS_ARE_UTF8 -void -js_InflateStringToBuffer(jschar *chars, const char *bytes, size_t length) +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length) +{ + jschar *chars = NULL; + size_t dstlen = 0; + + if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) + return NULL; + chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); + if (!chars) + return NULL; + js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); + chars[dstlen] = 0; + *length = dstlen; + 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 size = 0; + char *bytes = NULL; + if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) + return NULL; + bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); + if (!bytes) + return NULL; + js_DeflateStringToBuffer(cx, chars, length, bytes, &size); + bytes[size] = 0; + return bytes; +} + +JSBool +js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp) +{ + size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; + jschar c, c2; + uint32 v; + uint8 utf8buf[6]; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + c = *src++; + srclen--; + if ((c >= 0xDC00) && (c <= 0xDFFF)) + goto badSurrogate; + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + if (srclen < 1) + goto bufferTooSmall; + c2 = *src++; + srclen--; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + c = c2; + goto badSurrogate; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + if (v < 0x0080) { + /* no encoding necessary - performance hack */ + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (char) v; + utf8Len = 1; + } else { + utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); + if (utf8Len > dstlen) + goto bufferTooSmall; + if (dst) { + for (i = 0; i < utf8Len; i++) + *dst++ = (char) utf8buf[i]; + } + } + dstlen -= utf8Len; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badSurrogate: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", c); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_BAD_SURROGATE_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +JSBool +js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp) +{ + uint32 v; + size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + v = (uint8) *src; + n = 1; + if (v & 0x80) { + while (v & (0x80 >> n)) + n++; + if (n > srclen) + goto bufferTooSmall; + if (n == 1 || n > 6) + goto badCharacter; + for (j = 1; j < n; j++) { + if ((src[j] & 0xC0) != 0x80) + goto badCharacter; + } + v = Utf8ToOneUcs4Char(src, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF || dstlen < 2) { + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", v + 0x10000); + JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UTF8_CHAR_TOO_LARGE, + buffer); + } + return JS_FALSE; + } + if (dstlen < 2) + goto bufferTooSmall; + if (dst) { + *dst++ = (jschar)((v >> 10) + 0xD800); + v = (jschar)((v & 0x3FF) + 0xDC00); + } + dstlen--; + } + } + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (jschar) v; + dstlen--; + offset += n; + src += n; + srclen -= n; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badCharacter: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "%d", offset); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_MALFORMED_UTF8_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +#else /* !JS_C_STRINGS_ARE_UTF8 */ + +JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, + jschar *chars, size_t* charsLength) { size_t i; - INFLATE_STRING_BODY + if (length > *charsLength) { + for (i = 0; i < *charsLength; i++) + chars[i] = (unsigned char) bytes[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + *charsLength = length; + return JS_TRUE; } jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t length) +js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) { jschar *chars; - size_t i; + size_t i, length = *bytesLength; chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) + if (!chars) { + *bytesLength = 0; return NULL; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + chars[length] = 0; + *bytesLength = length; + return chars; +} - INFLATE_STRING_BODY +JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, + char *bytes, size_t* bytesLength) +{ + size_t i; - return chars; + if (length > *bytesLength) { + for (i = 0; i < *bytesLength; i++) + bytes[i] = (char) chars[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + *bytesLength = length; + return JS_TRUE; } /* @@ -2805,12 +3151,16 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t length) 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; + + bytes[length] = 0; return bytes; } +#endif /* !JS_C_STRINGS_ARE_UTF8 */ + static JSHashTable * GetDeflatedStringCache(void) { @@ -2879,7 +3229,7 @@ js_GetStringBytes(JSString *str) *bytes == (char) JSSTRING_CHARS(str)[0]); } else { bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); + JSSTRING_LENGTH(str)); if (bytes) { if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { #ifdef DEBUG @@ -2936,7 +3286,9 @@ js_GetStringBytes(JSString *str) * 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 + * 1 bit XML 1.0 name start character + * 1 bit XML 1.0 name character + * 2 bits reserved for future use * 5 bits character type */ @@ -4066,119 +4418,119 @@ const uint32 js_A[] = { 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 */ +0x00036089, /* 9 Nd, identifier part, decimal 16 */ +0x0827FF81, /* 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 */ +0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ 0x0000000C, /* 14 Zs */ 0x0000001C, /* 15 So */ -0x00070002, /* 16 Ll, identifier start */ +0x00070182, /* 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 */ +0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070181, /* 38 Lu, identifier start */ +0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070185, /* 40 Lo, identifier start */ +0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ 0x00000000, /* 46 unassigned */ -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 */ +0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070084, /* 59 Lm, identifier start */ +0x00030086, /* 60 Mn, identifier part */ +0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034089, /* 78 Nd, identifier part, decimal 0 */ +0x00000087, /* 79 Me */ +0x00030088, /* 80 Mc, identifier part */ +0x00037489, /* 81 Nd, identifier part, decimal 26 */ 0x00005A0B, /* 82 No, decimal 13 */ 0x00006E0B, /* 83 No, decimal 23 */ 0x0000740B, /* 84 No, decimal 26 */ 0x0000000B, /* 85 No */ -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 */ +0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ 0x00010010, /* 104 Cf, ignorable */ 0x0004000D, /* 105 Zl, whitespace */ 0x0004000E, /* 106 Zp, whitespace */ 0x0000400B, /* 107 No, decimal 0 */ 0x0000440B, /* 108 No, decimal 2 */ -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 */ +0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007818A, /* 113 Nl, identifier start, strange */ 0x0000420B, /* 114 No, decimal 1 */ 0x0000720B, /* 115 No, decimal 25 */ 0x06A0001C, /* 116 So, hasLower (add 26) */ 0x0690001C, /* 117 So, hasUpper (subtract 26) */ 0x00006C0B, /* 118 No, decimal 22 */ 0x0000560B, /* 119 No, decimal 11 */ -0x0007720A, /* 120 Nl, identifier start, decimal 25 */ -0x0007400A, /* 121 Nl, identifier start, decimal 0 */ +0x0007738A, /* 120 Nl, identifier start, decimal 25 */ +0x0007418A, /* 121 Nl, identifier start, decimal 0 */ 0x00000013, /* 122 Cs */ 0x00000012 /* 123 Co */ }; @@ -4228,13 +4580,19 @@ 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; + jschar *chars, c, c2; + uint32 v; uint8 utf8buf[6]; jschar hexBuf[4]; static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ JSString *R; + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + R = js_NewString(cx, NULL, 0, 0); if (!R) return JS_FALSE; @@ -4242,21 +4600,20 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, 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)) + 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)) { + 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; + if (c < 0xD800 || c > 0xDBFF) { + v = c; } else { k++; if (k == length) { @@ -4264,15 +4621,15 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } - C2 = chars[k]; - if ((C2 < 0xDC00) || (C2 > 0xDFFF)) { + 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; + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } - L = OneUcs4ToUtf8Char(utf8buf, V); + L = js_OneUcs4ToUtf8Char(utf8buf, v); for (j = 0; j < L; j++) { hexBuf[1] = HexDigits[utf8buf[j] >> 4]; hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; @@ -4298,22 +4655,27 @@ static JSBool Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) { size_t length, start, k; - jschar *chars, C, H; - uint32 V; + jschar *chars, c, H; + uint32 v; jsuint B; uint8 octets[6]; JSString *R; intN j, n; + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + 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 == '%') { + c = chars[k]; + if (c == '%') { start = k; if ((k + 2) >= length) goto bad; @@ -4322,7 +4684,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { - C = (jschar)B; + c = (jschar)B; } else { n = 1; while (B & (0x80 >> n)) @@ -4344,28 +4706,28 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) k += 2; octets[j] = (char)B; } - V = Utf8ToOneUcs4Char(octets, n); - if (V >= 0x10000) { - V -= 0x10000; - if (V > 0xFFFFF) + v = Utf8ToOneUcs4Char(octets, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF) goto bad; - C = (jschar)((V & 0x3FF) + 0xDC00); - H = (jschar)((V >> 10) + 0xD800); + c = (jschar)((v & 0x3FF) + 0xDC00); + H = (jschar)((v >> 10) + 0xD800); if (!AddCharsToURI(cx, R, &H, 1)) return JS_FALSE; } else { - C = (jschar)V; + c = (jschar)v; } } - if (js_strchr(reservedSet, C)) { + if (js_strchr(reservedSet, c)) { if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) return JS_FALSE; } else { - if (!AddCharsToURI(cx, R, &C, 1)) + if (!AddCharsToURI(cx, R, &c, 1)) return JS_FALSE; } } else { - if (!AddCharsToURI(cx, R, &C, 1)) + if (!AddCharsToURI(cx, R, &c, 1)) return JS_FALSE; } } @@ -4395,6 +4757,7 @@ str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); } @@ -4407,6 +4770,7 @@ str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); return Decode(cx, str, js_empty_ucstr, rval); } @@ -4419,6 +4783,7 @@ str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, rval); } @@ -4432,6 +4797,7 @@ str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); } @@ -4439,8 +4805,8 @@ str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * 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 +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) { int utf8Length = 1; @@ -4493,7 +4859,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); } - if (ucs4Char < minucs4Char || + if (ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { ucs4Char = 0xFFFD; } diff --git a/src/dom/js/jsstr.h b/src/dom/js/jsstr.h index 202d0d9ea..658a87f90 100644 --- a/src/dom/js/jsstr.h +++ b/src/dom/js/jsstr.h @@ -210,44 +210,44 @@ typedef enum JSCharType { #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) + (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) + (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) +#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER)) \ + >> JS_CTYPE(c)) & 1) /* * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or * digit or connector punctuation. */ -#define JS_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) +#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER) | \ + (1 << JSCT_NON_SPACING_MARK) | \ + (1 << JSCT_COMBINING_SPACING_MARK) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ + (1 << JSCT_CONNECTOR_PUNCTUATION)) \ + >> JS_CTYPE(c)) & 1) /* Unicode control-format characters, ignored in input */ #define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) @@ -259,12 +259,20 @@ typedef enum JSCharType { */ #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_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') + +#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ + (c) == '\n') +#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') +#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ + (c) == '-' || (c) == '_') +#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') +#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') #define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) +/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ /* XXXbe fs, etc. ? */ #define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) #define JS_ISPRINT(c) ((c) < 128 && isprint(c)) @@ -279,8 +287,6 @@ typedef enum JSCharType { ? (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') @@ -306,6 +312,8 @@ extern void js_FinishRuntimeStringState(JSContext *cx); /* Initialize the String class, returning its prototype object. */ +extern JSClass js_StringClass; + extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); @@ -344,6 +352,12 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str); extern JSObject * js_StringToObject(JSContext *cx, JSString *str); +/* + * Convert a value to a printable C string. + */ +extern JS_FRIEND_API(const char *) +js_ValueToPrintableString(JSContext *cx, jsval v); + /* * Convert a value to a string, returning null after reporting an error, * otherwise returning a new string reference. @@ -358,7 +372,7 @@ js_ValueToString(JSContext *cx, jsval v); extern JSString * js_ValueToSource(JSContext *cx, jsval v); -#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ +#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ /* * Compute a hash function from str. */ @@ -410,20 +424,33 @@ 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. + * was JS_malloc'ed. length is updated with the length of the new string in jschars. */ extern jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t length); +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. + * 'chars' must be large enough for 'length' jschars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes the number of chars moved. */ -extern void -js_InflateStringToBuffer(jschar *chars, const char *bytes, size_t length); +extern JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, jschar *chars, size_t* charsLength); + +/* + * Deflate JS chars to bytes into a buffer. + * 'bytes' must be large enough for 'length chars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes the number of bytes moved. + */ +extern JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t charsLength, char *bytes, size_t* length); /* * Associate bytes with str in the deflated string cache, returning true on @@ -443,6 +470,13 @@ JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *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. + */ +extern int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); + JS_END_EXTERN_C #endif /* jsstr_h___ */ diff --git a/src/dom/js/jstypes.h b/src/dom/js/jstypes.h index 35fc12e54..1c24293b5 100644 --- a/src/dom/js/jstypes.h +++ b/src/dom/js/jstypes.h @@ -107,21 +107,18 @@ #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 +#ifdef HAVE_VISIBILITY_PRAGMA +#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) +#else +#define JS_EXTERNAL_VIS +#endif + +#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type +#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type #define JS_DLL_CALLBACK #define JS_STATIC_DLL_CALLBACK(__x) static __x @@ -141,7 +138,7 @@ #if defined(_WIN32) && !defined(__MWERKS__) && !defined(__GNUC__) # define JS_IMPORT_DATA(__x) __declspec(dllimport) __x #else -# define JS_IMPORT_DATA(__x) __x +# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) #endif /* @@ -202,6 +199,19 @@ #define JS_BIT(n) ((JSUint32)1 << (n)) #define JS_BITMASK(n) (JS_BIT(n) - 1) +/*********************************************************************** +** MACROS: JS_PTR_TO_INT32 +** JS_PTR_TO_UINT32 +** JS_INT32_TO_PTR +** JS_UINT32_TO_PTR +** DESCRIPTION: +** Integer to pointer and pointer to integer conversion macros. +***********************************************************************/ +#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) +#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) +#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) +#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) + /*********************************************************************** ** MACROS: JS_HOWMANY ** JS_ROUNDUP @@ -215,13 +225,13 @@ #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) +#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) # 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" +# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" #endif JS_BEGIN_EXTERN_C @@ -382,7 +392,30 @@ typedef unsigned long JSUword; #include "jsotypes.h" +/*********************************************************************** +** MACROS: JS_LIKELY +** JS_UNLIKELY +** DESCRIPTION: +** These macros allow you to give a hint to the compiler about branch +** probability so that it can better optimize. Use them like this: +** +** if (JS_LIKELY(v == 1)) { +** ... expected code path ... +** } +** +** if (JS_UNLIKELY(v == 0)) { +** ... non-expected code path ... +** } +** +***********************************************************************/ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define JS_LIKELY(x) (__builtin_expect((x), 1)) +#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define JS_LIKELY(x) (x) +#define JS_UNLIKELY(x) (x) +#endif + JS_END_EXTERN_C #endif /* jstypes_h___ */ - diff --git a/src/dom/js/jsutil.c b/src/dom/js/jsutil.c index 6e4c21cad..d7fab0f88 100644 --- a/src/dom/js/jsutil.c +++ b/src/dom/js/jsutil.c @@ -51,107 +51,142 @@ # include #endif -#ifdef XP_MAC -# include -# include -# include "jsprf.h" +JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) +{ + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(WIN32) + DebugBreak(); + exit(3); #endif +#if defined(XP_OS2) + asm("int $3"); +#endif + abort(); +} -#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) +#if defined DEBUG_notme && defined XP_UNIX + +#define __USE_GNU 1 +#include +#include +#include +#include "jshash.h" +#include "jsprf.h" + +JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; + +static JSCallsite * +CallTree(uint32 *bp) { - 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; - } + uint32 *bpup, *bpdown, pc; + JSCallsite *parent, *site, **csp; + Dl_info info; + int ok, offset; + const char *symbol; + char *method; + + /* Reverse the stack frame list to avoid recursion. */ + bpup = NULL; + for (;;) { + bpdown = (uint32*) bp[0]; + bp[0] = (uint32) bpup; + if ((uint32*) bpdown[0] < bpdown) + break; + bpup = bp; + bp = bpdown; } - 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; + /* Reverse the stack again, finding and building a path in the tree. */ + parent = &js_calltree_root; + do { + bpup = (uint32*) bp[0]; + bp[0] = (uint32) bpdown; + pc = bp[1]; + + csp = &parent->kids; + while ((site = *csp) != NULL) { + if (site->pc == pc) { + /* Put the most recently used site at the front of siblings. */ + *csp = site->siblings; + site->siblings = parent->kids; + parent->kids = site; + + /* Site already built -- go up the stack. */ + goto upward; + } + csp = &site->siblings; + } - if ( --overflow <= 0 ) - break; + /* Check for recursion: see if pc is on our ancestor line. */ + for (site = parent; site; site = site->parent) { + if (site->pc == pc) + goto upward; } - length = 255 - overflow; - } - dst[0] = length; -} -static void jsdebugstr(const char *debuggerMsg) -{ - Str255 pStr; + /* + * Not in tree at all: let's find our symbolic callsite info. + * XXX static syms are masked by nearest lower global + */ + info.dli_fname = info.dli_sname = NULL; + ok = dladdr((void*) pc, &info); + if (ok < 0) { + fprintf(stderr, "dladdr failed!\n"); + return NULL; + } - PStrFromCStr(debuggerMsg, pStr); - DebugStr(pStr); +/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ + symbol = info.dli_sname; + offset = (char*)pc - (char*)info.dli_fbase; + method = symbol + ? strdup(symbol) + : JS_smprintf("%s+%X", + info.dli_fname ? info.dli_fname : "main", + offset); + if (!method) + return NULL; + + /* Create a new callsite record. */ + site = (JSCallsite *) malloc(sizeof(JSCallsite)); + if (!site) + return NULL; + + /* Insert the new site into the tree. */ + site->pc = pc; + site->name = method; + site->library = info.dli_fname; + site->offset = offset; + site->parent = parent; + site->siblings = parent->kids; + parent->kids = site; + site->kids = NULL; + + upward: + parent = site; + bpdown = bp; + bp = bpup; + } while (bp); + + return site; } -static void dprintf(const char *format, ...) +JSCallsite * +JS_Backtrace(int skip) { - va_list ap; - char *buffer; - - va_start(ap, format); - buffer = (char *)JS_vsmprintf(format, ap); - va_end(ap); + jmp_buf jb; + uint32 *bp, *bpdown; + + setjmp(jb); + + /* Stack walking code adapted from Kipp's "leaky". */ + bp = (uint32*) jb[0].__jmpbuf[JB_BP]; + while (--skip >= 0) { + bpdown = (uint32*) *bp++; + if (bpdown < bp) + break; + bp = bpdown; + } - jsdebugstr(buffer); - JS_smprintf_free(buffer); + return CallTree(bp); } -#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 -} +#endif /* DEBUG_notme && XP_UNIX */ diff --git a/src/dom/js/jsutil.h b/src/dom/js/jsutil.h index a34096d93..5e26c16ae 100644 --- a/src/dom/js/jsutil.h +++ b/src/dom/js/jsutil.h @@ -70,6 +70,25 @@ JS_Assert(const char *s, const char *file, JSIntn ln); */ extern JS_PUBLIC_API(void) JS_Abort(void); +#ifdef XP_UNIX + +typedef struct JSCallsite JSCallsite; + +struct JSCallsite { + uint32 pc; + char *name; + const char *library; + int offset; + JSCallsite *parent; + JSCallsite *siblings; + JSCallsite *kids; + void *handy; +}; + +extern JSCallsite *JS_Backtrace(int skip); + +#endif + JS_END_EXTERN_C #endif /* jsutil_h___ */ diff --git a/src/dom/js/jsxdrapi.c b/src/dom/js/jsxdrapi.c index e956072f9..0c9aeceed 100644 --- a/src/dom/js/jsxdrapi.c +++ b/src/dom/js/jsxdrapi.c @@ -498,7 +498,7 @@ 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; diff --git a/src/dom/js/jsxml.c b/src/dom/js/jsxml.c new file mode 100644 index 000000000..e7cde2edf --- /dev/null +++ b/src/dom/js/jsxml.c @@ -0,0 +1,8295 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XML_SUPPORT + +#include +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsprf.h" +#include "jsutil.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsxml.h" + +#ifdef DEBUG +#include /* for #ifdef DEBUG memset calls */ +#endif + +/* + * NOTES + * - in the js shell, you must use the -x command line option, or call + * options('xml') before compiling anything that uses XML literals + * + * TODO + * - XXXbe patrol + * - Fuse objects and their JSXML* private data into single GC-things + * - fix function::foo vs. x.(foo == 42) collision using proper namespacing + * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants + * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! + * - JS_TypeOfValue sure could use a cleaner interface to "types" + */ + +#ifdef DEBUG_brendan +#define METERING 1 +#endif + +#ifdef METERING +static struct { + jsrefcount qname; + jsrefcount qnameobj; + jsrefcount liveqname; + jsrefcount liveqnameobj; + jsrefcount namespace; + jsrefcount namespaceobj; + jsrefcount livenamespace; + jsrefcount livenamespaceobj; + jsrefcount xml; + jsrefcount xmlobj; + jsrefcount livexml; + jsrefcount livexmlobj; +} xml_stats; + +#define METER(x) JS_ATOMIC_INCREMENT(&(x)) +#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) +#else +#define METER(x) /* nothing */ +#define UNMETER(x) /* nothing */ +#endif + +/* + * Random utilities and global functions. + */ +const char js_AnyName_str[] = "AnyName"; +const char js_AttributeName_str[] = "AttributeName"; +const char js_isXMLName_str[] = "isXMLName"; +const char js_XMLList_str[] = "XMLList"; +const char js_localName_str[] = "localName"; +const char js_xml_parent_str[] = "parent"; +const char js_prefix_str[] = "prefix"; +const char js_toXMLString_str[] = "toXMLString"; +const char js_uri_str[] = "uri"; + +const char js_amp_entity_str[] = "&"; +const char js_gt_entity_str[] = ">"; +const char js_lt_entity_str[] = "<"; +const char js_quot_entity_str[] = """; + +#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) +#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') + +static JSBool +xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); + return JS_TRUE; +} + +/* + * Namespace class and library functions. + */ +enum namespace_tinyid { + NAMESPACE_PREFIX = -1, + NAMESPACE_URI = -2 +}; + +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLNamespace *ns; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); + if (!ns) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case NAMESPACE_PREFIX: + *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; + break; + case NAMESPACE_URI: + *vp = STRING_TO_JSVAL(ns->uri); + break; + } + return JS_TRUE; +} + +static void +namespace_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLNamespace *ns; + JSRuntime *rt; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + if (!ns) + return; + JS_ASSERT(ns->object == obj); + ns->object = NULL; + UNMETER(xml_stats.livenamespaceobj); + + rt = cx->runtime; + if (rt->functionNamespaceObject == obj) + rt->functionNamespaceObject = NULL; +} + +static void +namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len, + void *arg) +{ + uint32 i; + JSXMLNamespace *ns; + + for (i = 0; i < len; i++) { + ns = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[100]; + + JS_snprintf(buf, sizeof buf, "%s=%s", + ns->prefix ? JS_GetStringBytes(ns->prefix) : "", + JS_GetStringBytes(ns->uri)); +#else + const char *buf = NULL; +#endif + JS_MarkGCThing(cx, ns, buf, arg); + } + } +} + +static uint32 +namespace_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + JS_MarkGCThing(cx, ns, js_private_str, arg); + return 0; +} + +static JSBool +namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLNamespace *ns, *ns2; + JSObject *obj2; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { + *bp = JS_FALSE; + } else { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); + *bp = !js_CompareStrings(ns->uri, ns2->uri); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { + { "Namespace", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED, + JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, namespace_mark, NULL }, + namespace_equality, + NULL, NULL, + JSCLASS_NO_RESERVED_MEMBERS +}; + +#define NAMESPACE_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec namespace_props[] = { + {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, + {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); + if (!ns) + return JS_FALSE; + + *rval = STRING_TO_JSVAL(ns->uri); + return JS_TRUE; +} + +static JSFunctionSpec namespace_methods[] = { + {js_toString_str, namespace_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); + if (!ns) + return NULL; + ns->object = NULL; + ns->prefix = prefix; + ns->uri = uri; + ns->declared = declared; + METER(xml_stats.namespace); + METER(xml_stats.livenamespace); + return ns; +} + +void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns, void *arg) +{ + JS_MarkGCThing(cx, ns->object, js_object_str, arg); + JS_MarkGCThing(cx, ns->prefix, js_prefix_str, arg); + JS_MarkGCThing(cx, ns->uri, js_uri_str, arg); +} + +void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + UNMETER(xml_stats.livenamespace); +} + +JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = js_NewXMLNamespace(cx, prefix, uri, declared); + if (!ns) + return NULL; + return js_GetXMLNamespaceObject(cx, ns); +} + +JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) +{ + JSObject *obj; + + obj = ns->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == ns); + return obj; + } + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, ns)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + ns->object = obj; + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + return obj; +} + +/* + * QName class and library functions. + */ +enum qname_tinyid { + QNAME_URI = -1, + QNAME_LOCALNAME = -2 +}; + +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLQName *qn; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); + if (!qn) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case QNAME_URI: + *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; + break; + case QNAME_LOCALNAME: + *vp = STRING_TO_JSVAL(qn->localName); + break; + } + return JS_TRUE; +} + +static void +qname_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + if (!qn) + return; + JS_ASSERT(qn->object == obj); + qn->object = NULL; + UNMETER(xml_stats.liveqnameobj); +} + +static void +anyname_finalize(JSContext* cx, JSObject* obj) +{ + JSRuntime *rt; + + /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ + rt = cx->runtime; + if (rt->anynameObject == obj) + rt->anynameObject = NULL; + + qname_finalize(cx, obj); +} + +static uint32 +qname_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + JS_MarkGCThing(cx, qn, js_private_str, arg); + return 0; +} + +static JSBool +qname_identity(JSXMLQName *qna, JSXMLQName *qnb) +{ + if (!qna->uri ^ !qnb->uri) + return JS_FALSE; + if (qna->uri && js_CompareStrings(qna->uri, qnb->uri)) + return JS_FALSE; + return !js_CompareStrings(qna->localName, qnb->localName); +} + +static JSBool +qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLQName *qn, *qn2; + JSObject *obj2; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { + *bp = JS_FALSE; + } else { + qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); + *bp = qname_identity(qn, qn2); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { + { "QName", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED, + JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL }, + qname_equality, + NULL, NULL, + JSCLASS_NO_RESERVED_MEMBERS +}; + +/* + * Classes for the ECMA-357-internal types AttributeName and AnyName, which + * are like QName, except that they have no property getters. They share the + * qname_toString method, and therefore are exposed as constructable objects + * in this implementation. + */ +JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { + js_AttributeName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +JS_FRIEND_DATA(JSClass) js_AnyNameClass = { + js_AnyName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +#define QNAME_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec qname_props[] = { + {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, + {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *str, *qualstr; + size_t length; + jschar *chars; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + } else { + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); + if (!qn) + return JS_FALSE; + } + + if (!qn->uri) { + /* No uri means wildcard qualifier. */ + str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); + } else if (IS_EMPTY(qn->uri)) { + /* Empty string for uri means localName is in no namespace. */ + str = cx->runtime->emptyString; + } else { + qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); + str = js_ConcatStrings(cx, qn->uri, qualstr); + if (!str) + return JS_FALSE; + } + str = js_ConcatStrings(cx, str, qn->localName); + if (!str) + return JS_FALSE; + + if (str && clasp == &js_AttributeNameClass) { + length = JSSTRING_LENGTH(str); + chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + *chars = '@'; + js_strncpy(chars + 1, JSSTRING_CHARS(str), length); + 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 JSFunctionSpec qname_methods[] = { + {js_toString_str, qname_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); + if (!qn) + return NULL; + qn->object = NULL; + qn->uri = uri; + qn->prefix = prefix; + qn->localName = localName; + METER(xml_stats.qname); + METER(xml_stats.liveqname); + return qn; +} + +void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn, void *arg) +{ + JS_MarkGCThing(cx, qn->object, js_object_str, arg); + JS_MarkGCThing(cx, qn->uri, js_uri_str, arg); + JS_MarkGCThing(cx, qn->prefix, js_prefix_str, arg); + JS_MarkGCThing(cx, qn->localName, js_localName_str, arg); +} + +void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) +{ + UNMETER(xml_stats.liveqname); +} + +JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = js_NewXMLQName(cx, uri, prefix, localName); + if (!qn) + return NULL; + return js_GetXMLQNameObject(cx, qn); +} + +JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == qn); + return obj; + } + obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) + return obj; + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) + return NULL; + } + + obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) +{ + jsval argv[2]; + + /* + * ECMA-357 11.1.2, + * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ + * production, step 2. + */ + if (!JSVAL_IS_PRIMITIVE(nsval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { + nsval = JSVAL_NULL; + } + + argv[0] = nsval; + argv[1] = lnval; + return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); +} + +static JSBool +IsXMLName(const jschar *cp, size_t n) +{ + JSBool rv; + jschar c; + + rv = JS_FALSE; + if (n != 0 && JS_ISXMLNSSTART(*cp)) { + while (--n != 0) { + c = *++cp; + if (!JS_ISXMLNS(c)) + return rv; + } + rv = JS_TRUE; + } + return rv; +} + +JSBool +js_IsXMLName(JSContext *cx, jsval v) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *name; + JSErrorReporter older; + + /* + * Inline specialization of the QName constructor called with v passed as + * the only argument, to compute the localName for the constructed qname, + * without actually allocating the object or computing its uri and prefix. + * See ECMA-357 13.1.2.1 step 1 and 13.3.2. + */ + if (!JSVAL_IS_PRIMITIVE(v) && + (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), + clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass || + clasp == &js_AnyNameClass)) { + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + name = qn->localName; + } else { + older = JS_SetErrorReporter(cx, NULL); + name = js_ValueToString(cx, v); + JS_SetErrorReporter(cx, older); + if (!name) { + JS_ClearPendingException(cx); + return JS_FALSE; + } + } + + return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); +} + +static JSBool +Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval urival, prefixval; + JSObject *uriobj; + JSBool isNamespace, isQName; + JSClass *clasp; + JSString *empty, *prefix; + JSXMLNamespace *ns, *ns2; + JSXMLQName *qn; + + urival = argv[argc > 1]; + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(urival)) { + uriobj = JSVAL_TO_OBJECT(urival); + clasp = OBJ_GET_CLASS(cx, uriobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else uriobj = NULL; +#endif + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; + } + + /* Create and return a new QName object exactly as if constructed. */ + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + + /* + * Create and connect private data to rooted obj early, so we don't have + * to worry about rooting string newborns hanging off of the private data + * further below. + */ + empty = cx->runtime->emptyString; + ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); + if (!ns) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, ns)) + return JS_FALSE; + ns->object = obj; + + if (argc == 1) { + if (isNamespace) { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); + ns->uri = ns2->uri; + ns->prefix = ns2->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + ns->prefix = qn->prefix; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + if (!IS_EMPTY(ns->uri)) + ns->prefix = NULL; + } + } else if (argc == 2) { + if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + } + + prefixval = argv[0]; + if (IS_EMPTY(ns->uri)) { + if (!JSVAL_IS_VOID(prefixval)) { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + if (!IS_EMPTY(prefix)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return JS_FALSE; + } + } + } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { + /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ + ns->prefix = NULL; + } else { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + ns->prefix = prefix; + } + } + + return JS_TRUE; +} + +static JSBool +QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval nameval, nsval; + JSBool isQName, isNamespace; + JSXMLQName *qn; + JSString *uri, *prefix, *name; + JSObject *nsobj; + JSClass *clasp; + JSXMLNamespace *ns; + + nameval = argv[argc > 1]; + isQName = + !JSVAL_IS_PRIMITIVE(nameval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } + + /* + * Create and return a new QName object exactly as if constructed. + * Use the constructor's clasp so we can be shared by AttributeName + * (see below after this function). + */ + obj = js_NewObject(cx, + argv + ? JS_ValueToFunction(cx, argv[-2])->clasp + : &js_QNameClass.base, + NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + if (isQName) { + /* If namespace is not specified and name is a QName, clone it. */ + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); + if (argc == 1) { + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + goto out; + } + + /* Namespace and qname were passed -- use the qname's localName. */ + nameval = STRING_TO_JSVAL(qn->localName); + } + + if (argc == 0) { + name = cx->runtime->emptyString; + } else { + name = js_ValueToString(cx, nameval); + if (!name) + return JS_FALSE; + + /* Use argv[1] as a local root for name, even if it was not passed. */ + argv[1] = STRING_TO_JSVAL(name); + } + + nsval = argv[0]; + if (argc == 1 || JSVAL_IS_VOID(nsval)) { + if (IS_STAR(name)) { + nsval = JSVAL_NULL; + } else { + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return JS_FALSE; + } + } + + if (JSVAL_IS_NULL(nsval)) { + /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ + uri = prefix = NULL; + } else { + /* + * Inline specialization of the Namespace constructor called with + * nsval passed as the only argument, to compute the uri and prefix + * for the constructed namespace, without actually allocating the + * object or computing other members. See ECMA-357 13.3.2 6(a) and + * 13.2.2. + */ + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(nsval)) { + nsobj = JSVAL_TO_OBJECT(nsval); + clasp = OBJ_GET_CLASS(cx, nsobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else nsobj = NULL; +#endif + + if (isNamespace) { + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + uri = ns->uri; + prefix = ns->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { + uri = qn->uri; + prefix = qn->prefix; + } else { + uri = js_ValueToString(cx, nsval); + if (!uri) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(uri); /* local root */ + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + } + +out: + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, qn)) + return JS_FALSE; + qn->object = obj; + return JS_TRUE; +} + +static JSBool +AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + /* + * Since js_AttributeNameClass was initialized, obj will have that as its + * class, not js_QNameClass. + */ + return QName(cx, obj, argc, argv, rval); +} + +/* + * XMLArray library functions. + */ +static JSBool +namespace_identity(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix) { + if (js_CompareStrings(nsa->prefix, nsb->prefix)) + return JS_FALSE; + } else { + if (nsa->prefix || nsb->prefix) + return JS_FALSE; + } + return !js_CompareStrings(nsa->uri, nsb->uri); +} + +static JSBool +attr_identity(const void *a, const void *b) +{ + const JSXML *xmla = (const JSXML *) a; + const JSXML *xmlb = (const JSXML *) b; + + return qname_identity(xmla->name, xmlb->name); +} + +static void +XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) +{ + JSXMLArrayCursor *next; + + cursor->array = array; + cursor->index = 0; + next = cursor->next = array->cursors; + if (next) + next->prevp = &cursor->next; + cursor->prevp = &array->cursors; + array->cursors = cursor; + cursor->root = NULL; +} + +static void +XMLArrayCursorFinish(JSXMLArrayCursor *cursor) +{ + JSXMLArrayCursor *next; + + if (!cursor->array) + return; + next = cursor->next; + if (next) + next->prevp = cursor->prevp; + *cursor->prevp = next; + cursor->array = NULL; +} + +static void * +XMLArrayCursorNext(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index++]; +} + +static void * +XMLArrayCursorItem(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index]; +} + +static void +XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) +{ + while (cursor) { + GC_MARK(cx, cursor->root, "cursor->root", NULL); + cursor = cursor->next; + } +} + +/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ +static JSBool +XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + void **vector; + + if (capacity == 0) { + /* We could let realloc(p, 0) free this, but purify gets confused. */ + if (array->vector) + free(array->vector); + vector = NULL; + } else { + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + if (cx) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + array->capacity = JSXML_PRESET_CAPACITY | capacity; + array->vector = vector; + return JS_TRUE; +} + +static void +XMLArrayTrim(JSXMLArray *array) +{ + if (array->capacity & JSXML_PRESET_CAPACITY) + return; + if (array->length < array->capacity) + XMLArraySetCapacity(NULL, array, array->length); +} + +static JSBool +XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + array->length = array->capacity = 0; + array->vector = NULL; + array->cursors = NULL; + return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); +} + +static void +XMLArrayFinish(JSContext *cx, JSXMLArray *array) +{ + JSXMLArrayCursor *cursor; + + JS_free(cx, array->vector); + + while ((cursor = array->cursors) != NULL) + XMLArrayCursorFinish(cursor); + +#ifdef DEBUG + memset(array, 0xd5, sizeof *array); +#endif +} + +#define XML_NOT_FOUND ((uint32) -1) + +static uint32 +XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) +{ + void **vector; + uint32 i, n; + + /* The identity op must not reallocate array->vector. */ + vector = array->vector; + if (identity) { + for (i = 0, n = array->length; i < n; i++) { + if (identity(vector[i], elt)) + return i; + } + } else { + for (i = 0, n = array->length; i < n; i++) { + if (vector[i] == elt) + return i; + } + } + return XML_NOT_FOUND; +} + +/* + * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after + * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold + * should be greater than increment. + */ +#define LINEAR_THRESHOLD 256 +#define LINEAR_INCREMENT 32 + +static JSBool +XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) +{ + uint32 capacity, i; + int log2; + void **vector; + + if (index >= array->length) { + if (index >= JSXML_CAPACITY(array)) { + /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ + capacity = index + 1; + if (index >= LINEAR_THRESHOLD) { + capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); + } else { + JS_CEILING_LOG2(log2, capacity); + capacity = JS_BIT(log2); + } + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + array->capacity = capacity; + array->vector = vector; + for (i = array->length; i < index; i++) + vector[i] = NULL; + } + array->length = index + 1; + } + + array->vector[index] = elt; + return JS_TRUE; +} + +static JSBool +XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) +{ + uint32 j; + JSXMLArrayCursor *cursor; + + j = array->length; + JS_ASSERT(i <= j); + if (!XMLArraySetCapacity(cx, array, j + n)) + return JS_FALSE; + + array->length = j + n; + JS_ASSERT(n != (uint32)-1); + while (j != i) { + --j; + array->vector[j + n] = array->vector[j]; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > i) + cursor->index += n; + } + return JS_TRUE; +} + +static void * +XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) +{ + uint32 length; + void **vector, *elt; + JSXMLArrayCursor *cursor; + + length = array->length; + if (index >= length) + return NULL; + + vector = array->vector; + elt = vector[index]; + if (compress) { + while (++index < length) + vector[index-1] = vector[index]; + array->length = length - 1; + array->capacity = JSXML_CAPACITY(array); + } else { + vector[index] = NULL; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > index) + --cursor->index; + } + return elt; +} + +static void +XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) +{ + void **vector; + + JS_ASSERT(!array->cursors); + if (length >= array->length) + return; + + if (length == 0) { + if (array->vector) + free(array->vector); + vector = NULL; + } else { + vector = realloc(array->vector, length * sizeof(void *)); + if (!vector) + return; + } + + if (array->length > length) + array->length = length; + array->capacity = length; + array->vector = vector; +} + +#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) +#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ + XML_NOT_FOUND) +#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ + ? (t *) (a)->vector[i] \ + : NULL) +#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ + if ((a)->length <= (i)) \ + (a)->length = (i) + 1; \ + ((a)->vector[i] = (void *)(e)); \ + JS_END_MACRO +#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) +#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) +#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) +#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) +#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) + +/* + * Define XML setting property strings and constants early, so everyone can + * use the same names and their magic numbers (tinyids, flags). + */ +static const char js_ignoreComments_str[] = "ignoreComments"; +static const char js_ignoreProcessingInstructions_str[] + = "ignoreProcessingInstructions"; +static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; +static const char js_prettyPrinting_str[] = "prettyPrinting"; +static const char js_prettyIndent_str[] = "prettyIndent"; + +/* + * NB: These XML static property tinyids must + * (a) not collide with the generic negative tinyids at the top of jsfun.c; + * (b) index their corresponding xml_static_props array elements. + * Don't change 'em! + */ +enum xml_static_tinyid { + XML_IGNORE_COMMENTS, + XML_IGNORE_PROCESSING_INSTRUCTIONS, + XML_IGNORE_WHITESPACE, + XML_PRETTY_PRINTING, + XML_PRETTY_INDENT +}; + +static JSBool +xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool b; + uint8 flag; + + JS_ASSERT(JSVAL_IS_INT(id)); + if (!js_ValueToBoolean(cx, *vp, &b)) + return JS_FALSE; + + flag = JS_BIT(JSVAL_TO_INT(id)); + if (b) + cx->xmlSettingFlags |= flag; + else + cx->xmlSettingFlags &= ~flag; + return JS_TRUE; +} + +static JSPropertySpec xml_static_props[] = { + {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreProcessingInstructions_str, + XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, + xml_setting_getter, NULL}, + {0,0,0,0,0} +}; + +/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ +#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) +#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ + JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) +#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) +#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) +#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) + +/* + * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. + * This flag means a couple of things: + * + * - The top JSXML created for a parse tree must have an object owning it. + * + * - That the default namespace normally inherited from the temporary + * tag that wraps a runtime-concatenated XML source + * string must, in the case of a precompiled XML object tree, inherit via + * ad-hoc code in ParseNodeToXML. + * + * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. + */ +#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) + +/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ +#define IS_XML(str) \ + (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) + +#define IS_XMLNS(str) \ + (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) + +#define IS_XML_CHARS(chars) \ + (JS_TOLOWER((chars)[0]) == 'x' && \ + JS_TOLOWER((chars)[1]) == 'm' && \ + JS_TOLOWER((chars)[2]) == 'l') + +#define HAS_NS_AFTER_XML(chars) \ + (JS_TOLOWER((chars)[3]) == 'n' && \ + JS_TOLOWER((chars)[4]) == 's') + +#define IS_XMLNS_CHARS(chars) \ + (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) + +#define STARTS_WITH_XML(chars,length) \ + (length >= 3 && IS_XML_CHARS(chars)) + +static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; +static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; + +static JSXMLQName * +ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + JSBool isAttributeName) +{ + JSString *str, *uri, *prefix, *localName; + size_t length, offset; + const jschar *start, *limit, *colon; + uint32 n; + JSXMLNamespace *ns; + + JS_ASSERT(pn->pn_arity == PN_NULLARY); + str = ATOM_TO_STRING(pn->pn_atom); + length = JSSTRING_LENGTH(str); + start = JSSTRING_CHARS(str); + JS_ASSERT(length != 0 && *start != '@'); + JS_ASSERT(length != 1 || *start != '*'); + + uri = cx->runtime->emptyString; + limit = start + length; + colon = js_strchr_limit(start, ':', limit); + if (colon) { + offset = PTRDIFF(colon, start, jschar); + prefix = js_NewDependentString(cx, str, 0, offset, 0); + if (!prefix) + return NULL; + + if (STARTS_WITH_XML(start, offset)) { + if (offset == 3) { + uri = JS_InternString(cx, xml_namespace_str); + if (!uri) + return NULL; + } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { + uri = JS_InternString(cx, xmlns_namespace_str); + if (!uri) + return NULL; + } else { + uri = NULL; + } + } else { + uri = NULL; + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (ns->prefix && !js_CompareStrings(ns->prefix, prefix)) { + uri = ns->uri; + break; + } + } + } + + if (!uri) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return NULL; + } + + localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); + if (!localName) + return NULL; + } else { + if (isAttributeName) { + /* + * An unprefixed attribute is not in any namespace, so set prefix + * as well as uri to the empty string. + */ + prefix = uri; + } else { + /* + * Loop from back to front looking for the closest declared default + * namespace. + */ + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (!ns->prefix || IS_EMPTY(ns->prefix)) { + uri = ns->uri; + break; + } + } + prefix = NULL; + } + localName = str; + } + + return js_NewXMLQName(cx, uri, prefix, localName); +} + +static JSString * +ChompXMLWhitespace(JSContext *cx, JSString *str) +{ + size_t length, newlength, offset; + const jschar *cp, *start, *end; + jschar c; + + length = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (!JS_ISXMLSPACE(c)) + break; + } + while (end > cp) { + c = end[-1]; + if (!JS_ISXMLSPACE(c)) + break; + --end; + } + newlength = PTRDIFF(end, cp, jschar); + if (newlength == length) + return str; + offset = PTRDIFF(cp, start, jschar); + return js_NewDependentString(cx, str, offset, newlength, 0); +} + +static JSXML * +ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + uintN flags) +{ + JSXML *xml, *kid, *attr, *attrj; + JSString *str; + uint32 length, n, i, j; + JSParseNode *pn2, *pn3, *head, **pnp; + JSXMLNamespace *ns; + JSXMLQName *qn, *attrjqn; + JSXMLClass xml_class; + +#define PN2X_SKIP_CHILD ((JSXML *) 1) + + /* + * Cases return early to avoid common code that gets an outermost xml's + * object, which protects GC-things owned by xml and its descendants from + * garbage collection. + */ + xml = NULL; + if (!JS_EnterLocalRootScope(cx)) + return NULL; + switch (pn->pn_type) { + case TOK_XMLELEM: + length = inScopeNSes->length; + pn2 = pn->pn_head; + xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (!xml) + goto fail; + if (js_PushLocalRoot(cx, cx->localRootStack, (jsval)xml) < 0) + goto fail; + + flags &= ~XSF_PRECOMPILED_ROOT; + n = pn->pn_count; + JS_ASSERT(n >= 2); + n -= 2; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + while ((pn2 = pn2->pn_next) != NULL) { + if (!pn2->pn_next) { + /* Don't append the end tag! */ + JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + /* Store kid in xml right away, to protect it from GC. */ + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + kid->parent = xml; + ++i; + + /* XXX where is this documented in an XML spec, or in E4X? */ + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid->xml_value); + if (!str) + goto fail; + kid->xml_value = str; + } + } + + JS_ASSERT(i == n); + if (n < pn->pn_count - 2) + XMLArrayTrim(&xml->xml_kids); + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLLIST: + xml = js_NewXML(cx, JSXML_CLASS_LIST); + if (!xml) + goto fail; + + n = pn->pn_count; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Always ignore insignificant whitespace in lists -- we shouldn't + * condition this on an XML.ignoreWhitespace setting when the list + * constructor is XMLList (note XML/XMLList unification hazard). + */ + if (pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + ++i; + } + + if (n < pn->pn_count) + XMLArrayTrim(&xml->xml_kids); + break; + + case TOK_XMLSTAGO: + case TOK_XMLPTAGC: + length = inScopeNSes->length; + pn2 = pn->pn_head; + JS_ASSERT(pn2->pn_type == TOK_XMLNAME); + if (pn2->pn_arity == PN_LIST) + goto syntax; + + xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); + if (!xml) + goto fail; + + /* First pass: check syntax and process namespace declarations. */ + JS_ASSERT(pn->pn_count >= 1); + n = pn->pn_count - 1; + pnp = &pn2->pn_next; + head = *pnp; + while ((pn2 = *pnp) != NULL) { + size_t length; + const jschar *chars; + + if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + goto syntax; + + /* Enforce "Well-formedness constraint: Unique Att Spec". */ + for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { + if (pn3->pn_atom == pn2->pn_atom) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + str = ATOM_TO_STRING(pn2->pn_atom); + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + if (pn2->pn_type != TOK_XMLATTR) + goto syntax; + + length = JSSTRING_LENGTH(str); + chars = JSSTRING_CHARS(str); + if (length >= 5 && + IS_XMLNS_CHARS(chars) && + (length == 5 || chars[5] == ':')) { + JSString *uri, *prefix; + + uri = ATOM_TO_STRING(pn2->pn_atom); + if (length == 5) { + /* 10.3.2.1. Step 6(h)(i)(1)(a). */ + prefix = cx->runtime->emptyString; + } else { + prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); + if (!prefix) + goto fail; + } + + /* + * Once the new ns is appended to xml->xml_namespaces, it is + * protected from GC by the object that owns xml -- which is + * either xml->object if outermost, or the object owning xml's + * oldest ancestor if !outermost. + */ + ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); + if (!ns) + goto fail; + + /* + * Don't add a namespace that's already in scope. If someone + * extracts a child property from its parent via [[Get]], then + * we enforce the invariant, noted many times in ECMA-357, that + * the child's namespaces form a possibly-improper superset of + * its ancestors' namespaces. + */ + if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || + !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { + goto fail; + } + } + + JS_ASSERT(n >= 2); + n -= 2; + *pnp = pn2->pn_next; + /* XXXbe recycle pn2 */ + continue; + } + + pnp = &pn2->pn_next; + } + + /* + * If called from js_ParseNodeToXMLObject, emulate the effect of the + * ... wrapping done by "ToXML Applied to + * the String Type" (ECMA-357 10.3.1). + */ + if (flags & XSF_PRECOMPILED_ROOT) { + JS_ASSERT(length >= 1); + ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, + namespace_identity)); + ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); + if (!ns) + goto fail; + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + goto fail; + } + XMLArrayTrim(&xml->xml_namespaces); + + /* Second pass: process tag name and attributes, using namespaces. */ + pn2 = pn->pn_head; + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + xml->name = qn; + + JS_ASSERT((n & 1) == 0); + n >>= 1; + if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) + goto fail; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); + if (!qn) { + xml->xml_attrs.length = i; + goto fail; + } + + /* + * Enforce "Well-formedness constraint: Unique Att Spec", part 2: + * this time checking local name and namespace URI. + */ + for (j = 0; j < i; j++) { + attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); + attrjqn = attrj->name; + if (!js_CompareStrings(attrjqn->uri, qn->uri) && + !js_CompareStrings(attrjqn->localName, qn->localName)) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + JS_ASSERT(pn2->pn_type == TOK_XMLATTR); + + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); + attr->parent = xml; + attr->name = qn; + attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); + } + + /* Point tag closes its own namespace scope. */ + if (pn->pn_type == TOK_XMLPTAGC) + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: + str = ATOM_TO_STRING(pn->pn_atom); + qn = NULL; + if (pn->pn_type == TOK_XMLCOMMENT) { + if (flags & XSF_IGNORE_COMMENTS) + goto skip_child; + xml_class = JSXML_CLASS_COMMENT; + } else if (pn->pn_type == TOK_XMLPI) { + if (IS_XML(str)) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_RESERVED_ID, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(str))); + goto fail; + } + + if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) + goto skip_child; + + qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + + str = pn->pn_atom2 + ? ATOM_TO_STRING(pn->pn_atom2) + : cx->runtime->emptyString; + xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; + } else { + /* CDATA section content, or element text. */ + xml_class = JSXML_CLASS_TEXT; + } + + xml = js_NewXML(cx, xml_class); + if (!xml) + goto fail; + xml->name = qn; + if (pn->pn_type == TOK_XMLSPACE) + xml->xml_flags |= XMLF_WHITESPACE_TEXT; + xml->xml_value = str; + break; + + default: + goto syntax; + } + + JS_LeaveLocalRootScope(cx); + if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) + return NULL; + return xml; + +skip_child: + js_LeaveLocalRootScope(cx); + return PN2X_SKIP_CHILD; + +#undef PN2X_SKIP_CHILD + +syntax: + js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); +fail: + JS_LeaveLocalRootScope(cx); + return NULL; +} + +/* + * XML helper, object-ops, and library functions. We start with the helpers, + * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. + */ +static JSBool +GetXMLSetting(JSContext *cx, const char *name, jsval *vp) +{ + jsval v; + + if (!js_FindConstructor(cx, NULL, js_XML_str, &v)) + return JS_FALSE; + if (!JSVAL_IS_FUNCTION(cx, v)) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); +} + +static JSBool +FillSettingsCache(JSContext *cx) +{ + int i; + const char *name; + jsval v; + JSBool isSet; + + /* Note: XML_PRETTY_INDENT is not a boolean setting. */ + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) + return JS_FALSE; + if (isSet) + cx->xmlSettingFlags |= JS_BIT(i); + else + cx->xmlSettingFlags &= ~JS_BIT(i); + } + + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return JS_TRUE; +} + +static JSBool +GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) +{ + int i; + + if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) + return JS_FALSE; + + for (i = 0; xml_static_props[i].name; i++) { + if (!strcmp(xml_static_props[i].name, name)) { + *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; + return JS_TRUE; + } + } + *bp = JS_FALSE; + return JS_TRUE; +} + +static JSBool +GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) +{ + jsval v; + + return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); +} + +static JSBool +GetXMLSettingFlags(JSContext *cx, uintN *flagsp) +{ + JSBool flag; + + /* Just get the first flag to validate the setting flags cache. */ + if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) + return JS_FALSE; + *flagsp = cx->xmlSettingFlags; + return JS_TRUE; +} + +static JSXML * +ParseXMLSource(JSContext *cx, JSString *src) +{ + jsval nsval; + JSXMLNamespace *ns; + size_t urilen, srclen, length, offset, dstlen; + jschar *chars; + const jschar *srcp, *endp; + void *mark; + JSTokenStream *ts; + uintN lineno; + JSStackFrame *fp; + JSOp op; + JSParseNode *pn; + JSXML *xml; + JSXMLArray nsarray; + uintN flags; + + static const char prefix[] = ""; + static const char suffix[] = ""; + +#define constrlen(constr) (sizeof(constr) - 1) + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + urilen = JSSTRING_LENGTH(ns->uri); + srclen = JSSTRING_LENGTH(src); + length = constrlen(prefix) + urilen + constrlen(middle) + srclen + + constrlen(suffix); + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + dstlen = length; + js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); + offset = dstlen; + js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); + offset += urilen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); + offset += dstlen; + srcp = JSSTRING_CHARS(src); + js_strncpy(chars + offset, srcp, srclen); + offset += srclen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen); + chars [offset + dstlen] = 0; + + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewBufferTokenStream(cx, chars, length); + if (!ts) + return NULL; + for (fp = cx->fp; fp && !fp->pc; fp = fp->down) + continue; + if (fp) { + op = (JSOp) *fp->pc; + if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { + ts->filename = fp->script->filename; + lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + for (endp = srcp + srclen; srcp < endp; srcp++) + if (*srcp == '\n') + --lineno; + ts->lineno = lineno; + } + } + + JS_KEEP_ATOMS(cx->runtime); + pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); + xml = NULL; + if (pn && XMLArrayInit(cx, &nsarray, 1)) { + if (GetXMLSettingFlags(cx, &flags)) + xml = ParseNodeToXML(cx, pn, &nsarray, flags); + + XMLArrayFinish(cx, &nsarray); + } + JS_UNKEEP_ATOMS(cx->runtime); + + JS_ARENA_RELEASE(&cx->tempPool, mark); + JS_free(cx, chars); + return xml; + +#undef constrlen +} + +/* + * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). + * + * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce + * the constraint: + * + * for all x belonging to XML: + * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] + * + * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here + * (in new sub-step 6(a), renumbering the others to (b) and (c)). + * + * Same goes for 10.4.1 Step 7(a). + * + * In order for XML.prototype.namespaceDeclarations() to work correctly, the + * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be + * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such + * undeclared namespaces associated with x not belonging to ancestorNS. + */ +static JSXML * +OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) +{ + JSXMLNamespace *ns; + + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); + xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!ns || !xml) + return xml; + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return NULL; + ns->declared = JS_FALSE; + } + xml->parent = NULL; + return xml; +} + +static JSObject * +ToXML(JSContext *cx, jsval v) +{ + JSObject *obj; + JSXML *xml; + JSClass *clasp; + JSString *str; + uint32 length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length != 1) + goto bad; + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + return js_GetXMLObject(cx, xml); + } + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + length = 0; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + xml = NULL; +#endif + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + if (length == 0) { + obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); + if (!obj) + return NULL; + } else if (length == 1) { + xml = OrphanXMLChild(cx, xml, 0); + if (!xml) + return NULL; + obj = js_GetXMLObject(cx, xml); + if (!obj) + return NULL; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); + return NULL; + } + return obj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid); + +static JSObject * +ToXMLList(JSContext *cx, jsval v) +{ + JSObject *obj, *listobj; + JSXML *xml, *list, *kid; + JSClass *clasp; + JSString *str; + uint32 i, length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return NULL; + return listobj; + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + xml = NULL; + length = 0; + } else { + if (!JS_EnterLocalRootScope(cx)) + return NULL; + xml = ParseXMLSource(cx, str); + if (!xml) { + JS_LeaveLocalRootScope(cx); + return NULL; + } + length = JSXML_LENGTH(xml); + } + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (i = 0; i < length; i++) { + kid = OrphanXMLChild(cx, xml, i); + if (!kid) + return NULL; + if (!Append(cx, list, kid)) { + listobj = NULL; + break; + } + } + } + + if (xml) + JS_LeaveLocalRootScope(cx); + return listobj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +/* + * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString + * and their library-public js_* counterparts. The guts of MakeXMLCDataString, + * MakeXMLCommentString, and MakeXMLPIString are further factored into a common + * MakeXMLSpecialString subroutine. + * + * These functions take ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, + JSString *str, JSString *str2, + const jschar *prefix, size_t prefixlength, + const jschar *suffix, size_t suffixlength) +{ + JSStringBuffer localSB; + size_t length, length2, newlength; + jschar *bp, *base; + + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + + length = JSSTRING_LENGTH(str); + length2 = str2 ? JSSTRING_LENGTH(str2) : 0; + newlength = STRING_BUFFER_OFFSET(sb) + + prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + + suffixlength; + bp = base = (jschar *) + JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + js_FinishStringBuffer(sb); + return NULL; + } + + bp += STRING_BUFFER_OFFSET(sb); + js_strncpy(bp, prefix, prefixlength); + bp += prefixlength; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + if (length2 != 0) { + *bp++ = (jschar) ' '; + js_strncpy(bp, JSSTRING_CHARS(str2), length2); + bp += length2; + } + js_strncpy(bp, suffix, suffixlength); + bp[suffixlength] = 0; + + str = js_NewString(cx, base, newlength, 0); + if (!str) + free(base); + return str; +} + +static JSString * +MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', + 'C', 'D', 'A', 'T', 'A', + '['}; + static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + cdata_prefix_ucNstr, 9, + cdata_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; + static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + comment_prefix_ucNstr, 4, + comment_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, + JSString *value) +{ + static const jschar pi_prefix_ucNstr[] = {'<', '?'}; + static const jschar pi_suffix_ucNstr[] = {'?', '>'}; + + return MakeXMLSpecialString(cx, sb, name, value, + pi_prefix_ucNstr, 2, + pi_suffix_ucNstr, 2); +} + +/* + * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends + * equals, a double quote, an attribute value, and a closing double quote. + */ +static void +AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) +{ + js_AppendCString(sb, "=\""); + valstr = js_EscapeAttributeValue(cx, valstr); + if (!valstr) { + free(sb->base); + sb->base = STRING_BUFFER_ERROR_BASE; + return; + } + js_AppendJSString(sb, valstr); + js_AppendChar(sb, '"'); +} + +/* + * ECMA-357 10.2.1.1 EscapeElementValue helper method. + * + * This function takes ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '<' || c == '>') + newlength += 3; + else if (c == '&') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '>') + js_AppendCString(sb, js_gt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * This function takes ownership of sb->base, if sb is non-null, in all cases. + */ +static JSString * +EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '"') + newlength += 5; + else if (c == '<') + newlength += 3; + else if (c == '&' || c == '\n' || c == '\r' || c == '\t') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '"') + js_AppendCString(sb, js_quot_entity_str); + else if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else if (c == '\n') + js_AppendCString(sb, " "); + else if (c == '\r') + js_AppendCString(sb, " "); + else if (c == '\t') + js_AppendCString(sb, " "); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ +static JSXMLNamespace * +GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) +{ + JSXMLNamespace *match, *ns; + uint32 i, n; + jsval argv[2]; + JSObject *nsobj; + + JS_ASSERT(qn->uri); + if (!qn->uri) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + qn->prefix + ? js_ValueToPrintableString(cx, + STRING_TO_JSVAL(qn->prefix)) + : js_type_str[JSTYPE_VOID]); + return NULL; + } + + /* Look for a matching namespace in inScopeNSes, if provided. */ + match = NULL; + if (inScopeNSes) { + for (i = 0, n = inScopeNSes->length; i < n; i++) { + ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); + if (!ns) + continue; + + /* + * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: + * If we preserve prefixes, we must match null qn->prefix against + * an empty ns->prefix, in order to avoid generating redundant + * prefixed and default namespaces for cases such as: + * + * x = + * print(x.toXMLString()); + * + * Per 10.3.2.1, the namespace attribute in t has an empty string + * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): + * + * 1. If the [local name] property of a is "xmlns" + * a. Map ns.prefix to the empty string + * + * But t's name has a null prefix in this implementation, meaning + * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to + * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without + * saying how "no value" maps to an ECMA-357 value -- but it must + * map to the *undefined* prefix value). + * + * Since "" != undefined (or null, in the current implementation) + * the ECMA-357 spec will fail to match in [[GetNamespace]] called + * on t with argument {} U {(prefix="", uri="http://foo.com")}. + * This spec bug leads to ToXMLString results that duplicate the + * declared namespace. + */ + if (!js_CompareStrings(ns->uri, qn->uri) && + (ns->prefix == qn->prefix || + ((ns->prefix && qn->prefix) + ? !js_CompareStrings(ns->prefix, qn->prefix) + : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { + match = ns; + break; + } + } + } + + /* If we didn't match, make a new namespace from qn. */ + if (!match) { + argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; + argv[1] = STRING_TO_JSVAL(qn->uri); + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return NULL; + match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + } + return match; +} + +static JSString * +GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +{ + const jschar *cp, *start, *end; + size_t length, newlength, offset; + uint32 i, n, m, serial; + jschar *bp, *dp; + JSBool done; + JSXMLNamespace *ns; + JSString *prefix; + + JS_ASSERT(!IS_EMPTY(uri)); + + /* + * Try peeling off the last filename suffix or pathname component till + * we have a valid XML name. This heuristic will prefer "xul" given + * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any + * likely URI of the form ".../xbl2/2005". + */ + start = JSSTRING_CHARS(uri); + cp = end = start + JSSTRING_LENGTH(uri); + while (--cp > start) { + if (*cp == '.' || *cp == '/' || *cp == ':') { + ++cp; + if (IsXMLName(cp, PTRDIFF(end, cp, jschar))) + break; + end = --cp; + } + } + length = PTRDIFF(end, cp, jschar); + + /* + * Now search through decls looking for a collision. If we collide with + * an existing prefix, start tacking on a hyphen and a serial number. + */ + serial = 0; + bp = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + newlength = 0; +#endif + do { + done = JS_TRUE; + for (i = 0, n = decls->length; i < n; i++) { + ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); + if (ns && ns->prefix && + JSSTRING_LENGTH(ns->prefix) == length && + !memcmp(JSSTRING_CHARS(ns->prefix), cp, + length * sizeof(jschar))) { + if (!bp) { + newlength = length + 2 + (size_t) log10(n); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + js_strncpy(bp, cp, length); + } + + ++serial; + JS_ASSERT(serial <= n); + dp = bp + length + 2 + (size_t) log10(serial); + *dp = 0; + for (m = serial; m != 0; m /= 10) + *--dp = (jschar)('0' + m % 10); + *--dp = '-'; + JS_ASSERT(dp == bp + length); + + done = JS_FALSE; + break; + } + } + } while (!done); + + if (!bp) { + offset = PTRDIFF(cp, start, jschar); + prefix = js_NewDependentString(cx, uri, offset, length, 0); + } else { + prefix = js_NewString(cx, bp, newlength, 0); + if (!prefix) + JS_free(cx, bp); + } + return prefix; +} + +static JSBool +namespace_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsb->prefix) + return nsa->prefix && !js_CompareStrings(nsa->prefix, nsb->prefix); + return !js_CompareStrings(nsa->uri, nsb->uri); +} + +/* ECMA-357 10.2.1 and 10.2.2 */ +static JSString * +XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, + uintN indentLevel) +{ + JSBool pretty, indentKids; + JSStringBuffer sb; + JSString *str, *prefix, *kidstr; + JSXMLArrayCursor cursor; + uint32 i, n; + JSXMLArray empty, decls, ancdecls; + JSXMLNamespace *ns, *ns2; + uintN nextIndentLevel; + JSXML *attr, *kid; + + if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) + return NULL; + + js_InitStringBuffer(&sb); + if (pretty) + js_RepeatChar(&sb, ' ', indentLevel); + str = NULL; + + switch (xml->xml_class) { + case JSXML_CLASS_TEXT: + /* Step 4. */ + if (pretty) { + str = ChompXMLWhitespace(cx, xml->xml_value); + if (!str) + return NULL; + } else { + str = xml->xml_value; + } + return EscapeElementValue(cx, &sb, str); + + case JSXML_CLASS_ATTRIBUTE: + /* Step 5. */ + return EscapeAttributeValue(cx, &sb, xml->xml_value); + + case JSXML_CLASS_COMMENT: + /* Step 6. */ + return MakeXMLCommentString(cx, &sb, xml->xml_value); + + case JSXML_CLASS_PROCESSING_INSTRUCTION: + /* Step 7. */ + return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); + + case JSXML_CLASS_LIST: + /* ECMA-357 10.2.2. */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + i = 0; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && i != 0) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + ++i; + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto list_out; + + if (!sb.base) { + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return cx->runtime->emptyString; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); + list_out: + if (!str) + js_FinishStringBuffer(&sb); + return str; + + default:; + } + + /* After this point, control must flow through label out: to exit. */ + if (!JS_EnterLocalRootScope(cx)) + return NULL; + + /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ + if (!ancestorNSes) { + XMLArrayInit(cx, &empty, 0); + ancestorNSes = ∅ + } + XMLArrayInit(cx, &decls, 0); + ancdecls.capacity = 0; + + /* Clone in-scope namespaces not in ancestorNSes into decls. */ + XMLArrayCursorInit(&cursor, &xml->xml_namespaces); + while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { + /* NOTE: may want to exclude unused namespaces here. */ + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); + if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (ns) + goto out; + + /* + * Union ancestorNSes and decls into ancdecls. Note that ancdecls does + * not own its member references. In the spec, ancdecls has no name, but + * is always written out as (AncestorNamespaces U namespaceDeclarations). + */ + if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) + goto out; + for (i = 0, n = ancestorNSes->length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + for (i = 0, n = decls.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + + /* Step 11, except we don't clone ns unless its prefix is undefined. */ + ns = GetNamespace(cx, xml->name, &ancdecls); + if (!ns) + goto out; + + /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ + if (!ns->prefix) { + /* + * Create a namespace prefix that isn't used by any member of decls. + * Assign the new prefix to a copy of ns. Flag this namespace as if + * it were declared, for assertion-testing's sake later below. + * + * Erratum: if ns->prefix and xml->name are both null (*undefined* in + * ECMA-357), we know that xml was named using the default namespace + * (proof: see GetNamespace and the Namespace constructor called with + * two arguments). So we ought not generate a new prefix here, when + * we can declare ns as the default namespace for xml. + * + * This helps descendants inherit the namespace instead of redundantly + * redeclaring it with generated prefixes in each descendant. + */ + if (!xml->name->prefix) { + prefix = cx->runtime->emptyString; + } else { + prefix = GeneratePrefix(cx, ns->uri, &ancdecls); + if (!prefix) + goto out; + } + ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); + if (!ns) + goto out; + + /* + * If the xml->name was unprefixed, we must remove any declared default + * namespace from decls before appending ns. How can you get a default + * namespace in decls that doesn't match the one from name? Apparently + * by calling x.setNamespace(ns) where ns has no prefix. The other way + * to fix this is to update x's in-scope namespaces when setNamespace + * is called, but that's not specified by ECMA-357. + * + * Likely Erratum here, depending on whether the lack of update to x's + * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an + * erratum or not. Note that changing setNamespace to update the list + * of in-scope namespaces will change x.namespaceDeclarations(). + */ + if (IS_EMPTY(prefix)) { + i = XMLArrayFindMember(&decls, ns, namespace_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &decls, i, JS_TRUE); + } + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || + !XMLARRAY_APPEND(cx, &decls, ns)) { + goto out; + } + } + + /* Format the element or point-tag into sb. */ + js_AppendChar(&sb, '<'); + + if (ns->prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + js_AppendJSString(&sb, xml->name->localName); + + /* + * Step 16 makes a union to avoid writing two loops in step 17, to share + * common attribute value appending spec-code. We prefer two loops for + * faster code and less data overhead. + */ + + /* Step 17(c): append XML namespace declarations. */ + XMLArrayCursorInit(&cursor, &decls); + while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + JS_ASSERT(ns2->declared); + + js_AppendCString(&sb, " xmlns"); + + /* 17(c)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + ns2->prefix = prefix; + } + + /* 17(c)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendChar(&sb, ':'); + js_AppendJSString(&sb, ns2->prefix); + } + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, ns2->uri); + } + XMLArrayCursorFinish(&cursor); + if (ns2) + goto out; + + /* Step 17(b): append attributes. */ + XMLArrayCursorInit(&cursor, &xml->xml_attrs); + while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + js_AppendChar(&sb, ' '); + ns2 = GetNamespace(cx, attr->name, &ancdecls); + if (!ns2) + break; + + /* 17(b)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + + /* Again, we avoid copying ns2 until we know it's prefix-less. */ + ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); + if (!ns2) + break; + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || + !XMLARRAY_APPEND(cx, &decls, ns2)) { + break; + } + } + + /* 17(b)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendJSString(&sb, ns2->prefix); + js_AppendChar(&sb, ':'); + } + + /* 17(b)(iv). */ + js_AppendJSString(&sb, attr->name->localName); + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, attr->xml_value); + } + XMLArrayCursorFinish(&cursor); + if (attr) + goto out; + + /* Step 18: handle point tags. */ + n = xml->xml_kids.length; + if (n == 0) { + js_AppendCString(&sb, "/>"); + } else { + /* Steps 19 through 25: handle element content, and open the end-tag. */ + js_AppendChar(&sb, '>'); + indentKids = n > 1 || + (n == 1 && + (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && + kid->xml_class != JSXML_CLASS_TEXT); + + if (pretty && indentKids) { + if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) + goto out; + nextIndentLevel = indentLevel + i; + } else { + nextIndentLevel = 0; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && indentKids) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto out; + + if (pretty && indentKids) { + js_AppendChar(&sb, '\n'); + js_RepeatChar(&sb, ' ', indentLevel); + } + js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + + /* Step 27. */ + js_AppendJSString(&sb, xml->name->localName); + js_AppendChar(&sb, '>'); + } + + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + goto out; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); +out: + JS_LeaveLocalRootScope(cx); + if (!str && STRING_BUFFER_OK(&sb)) + js_FinishStringBuffer(&sb); + XMLArrayFinish(cx, &decls); + if (ancdecls.capacity != 0) + XMLArrayFinish(cx, &ancdecls); + return str; +} + +/* ECMA-357 10.2 */ +static JSString * +ToXMLString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + JSXML *xml; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + js_type_str[JSVAL_IS_NULL(v) + ? JSTYPE_NULL + : JSTYPE_VOID]); + return NULL; + } + + if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) + return js_ValueToString(cx, v); + + if (JSVAL_IS_STRING(v)) + return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); + + obj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, obj)) { + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + str = js_ValueToString(cx, v); + if (!str) + return NULL; + return EscapeElementValue(cx, NULL, str); + } + + /* Handle non-element cases in this switch, returning from each case. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + return XMLToXMLString(cx, xml, NULL, 0); +} + +static JSXMLQName * +ToAttributeName(JSContext *cx, jsval v) +{ + JSString *name, *uri, *prefix; + JSObject *obj; + JSClass *clasp; + JSXMLQName *qn; + JSTempValueRooter tvr; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + uri = prefix = cx->runtime->emptyString; + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_ATTR_NAME, + JS_GetStringBytes(name)); + } + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass) + return (JSXMLQName *) JS_GetPrivate(cx, obj); + + if (clasp == &js_QNameClass.base) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + } else { + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + uri = prefix = cx->runtime->emptyString; + } + } + + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return NULL; + + /* + * Temp and local root scope APIs take GC-thing pointers tagged as jsvals + * and blindly untag. Since qn is a GC-thing pointer, we can treat it as + * an object pointer. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(qn), &tvr); + obj = js_GetAttributeNameObject(cx, qn); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!obj) + return NULL; + return qn; +} + +static JSXMLQName * +ToXMLName(JSContext *cx, jsval v, jsid *funidp) +{ + JSString *name; + JSObject *obj; + JSClass *clasp; + uint32 index; + JSXMLQName *qn; + JSAtom *atom; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) + goto bad; + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) + goto out; + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + goto construct; + } + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + + /* + * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: + * + * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception + * + * First, _P_ should be _s_, to refer to the given string. + * + * Second, why does ToXMLName applied to the string type throw TypeError + * only for numeric literals without any leading or trailing whitespace? + * + * If the idea is to reject uint32 property names, then the check needs to + * be stricter, to exclude hexadecimal and floating point literals. + */ + if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) + goto bad; + + if (*JSSTRING_CHARS(name) == '@') { + name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); + if (!name) + return NULL; + *funidp = 0; + return ToAttributeName(cx, STRING_TO_JSVAL(name)); + } + +construct: + v = STRING_TO_JSVAL(name); + obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); + if (!obj) + return NULL; + +out: + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; + if (qn->uri && atom && + (qn->uri == ATOM_TO_STRING(atom) || + !js_CompareStrings(qn->uri, ATOM_TO_STRING(atom)))) { + if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) + return NULL; + } else { + *funidp = 0; + } + return qn; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); + return NULL; +} + +/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ +static JSBool +AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *match, *ns2; + uint32 i, n, m; + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ + if (!ns->prefix) { + match = NULL; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && !js_CompareStrings(ns2->uri, ns->uri)) { + match = ns2; + break; + } + } + if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) + return JS_FALSE; + } else { + if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) + return JS_TRUE; + match = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + m = XML_NOT_FOUND; +#endif + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && ns2->prefix && + !js_CompareStrings(ns2->prefix, ns->prefix)) { + match = ns2; + m = i; + break; + } + } + if (match && js_CompareStrings(match->uri, ns->uri)) { + ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, + JSXMLNamespace); + JS_ASSERT(ns2 == match); + match->prefix = NULL; + if (!AddInScopeNamespace(cx, xml, match)) + return JS_FALSE; + } + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return JS_FALSE; + } + + /* OPTION: enforce that descendants have superset namespaces. */ + return JS_TRUE; +} + +/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *xml) +{ + uint32 i, j, k, n; + JSXML *kid; + + JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); + i = list->xml_kids.length; + n = 1; + if (xml->xml_class == JSXML_CLASS_LIST) { + list->xml_target = xml->xml_target; + list->xml_targetprop = xml->xml_targetprop; + n = JSXML_LENGTH(xml); + k = i + n; + if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) + return JS_FALSE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); + if (kid) + XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); + } + return JS_TRUE; + } + + list->xml_target = xml->parent; + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + list->xml_targetprop = NULL; + else + list->xml_targetprop = xml->name; + if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) + return JS_FALSE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); + +static JSXML * +DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) +{ + JSXML *copy; + JSBool ok; + + /* Our caller may not be protecting newborns with a local root scope. */ + if (!JS_EnterLocalRootScope(cx)) + return NULL; + copy = DeepCopyInLRS(cx, xml, flags); + if (copy) { + if (obj) { + /* Caller provided the object for this copy, hook 'em up. */ + ok = JS_SetPrivate(cx, obj, copy); + if (ok) + copy->object = obj; + } else { + ok = js_GetXMLObject(cx, copy) != NULL; + } + if (!ok) + copy = NULL; + } + JS_LeaveLocalRootScope(cx); + return copy; +} + +/* + * (i) We must be in a local root scope (InLRS). + * (ii) parent must have a rooted object. + * (iii) from's owning object must be locked if not thread-local. + */ +static JSBool +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, + uintN flags) +{ + uint32 j, n; + JSXMLArrayCursor cursor; + JSBool ok; + JSXML *kid, *kid2; + JSString *str; + + JS_ASSERT(cx->localRootStack); + + n = from->length; + if (!XMLArraySetCapacity(cx, to, n)) + return JS_FALSE; + + XMLArrayCursorInit(&cursor, from); + j = 0; + ok = JS_TRUE; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if ((flags & XSF_IGNORE_COMMENTS) && + kid->xml_class == JSXML_CLASS_COMMENT) { + continue; + } + if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && + kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { + continue; + } + if ((flags & XSF_IGNORE_WHITESPACE) && + (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { + continue; + } + kid2 = DeepCopyInLRS(cx, kid, flags); + if (!kid2) { + to->length = j; + ok = JS_FALSE; + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid2->xml_value); + if (!str) { + to->length = j; + ok = JS_FALSE; + break; + } + kid2->xml_value = str; + } + + XMLARRAY_SET_MEMBER(to, j, kid2); + ++j; + if (parent->xml_class != JSXML_CLASS_LIST) + kid2->parent = parent; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (j < n) + XMLArrayTrim(to); + return JS_TRUE; +} + +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) +{ + JSXML *copy; + JSXMLQName *qn; + JSBool ok; + uint32 i, n; + JSXMLNamespace *ns, *ns2; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + copy = js_NewXML(cx, xml->xml_class); + if (!copy) + return NULL; + qn = xml->name; + if (qn) { + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) { + ok = JS_FALSE; + goto out; + } + } + copy->name = qn; + copy->xml_flags = xml->xml_flags; + + if (JSXML_HAS_VALUE(xml)) { + copy->xml_value = xml->xml_value; + ok = JS_TRUE; + } else { + ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); + if (!ok) + goto out; + + if (xml->xml_class == JSXML_CLASS_LIST) { + copy->xml_target = xml->xml_target; + copy->xml_targetprop = xml->xml_targetprop; + } else { + n = xml->xml_namespaces.length; + ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); + if (!ok) + goto out; + for (i = 0; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); + if (!ns2) { + copy->xml_namespaces.length = i; + ok = JS_FALSE; + goto out; + } + XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); + } + + ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, + 0); + if (!ok) + goto out; + } + } + +out: + if (!ok) + return NULL; + return copy; +} + +static void +ReportBadXMLName(JSContext *cx, jsval id) +{ + JSString *name; + + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + JS_GetStringBytes(name)); + } +} + +/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ +static JSBool +DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) +{ + uint32 index; + JSXML *kid; + + if (!js_IdIsIndex(id, &index)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid) + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); + +static JSBool +MatchAttrName(JSXMLQName *nameqn, JSXML *attr) +{ + JSXMLQName *attrqn = attr->name; + + return (IS_STAR(nameqn->localName) || + !js_CompareStrings(attrqn->localName, nameqn->localName)) && + (!nameqn->uri || + !js_CompareStrings(attrqn->uri, nameqn->uri)); +} + +static JSBool +MatchElemName(JSXMLQName *nameqn, JSXML *elem) +{ + return (IS_STAR(nameqn->localName) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + !js_CompareStrings(elem->name->localName, nameqn->localName))) && + (!nameqn->uri || + (elem->xml_class == JSXML_CLASS_ELEMENT && + !js_CompareStrings(elem->name->uri, nameqn->uri))); +} + +/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ +static JSBool +DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) +{ + uint32 i, n; + JSXML *attr, *kid; + + if (xml->xml_class == JSXML_CLASS_ELEMENT && + OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (attr && MatchAttrName(nameqn, attr)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && + MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + if (!DescendantsHelper(cx, kid, nameqn, list)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSXML * +Descendants(JSContext *cx, JSXML *xml, jsval id) +{ + jsid funid; + JSXMLQName *nameqn; + JSObject *listobj; + JSXML *list, *kid; + uint32 i, n; + JSBool ok; + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return NULL; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (funid) + return list; + + /* + * Protect nameqn's object and strings from GC by linking list to it + * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, + * which protects list. Any other object allocations occuring beneath + * DescendantsHelper use local roots. + */ + list->name = nameqn; + if (!JS_EnterLocalRootScope(cx)) + return NULL; + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = DescendantsHelper(cx, kid, nameqn, list); + if (!ok) + break; + } + } + } else { + ok = DescendantsHelper(cx, xml, nameqn, list); + } + JS_LeaveLocalRootScope(cx); + if (!ok) + return NULL; + list->name = NULL; + return list; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +/* Recursive (JSXML *) parameterized version of Equals. */ +static JSBool +XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) +{ + JSXMLQName *qn, *vqn; + uint32 i, j, n; + JSXMLArrayCursor cursor, vcursor; + JSXML *kid, *vkid, *attr, *vattr; + JSBool ok; + JSObject *xobj, *vobj; + +retry: + if (xml->xml_class != vxml->xml_class) { + if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) + goto retry; + } + if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); + if (vxml) + goto retry; + } + *bp = JS_FALSE; + return JS_TRUE; + } + + qn = xml->name; + vqn = vxml->name; + if (qn) { + *bp = vqn && + !js_CompareStrings(qn->localName, vqn->localName) && + !js_CompareStrings(qn->uri, vqn->uri); + } else { + *bp = vqn == NULL; + } + if (!*bp) + return JS_TRUE; + + if (JSXML_HAS_VALUE(xml)) { + *bp = !js_CompareStrings(xml->xml_value, vxml->xml_value); + } else if (xml->xml_kids.length != vxml->xml_kids.length) { + *bp = JS_FALSE; + } else { + XMLArrayCursorInit(&cursor, &xml->xml_kids); + XMLArrayCursorInit(&vcursor, &vxml->xml_kids); + for (;;) { + kid = (JSXML *) XMLArrayCursorNext(&cursor); + vkid = (JSXML *) XMLArrayCursorNext(&vcursor); + if (!kid || !vkid) { + *bp = !kid && !vkid; + ok = JS_TRUE; + break; + } + xobj = js_GetXMLObject(cx, kid); + vobj = js_GetXMLObject(cx, vkid); + ok = xobj && vobj && + xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); + if (!ok || !*bp) + break; + } + XMLArrayCursorFinish(&vcursor); + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { + n = xml->xml_attrs.length; + if (n != vxml->xml_attrs.length) + *bp = JS_FALSE; + for (i = 0; *bp && i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); + if (j == XML_NOT_FOUND) { + *bp = JS_FALSE; + break; + } + vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); + if (!vattr) + continue; + *bp = !js_CompareStrings(attr->xml_value, vattr->xml_value); + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ +static JSBool +Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) +{ + JSObject *vobj; + JSXML *vxml; + + if (JSVAL_IS_PRIMITIVE(v)) { + *bp = JS_FALSE; + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!vxml) + return JS_TRUE; + vobj = js_GetXMLObject(cx, vxml); + if (!vobj) + return JS_FALSE; + return js_XMLObjectOps.equality(cx, vobj, v, bp); + } + if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) + *bp = JS_TRUE; + } + } else { + vobj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, vobj)) { + *bp = JS_FALSE; + } else { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (!XMLEquals(cx, xml, vxml, bp)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) +{ + JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); + + do { + if (xml == kid) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CYCLIC_VALUE, js_XML_str); + return JS_FALSE; + } + } while ((xml = xml->parent) != NULL); + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.11 XML [[Insert]]. */ +static JSBool +Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) +{ + uint32 j, n; + JSXML *vxml, *kid; + JSObject *vobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + n = 1; + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) + return JS_TRUE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + if (!CheckCycle(cx, xml, kid)) + return JS_FALSE; + } + } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + } + } + } + if (!vxml) { + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + } + + if (i > xml->xml_kids.length) + i = xml->xml_kids.length; + + if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) + return JS_FALSE; + + if (vxml->xml_class == JSXML_CLASS_LIST) { + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + kid->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); + + /* OPTION: enforce that descendants have superset namespaces. */ + } + } else { + vxml->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + return JS_TRUE; +} + +static JSBool +IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idvp = INT_TO_JSVAL(index); + } else { + str = js_NumberToString(cx, (jsdouble) index); + if (!str) + return JS_FALSE; + *idvp = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.12 XML [[Replace]]. */ +static JSBool +Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) +{ + uint32 i, n; + JSXML *vxml, *kid; + JSObject *vobj; + jsval junk; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (!js_IdIsIndex(id, &i)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + /* + * 9.1.1.12 + * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. + * It should therefore constrain callers to pass in _i <= x.[[Length]]_. + */ + n = xml->xml_kids.length; + if (i >= n) { + if (!IndexToIdVal(cx, n, &id)) + return JS_FALSE; + i = n; + } + + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { + case JSXML_CLASS_ELEMENT: + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + goto do_replace; + + case JSXML_CLASS_LIST: + if (i < n && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!Insert(cx, xml, i, v)) + return JS_FALSE; + break; + + default: + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + + do_replace: + vxml->parent = xml; + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid) + kid->parent = NULL; + } + if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) + return JS_FALSE; + break; + } + + return JS_TRUE; +} + +/* Forward declared -- its implementation uses other statics that call it. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result); + +/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ +static JSBool +DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *kid, *parent; + JSBool isIndex; + JSXMLArray *array; + uint32 length, index, deleteCount; + JSXMLQName *nameqn; + jsid funid; + JSObject *nameobj, *kidobj; + JSXMLNameMatcher matcher; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + isIndex = js_IdIsIndex(id, &index); + if (JSXML_HAS_KIDS(xml)) { + array = &xml->xml_kids; + length = array->length; + } else { + array = NULL; + length = 0; + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.3. */ + if (isIndex && index < length) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (parent) { + JS_ASSERT(parent != xml); + JS_ASSERT(JSXML_HAS_KIDS(parent)); + + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameqn = kid->name; + nameobj = js_GetAttributeNameObject(cx, nameqn); + if (!nameobj || !js_GetXMLObject(cx, parent)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(nameobj); + if (!DeleteProperty(cx, parent->object, id, vp)) + return JS_FALSE; + } else { + index = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(index != XML_NOT_FOUND); + if (!IndexToIdVal(cx, index, &id)) + return JS_FALSE; + if (!DeleteByIndex(cx, parent, id, vp)) + return JS_FALSE; + } + } + + XMLArrayDelete(cx, array, index, JS_TRUE); + } else { + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) + return JS_FALSE; + } + } + } + } else { + /* ECMA-357 9.1.1.3. */ + if (isIndex) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + goto out; + nameobj = nameqn->object; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto out; + array = &xml->xml_attrs; + length = array->length; + matcher = MatchAttrName; + } else { + matcher = MatchElemName; + } + if (length != 0) { + deleteCount = 0; + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && matcher(nameqn, kid)) { + kid->parent = NULL; + XMLArrayDelete(cx, array, index, JS_FALSE); + ++deleteCount; + } else if (deleteCount != 0) { + XMLARRAY_SET_MEMBER(array, + index - deleteCount, + array->vector[index]); + } + } + array->length -= deleteCount; + } + } + +out: + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +/* + * Class compatibility mask flag bits stored in xml_methods[i].extra. If XML + * and XMLList are unified (an incompatible change to ECMA-357), then we don't + * need any of this. + */ +#define XML_MASK 0x1 +#define XMLLIST_MASK 0x2 +#define GENERIC_MASK (XML_MASK | XMLLIST_MASK) +#define CLASS_TO_MASK(c) (1 + ((c) == JSXML_CLASS_LIST)) + +static JSBool +GetFunction(JSContext *cx, JSObject *obj, JSXML *xml, jsid id, jsval *vp) +{ + JSFunction *fun; + + do { + /* XXXbe really want a separate scope for function::*. */ + if (!js_GetProperty(cx, obj, id, vp)) + return JS_FALSE; + if (JSVAL_IS_FUNCTION(cx, *vp)) { + if (xml && OBJECT_IS_XML(cx, obj)) { + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); + if (fun->spare && + (fun->spare & CLASS_TO_MASK(xml->xml_class)) == 0) { + /* XML method called on XMLList or vice versa. */ + *vp = JSVAL_VOID; + } + } + break; + } + } while ((obj = OBJ_GET_PROTO(cx, obj)) != NULL); + return JS_TRUE; +} + +static JSBool +SyncInScopeNamespaces(JSContext *cx, JSXML *xml) +{ + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + nsarray = &xml->xml_namespaces; + while ((xml = xml->parent) != NULL) { + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ +static JSBool +GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list, *kid; + uint32 index; + JSObject *kidobj, *listobj, *nameobj; + JSXMLQName *nameqn; + jsid funid; + JSBool ok; + JSXMLArrayCursor cursor; + jsval kidval; + JSXMLArray *array; + JSXMLNameMatcher matcher; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + +#ifdef __GNUC__ + list = NULL; /* quell GCC overwarning */ +#endif + +retry: + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.1 starts here. */ + if (js_IdIsIndex(id, &index)) { + /* + * Erratum: 9.2 is not completely clear that indexed properties + * correspond to kids, but that's what it seems to say, and it's + * what any sane user would want. + */ + if (index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(kidobj); + } else { + *vp = JSVAL_VOID; + } + return JS_TRUE; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return GetFunction(cx, obj, xml, funid, vp); + + /* + * Recursion through GetProperty may allocate more list objects, so + * we make use of local root scopes here. Each new allocation will + * push the newborn onto the local root stack. + */ + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* + * NB: nameqn is already protected from GC by cx->newborn[GCX_OBJECT] + * until listobj is created. After that, a local root keeps listobj + * alive, and listobj's private keeps nameqn alive via targetprop. + */ + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + } else { + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) { + ok = JS_FALSE; + break; + } + ok = GetProperty(cx, kidobj, id, &kidval); + if (!ok) + break; + kidobj = JSVAL_TO_OBJECT(kidval); + kid = (JSXML *) JS_GetPrivate(cx, kidobj); + if (JSXML_LENGTH(kid) > 0) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } + } else { + /* ECMA-357 9.1.1.1 starts here. */ + if (js_IdIsIndex(id, &index)) { + obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!obj) + return JS_FALSE; + xml = (JSXML *) JS_GetPrivate(cx, obj); + goto retry; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return GetFunction(cx, obj, xml, funid, vp); + nameobj = nameqn->object; + + /* + * Recursion through GetProperty may allocate more list objects, so + * we make use of local root scopes here. Each new allocation will + * push the newborn onto the local root stack. + */ + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + } else { + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + if (JSXML_HAS_KIDS(xml)) { + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + XMLArrayCursorInit(&cursor, array); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (matcher(nameqn, kid)) { + if (array == &xml->xml_kids && + kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = SyncInScopeNamespaces(cx, kid); + if (!ok) + break; + } + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + XMLArrayCursorFinish(&cursor); + } + } + } + + /* Common tail code for list and non-list cases. */ + JS_LeaveLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* + * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given list's + * [[TargetProperty]] to the property that is being appended. This means + * that any use of the internal [[Get]] property returns a list which, + * when used by e.g. [[Insert]] duplicates the last element matched by id. + * See bug 336921. + */ + list->xml_targetprop = nameqn; + *vp = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +static JSXML * +CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) +{ + JS_ASSERT(xml->object != obj); + + xml = DeepCopy(cx, xml, obj, 0); + if (!xml) + return NULL; + + JS_ASSERT(xml->object == obj); + return xml; +} + +#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ + (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) + +static JSString * +KidToString(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + JSObject *kidobj; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) + return cx->runtime->emptyString; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return NULL; + return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); +} + +/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ +static JSBool +PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok, primitiveAssign; + enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; + jsval roots[3]; + JSTempValueRooter tvr; + JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; + JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSXMLQName *targetprop, *nameqn, *attrqn; + uint32 index, i, j, k, n, q; + jsval attrval, nsval, junk; + jsid funid; + JSString *left, *right, *space; + JSXMLNamespace *ns; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + vobj = JSVAL_TO_OBJECT(*vp); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + /* Control flow after here must exit via label out. */ + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + roots[ID_ROOT] = id; + roots[VAL_ROOT] = *vp; + JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.2. */ + if (js_IdIsIndex(id, &index)) { + /* Step 1 sets i to the property index. */ + i = index; + + /* 2(a-b). */ + if (xml->xml_target) { + ok = ResolveValue(cx, xml->xml_target, &rxml); + if (!ok) + goto out; + if (!rxml) + goto out; + JS_ASSERT(rxml->object); + } else { + rxml = NULL; + } + + /* 2(c). */ + if (index >= xml->xml_kids.length) { + /* 2(c)(i). */ + if (rxml) { + if (rxml->xml_class == JSXML_CLASS_LIST) { + if (rxml->xml_kids.length != 1) + goto out; + rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); + if (!rxml) + goto out; + ok = js_GetXMLObject(cx, rxml) != NULL; + if (!ok) + goto out; + } + + /* + * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets + * _y.[[Parent]] = r_ where _r_ is the result of + * [[ResolveValue]] called on _x.[[TargetObject]] in + * 2(a)(i). This can result in text parenting text: + * + * var MYXML = new XML(); + * MYXML.appendChild(new XML("Giants")); + * + * (testcase from Werner Sharp ). + * + * To match insertChildAfter, insertChildBefore, + * prependChild, and setChildren, we should silently + * do nothing in this case. + */ + if (!JSXML_HAS_KIDS(rxml)) + goto out; + } + + /* 2(c)(ii) is distributed below as several js_NewXML calls. */ + targetprop = xml->xml_targetprop; + if (!targetprop || IS_STAR(targetprop->localName)) { + /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ + kid = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!kid) + goto bad; + } else { + nameobj = js_GetXMLQNameObject(cx, targetprop); + if (!nameobj) + goto bad; + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* + * 2(c)(iii)(1-3). + * Note that rxml can't be null here, because target + * and targetprop are non-null. + */ + ok = GetProperty(cx, rxml->object, id, &attrval); + if (!ok) + goto out; + attrobj = JSVAL_TO_OBJECT(attrval); + attr = (JSXML *) JS_GetPrivate(cx, attrobj); + if (JSXML_LENGTH(attr) != 0) + goto out; + + kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + } else { + /* 2(c)(v). */ + kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); + } + if (!kid) + goto bad; + + /* An important bit of 2(c)(ii). */ + kid->name = targetprop; + } + + /* Final important bit of 2(c)(ii). */ + kid->parent = rxml; + + /* 2(c)(vi-vii). */ + i = xml->xml_kids.length; + if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { + /* + * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. + * y.[[Parent]] is here called kid->parent, which we know + * from 2(c)(ii) is _r_, here called rxml. So let's just + * test that! Erratum, the spec should be simpler here. + */ + if (rxml) { + JS_ASSERT(JSXML_HAS_KIDS(rxml)); + n = rxml->xml_kids.length; + j = n - 1; + if (n != 0 && i != 0) { + for (n = j, j = 0; j < n; j++) { + if (rxml->xml_kids.vector[j] == + xml->xml_kids.vector[i-1]) { + break; + } + } + } + + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); + if (!ok) + goto out; + } + + /* + * 2(c)(vii)(2-3). + * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a + * typo for [[TargetProperty]]. + */ + if (vxml) { + kid->name = (vxml->xml_class == JSXML_CLASS_LIST) + ? vxml->xml_targetprop + : vxml->name; + } + } + + /* 2(c)(viii). */ + ok = Append(cx, xml, kid); + if (!ok) + goto out; + } + + /* 2(d). */ + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 2(e). */ + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameobj = js_GetAttributeNameObject(cx, kid->name); + if (!nameobj) + goto bad; + id = OBJECT_TO_JSVAL(nameobj); + + if (parent) { + /* 2(e)(i). */ + parentobj = parent->object; + ok = PutProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + + /* 2(e)(ii). */ + ok = GetProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); + + /* 2(e)(iii). */ + xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; + } + } + + /* 2(f). */ + else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + /* 2(f)(i) Create a shallow copy _c_ of _V_. */ + copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!copyobj) + goto bad; + copy = (JSXML *) JS_GetPrivate(cx, copyobj); + n = vxml->xml_kids.length; + ok = XMLArraySetCapacity(cx, ©->xml_kids, n); + if (!ok) + goto out; + for (k = 0; k < n; k++) { + kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); + XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); + } + + JS_ASSERT(parent != xml); + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); + if (!ok) + goto out; + +#ifdef DEBUG + /* Erratum: this loop in the spec is useless. */ + for (j = 0, n = copy->xml_kids.length; j < n; j++) { + kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); + JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) + == kid2); + } +#endif + } + + /* + * 2(f)(iv-vi). + * Erratum: notice the unhandled zero-length V basis case and + * the off-by-one errors for the n != 0 cases in the spec. + */ + if (n == 0) { + XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); + } else { + ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); + if (!ok) + goto out; + + for (j = 0; j < n; j++) + xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; + } + } + + /* 2(g). */ + else if (vxml || JSXML_HAS_VALUE(kid)) { + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, *vp); + if (!ok) + goto out; + + vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); + if (!vxml) + goto out; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); + } + + /* + * 2(g)(iii). + * Erratum: _V_ may not be of type XML, but all index-named + * properties _x[i]_ in an XMLList _x_ must be of type XML, + * according to 9.2.1.1 Overview and other places in the spec. + * + * Thanks to 2(d), we know _V_ (*vp here) is either a string + * or an XML/XMLList object. If *vp is a string, call ToXML + * on it to satisfy the constraint. + */ + if (!vxml) { + JS_ASSERT(JSVAL_IS_STRING(*vp)); + vobj = ToXML(cx, *vp); + if (!vobj) + goto bad; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + + /* 2(h). */ + else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + id = ATOM_KEY(cx->runtime->atomState.starAtom); + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * 3. + * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null + * or an r with r.[[Length]] != 1, throw TypeError. + */ + n = JSXML_LENGTH(xml); + if (n > 1) + goto type_error; + if (n == 0) { + ok = ResolveValue(cx, xml, &rxml); + if (!ok) + goto out; + if (!rxml || JSXML_LENGTH(rxml) != 1) + goto type_error; + ok = Append(cx, xml, rxml); + if (!ok) + goto out; + } + JS_ASSERT(JSXML_LENGTH(xml) == 1); + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + goto out; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * ECMA-357 9.1.1.2. + * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted + * effort in ToString or [[DeepCopy]]. + */ + if (js_IdIsIndex(id, &index)) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + goto bad; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + goto bad; + if (funid) { + ok = js_SetProperty(cx, obj, funid, vp); + goto out; + } + nameobj = nameqn->object; + + if (JSXML_HAS_VALUE(xml)) + goto out; + + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } else { + rxml = DeepCopyInLRS(cx, vxml, 0); + if (!rxml || !js_GetXMLObject(cx, rxml)) + goto bad; + vxml = rxml; + *vp = OBJECT_TO_JSVAL(vxml->object); + } + roots[VAL_ROOT] = *vp; + + /* + * 6. + * Erratum: why is this done here, so early? use is way later.... + */ + ok = js_GetDefaultXMLNamespace(cx, &nsval); + if (!ok) + goto out; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* 7(a). */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) + goto out; + + /* 7(b-c). */ + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + } else { + left = KidToString(cx, vxml, 0); + if (!left) + goto bad; + + space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + for (i = 1; i < n; i++) { + left = js_ConcatStrings(cx, left, space); + if (!left) + goto bad; + right = KidToString(cx, vxml, i); + if (!right) + goto bad; + left = js_ConcatStrings(cx, left, right); + if (!left) + goto bad; + } + + roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); + } + } else { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 7(d-e). */ + match = NULL; + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrqn = attr->name; + if (!js_CompareStrings(attrqn->localName, nameqn->localName) && + (!nameqn->uri || + !js_CompareStrings(attrqn->uri, nameqn->uri))) { + if (!match) { + match = attr; + } else { + nameobj = js_GetAttributeNameObject(cx, attrqn); + if (!nameobj) + goto bad; + + id = OBJECT_TO_JSVAL(nameobj); + ok = DeleteProperty(cx, obj, id, &junk); + if (!ok) + goto out; + --i; + } + } + } + + /* 7(f). */ + attr = match; + if (!attr) { + /* 7(f)(i-ii). */ + if (!nameqn->uri) { + left = right = cx->runtime->emptyString; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 7(f)(iii). */ + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto bad; + attr->parent = xml; + attr->name = nameqn; + + /* 7(f)(iv). */ + ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); + if (!ok) + goto out; + + /* 7(f)(v-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = AddInScopeNamespace(cx, xml, ns); + if (!ok) + goto out; + } + + /* 7(g). */ + attr->xml_value = JSVAL_TO_STRING(*vp); + goto out; + } + + /* 8-9. */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && + !IS_STAR(nameqn->localName)) { + goto out; + } + + /* 10-11. */ + id = JSVAL_VOID; + primitiveAssign = !vxml && !IS_STAR(nameqn->localName); + + /* 12. */ + k = n = xml->xml_kids.length; + kid2 = NULL; + while (k != 0) { + --k; + kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id)) { + ok = DeleteByIndex(cx, xml, id, &junk); + if (!ok) + goto out; + } + ok = IndexToIdVal(cx, k, &id); + if (!ok) + goto out; + kid2 = kid; + } + } + + /* + * Erratum: ECMA-357 specified child insertion inconsistently: + * insertChildBefore and insertChildAfter insert an arbitrary XML + * instance, and therefore can create cycles, but appendChild as + * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on + * its argument. But the "Semantics" in 13.4.4.3 do not include + * any [[DeepCopy]] call. + * + * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) + * required adding cycle detection, and allowing duplicate kids to + * be created (see comment 6 in the bug). Allowing duplicate kid + * references means the loop above will delete all but the lowest + * indexed reference, and each [[DeleteByIndex]] nulls the kid's + * parent. Thus the need to restore parent here. This is covered + * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. + */ + if (kid2) { + JS_ASSERT(kid2->parent == xml || !kid2->parent); + if (!kid2->parent) + kid2->parent = xml; + } + + /* 13. */ + if (JSVAL_IS_VOID(id)) { + /* 13(a). */ + ok = IndexToIdVal(cx, n, &id); + if (!ok) + goto out; + + /* 13(b). */ + if (primitiveAssign) { + if (!nameqn->uri) { + ns = (JSXMLNamespace *) + JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + left = ns->uri; + right = ns->prefix; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 13(b)(iii). */ + vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); + if (!vobj) + goto bad; + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + vxml->parent = xml; + vxml->name = nameqn; + + /* 13(b)(iv-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); + if (!ok) + goto out; + ok = AddInScopeNamespace(cx, vxml, ns); + if (!ok) + goto out; + } + } + + /* 14. */ + if (primitiveAssign) { + JSXMLArrayCursor cursor; + + js_IdIsIndex(id, &index); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + cursor.index = index; + kid = (JSXML *) XMLArrayCursorItem(&cursor); + if (JSXML_HAS_KIDS(kid)) { + XMLArrayFinish(cx, &kid->xml_kids); + ok = XMLArrayInit(cx, &kid->xml_kids, 1); + } + + /* 14(b-c). */ + /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ + if (ok) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { + roots[VAL_ROOT] = *vp; + if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) + ok = Replace(cx, kid, JSVAL_ZERO, *vp); + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 15(a). */ + ok = Replace(cx, xml, id, *vp); + } + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + JS_LeaveLocalRootScope(cx); + return ok; + +type_error: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_PUT, + js_ValueToPrintableString(cx, id)); +bad: + ok = JS_FALSE; + goto out; +} + +/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result) +{ + JSXML *target, *base; + JSXMLQName *targetprop; + JSObject *targetpropobj; + jsval id, tv; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { + if (!js_GetXMLObject(cx, list)) + return JS_FALSE; + *result = list; + return JS_TRUE; + } + + target = list->xml_target; + targetprop = list->xml_targetprop; + if (!target || !targetprop || IS_STAR(targetprop->localName)) { + *result = NULL; + return JS_TRUE; + } + + targetpropobj = js_GetXMLQNameObject(cx, targetprop); + if (!targetpropobj) + return JS_FALSE; + if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { + *result = NULL; + return JS_TRUE; + } + + if (!ResolveValue(cx, target, &base)) + return JS_FALSE; + if (!base) { + *result = NULL; + return JS_TRUE; + } + if (!js_GetXMLObject(cx, base)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(targetpropobj); + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + + if (JSXML_LENGTH(target) == 0) { + if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { + *result = NULL; + return JS_TRUE; + } + tv = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!PutProperty(cx, base->object, id, &tv)) + return JS_FALSE; + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + } + + *result = target; + return JS_TRUE; +} + +/* + * HasProperty must be able to return a found JSProperty and the object in + * which it was found, if id is of the form function::name. For other ids, + * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp + * and null in *objp. + * + * DROP_PROPERTY helps HasProperty callers drop function properties without + * trying to drop the magic FOUND_XML_PROPERTY cookie. + */ +#define FOUND_XML_PROPERTY ((JSProperty *) 1) +#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ + ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ + : (void) 0) + +/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ +static JSBool +HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, + JSProperty **propp) +{ + JSXML *xml, *kid; + JSXMLArrayCursor cursor; + JSObject *kidobj; + JSXMLQName *qn; + jsid funid; + JSXMLArray *array; + JSXMLNameMatcher matcher; + uint32 i, n; + + *objp = NULL; + *propp = NULL; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + n = JSXML_LENGTH(xml); + if (js_IdIsIndex(id, &i)) { + if (i < n) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) + break; + if (*propp) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (kid) + return *propp != NULL; + } else { + if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { + if (i == 0) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + qn = ToXMLName(cx, id, &funid); + if (!qn) + return JS_FALSE; + if (funid) + return js_LookupProperty(cx, obj, funid, objp, propp); + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + for (i = 0, n = array->length; i < n; i++) { + kid = XMLARRAY_MEMBER(array, i, JSXML); + if (kid && matcher(qn, kid)) { + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + } + } + + return JS_TRUE; +} + +static void +xml_finalize(JSContext *cx, JSObject *obj) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (!xml) + return; + if (xml->object == obj) + xml->object = NULL; + UNMETER(xml_stats.livexmlobj); +} + +static void +xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len, void *arg) +{ + uint32 i; + JSXML *elt; + + for (i = 0; i < len; i++) { + elt = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[120]; + + if (elt->xml_class == JSXML_CLASS_LIST) { + strcpy(buf, js_XMLList_str); + } else if (JSXML_HAS_NAME(elt)) { + JSXMLQName *qn = elt->name; + + JS_snprintf(buf, sizeof buf, "%s::%s", + qn->uri ? JS_GetStringBytes(qn->uri) : "*", + JS_GetStringBytes(qn->localName)); + } else { + JSString *str = elt->xml_value; + size_t srclen = JSSTRING_LENGTH(str); + size_t dstlen = sizeof buf; + + if (srclen >= sizeof buf / 6) + srclen = sizeof buf / 6 - 1; + js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, + buf, &dstlen); + } +#else + const char *buf = NULL; +#endif + JS_MarkGCThing(cx, elt, buf, arg); + } + } +} + +/* + * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to + * be native. Therefore, xml_lookupProperty must return a valid JSProperty + * pointer parameter via *propp to signify "property found". Since the only + * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from + * js_FindXMLProperty (in this file) and js_FindProperty (in jsobj.c, called + * from jsinterp.c), the only time we add a JSScopeProperty here is when an + * unqualified name or XML name is being accessed. + * + * This scope property both speeds up subsequent js_Find*Property calls, and + * keeps the JSOP_NAME code in js_Interpret happy by giving it an sprop with + * (getter, setter) == (GetProperty, PutProperty). We can't use that getter + * and setter as js_XMLClass's getProperty and setProperty, because doing so + * would break the XML methods, which are function-valued properties of the + * XML.prototype object. + * + * NB: xml_deleteProperty must take care to remove any property added here. + */ +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + JSScopeProperty *sprop; + + if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) + return JS_FALSE; + + if (*propp == FOUND_XML_PROPERTY) { + sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SPROP_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!sprop) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + *objp = obj; + *propp = (JSProperty *) sprop; + } + return JS_TRUE; +} + +static JSBool +xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + if (JSVAL_IS_FUNCTION(cx, value) || getter || setter || + (attrs & JSPROP_ENUMERATE) == 0 || + (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, + propp); + } + + if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) + return JS_FALSE; + if (propp) + *propp = NULL; + return JS_TRUE; +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (id == JS_DEFAULT_XML_NAMESPACE_ID) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + return GetProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return PutProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + JSBool *foundp) +{ + JSObject *pobj; + + if (prop) { + *foundp = JS_TRUE; + } else { + if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) + return JS_FALSE; + if (prop) + DROP_PROPERTY(cx, pobj, prop); + *foundp = (prop != NULL); + } + return JS_TRUE; +} + +static JSBool +xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + *attrsp = found ? JSPROP_ENUMERATE : 0; + return JS_TRUE; +} + +static JSBool +xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + if (found) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_XML_ATTRS); + } + return !found; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + /* + * If this object has its own (mutable) scope, and if id isn't an index, + * then we may have added a property to the scope in xml_lookupProperty + * for it to return to mean "found" and to provide a handle for access + * operations to call the property's getter or setter. The property also + * helps speed up unqualified accesses via the property cache, avoiding + * what amount to two HasProperty searches. + * + * But now it's time to remove any such property, to purge the property + * cache and remove the scope entry. + */ + if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { + if (!js_DeleteProperty(cx, obj, id, rval)) + return JS_FALSE; + } + + return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); +} + +static JSBool +xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSXML *xml; + + if (hint == JSTYPE_OBJECT) { + /* Called from for..in code in js_Interpret: return an XMLList. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!obj) + return JS_FALSE; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); +} + +static JSBool +xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSXML *xml; + uint32 length, index; + JSXMLArrayCursor *cursor; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + *idp = INT_TO_JSID(index); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return JS_TRUE; +} + +static uint32 +xml_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + JS_MarkGCThing(cx, xml, js_private_str, arg); + return js_Mark(cx, obj, arg); +} + +static void +xml_clear(JSContext *cx, JSObject *obj) +{ +} + +static JSBool +HasSimpleContent(JSXML *xml) +{ + JSXML *kid; + JSBool simple; + uint32 i, n; + +again: + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + return JS_FALSE; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) + return JS_TRUE; + if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + xml = kid; + goto again; + } + } + /* FALL THROUGH */ + default: + simple = JS_TRUE; + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + simple = JS_FALSE; + break; + } + } + return simple; + } +} + +/* + * 11.2.2.1 Step 3(d) onward. + */ +static JSObject * +xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSXML *xml; + JSTempValueRooter tvr; + jsval roots[2]; + enum { + FUN_ROOT = 0, + OBJ_ROOT = 1 + }; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + xml = (JSXML *) JS_GetPrivate(cx, obj); + memset(roots, 0, sizeof(roots)); + JS_PUSH_TEMP_ROOT(cx, sizeof roots / sizeof *roots, roots, &tvr); + + /* From this point the control must flow through out: or bad: */ + retry: + if (!GetFunction(cx, obj, xml, id, &roots[FUN_ROOT])) + goto bad; + if (JSVAL_IS_VOID(roots[FUN_ROOT]) && OBJECT_IS_XML(cx, obj)) { + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + obj = js_GetXMLObject(cx, xml); + if (!obj) + goto bad; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + goto retry; + } + } + } else if (HasSimpleContent(xml)) { + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + goto bad; + if (!js_ValueToObject(cx, STRING_TO_JSVAL(str), &obj)) + goto bad; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + if (!js_GetProperty(cx, obj, id, &roots[FUN_ROOT])) + goto bad; + } + } + out: + *vp = roots[FUN_ROOT]; + if (obj) { + /* + * If we just POP tvr, then it is possible that nothing roots obj, see + * bug 353165. To allow our callers to assume at least weakly rooting + * of the result, we root obj via newborn array. Similarly we root the + * value of roots[FUNCTION] since getMethod callers have a bad habit + * of passing a pointer to unrooted local value as vp. + */ + cx->newborn[GCX_OBJECT] = (JSGCThing *)obj; + cx->lastInternalResult = roots[FUN_ROOT]; + } + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; + bad: + obj = NULL; + goto out; +} + +static JSBool +xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return js_SetProperty(cx, obj, id, vp); +} + +static JSBool +xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp) +{ + JSXML *xml, *kid; + uint32 length, index; + JSXMLArrayCursor *cursor; + JSObject *kidobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + JS_ASSERT(INT_FITS_IN_JSVAL(length)); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + if (vp) + *vp = JSVAL_VOID; + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { + if (++index == length) + goto destroy; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + JS_ASSERT(INT_FITS_IN_JSVAL(index)); + *idp = INT_TO_JSID(index); + *vp = OBJECT_TO_JSVAL(kidobj); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + destroy: + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXML *xml, *vxml; + JSObject *vobj; + JSBool ok; + JSString *str, *vstr; + jsdouble d, d2; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, xml, v, bp); + } else if (vxml) { + if (vxml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); + } else { + if (((xml->xml_class == JSXML_CLASS_TEXT || + xml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(vxml)) || + ((vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(xml))) { + ok = JS_EnterLocalRootScope(cx); + if (ok) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = !js_CompareStrings(str, vstr); + JS_LeaveLocalRootScope(cx); + } + } else { + ok = XMLEquals(cx, xml, vxml, bp); + } + } + } else { + ok = JS_EnterLocalRootScope(cx); + if (ok) { + if (HasSimpleContent(xml)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = !js_CompareStrings(str, vstr); + } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) { + ok = JS_FALSE; + } else if (JSVAL_IS_STRING(v)) { + *bp = !js_CompareStrings(str, JSVAL_TO_STRING(v)); + } else { + ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); + if (ok) { + d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) + : *JSVAL_TO_DOUBLE(v); + *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); + } + } + } else { + *bp = JS_FALSE; + } + JS_LeaveLocalRootScope(cx); + } + } + return ok; +} + +static JSBool +xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + JSBool ok; + JSObject *listobj, *robj; + JSXML *list, *lxml, *rxml; + + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + goto out; + } + + list = (JSXML *) JS_GetPrivate(cx, listobj); + lxml = (JSXML *) JS_GetPrivate(cx, obj); + ok = Append(cx, list, lxml); + if (!ok) + goto out; + + if (VALUE_IS_XML(cx, v)) { + rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + } else { + robj = ToXML(cx, v); + if (!robj) { + ok = JS_FALSE; + goto out; + } + rxml = (JSXML *) JS_GetPrivate(cx, robj); + } + ok = Append(cx, list, rxml); + if (!ok) + goto out; + + *vp = OBJECT_TO_JSVAL(listobj); +out: + JS_LeaveLocalRootScope(cx); + return ok; +} + +/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { + { js_NewObjectMap, js_DestroyObjectMap, + xml_lookupProperty, xml_defineProperty, + xml_getProperty, xml_setProperty, + xml_getAttributes, xml_setAttributes, + xml_deleteProperty, xml_defaultValue, + xml_enumerate, js_CheckAccess, + NULL, NULL, + NULL, NULL, + NULL, xml_hasInstance, + js_SetProtoOrParent, js_SetProtoOrParent, + xml_mark, xml_clear, + NULL, NULL }, + xml_getMethod, xml_setMethod, + xml_enumerateValues, xml_equality, + xml_concatenate +}; + +static JSObjectOps * +xml_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_XMLObjectOps.base; +} + +JS_FRIEND_DATA(JSClass) js_XMLClass = { + js_XML_str, JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, + xml_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static JSObject * +CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, + uintN argc, jsval *argv) +{ + JSObject *tmp; + jsval rval; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); + return JSVAL_TO_OBJECT(rval); +} + +#define XML_METHOD_PROLOG \ + JS_BEGIN_MACRO \ + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ + if (!xml) \ + return JS_FALSE; \ + JS_END_MACRO + +static JSBool +xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + if (!AddInScopeNamespace(cx, xml, ns)) + return JS_FALSE; + ns->declared = JS_TRUE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *vxml; + jsval name, v; + JSObject *vobj; + + XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!js_GetAnyName(cx, &name)) + return JS_FALSE; + + if (!GetProperty(cx, obj, name, &v)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vobj = JSVAL_TO_OBJECT(v); + JS_ASSERT(OBJECT_IS_XML(cx, vobj)); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); + + if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) + return JS_FALSE; + if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, argv[0]); + if (!qn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ + return GetProperty(cx, obj, argv[0], rval); +} + +/* XML and XMLList */ +static JSBool +xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSXMLQName *qn; + JSTempValueRooter tvr; + JSBool ok; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + qn = ToAttributeName(cx, name); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn->object); + JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); + ok = GetProperty(cx, obj, name, rval); + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, + jsval *rval) +{ + uint32 index; + JSXML *kid; + JSObject *kidobj; + + /* ECMA-357 13.4.4.6 */ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + + if (js_IdIsIndex(name, &index)) { + if (index >= JSXML_LENGTH(xml)) { + *rval = JSVAL_VOID; + } else { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *rval = JSVAL_VOID; + } else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(kidobj); + } + } + return JS_TRUE; + } + + return GetProperty(cx, obj, name, rval); +} + +static JSBool +xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSXMLArrayCursor cursor; + jsval name, v; + JSObject *listobj, *kidobj; + + XML_METHOD_PROLOG; + name = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 13.5.4.4 */ + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + if (!xml_child_helper(cx, kidobj, kid, name, &v)) + break; + if (JSVAL_IS_VOID(v)) { + /* The property didn't exist in this kid. */ + continue; + } + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && + !Append(cx, list, vxml)) { + break; + } + } + XMLArrayCursorFinish(&cursor); + return !kid; + } + + return xml_child_helper(cx, obj, xml, name, rval); +} + +static JSBool +xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *parent; + uint32 i, n; + + XML_METHOD_PROLOG; + parent = xml->parent; + if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { + if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) + break; + } + JS_ASSERT(i < n); + return js_NewNumberValue(cx, i, rval); +} + +/* XML and XMLList */ +static JSBool +xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSObject *listobj, *kidobj; + JSBool ok; + uint32 i, n; + jsval v; + + XML_METHOD_PROLOG; + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_comments(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.9 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval value; + JSBool eq; + JSXMLArrayCursor cursor; + JSObject *kidobj; + + XML_METHOD_PROLOG; + value = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + eq = JS_FALSE; + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) + break; + if (eq) + break; + } + XMLArrayCursorFinish(&cursor); + if (kid) + return JS_FALSE; + } else { + if (!xml_equality(cx, obj, value, &eq)) + return JS_FALSE; + } + *rval = BOOLEAN_TO_JSVAL(eq); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *copy; + + XML_METHOD_PROLOG; + copy = DeepCopy(cx, xml, NULL, 0); + if (!copy) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list; + jsval name; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + list = Descendants(cx, xml, name); + if (!list) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSObject *listobj, *kidobj; + JSBool ok; + JSXMLArrayCursor cursor; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + if (funid) + return JS_TRUE; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_elements(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && + MatchElemName(nameqn, kid)) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSObject *pobj; + JSProperty *prop; + + if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) + return JS_FALSE; + + name = argv[0]; + if (!HasProperty(cx, obj, name, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, + rval); + } + DROP_PROPERTY(cx, pobj, prop); + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; +again: + switch (xml->xml_class) { + case JSXML_CLASS_ATTRIBUTE: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + *rval = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) { + *rval = JSVAL_TRUE; + } else if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + obj = kidobj; + xml = (JSXML *) JS_GetPrivate(cx, obj); + goto again; + } + } + /* FALL THROUGH */ + default: + *rval = JSVAL_FALSE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + *rval = JSVAL_TRUE; + break; + } + } + break; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); + return JS_TRUE; +} + +typedef struct JSTempRootedNSArray { + JSTempValueRooter tvr; + JSXMLArray array; + jsval value; /* extra root for temporaries */ +} JSTempRootedNSArray; + +JS_STATIC_DLL_CALLBACK(void) +mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) +{ + JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; + + namespace_mark_vector(cx, + (JSXMLNamespace **)tmp->array.vector, + tmp->array.length, NULL); + XMLArrayCursorMark(cx, tmp->array.cursors); + if (JSVAL_IS_GCTHING(tmp->value)) + GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value", NULL); +} + +static void +InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + XMLArrayInit(cx, &tmp->array, 0); + tmp->value = JSVAL_NULL; + JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); +} + +static void +FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); + JS_POP_TEMP_ROOT(cx, &tmp->tvr); + XMLArrayFinish(cx, &tmp->array); +} + +/* + * Populate a new JS array with elements of JSTempRootedNSArray.array and + * place the result into rval. rval must point to a rooted location. + */ +static JSBool +TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) +{ + JSObject *arrayobj; + uint32 i, n; + JSXMLNamespace *ns; + JSObject *nsobj; + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + for (i = 0, n = tmp->array.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); + if (!ns) + continue; + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) + return JS_FALSE; + tmp->value = OBJECT_TO_JSVAL(nsobj); + if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) +{ + uint32 length, i, j, n; + JSXMLNamespace *ns, *ns2; + + length = nsarray->length; + do { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + continue; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + + for (j = 0; j < length; j++) { + ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); + if (ns2 && + ((ns2->prefix && ns->prefix) + ? !js_CompareStrings(ns2->prefix, ns->prefix) + : !js_CompareStrings(ns2->uri, ns->uri))) { + break; + } + } + + if (j == length) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + ++length; + } + } + } while ((xml = xml->parent) != NULL); + JS_ASSERT(length == nsarray->length); + + return JS_TRUE; +} + +static JSBool +xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSTempRootedNSArray namespaces; + JSBool ok; + + XML_METHOD_PROLOG; + + InitTempNSArray(cx, &namespaces); + ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && + TempNSArrayToJSArray(cx, &namespaces, rval); + FinishTempNSArray(cx, &namespaces); + return ok; +} + +static JSBool +xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = 0; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + ++i; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = xml->xml_kids.length; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_LIST) { + *rval = JSVAL_ONE; + } else { + if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; + return JS_TRUE; +} + +static JSBool +xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + JSObject *nameobj; + + XML_METHOD_PROLOG; + if (!xml->name) { + *rval = JSVAL_NULL; + } else { + nameobj = js_GetXMLQNameObject(cx, xml->name); + if (!nameobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(nameobj); + } + return JS_TRUE; +} + +static JSBool +xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *prefix; + JSTempRootedNSArray inScopeNSes; + JSBool ok; + jsuint i, length; + JSXMLNamespace *ns; + JSObject *nsobj; + + XML_METHOD_PROLOG; + if (argc == 0 && + (xml->xml_class == JSXML_CLASS_TEXT || + xml->xml_class == JSXML_CLASS_COMMENT || + xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + if (argc == 0) { + prefix = NULL; + } else { + prefix = js_ValueToString(cx, argv[0]); + if (!prefix) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(prefix); /* local root */ + } + + /* After this point the control must flow through label out. */ + InitTempNSArray(cx, &inScopeNSes); + ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); + if (!ok) + goto out; + + if (!prefix) { + ns = GetNamespace(cx, xml->name, &inScopeNSes.array); + if (!ns) { + ok = JS_FALSE; + goto out; + } + } else { + ns = NULL; + for (i = 0, length = inScopeNSes.array.length; i < length; i++) { + ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); + if (ns && ns->prefix && !js_CompareStrings(ns->prefix, prefix)) + break; + ns = NULL; + } + } + + if (!ns) { + *rval = JSVAL_VOID; + } else { + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) { + ok = JS_FALSE; + goto out; + } + *rval = OBJECT_TO_JSVAL(nsobj); + } + + out: + FinishTempNSArray(cx, &inScopeNSes); + return JS_TRUE; +} + +static JSBool +xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *yml; + JSBool ok; + JSTempRootedNSArray ancestors, declared; + uint32 i, n; + JSXMLNamespace *ns; + + XML_METHOD_PROLOG; + if (JSXML_HAS_VALUE(xml) || xml->xml_class == JSXML_CLASS_LIST) + return JS_TRUE; + + /* From here, control flow must goto out to finish these arrays. */ + ok = JS_TRUE; + InitTempNSArray(cx, &ancestors); + InitTempNSArray(cx, &declared); + yml = xml; + + while ((yml = yml->parent) != NULL) { + JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); + for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); + if (ns && + !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); + if (!ok) + goto out; + } + } + } + + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &declared.array, ns); + if (!ok) + goto out; + } + } + + ok = TempNSArrayToJSArray(cx, &declared, rval); + +out: + /* Finishing must be in reverse order of initialization to follow LIFO. */ + FinishTempNSArray(cx, &declared); + FinishTempNSArray(cx, &ancestors); + return ok; +} + +static const char js_attribute_str[] = "attribute"; +static const char js_text_str[] = "text"; + +/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ +const char *js_xml_class_str[] = { + "list", + "element", + js_attribute_str, + "processing-instruction", + js_text_str, + "comment" +}; + +static JSBool +xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + XML_METHOD_PROLOG; + str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) +{ + jsval junk; + + if (xml->xml_class == JSXML_CLASS_LIST) + return DeleteProperty(cx, obj, id, &junk); + return DeleteByIndex(cx, xml, id, &junk); +} + +/* + * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace + * text between tags to be removed by normalize. + */ +static JSBool +IsXMLSpace(JSString *str) +{ + const jschar *cp, *end; + + cp = JSSTRING_CHARS(str); + end = cp + JSSTRING_LENGTH(str); + while (cp < end) { + if (!JS_ISXMLSPACE(*cp)) + return JS_FALSE; + ++cp; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid, *kid2; + uint32 i, n; + JSObject *kidobj; + JSString *str; + jsval junk; + + XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) + return JS_FALSE; + } else if (kid->xml_class == JSXML_CLASS_TEXT) { + while (i + 1 < n && + (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && + kid2->xml_class == JSXML_CLASS_TEXT) { + str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); + if (!str) + return JS_FALSE; + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) + return JS_FALSE; + n = xml->xml_kids.length; + kid->xml_value = str; + } + if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) + return JS_FALSE; + n = xml->xml_kids.length; + --i; + } + } + } + + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *parent, *kid; + uint32 i, n; + JSObject *parentobj; + + XML_METHOD_PROLOG; + parent = xml->parent; + if (xml->xml_class == JSXML_CLASS_LIST) { + *rval = JSVAL_VOID; + n = xml->xml_kids.length; + if (n == 0) + return JS_TRUE; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + return JS_TRUE; + parent = kid->parent; + for (i = 1; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->parent != parent) + return JS_TRUE; + } + } + + if (!parent) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(parentobj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSObject *listobj, *kidobj; + JSBool ok; + JSXMLArrayCursor cursor; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + if (funid) + return JS_TRUE; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_processingInstructions(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 13.4.4.28 Step 4. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (IS_STAR(nameqn->localName) || + !js_CompareStrings(nameqn->localName, kid->name->localName))) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +static JSBool +xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return Insert(cx, xml, 0, argv[0]); +} + +/* XML and XMLList */ +static JSBool +xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + uint32 index; + + XML_METHOD_PROLOG; + name = argv[0]; + *rval = JSVAL_FALSE; + if (js_IdIsIndex(name, &index)) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *rval = BOOLEAN_TO_JSVAL(index == 0); + } + } + return JS_TRUE; +} + +static JSBool +namespace_full_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix && + js_CompareStrings(nsa->prefix, nsb->prefix)) { + return JS_FALSE; + } + return !js_CompareStrings(nsa->uri, nsb->uri); +} + +static JSBool +xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *thisns, *attrns; + uint32 i, n; + JSXML *attr, *kid; + + thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); + JS_ASSERT(thisns); + if (thisns == ns) + return JS_TRUE; + + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); + JS_ASSERT(attrns); + if (attrns == ns) + return JS_TRUE; + } + + i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + if (!xml_removeNamespace_helper(cx, kid, ns)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + + /* NOTE: remove ns from each ancestor if not used by that ancestor. */ + return xml_removeNamespace_helper(cx, xml, ns); +} + +static JSBool +xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *vxml, *kid; + jsval name, value, id, junk; + uint32 index; + JSObject *nameobj; + JSXMLQName *nameqn; + + XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + value = argv[1]; + vxml = VALUE_IS_XML(cx, value) + ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) + : NULL; + if (!vxml) { + if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) + return JS_FALSE; + value = argv[1]; + } else { + vxml = DeepCopy(cx, vxml, NULL, 0); + if (!vxml) + return JS_FALSE; + value = argv[1] = OBJECT_TO_JSVAL(vxml->object); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + name = argv[0]; + if (js_IdIsIndex(name, &index)) + return Replace(cx, xml, name, value); + + /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ + nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); + if (!nameobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameobj); + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + id = JSVAL_VOID; + index = xml->xml_kids.length; + while (index != 0) { + --index; + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!IndexToIdVal(cx, index, &id)) + return JS_FALSE; + } + } + if (JSVAL_IS_VOID(id)) + return JS_TRUE; + return Replace(cx, xml, id, value); +} + +static JSBool +xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), + &argv[0])) { + return JS_FALSE; + } + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + JSXMLQName *nameqn; + JSString *namestr; + + XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { + nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); + namestr = nameqn->localName; + } else { + if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) + return JS_FALSE; + name = argv[0]; + namestr = JSVAL_TO_STRING(name); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name->localName = namestr; + return JS_TRUE; +} + +static JSBool +xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *nsowner; + jsval name; + JSXMLQName *nameqn; + JSObject *nameobj; + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && + !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) + ->uri) { + name = argv[0] = STRING_TO_JSVAL(nameqn->localName); + } + + nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); + if (!nameobj) + return JS_FALSE; + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + /* ECMA-357 13.4.4.35 Step 4. */ + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + nameqn->uri = cx->runtime->emptyString; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name = nameqn; + + /* + * Erratum: nothing in 13.4.4.35 talks about making the name match the + * in-scope namespaces, either by finding an in-scope namespace with a + * matching uri and setting the new name's prefix to that namespace's + * prefix, or by extending the in-scope namespaces for xml (which are in + * xml->parent if xml is an attribute or a PI). + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + + if (nameqn->prefix) { + /* + * The name being set has a prefix, which originally came from some + * namespace object (which may be the null namespace, where both the + * prefix and uri are the empty string). We must go through a full + * GetNamespace in case that namespace is in-scope in nsowner. + * + * If we find such an in-scope namespace, we return true right away, + * in this block. Otherwise, we fall through to the final return of + * AddInScopeNamespace(cx, nsowner, ns). + */ + ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); + if (!ns) + return JS_FALSE; + + /* XXXbe have to test membership to see whether GetNamespace added */ + if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) + return JS_TRUE; + } else { + /* + * At this point, we know nameqn->prefix is null, so nameqn->uri can't + * be the empty string (the null namespace always uses the empty string + * for both prefix and uri). + * + * This means we must inline GetNamespace and specialize it to match + * uri only, never prefix. If we find a namespace with nameqn's uri + * already in nsowner->xml_namespaces, then all that we need do is set + * nameqn->prefix to that namespace's prefix. + * + * If no such namespace exists, we can create one without going through + * the constructor, because we know nameqn->uri is non-empty (so prefix + * does not need to be converted from null to empty by QName). + */ + JS_ASSERT(!IS_EMPTY(nameqn->uri)); + + nsarray = &nsowner->xml_namespaces; + for (i = 0, n = nsarray->length; i < n; i++) { + ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); + if (ns && !js_CompareStrings(ns->uri, nameqn->uri)) { + nameqn->prefix = ns->prefix; + return JS_TRUE; + } + } + + ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); + if (!ns) + return JS_FALSE; + } + + return AddInScopeNamespace(cx, nsowner, ns); +} + +static JSBool +xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *nsowner; + JSObject *nsobj, *qnobj; + JSXMLNamespace *ns; + jsval qnargv[2]; + + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT && + xml->xml_class != JSXML_CLASS_ATTRIBUTE) { + return JS_TRUE; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml || !js_GetXMLQNameObject(cx, xml->name)) + return JS_FALSE; + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); + if (!nsobj) + return JS_FALSE; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + ns->declared = JS_TRUE; + + qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); + qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); + qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); + if (!qnobj) + return JS_FALSE; + + xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); + + /* + * Erratum: the spec fails to update the governing in-scope namespaces. + * See the erratum noted in xml_setName, above. + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + return AddInScopeNamespace(cx, nsowner, ns); +} + +/* XML and XMLList */ +static JSBool +xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSObject *listobj, *kidobj; + uint32 i, n; + JSBool ok; + jsval v; + + XML_METHOD_PROLOG; + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_text(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + return JS_FALSE; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) + return JS_FALSE; + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_TEXT) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSString * +xml_toString_helper(JSContext *cx, JSXML *xml) +{ + JSString *str, *kidstr; + JSXML *kid; + JSXMLArrayCursor cursor; + + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || + xml->xml_class == JSXML_CLASS_TEXT) { + return xml->xml_value; + } + + if (!HasSimpleContent(xml)) + return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); + + str = cx->runtime->emptyString; + JS_EnterLocalRootScope(cx); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class != JSXML_CLASS_COMMENT && + kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { + kidstr = xml_toString_helper(cx, kid); + if (!kidstr) { + str = NULL; + break; + } + str = js_ConcatStrings(cx, str, kidstr); + if (!str) + break; + } + } + XMLArrayCursorFinish(&cursor); + JS_LeaveLocalRootScope(cx); + return str; +} + +static JSBool +xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + XML_METHOD_PROLOG; + str = xml_toString_helper(cx, xml); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec xml_methods[] = { + {"addNamespace", xml_addNamespace, 1,0,XML_MASK}, + {"appendChild", xml_appendChild, 1,0,XML_MASK}, + {js_attribute_str, xml_attribute, 1,0,GENERIC_MASK}, + {"attributes", xml_attributes, 0,0,GENERIC_MASK}, + {"child", xml_child, 1,0,GENERIC_MASK}, + {"childIndex", xml_childIndex, 0,0,XML_MASK}, + {"children", xml_children, 0,0,GENERIC_MASK}, + {"comments", xml_comments, 0,0,GENERIC_MASK}, + {"contains", xml_contains, 1,0,GENERIC_MASK}, + {"copy", xml_copy, 0,0,GENERIC_MASK}, + {"descendants", xml_descendants, 1,0,GENERIC_MASK}, + {"elements", xml_elements, 1,0,GENERIC_MASK}, + {"hasOwnProperty", xml_hasOwnProperty, 1,0,GENERIC_MASK}, + {"hasComplexContent", xml_hasComplexContent, 1,0,GENERIC_MASK}, + {"hasSimpleContent", xml_hasSimpleContent, 1,0,GENERIC_MASK}, + {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,XML_MASK}, + {"insertChildAfter", xml_insertChildAfter, 2,0,XML_MASK}, + {"insertChildBefore", xml_insertChildBefore, 2,0,XML_MASK}, + {js_length_str, xml_length, 0,0,GENERIC_MASK}, + {js_localName_str, xml_localName, 0,0,XML_MASK}, + {js_name_str, xml_name, 0,0,XML_MASK}, + {js_namespace_str, xml_namespace, 1,0,XML_MASK}, + {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,XML_MASK}, + {"nodeKind", xml_nodeKind, 0,0,XML_MASK}, + {"normalize", xml_normalize, 0,0,GENERIC_MASK}, + {js_xml_parent_str, xml_parent, 0,0,GENERIC_MASK}, + {"processingInstructions",xml_processingInstructions,1,0,GENERIC_MASK}, + {"prependChild", xml_prependChild, 1,0,XML_MASK}, + {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,GENERIC_MASK}, + {"removeNamespace", xml_removeNamespace, 1,0,XML_MASK}, + {"replace", xml_replace, 2,0,XML_MASK}, + {"setChildren", xml_setChildren, 1,0,XML_MASK}, + {"setLocalName", xml_setLocalName, 1,0,XML_MASK}, + {"setName", xml_setName, 1,0,XML_MASK}, + {"setNamespace", xml_setNamespace, 1,0,XML_MASK}, + {js_text_str, xml_text, 0,0,GENERIC_MASK}, + {js_toString_str, xml_toString, 0,0,GENERIC_MASK}, + {js_toXMLString_str, xml_toXMLString, 0,0,GENERIC_MASK}, + {js_toSource_str, xml_toXMLString, 0,0,GENERIC_MASK}, + {js_valueOf_str, xml_valueOf, 0,0,GENERIC_MASK}, + {0,0,0,0,0} +}; + +static JSBool +CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) +{ + int i; + const char *name; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + } + + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +SetDefaultXMLSettings(JSContext *cx, JSObject *obj) +{ + int i; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + v = JSVAL_TRUE; + if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) + return JS_FALSE; + } + v = INT_TO_JSVAL(2); + return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); +} + +static JSBool +xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return CopyXMLSettings(cx, obj, settings); +} + +static JSBool +xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + JSBool ok; + JSObject *settings; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + cx->xmlSettingFlags = 0; + ok = SetDefaultXMLSettings(cx, obj); + } else { + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + settings = JSVAL_TO_OBJECT(v); + cx->xmlSettingFlags = 0; + ok = CopyXMLSettings(cx, settings, obj); + } + if (ok) + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return ok; +} + +static JSBool +xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return SetDefaultXMLSettings(cx, settings); +} + +static JSFunctionSpec xml_static_methods[] = { + {"settings", xml_settings, 0,0,0}, + {"setSettings", xml_setSettings, 1,0,0}, + {"defaultSettings", xml_defaultSettings, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSXML *xml, *copy; + JSObject *xobj, *vobj; + JSClass *clasp; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + xobj = ToXML(cx, v); + if (!xobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(xobj); + xml = (JSXML *) JS_GetPrivate(cx, xobj); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, vobj); + if (clasp == &js_XMLClass || + (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { + /* No need to lock obj, it's newly constructed and thread local. */ + copy = DeepCopy(cx, xml, obj, 0); + if (!copy) + return JS_FALSE; + JS_ASSERT(copy->object == obj); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool +XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSObject *vobj, *listobj; + JSXML *xml, *list; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + xml = (JSXML *) JS_GetPrivate(cx, vobj); + if (xml->xml_class == JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return JS_FALSE; + return JS_TRUE; + } + } + } + + /* Toggle on XML support since the script has explicitly requested it. */ + listobj = ToXMLList(cx, v); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) +#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) +#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) + +static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { + JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ + JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ + JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ +}; + +#ifdef DEBUG_notme +JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); +uint32 xml_serial; +#endif + +JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + + xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); + if (!xml) + return NULL; + + xml->object = NULL; + xml->domnode = NULL; + xml->parent = NULL; + xml->name = NULL; + xml->xml_class = xml_class; + xml->xml_flags = 0; + if (JSXML_CLASS_HAS_VALUE(xml_class)) { + xml->xml_value = cx->runtime->emptyString; + } else { + XMLArrayInit(cx, &xml->xml_kids, 0); + if (xml_class == JSXML_CLASS_LIST) { + xml->xml_target = NULL; + xml->xml_targetprop = NULL; + } else { + XMLArrayInit(cx, &xml->xml_namespaces, 0); + XMLArrayInit(cx, &xml->xml_attrs, 0); + } + } + +#ifdef DEBUG_notme + JS_APPEND_LINK(&xml->links, &xml_leaks); + xml->serial = xml_serial++; +#endif + METER(xml_stats.xml); + METER(xml_stats.livexml); + return xml; +} + +static void +xml_mark_tail(JSContext *cx, JSXML *xml, void *arg) +{ + XMLArrayTrim(&xml->xml_kids); + + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_target) + JS_MarkGCThing(cx, xml->xml_target, "target", arg); + if (xml->xml_targetprop) + JS_MarkGCThing(cx, xml->xml_targetprop, "targetprop", arg); + } else { + namespace_mark_vector(cx, + (JSXMLNamespace **) xml->xml_namespaces.vector, + xml->xml_namespaces.length, + arg); + XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); + XMLArrayTrim(&xml->xml_namespaces); + + xml_mark_vector(cx, + (JSXML **) xml->xml_attrs.vector, + xml->xml_attrs.length, + arg); + XMLArrayCursorMark(cx, xml->xml_attrs.cursors); + XMLArrayTrim(&xml->xml_attrs); + } +} + +void +js_MarkXML(JSContext *cx, JSXML *xml, void *arg) +{ + JS_MarkGCThing(cx, xml->object, js_object_str, arg); + JS_MarkGCThing(cx, xml->name, js_name_str, arg); + JS_MarkGCThing(cx, xml->parent, js_xml_parent_str, arg); + + if (JSXML_HAS_VALUE(xml)) { + JS_MarkGCThing(cx, xml->xml_value, "value", arg); + } else { + xml_mark_vector(cx, + (JSXML **) xml->xml_kids.vector, + xml->xml_kids.length, + arg); + XMLArrayCursorMark(cx, xml->xml_kids.cursors); + + xml_mark_tail(cx, xml, arg); + } +} + +void +js_FinalizeXML(JSContext *cx, JSXML *xml) +{ + if (JSXML_HAS_KIDS(xml)) { + XMLArrayFinish(cx, &xml->xml_kids); + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + XMLArrayFinish(cx, &xml->xml_namespaces); + XMLArrayFinish(cx, &xml->xml_attrs); + } + } + +#ifdef DEBUG_notme + JS_REMOVE_LINK(&xml->links); +#endif + + UNMETER(xml_stats.livexml); +} + +JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) +{ + jsval nsval; + JSXMLNamespace *ns; + JSXMLArray nsarray; + JSXML *xml; + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + if (!XMLArrayInit(cx, &nsarray, 1)) + return NULL; + + XMLARRAY_APPEND(cx, &nsarray, ns); + xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); + XMLArrayFinish(cx, &nsarray); + if (!xml) + return NULL; + + return xml->object; +} + +JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + JSObject *obj; + JSTempValueRooter tvr; + + xml = js_NewXML(cx, xml_class); + if (!xml) + return NULL; + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(xml), &tvr); + obj = js_GetXMLObject(cx, xml); + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSObject * +NewXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, xml)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + return obj; +} + +JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = xml->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == xml); + return obj; + } + + /* + * A JSXML cannot be shared among threads unless it has an object. + * A JSXML cannot be given an object unless: + * (a) it has no parent; or + * (b) its parent has no object (therefore is thread-private); or + * (c) its parent's object is locked. + * + * Once given an object, a JSXML is immutable. + */ + JS_ASSERT(!xml->parent || + !xml->parent->object || + JS_IS_OBJ_LOCKED(cx, xml->parent->object)); + + obj = NewXMLObject(cx, xml); + if (!obj) + return NULL; + xml->object = obj; + return obj; +} + +JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, + namespace_props, namespace_methods, NULL, NULL); +} + +JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj) +{ + jsval v; + + if (!js_GetAnyName(cx, &v)) + return NULL; + return JSVAL_TO_OBJECT(v); +} + +JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *pobj, *ctor; + JSFunctionSpec *fs; + JSFunction *fun; + JSXML *xml; + JSProperty *prop; + JSScopeProperty *sprop; + jsval cval, argv[1], junk; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + /* Define the XML class constructor and prototype. */ + proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, + NULL, NULL, + xml_static_props, xml_static_methods); + if (!proto) + return NULL; + + /* + * XXX Hack alert: expand JS_DefineFunctions here to copy fs->extra into + * fun->spare, clearing fun->extra. No xml_methods require extra local GC + * roots allocated after actual arguments on the VM stack, but we need a + * way to tell which methods work only on XML objects, which work only on + * XMLList objects, and which work on either. + */ + for (fs = xml_methods; fs->name; fs++) { + fun = JS_DefineFunction(cx, proto, fs->name, fs->call, fs->nargs, + fs->flags); + if (!fun) + return NULL; + fun->extra = 0; + fun->spare = fs->extra; + } + + xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml || !JS_SetPrivate(cx, proto, xml)) + return NULL; + xml->object = proto; + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + + /* + * Prepare to set default settings on the XML constructor we just made. + * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, + * which is xml_getProperty, which creates a new XMLList every time! We + * must instead call js_LookupProperty directly. + */ + if (!js_LookupProperty(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &pobj, &prop)) { + return NULL; + } + JS_ASSERT(prop); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, prop); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, cval)); + + /* Set default settings. */ + ctor = JSVAL_TO_OBJECT(cval); + argv[0] = JSVAL_VOID; + if (!xml_setSettings(cx, ctor, 1, argv, &junk)) + return NULL; + + /* Define the XMLList function and give it the same prototype as XML. */ + fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); + if (!fun) + return NULL; + if (!js_SetClassPrototype(cx, fun->object, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj) +{ + if (!js_InitNamespaceClass(cx, obj)) + return NULL; + if (!js_InitQNameClass(cx, obj)) + return NULL; + if (!js_InitAttributeNameClass(cx, obj)) + return NULL; + if (!js_InitAnyNameClass(cx, obj)) + return NULL; + return js_InitXMLClass(cx, obj); +} + +JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSAtom *atom; + JSString *prefix, *uri; + + /* An invalid URI, for internal use only, guaranteed not to collide. */ + static const char anti_uri[] = "@mozilla.org/js/function"; + + rt = cx->runtime; + obj = rt->functionNamespaceObject; + if (!obj) { + atom = js_Atomize(cx, js_function_str, 8, 0); + JS_ASSERT(atom); + prefix = ATOM_TO_STRING(atom); + + atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); + if (!atom) + return JS_FALSE; + rt->atomState.lazy.functionNamespaceURIAtom = atom; + + uri = ATOM_TO_STRING(atom); + obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); + if (!obj) + return JS_FALSE; + + /* + * Avoid entraining any in-scope Object.prototype. The loss of + * Namespace.prototype is not detectable, as there is no way to + * refer to this instance in scripts. When used to qualify method + * names, its prefix and uri references are copied to the QName. + */ + OBJ_SET_PROTO(cx, obj, NULL); + OBJ_SET_PARENT(cx, obj, NULL); + rt->functionNamespaceObject = obj; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- + * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, + * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a + * lightweight function activation). There's no requirement that fp->varobj + * lie directly on fp->scopeChain, although it should be reachable using the + * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). + * + * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it + * creates a default namespace via 'new Namespace()'. In contrast, Set uses + * its v argument as the uri of a new Namespace, with "" as the prefix. See + * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, + * the default XML namespace will be set to ("", n.uri). So the uri string + * is really the only usefully stored value of the default namespace. + */ +JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) +{ + JSStackFrame *fp; + JSObject *nsobj, *obj, *tmp; + jsval v; + + fp = cx->fp; + nsobj = fp->xmlNamespace; + if (nsobj) { + *vp = OBJECT_TO_JSVAL(nsobj); + return JS_TRUE; + } + + obj = NULL; + for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { + obj = tmp; + if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + *vp = v; + return JS_TRUE; + } + } + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + if (obj && + !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + fp->xmlNamespace = nsobj; + *vp = v; + return JS_TRUE; +} + +JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v) +{ + jsval argv[2]; + JSObject *nsobj, *varobj; + JSStackFrame *fp; + + argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); + argv[1] = v; + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + + fp = cx->fp; + varobj = fp->varobj; + if (varobj) { + if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + } else { + JS_ASSERT(fp->fun && !(fp->fun->flags & JSFUN_HEAVYWEIGHT)); + } + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + return JS_TRUE; +} + +JSBool +js_ToAttributeName(JSContext *cx, jsval *vp) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, *vp); + if (!qn) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(qn->object); + return JS_TRUE; +} + +JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str) +{ + return EscapeAttributeValue(cx, NULL, str); +} + +JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) +{ + size_t len, len2, newlen; + jschar *chars; + + if (JSSTRING_IS_DEPENDENT(str) || + !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { + str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + 0); + if (!str) + return NULL; + } + + len = str->length; + len2 = JSSTRING_LENGTH(str2); + newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); + if (!chars) + return NULL; + + /* + * Reallocating str (because we know it has no other references) requires + * purging any deflated string cached for it. + */ + js_PurgeDeflatedStringCache(str); + + str->chars = chars; + str->length = newlen; + chars += len; + if (isName) { + *chars++ = ' '; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + } else { + *chars++ = '='; + *chars++ = '"'; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + *chars++ = '"'; + } + *chars = 0; + return str; +} + +JSString * +js_EscapeElementValue(JSContext *cx, JSString *str) +{ + return EscapeElementValue(cx, NULL, str); +} + +JSString * +js_ValueToXMLString(JSContext *cx, jsval v) +{ + return ToXMLString(cx, v); +} + +static JSBool +anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = ATOM_KEY(cx->runtime->atomState.starAtom); + return JS_TRUE; +} + +JSBool +js_GetAnyName(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSXMLQName *qn; + + rt = cx->runtime; + obj = rt->anynameObject; + if (!obj) { + qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + if (!qn) + return JS_FALSE; + + obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return JS_FALSE; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + /* + * Avoid entraining any in-scope Object.prototype. This loses the + * default toString inheritance, but no big deal: we want a better + * custom one for clearer diagnostics. + */ + if (!JS_DefineFunction(cx, obj, js_toString_str, anyname_toString, + 0, 0)) { + return JS_FALSE; + } + OBJ_SET_PROTO(cx, obj, NULL); + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + rt->anynameObject = obj; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) +{ + JSXMLQName *qn; + jsid funid, id; + JSObject *obj, *pobj, *lastobj; + JSProperty *prop; + const char *printable; + + qn = ToXMLName(cx, name, &funid); + if (!qn) + return JS_FALSE; + id = OBJECT_TO_JSID(qn->object); + + obj = cx->fp->scopeChain; + do { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + + /* + * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML + * object to carry scope chain linkage in js_FilterXMLList. + */ + pobj = OBJ_THIS_OBJECT(cx, obj); + if (OBJECT_IS_XML(cx, pobj)) { + *objp = pobj; + *namep = ID_TO_VALUE(id); + return JS_TRUE; + } + } + + lastobj = obj; + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + printable = js_ValueToPrintableString(cx, name); + if (printable) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNDEFINED_XML_NAME, printable); + } + return JS_FALSE; +} + +JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return GetProperty(cx, obj, name, vp); +} + +JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return PutProperty(cx, obj, name, vp); +} + +static JSXML * +GetPrivate(JSContext *cx, JSObject *obj, const char *method) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_METHOD, + js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); + } + return xml; +} + +JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list; + + xml = GetPrivate(cx, obj, "descendants internal method"); + if (!xml) + return JS_FALSE; + + list = Descendants(cx, xml, id); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) +{ + JSXML *list; + uint32 n; + jsval junk; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (n = list->xml_kids.length; n != 0; --n) { + if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) +{ + JSBool ok, match; + JSStackFrame *fp; + JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; + JSXML *xml, *list, *result, *kid; + JSXMLArrayCursor cursor; + + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* All control flow after this point must exit via label out or bad. */ + fp = cx->fp; + scobj = fp->scopeChain; + withobj = NULL; + xml = GetPrivate(cx, obj, "filtering predicate operator"); + if (!xml) + goto bad; + + if (xml->xml_class == JSXML_CLASS_LIST) { + list = xml; + } else { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + goto bad; + list = (JSXML *) JS_GetPrivate(cx, listobj); + ok = Append(cx, list, xml); + if (!ok) + goto out; + } + + resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!resobj) + goto bad; + result = (JSXML *) JS_GetPrivate(cx, resobj); + + /* Hoist the scope chain update out of the loop over kids. */ + withobj = js_NewWithObject(cx, NULL, scobj, -1); + if (!withobj) + goto bad; + fp->scopeChain = withobj; + + XMLArrayCursorInit(&cursor, &list->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + OBJ_SET_PROTO(cx, withobj, kidobj); + ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); + if (ok && match) + ok = Append(cx, result, kid); + if (!ok) + break; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + goto out; + if (kid) + goto bad; + + *vp = OBJECT_TO_JSVAL(resobj); + +out: + if (withobj) { + fp->scopeChain = scobj; + JS_SetPrivate(cx, withobj, NULL); + } + JS_LeaveLocalRootScope(cx); + return ok; +bad: + ok = JS_FALSE; + goto out; +} + +JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v) +{ + return ToXML(cx, v); +} + +JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v) +{ + return ToXMLList(cx, v); +} + +JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj) +{ + uintN flags; + JSXML *xml; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (flags & (XSF_IGNORE_COMMENTS | + XSF_IGNORE_PROCESSING_INSTRUCTIONS | + XSF_IGNORE_WHITESPACE)) { + xml = DeepCopy(cx, xml, NULL, flags); + if (!xml) + return NULL; + return xml->object; + } + return NewXMLObject(cx, xml); +} + +JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value) +{ + uintN flags; + JSObject *obj; + JSXML *xml; + JSXMLQName *qn; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + + if ((xml_class == JSXML_CLASS_COMMENT && + (flags & XSF_IGNORE_COMMENTS)) || + (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { + return js_NewXMLObject(cx, JSXML_CLASS_TEXT); + } + + obj = js_NewXMLObject(cx, xml_class); + if (!obj) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (name) { + qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + if (!qn) + return NULL; + xml->name = qn; + } + xml->xml_value = value; + return obj; +} + +JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str) +{ + return MakeXMLCDATAString(cx, NULL, str); +} + +JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str) +{ + return MakeXMLCommentString(cx, NULL, str); +} + +JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) +{ + return MakeXMLPIString(cx, NULL, name, str); +} + +#endif /* JS_HAS_XML_SUPPORT */ diff --git a/src/dom/js/jsxml.h b/src/dom/js/jsxml.h new file mode 100644 index 000000000..294e66c90 --- /dev/null +++ b/src/dom/js/jsxml.h @@ -0,0 +1,329 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxml_h___ +#define jsxml_h___ + +#include "jsstddef.h" +#include "jspubtd.h" + +extern const char js_AnyName_str[]; +extern const char js_AttributeName_str[]; +extern const char js_isXMLName_str[]; +extern const char js_XMLList_str[]; + +extern const char js_amp_entity_str[]; +extern const char js_gt_entity_str[]; +extern const char js_lt_entity_str[]; +extern const char js_quot_entity_str[]; + +struct JSXMLNamespace { + JSObject *object; + JSString *prefix; + JSString *uri; + JSBool declared; /* true if declared in its XML tag */ +}; + +extern JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns, void *arg); + +extern void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); + +struct JSXMLQName { + JSObject *object; + JSString *uri; + JSString *prefix; + JSString *localName; +}; + +extern JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn, void *arg); + +extern void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); + +typedef JSBool +(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); + +struct JSXMLArray { + uint32 length; + uint32 capacity; + void **vector; + JSXMLArrayCursor *cursors; +}; + +#define JSXML_PRESET_CAPACITY JS_BIT(31) +#define JSXML_CAPACITY_MASK JS_BITMASK(31) +#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) + +struct JSXMLArrayCursor { + JSXMLArray *array; + uint32 index; + JSXMLArrayCursor *next; + JSXMLArrayCursor **prevp; + void *root; +}; + +/* + * NB: don't reorder this enum without changing all array initializers that + * depend on it in jsxml.c. + */ +typedef enum JSXMLClass { + JSXML_CLASS_LIST, + JSXML_CLASS_ELEMENT, + JSXML_CLASS_ATTRIBUTE, + JSXML_CLASS_PROCESSING_INSTRUCTION, + JSXML_CLASS_TEXT, + JSXML_CLASS_COMMENT, + JSXML_CLASS_LIMIT +} JSXMLClass; + +#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_NAME(class_) \ + ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ + (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) + +#ifdef DEBUG_notme +#include "jsclist.h" +#endif + +struct JSXML { +#ifdef DEBUG_notme + JSCList links; + uint32 serial; +#endif + JSObject *object; + void *domnode; /* DOM node if mapped info item */ + JSXML *parent; + JSXMLQName *name; + uint16 xml_class; /* discriminates u, below */ + uint16 xml_flags; /* flags, see below */ + union { + struct JSXMLListVar { + JSXMLArray kids; /* NB: must come first */ + JSXML *target; + JSXMLQName *targetprop; + } list; + struct JSXMLVar { + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; + } elem; + JSString *value; + } u; + + /* Don't add anything after u -- see js_NewXML for why. */ +}; + +/* union member shorthands */ +#define xml_kids u.list.kids +#define xml_target u.list.target +#define xml_targetprop u.list.targetprop +#define xml_namespaces u.elem.namespaces +#define xml_attrs u.elem.attrs +#define xml_value u.value + +/* xml_flags values */ +#define XMLF_WHITESPACE_TEXT 0x1 + +/* xml_class-testing macros */ +#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) +#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) +#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) +#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ + ? (xml)->xml_kids.length \ + : 0) + +extern JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class); + +extern void +js_MarkXML(JSContext *cx, JSXML *xml, void *arg); + +extern void +js_FinalizeXML(JSContext *cx, JSXML *xml); + +extern JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); + +extern JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); + +extern JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml); + +extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; +extern JS_FRIEND_DATA(JSClass) js_XMLClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; +extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; +extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; + +/* + * Macros to test whether an object or a value is of type "xml" (per typeof). + * NB: jsapi.h must be included before any call to VALUE_IS_XML. + */ +#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) +#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ + OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) + +extern JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj); + +extern JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v); + +/* + * Return true if v is a XML QName object, or if it converts to a string that + * contains a valid XML qualified name (one containing no :), false otherwise. + * NB: This function is an infallible predicate, it hides exceptions. + */ +extern JSBool +js_IsXMLName(JSContext *cx, jsval v); + +extern JSBool +js_ToAttributeName(JSContext *cx, jsval *vp); + +extern JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str); + +extern JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, + JSString *str2); + +extern JSString * +js_EscapeElementValue(JSContext *cx, JSString *str); + +extern JSString * +js_ValueToXMLString(JSContext *cx, jsval v); + +extern JSBool +js_GetAnyName(JSContext *cx, jsval *vp); + +extern JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); + +extern JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); + +extern JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); + +extern JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v); + +extern JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v); + +extern JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj); + +extern JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value); + +extern JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); + +#endif /* jsxml_h___ */ diff --git a/src/dom/js/prmjtime.c b/src/dom/js/prmjtime.c index 774f83999..6e08423af 100644 --- a/src/dom/js/prmjtime.c +++ b/src/dom/js/prmjtime.c @@ -62,19 +62,6 @@ #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 */ @@ -85,128 +72,6 @@ extern int gettimeofday(struct timeval *tv); #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)) || \ @@ -239,32 +104,6 @@ PRMJ_LocalGMTDifference() 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 */ @@ -323,14 +162,6 @@ PRMJ_Now(void) 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); @@ -378,24 +209,6 @@ PRMJ_Now(void) 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 */ @@ -403,24 +216,6 @@ 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; @@ -467,14 +262,13 @@ PRMJ_DSTOffset(JSInt64 local_time) 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) +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) struct tm a; /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int diff --git a/src/dom/js/prmjtime.h b/src/dom/js/prmjtime.h index 6a94a11b1..b74fe845e 100644 --- a/src/dom/js/prmjtime.h +++ b/src/dom/js/prmjtime.h @@ -57,21 +57,21 @@ 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 */ + 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 +#define PRMJ_USEC_PER_SEC 1000000L +#define PRMJ_USEC_PER_MSEC 1000L /* Return the current local time in micro-seconds */ extern JSInt64