diff --git a/lib/javascript/Makefile b/lib/javascript/Makefile index 587ab86c..51168ba7 100644 --- a/lib/javascript/Makefile +++ b/lib/javascript/Makefile @@ -1,5 +1,4 @@ # -*- Mode: makefile -*- -# vim: ft=make # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -54,20 +53,19 @@ include config.mk #NS_USE_NATIVE = 1 -ifdef NARCISSUS -DEFINES += -DNARCISSUS +ifdef USE_MSVC +OTHER_LIBS += fdlibm/$(OBJDIR)/fdlibm.lib +else +OTHER_LIBS += -Lfdlibm/$(OBJDIR) -lfdm endif -# Look in OBJDIR to find jsautocfg.h and jsautokw.h -INCLUDES += -I$(OBJDIR) - ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE -INCLUDES += -I$(DIST)/include/nspr +INCLUDES += -I../../dist/$(OBJDIR)/include ifdef USE_MSVC -OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib +OTHER_LIBS += ../../dist/$(OBJDIR)/lib/libnspr${NSPR_LIBSUFFIX}.lib else -OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) +OTHER_LIBS += -L../../dist/$(OBJDIR)/lib -lnspr${NSPR_LIBSUFFIX} endif endif @@ -122,9 +120,9 @@ endif # # Server-related changes : # -ifdef NES40 -DEFINES += -DNES40 -endif +ifdef NES40 +DEFINES += -DNES40 +endif # # Line editing support. @@ -163,7 +161,6 @@ JS_HFILES = \ jsfun.h \ jsgc.h \ jsinterp.h \ - jsiter.h \ jslibmath.h \ jslock.h \ jsmath.h \ @@ -204,16 +201,13 @@ OTHER_HFILES = \ prmjtime.h \ resource.h \ jsopcode.tbl \ - jsproto.tbl \ js.msg \ jsshell.msg \ - jskeyword.tbl \ $(NULL) ifndef PREBUILT_CPUCFG OTHER_HFILES += $(OBJDIR)/jsautocfg.h endif -OTHER_HFILES += $(OBJDIR)/jsautokw.h HFILES = $(JS_HFILES) $(API_HFILES) $(OTHER_HFILES) @@ -234,7 +228,6 @@ JS_CFILES = \ jsgc.c \ jshash.c \ jsinterp.c \ - jsiter.c \ jslock.c \ jslog2.c \ jslong.c \ @@ -255,6 +248,24 @@ JS_CFILES = \ prmjtime.c \ $(NULL) +PREDIRS += fdlibm + +ifdef USE_MSVC +FDLIBM_LIBRARY = fdlibm/$(OBJDIR)/fdlibm.lib +else +FDLIBM_LIBRARY = fdlibm/$(OBJDIR)/libfdm.a +endif +JSMATH_PRELINK = $(OBJDIR)/jsmathtemp.o +# Flag for incremental linking +DASH_R = -r + +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) +# Don't use wildcard here, because we only want this resolved at link time. +OBJS += fdlibm/*.o +endif +endif + ifdef JS_LIVECONNECT DIRS += liveconnect endif @@ -292,50 +303,11 @@ include jsconfig.mk nsinstall-target: cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR) - -# -# Rules for keyword switch generation -# - -GARBAGE += $(OBJDIR)/jsautokw.h $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) -GARBAGE += $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - -$(OBJDIR)/jsscan.$(OBJ_SUFFIX): $(OBJDIR)/jsautokw.h jskeyword.tbl - -$(OBJDIR)/jskwgen.$(OBJ_SUFFIX): jskwgen.c jskeyword.tbl - -$(OBJDIR)/jsautokw.h: $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) jskeyword.tbl - $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) $@ - ifdef USE_MSVC - -$(OBJDIR)/jskwgen.obj: jskwgen.c jskeyword.tbl - @$(MAKE_OBJDIR) - $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $< - -$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ - -else - -$(OBJDIR)/jskwgen.o: jskwgen.c jskeyword.tbl - @$(MAKE_OBJDIR) - $(CC) -o $@ -c $(CFLAGS) $< - -$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ - -endif - -# -# JS shell executable -# - -ifdef USE_MSVC -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(FDLIBM_LIBRARY) link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ else -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(FDLIBM_LIBRARY) $(CC) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) $(OTHER_LIBS) \ $(PROG_LIBS) endif @@ -355,11 +327,43 @@ $(OBJDIR)/jsautocfg.h: $(OBJDIR)/jscpucfg $(OBJDIR)/jscpucfg: $(OBJDIR)/jscpucfg.o $(CC) -o $@ $(OBJDIR)/jscpucfg.o +# Look in OBJDIR to find jsautocfg.h +INCLUDES += -I$(OBJDIR) + # Add to TARGETS for clobber rule TARGETS += $(OBJDIR)/jsautocfg.h $(OBJDIR)/jscpucfg \ - $(OBJDIR)/jscpucfg.o + $(OBJDIR)/jscpucfg.o $(OBJDIR)/jsmathtemp.o endif + +# special rule for jsmath.o since we want to incrementally link +# against fdlibm to pull in only what is needed +$(OBJDIR)/jsmath.o: jsmath.c jsmath.h jslibmath.h $(FDLIBM_LIBRARY) +ifneq (,$(filter OS2 WINNT,$(OS_ARCH))) + $(CC) -Fo$(JSMATH_PRELINK) -c $(CFLAGS) $< +else + $(CC) -o $(JSMATH_PRELINK) -c $(CFLAGS) $< +endif + +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) + @cp $(JSMATH_PRELINK) $@ +else + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif +else +ifdef USE_MSVC + @echo Warning: to use $(LIBRARY) must also link against $(FDLIBM_LIBRARY) + @cp $(JSMATH_PRELINK) $@ +endif + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif + +# Note: generated headers must be built before descending +# into fdlibm directory +$(FDLIBM_LIBRARY) : + $(MAKE) -C fdlibm -f Makefile.ref $(@F) + # # Hardwire dependencies on jsopcode.tbl # @@ -373,3 +377,6 @@ TARFILES = files `cat files` SUFFIXES: .i %.i: %.c $(CC) -C -E $(CFLAGS) $< > $*.i + +install: + echo "no install for javascript" diff --git a/lib/javascript/Makefile.in b/lib/javascript/Makefile.in index 08bb674f..444bc67c 100644 --- a/lib/javascript/Makefile.in +++ b/lib/javascript/Makefile.in @@ -60,10 +60,7 @@ PACKAGE_FILE = js.pkg # other modules which are always built shared. Failure to do so results in # the js code getting copied into xpinstall and jsd as well as mozilla-bin, # and then the static data cells used for locking no longer work. - -ifndef JS_STATIC_BUILD FORCE_SHARED_LIB = 1 -endif CSRCS = \ jsapi.c \ @@ -82,7 +79,6 @@ CSRCS = \ jsgc.c \ jshash.c \ jsinterp.c \ - jsiter.c \ jslock.c \ jslog2.c \ jslong.c \ @@ -105,7 +101,6 @@ CSRCS = \ EXPORTS = \ jsautocfg.h \ - jsautokw.h \ js.msg \ jsapi.h \ jsarray.h \ @@ -125,7 +120,6 @@ EXPORTS = \ jsgc.h \ jshash.h \ jsinterp.h \ - jsiter.h \ jslock.h \ jslong.h \ jsmath.h \ @@ -137,7 +131,6 @@ EXPORTS = \ jsotypes.h \ jsparse.h \ jsprf.h \ - jsproto.tbl \ jsprvtd.h \ jspubtd.h \ jsregexp.h \ @@ -156,6 +149,8 @@ ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) EXPORTS += jscpucfg.h endif +FDLIBM_LIBRARY = fdlibm/$(LIB_PREFIX)fdm.$(LIB_SUFFIX) +JSMATH_PRELINK = jsmathtemp.o JS_SAFE_ARENA = 1 DASH_R = -r @@ -164,6 +159,10 @@ include $(topsrcdir)/config/config.mk EXTRA_DSO_LDOPTS += $(NSPR_LIBS) +ifeq (,$(filter-out OS2 WINNT WINCE,$(OS_ARCH))) +SHARED_LIBRARY_LIBS += $(FDLIBM_LIBRARY) +endif + # When using gcc the assembly is inlined in the C-file (see jslock.c) ifeq ($(OS_ARCH),SunOS) ifneq ($(OS_TEST),i86pc) @@ -191,11 +190,6 @@ JSJAVA_CFLAGS = \ -I$(topsrcdir)/sun-java/include \ $(JSJAVA_STUBHEADERS) -# Define keyword generator before rules.mk, see bug 323979 comment 50 - -HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX) -GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX) - include $(topsrcdir)/config/rules.mk DEFINES += -DEXPORT_JS_API @@ -215,7 +209,7 @@ LDFLAGS += -OPT:NOICF endif endif -GARBAGE += jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg +GARBAGE += $(JSMATH_PRELINK) jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg ifneq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) TARGETS += jscpucfg$(HOST_BIN_SUFFIX) @@ -294,6 +288,13 @@ LDFLAGS += -lposix4 -ldl -lnsl -lsocket endif endif +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) +# Don't use wildcard here, because we only want this resolved at link time. +OBJS += fdlibm/*.o +endif +endif + ifeq ($(OS_ARCH),IRIX) ifndef GNU_CC _COMPILE_CFLAGS = $(patsubst -O%,-O1,$(COMPILE_CFLAGS)) @@ -304,6 +305,31 @@ jsapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsregexp.o jspa endif endif +# On OS/2 & win32 we are already linking against fdlibm, so don't bother +# creating jsmathtemp +ifeq (,$(filter OS2 WINNT OpenVMS,$(OS_ARCH))) +# special rule for jsmath.o since we want to incrementally link +# against fdlibm to pull in only what is needed +# Do this in a single step to avoid dependency problems +jsmath.o: jsmath.c $(FDLIBM_LIBRARY) Makefile.in + $(REPORT_BUILD) + @$(MAKE_DEPS_AUTO) + $(ELOG) $(CC) $(OUTOPTION)$(JSMATH_PRELINK) -c $(COMPILE_CFLAGS) $< +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) + @cp $(JSMATH_PRELINK) $@ +else + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif +else + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif + @$(RM) -f $(JSMATH_PRELINK) +else +# Create dependency so we build fdlibm +jsmath.o: $(FDLIBM_LIBRARY) Makefile.in +endif + # An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result. # This suppresses optimization for this single compilation unit. ifeq ($(OS_ARCH),AIX) @@ -317,6 +343,9 @@ jsdtoa.o: jsdtoa.c Makefile.in $(CC) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< endif +$(FDLIBM_LIBRARY): + $(MAKE) -C $(@D) $(@F) + jsopcode.h jsopcode.c: jsopcode.tbl ifeq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) @@ -379,10 +408,3 @@ jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.c Makefile.in endif endif -# Extra dependancies and rules for keyword switch code -jsscan.$(OBJ_SUFFIX): jsautokw.h jskeyword.tbl - -host_jskwgen.$(OBJ_SUFFIX): jsconfig.h jskeyword.tbl - -jsautokw.h: host_jskwgen$(HOST_BIN_SUFFIX) - ./host_jskwgen$(HOST_BIN_SUFFIX) $@ diff --git a/lib/javascript/Makefile.ref b/lib/javascript/Makefile.ref index 587ab86c..3b6fbb5a 100644 --- a/lib/javascript/Makefile.ref +++ b/lib/javascript/Makefile.ref @@ -1,5 +1,4 @@ # -*- Mode: makefile -*- -# vim: ft=make # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -54,20 +53,19 @@ include config.mk #NS_USE_NATIVE = 1 -ifdef NARCISSUS -DEFINES += -DNARCISSUS +ifdef USE_MSVC +OTHER_LIBS += fdlibm/$(OBJDIR)/fdlibm.lib +else +OTHER_LIBS += -Lfdlibm/$(OBJDIR) -lfdm endif -# Look in OBJDIR to find jsautocfg.h and jsautokw.h -INCLUDES += -I$(OBJDIR) - ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE -INCLUDES += -I$(DIST)/include/nspr +INCLUDES += -I../../dist/$(OBJDIR)/include ifdef USE_MSVC -OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib +OTHER_LIBS += ../../dist/$(OBJDIR)/lib/libnspr${NSPR_LIBSUFFIX}.lib else -OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) +OTHER_LIBS += -L../../dist/$(OBJDIR)/lib -lnspr${NSPR_LIBSUFFIX} endif endif @@ -122,9 +120,9 @@ endif # # Server-related changes : # -ifdef NES40 -DEFINES += -DNES40 -endif +ifdef NES40 +DEFINES += -DNES40 +endif # # Line editing support. @@ -163,7 +161,6 @@ JS_HFILES = \ jsfun.h \ jsgc.h \ jsinterp.h \ - jsiter.h \ jslibmath.h \ jslock.h \ jsmath.h \ @@ -204,16 +201,13 @@ OTHER_HFILES = \ prmjtime.h \ resource.h \ jsopcode.tbl \ - jsproto.tbl \ js.msg \ jsshell.msg \ - jskeyword.tbl \ $(NULL) ifndef PREBUILT_CPUCFG OTHER_HFILES += $(OBJDIR)/jsautocfg.h endif -OTHER_HFILES += $(OBJDIR)/jsautokw.h HFILES = $(JS_HFILES) $(API_HFILES) $(OTHER_HFILES) @@ -234,7 +228,6 @@ JS_CFILES = \ jsgc.c \ jshash.c \ jsinterp.c \ - jsiter.c \ jslock.c \ jslog2.c \ jslong.c \ @@ -255,6 +248,24 @@ JS_CFILES = \ prmjtime.c \ $(NULL) +PREDIRS += fdlibm + +ifdef USE_MSVC +FDLIBM_LIBRARY = fdlibm/$(OBJDIR)/fdlibm.lib +else +FDLIBM_LIBRARY = fdlibm/$(OBJDIR)/libfdm.a +endif +JSMATH_PRELINK = $(OBJDIR)/jsmathtemp.o +# Flag for incremental linking +DASH_R = -r + +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) +# Don't use wildcard here, because we only want this resolved at link time. +OBJS += fdlibm/*.o +endif +endif + ifdef JS_LIVECONNECT DIRS += liveconnect endif @@ -292,50 +303,11 @@ include jsconfig.mk nsinstall-target: cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR) - -# -# Rules for keyword switch generation -# - -GARBAGE += $(OBJDIR)/jsautokw.h $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) -GARBAGE += $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - -$(OBJDIR)/jsscan.$(OBJ_SUFFIX): $(OBJDIR)/jsautokw.h jskeyword.tbl - -$(OBJDIR)/jskwgen.$(OBJ_SUFFIX): jskwgen.c jskeyword.tbl - -$(OBJDIR)/jsautokw.h: $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) jskeyword.tbl - $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) $@ - ifdef USE_MSVC - -$(OBJDIR)/jskwgen.obj: jskwgen.c jskeyword.tbl - @$(MAKE_OBJDIR) - $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $< - -$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ - -else - -$(OBJDIR)/jskwgen.o: jskwgen.c jskeyword.tbl - @$(MAKE_OBJDIR) - $(CC) -o $@ -c $(CFLAGS) $< - -$(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ - -endif - -# -# JS shell executable -# - -ifdef USE_MSVC -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(FDLIBM_LIBRARY) link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ else -$(PROGRAM): $(PROG_OBJS) $(LIBRARY) +$(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(FDLIBM_LIBRARY) $(CC) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) $(OTHER_LIBS) \ $(PROG_LIBS) endif @@ -355,11 +327,43 @@ $(OBJDIR)/jsautocfg.h: $(OBJDIR)/jscpucfg $(OBJDIR)/jscpucfg: $(OBJDIR)/jscpucfg.o $(CC) -o $@ $(OBJDIR)/jscpucfg.o +# Look in OBJDIR to find jsautocfg.h +INCLUDES += -I$(OBJDIR) + # Add to TARGETS for clobber rule TARGETS += $(OBJDIR)/jsautocfg.h $(OBJDIR)/jscpucfg \ - $(OBJDIR)/jscpucfg.o + $(OBJDIR)/jscpucfg.o $(OBJDIR)/jsmathtemp.o endif + +# special rule for jsmath.o since we want to incrementally link +# against fdlibm to pull in only what is needed +$(OBJDIR)/jsmath.o: jsmath.c jsmath.h jslibmath.h $(FDLIBM_LIBRARY) +ifneq (,$(filter OS2 WINNT,$(OS_ARCH))) + $(CC) -Fo$(JSMATH_PRELINK) -c $(CFLAGS) $< +else + $(CC) -o $(JSMATH_PRELINK) -c $(CFLAGS) $< +endif + +ifeq ($(OS_ARCH),QNX) +ifneq ($(OS_TARGET),NTO) + @cp $(JSMATH_PRELINK) $@ +else + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif +else +ifdef USE_MSVC + @echo Warning: to use $(LIBRARY) must also link against $(FDLIBM_LIBRARY) + @cp $(JSMATH_PRELINK) $@ +endif + $(LD) $(DASH_R) -o $@ $(JSMATH_PRELINK) $(FDLIBM_LIBRARY) +endif + +# Note: generated headers must be built before descending +# into fdlibm directory +$(FDLIBM_LIBRARY) : + $(MAKE) -C fdlibm -f Makefile.ref $(@F) + # # Hardwire dependencies on jsopcode.tbl # diff --git a/lib/javascript/config.mk b/lib/javascript/config.mk index c1fa0a4c..f622d30a 100644 --- a/lib/javascript/config.mk +++ b/lib/javascript/config.mk @@ -166,12 +166,11 @@ endif endif # Name of the binary code directories -# ifdef BUILD_IDG -# OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJD -# else -# OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJ -# endif -OBJDIR = obj +ifdef BUILD_IDG +OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJD +else +OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJ +endif VPATH = $(OBJDIR) # Automatic make dependencies file diff --git a/lib/javascript/config/Darwin.mk b/lib/javascript/config/Darwin.mk index 23b503e3..9b9b6ff0 100644 --- a/lib/javascript/config/Darwin.mk +++ b/lib/javascript/config/Darwin.mk @@ -49,9 +49,7 @@ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN RANLIB = ranlib -MKSHLIB = $(CC) -dynamiclib $(XMKSHLIBOPTS) -framework System - -SO_SUFFIX = dylib +MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< diff --git a/lib/javascript/config/WINNT4.0.mk b/lib/javascript/config/WINNT4.0.mk index 15a5a6fd..fe3fd0a5 100644 --- a/lib/javascript/config/WINNT4.0.mk +++ b/lib/javascript/config/WINNT4.0.mk @@ -97,9 +97,7 @@ PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf diff --git a/lib/javascript/config/WINNT5.0.mk b/lib/javascript/config/WINNT5.0.mk index 2b796a4f..fe3fd0a5 100644 --- a/lib/javascript/config/WINNT5.0.mk +++ b/lib/javascript/config/WINNT5.0.mk @@ -89,7 +89,7 @@ OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc @@ -97,9 +97,7 @@ PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf diff --git a/lib/javascript/config/WINNT5.1.mk b/lib/javascript/config/WINNT5.1.mk index 2b796a4f..fe3fd0a5 100644 --- a/lib/javascript/config/WINNT5.1.mk +++ b/lib/javascript/config/WINNT5.1.mk @@ -89,7 +89,7 @@ OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc @@ -97,9 +97,7 @@ PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf diff --git a/lib/javascript/config/WINNT5.2.mk b/lib/javascript/config/WINNT5.2.mk index 2b796a4f..fe3fd0a5 100644 --- a/lib/javascript/config/WINNT5.2.mk +++ b/lib/javascript/config/WINNT5.2.mk @@ -89,7 +89,7 @@ OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif -OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) +OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc @@ -97,9 +97,7 @@ PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ - winmm.lib \ - -nologo\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf diff --git a/lib/javascript/js.c b/lib/javascript/js.c index fb4332f5..b3961667 100644 --- a/lib/javascript/js.c +++ b/lib/javascript/js.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -46,7 +46,6 @@ #include #include #include -#include #include "jstypes.h" #include "jsarena.h" #include "jsutil.h" @@ -99,10 +98,7 @@ typedef enum JSShellExitCode { } JSShellExitCode; size_t gStackChunkSize = 8192; - -/* Assume that we can not use more than 5e5 bytes of C stack by default. */ -static size_t gMaxStackSize = 500000; - +static size_t gMaxStackSize = 0; static jsuword gStackBase; int gExitCode = 0; JSBool gQuitting = JS_FALSE; @@ -130,8 +126,6 @@ typedef enum JSShellErrNum { static const JSErrorFormatString * my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); -static JSObject * -split_setup(JSContext *cx); #ifdef EDITLINE extern char *readline(const char *prompt); @@ -170,7 +164,7 @@ GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { } static void -Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) +Process(JSContext *cx, JSObject *obj, char *filename) { JSBool ok, hitEOF; JSScript *script; @@ -183,7 +177,7 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) FILE *file; jsuword stackLimit; - if (forceTTY || !filename || strcmp(filename, "-") == 0) { + if (!filename || strcmp(filename, "-") == 0) { file = stdin; } else { file = fopen(filename, "r"); @@ -209,7 +203,7 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) } JS_SetThreadStackLimit(cx, stackLimit); - if (!forceTTY && !isatty(fileno(file))) { + if (!isatty(fileno(file))) { /* * It's not interactive - just execute it. * @@ -232,7 +226,6 @@ Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) (void)JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } - return; } @@ -285,7 +278,7 @@ static int usage(void) { fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); - fprintf(gErrFile, "usage: js [-PswWxCi] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-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; } @@ -322,7 +315,6 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; - JSBool forceTTY = JS_FALSE; /* * Scan past all optional arguments so we can create the arguments object @@ -380,9 +372,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) switch (argv[i][1]) { case 'v': - if (++i == argc) + if (++i == argc) { return usage(); - + } JS_SetVersion(cx, (JSVersion) atoi(argv[i])); break; @@ -431,11 +423,10 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) break; case 'f': - if (++i == argc) + if (++i == argc) { return usage(); - - Process(cx, obj, argv[i], JS_FALSE); - + } + Process(cx, obj, argv[i]); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks the test @@ -448,8 +439,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) { jsval rval; - if (++i == argc) + if (++i == argc) { return usage(); + } /* Pass a filename of -e to imitate PERL */ JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), @@ -464,29 +456,21 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) isInteractive = JS_FALSE; break; - case 'i': - isInteractive = forceTTY = JS_TRUE; - break; - case 'S': - if (++i == argc) + if (++i == argc) { return usage(); - + } /* Set maximum stack size. */ gMaxStackSize = atoi(argv[i]); break; - case 'z': - obj = split_setup(cx); - break; - default: return usage(); } } if (filename || isInteractive) - Process(cx, obj, filename, forceTTY); + Process(cx, obj, filename); return gExitCode; } @@ -567,6 +551,12 @@ Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_TRUE; } +static void +my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); + +static void +my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); + static JSBool Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -576,6 +566,7 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSScript *script; JSBool ok; jsval result; + JSErrorReporter older; uint32 oldopts; for (i = 0; i < argc; i++) { @@ -585,6 +576,7 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) argv[i] = STRING_TO_JSVAL(str); filename = JS_GetStringBytes(str); errno = 0; + older = JS_SetErrorReporter(cx, my_LoadErrorReporter); oldopts = JS_GetOptions(cx); JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); script = JS_CompileFile(cx, obj, filename); @@ -597,6 +589,7 @@ Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_DestroyScript(cx, script); } JS_SetOptions(cx, oldopts); + JS_SetErrorReporter(cx, older); if (!ok) return JS_FALSE; } @@ -624,7 +617,7 @@ ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!buf) return JS_FALSE; - while ((gotlength = + while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { buflength += gotlength; @@ -661,7 +654,7 @@ ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) buf = tmp; - /* + /* * Turn buf into a JSString. Note that buflength includes the trailing null * character. */ @@ -892,94 +885,25 @@ PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #ifdef DEBUG -static void -GetSwitchTableBounds(JSScript *script, uintN offset, - uintN *start, uintN *end) -{ - jsbytecode *pc; - JSOp op; - ptrdiff_t jmplen; - jsint low, high, n; - - pc = script->code + offset; - op = *pc; - switch (op) { - case JSOP_TABLESWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto jump_table; - case JSOP_TABLESWITCH: - jmplen = JUMP_OFFSET_LEN; - jump_table: - pc += jmplen; - low = GET_JUMP_OFFSET(pc); - pc += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc); - pc += JUMP_OFFSET_LEN; - n = high - low + 1; - break; - - case JSOP_LOOKUPSWITCHX: - jmplen = JUMPX_OFFSET_LEN; - goto lookup_table; - default: - JS_ASSERT(op == JSOP_LOOKUPSWITCH); - jmplen = JUMP_OFFSET_LEN; - lookup_table: - pc += jmplen; - n = GET_ATOM_INDEX(pc); - pc += ATOM_INDEX_LEN; - jmplen += ATOM_INDEX_LEN; - break; - } - - *start = (uintN)(pc - script->code); - *end = *start + (uintN)(n * jmplen); -} - - -/* - * SrcNotes assumes that SRC_METHODBASE should be distinguished from SRC_LABEL - * using the bytecode the source note points to. - */ -JS_STATIC_ASSERT(SRC_LABEL == SRC_METHODBASE); - static void SrcNotes(JSContext *cx, JSScript *script) { - uintN offset, delta, caseOff, switchTableStart, switchTableEnd; + uintN offset, delta, caseOff; jssrcnote *notes, *sn; JSSrcNoteType type; - const char *name; - JSOp op; jsatomid atomIndex; JSAtom *atom; fprintf(gOutFile, "\nSource notes:\n"); offset = 0; notes = SCRIPT_NOTES(script); - switchTableEnd = switchTableStart = 0; for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { delta = SN_DELTA(sn); offset += delta; - type = (JSSrcNoteType) SN_TYPE(sn); - name = js_SrcNoteSpec[type].name; - if (type == SRC_LABEL) { - /* Heavily overloaded case. */ - if (switchTableStart <= offset && offset < switchTableEnd) { - name = "case"; - } else { - op = script->code[offset]; - if (op == JSOP_GETMETHOD || op == JSOP_SETMETHOD) { - /* This is SRC_METHODBASE which we print as SRC_PCBASE. */ - type = SRC_PCBASE; - name = "methodbase"; - } else { - JS_ASSERT(op == JSOP_NOP); - } - } - } fprintf(gOutFile, "%3u: %5u [%4u] %-8s", - PTRDIFF(sn, notes, jssrcnote), offset, delta, name); + PTRDIFF(sn, notes, jssrcnote), offset, delta, + js_SrcNoteSpec[SN_TYPE(sn)].name); + type = (JSSrcNoteType) SN_TYPE(sn); switch (type) { case SRC_SETLINE: fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); @@ -990,17 +914,11 @@ SrcNotes(JSContext *cx, JSScript *script) (uintN) js_GetSrcNoteOffset(sn, 1), (uintN) js_GetSrcNoteOffset(sn, 2)); break; - case SRC_IF_ELSE: - fprintf(gOutFile, " else %u elseif %u", - (uintN) js_GetSrcNoteOffset(sn, 0), - (uintN) js_GetSrcNoteOffset(sn, 1)); - break; case SRC_COND: + case SRC_IF_ELSE: case SRC_WHILE: case SRC_PCBASE: case SRC_PCDELTA: - case SRC_DECL: - case SRC_BRACE: fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); break; case SRC_LABEL: @@ -1030,17 +948,11 @@ SrcNotes(JSContext *cx, JSScript *script) caseOff = (uintN) js_GetSrcNoteOffset(sn, 1); if (caseOff) fprintf(gOutFile, " first case offset %u", caseOff); - GetSwitchTableBounds(script, offset, - &switchTableStart, &switchTableEnd); break; case SRC_CATCH: delta = (uintN) js_GetSrcNoteOffset(sn, 0); - if (delta) { - if (script->main[offset] == JSOP_LEAVEBLOCK) - fprintf(gOutFile, " stack depth %u", delta); - else - fprintf(gOutFile, " guard delta %u", delta); - } + if (delta) + fprintf(gOutFile, " guard size %u", delta); break; default:; } @@ -1098,12 +1010,12 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) for (i = 0; i < argc; i++) { script = ValueToScript(cx, argv[i]); if (!script) - return JS_FALSE; + continue; - if (VALUE_IS_FUNCTION(cx, argv[i])) { + if (JSVAL_IS_FUNCTION(cx, argv[i])) { JSFunction *fun = JS_ValueToFunction(cx, argv[i]); if (fun && (fun->flags & JSFUN_FLAGS_MASK)) { - uint16 flags = fun->flags; + uint8 flags = fun->flags; fputs("flags:", stdout); #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout); @@ -1113,10 +1025,6 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) SHOW_FLAG(GETTER); SHOW_FLAG(BOUND_METHOD); SHOW_FLAG(HEAVYWEIGHT); - SHOW_FLAG(THISP_STRING); - SHOW_FLAG(THISP_NUMBER); - SHOW_FLAG(THISP_BOOLEAN); - SHOW_FLAG(INTERPRETED); #undef SHOW_FLAG putchar('\n'); @@ -1146,7 +1054,7 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, for (i = 0; i < argc; i++) { script = ValueToScript(cx, argv[i]); if (!script) - return JS_FALSE; + continue; if (!script || !script->filename) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, @@ -1277,7 +1185,7 @@ DumpScope(JSContext *cx, JSObject *obj, FILE *fp) for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; - fprintf(fp, "%3u %p", i, (void *)sprop); + fprintf(fp, "%3u %p", i, sprop); if (JSID_IS_INT(sprop->id)) { fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); } else if (JSID_IS_ATOM(sprop->id)) { @@ -1298,7 +1206,7 @@ DumpScope(JSContext *cx, JSObject *obj, FILE *fp) #undef DUMP_ATTR fprintf(fp, " slot %lu flags %x shortid %d\n", - (unsigned long)sprop->slot, sprop->flags, sprop->shortid); + sprop->slot, sprop->flags, sprop->shortid); } } @@ -1520,11 +1428,7 @@ ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - char version[20] = "\n"; -#if JS_VERSION < 150 - sprintf(version, " for version %d\n", JS_VERSION); -#endif - fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version); + fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__); return JS_TRUE; } @@ -1664,7 +1568,7 @@ static JSBool StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE; + *rval = JS_StringsAreUTF8 () ? JSVAL_TRUE : JSVAL_FALSE; return JS_TRUE; } @@ -1686,20 +1590,20 @@ TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* The following throw errors if compiled with UTF-8. */ switch (mode) { /* mode 1: malformed UTF-8 string. */ - case 1: - JS_NewStringCopyZ(cx, badUtf8); + case 1: + JS_NewStringCopyZ(cx, badUtf8); break; /* mode 2: big UTF-8 character. */ - case 2: - JS_NewStringCopyZ(cx, bigUtf8); + case 2: + JS_NewStringCopyZ(cx, bigUtf8); break; /* mode 3: bad surrogate character. */ - case 3: - JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); + 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); + case 4: + JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); break; default: JS_ReportError(cx, "invalid mode parameter"); @@ -1708,460 +1612,43 @@ TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return !JS_IsExceptionPending (cx); } -static JSBool -ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JS_ReportError(cx, "This is an error"); - return JS_FALSE; -} - -#define LAZY_STANDARD_CLASSES - -/* A class for easily testing the inner/outer object callbacks. */ -typedef struct ComplexObject { - JSBool isInner; - JSObject *inner; - JSObject *outer; -} ComplexObject; - -static JSObject * -split_create_outer(JSContext *cx); - -static JSObject * -split_create_inner(JSContext *cx, JSObject *outer); - -static ComplexObject * -split_get_private(JSContext *cx, JSObject *obj); - -JS_STATIC_DLL_CALLBACK(JSBool) -split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - jsid asId; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - /* Make sure to define this property on the inner object. */ - if (!JS_ValueToId(cx, *vp, &asId)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL, - JSPROP_ENUMERATE, NULL); - } - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - if (JSVAL_IS_STRING(id)) { - JSString *str; - - str = JSVAL_TO_STRING(id); - return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); - } - if (JSVAL_IS_INT(id)) - return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); - return JS_TRUE; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - if (JSVAL_IS_STRING(id)) { - JSString *str; - - str = JSVAL_TO_STRING(id); - return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), - JS_GetStringLength(str), vp); - } - if (JSVAL_IS_INT(id)) - return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); - return JS_TRUE; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - ComplexObject *cpx; - jsid asId; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - /* Make sure to define this property on the inner object. */ - if (!JS_ValueToId(cx, *vp, &asId)) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp); - } - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - ComplexObject *cpx; - JSObject *iterator; - - switch (enum_op) { - case JSENUMERATE_INIT: - cpx = JS_GetPrivate(cx, obj); - - if (!cpx->isInner && cpx->inner) - obj = cpx->inner; - - iterator = JS_NewPropertyIterator(cx, obj); - if (!iterator) - return JS_FALSE; - - *statep = OBJECT_TO_JSVAL(iterator); - if (idp) - *idp = JSVAL_ZERO; - break; - - case JSENUMERATE_NEXT: - iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); - if (!JS_NextProperty(cx, iterator, idp)) - return JS_FALSE; - - if (*idp != JSVAL_VOID) - break; - /* Fall through. */ - - case JSENUMERATE_DESTROY: - /* Let GC at our iterator object. */ - *statep = JSVAL_NULL; - break; - } - - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - ComplexObject *cpx; - - cpx = split_get_private(cx, obj); - if (!cpx) - return JS_TRUE; - if (!cpx->isInner && cpx->inner) { - jsid asId; - JSProperty *prop; - - if (!JS_ValueToId(cx, id, &asId)) - return JS_FALSE; - - if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, cpx->inner, prop); - - return JS_TRUE; - } - -#ifdef LAZY_STANDARD_CLASSES - if (!(flags & JSRESOLVE_ASSIGNING)) { - JSBool resolved; - - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - - if (resolved) { - *objp = obj; - return JS_TRUE; - } - } -#endif - - /* XXX For additional realism, let's resolve some random property here. */ - return JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(void) -split_finalize(JSContext *cx, JSObject *obj) -{ - JS_free(cx, JS_GetPrivate(cx, obj)); -} - -JS_STATIC_DLL_CALLBACK(uint32) -split_mark(JSContext *cx, JSObject *obj, void *arg) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - - if (!cpx->isInner && cpx->inner) { - /* Mark the inner object. */ - JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg); - } - - return 0; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -split_outerObject(JSContext *cx, JSObject *obj) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - return cpx->isInner ? cpx->outer : obj; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -split_innerObject(JSContext *cx, JSObject *obj) -{ - ComplexObject *cpx; - - cpx = JS_GetPrivate(cx, obj); - return !cpx->isInner ? cpx->inner : obj; -} - -static JSExtendedClass split_global_class = { - {"split_global", - JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED, - split_addProperty, split_delProperty, - split_getProperty, split_setProperty, - (JSEnumerateOp)split_enumerate, - (JSResolveOp)split_resolve, - JS_ConvertStub, split_finalize, - NULL, NULL, NULL, NULL, NULL, NULL, - split_mark, NULL}, - NULL, split_outerObject, split_innerObject, - NULL, NULL, NULL, NULL, NULL -}; - -JSObject * -split_create_outer(JSContext *cx) -{ - ComplexObject *cpx; - JSObject *obj; - - cpx = JS_malloc(cx, sizeof *obj); - if (!cpx) - return NULL; - cpx->outer = NULL; - cpx->inner = NULL; - cpx->isInner = JS_FALSE; - - obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); - if (!obj) { - JS_free(cx, cpx); - return NULL; - } - - JS_ASSERT(!JS_GetParent(cx, obj)); - if (!JS_SetPrivate(cx, obj, cpx)) { - JS_free(cx, cpx); - return NULL; - } - - return obj; -} - -static JSObject * -split_create_inner(JSContext *cx, JSObject *outer) -{ - ComplexObject *cpx, *outercpx; - JSObject *obj; - - JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base); - - cpx = JS_malloc(cx, sizeof *cpx); - if (!cpx) - return NULL; - cpx->outer = outer; - cpx->inner = NULL; - cpx->isInner = JS_TRUE; - - obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); - if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) { - JS_free(cx, cpx); - return NULL; - } - - outercpx = JS_GetPrivate(cx, outer); - outercpx->inner = obj; - - return obj; -} - -static ComplexObject * -split_get_private(JSContext *cx, JSObject *obj) -{ - do { - if (JS_GET_CLASS(cx, obj) == &split_global_class.base) - return JS_GetPrivate(cx, obj); - obj = JS_GetParent(cx, obj); - } while (obj); - - return NULL; -} - -static JSBool -sandbox_enumerate(JSContext *cx, JSObject *obj) -{ - jsval v; - JSBool b; - - if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) - return JS_FALSE; - return !b || JS_EnumerateStandardClasses(cx, obj); -} - -static JSBool -sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - jsval v; - JSBool b, resolved; - - if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) - return JS_FALSE; - if (b && (flags & JSRESOLVE_ASSIGNING) == 0) { - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - if (resolved) { - *objp = obj; - return JS_TRUE; - } - } - *objp = NULL; - return JS_TRUE; -} - -static JSClass sandbox_class = { - "sandbox", - JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - sandbox_enumerate, (JSResolveOp)sandbox_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSObject *sobj; - JSContext *scx; - const jschar *src; - size_t srclen; - JSBool lazy, ok; - jsval v; - JSStackFrame *fp; - - sobj = NULL; - if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj)) - return JS_FALSE; - - scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize); - if (!scx) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - src = JS_GetStringChars(str); - srclen = JS_GetStringLength(str); - lazy = JS_FALSE; - if (srclen == 4 && - src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { - lazy = JS_TRUE; - srclen = 0; - } - - if (!sobj) { - sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL); - if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) { - ok = JS_FALSE; - goto out; - } - v = BOOLEAN_TO_JSVAL(v); - ok = JS_SetProperty(cx, sobj, "lazy", &v); - if (!ok) - goto out; - } - - if (srclen == 0) { - *rval = OBJECT_TO_JSVAL(sobj); - ok = JS_TRUE; - } else { - fp = JS_GetScriptedCaller(cx, NULL); - ok = JS_EvaluateUCScript(scx, sobj, src, srclen, - fp->script->filename, - JS_PCToLineNumber(cx, fp->script, fp->pc), - rval); - } - -out: - JS_DestroyContext(scx); - return ok; -} - static JSFunctionSpec shell_functions[] = { - {"version", Version, 0,0,0}, - {"options", Options, 0,0,0}, - {"load", Load, 1,0,0}, - {"readline", ReadLine, 0,0,0}, - {"print", Print, 0,0,0}, - {"help", Help, 0,0,0}, - {"quit", Quit, 0,0,0}, - {"gc", GC, 0,0,0}, - {"trap", Trap, 3,0,0}, - {"untrap", Untrap, 2,0,0}, - {"line2pc", LineToPC, 0,0,0}, - {"pc2line", PCToLine, 0,0,0}, - {"stringsAreUtf8", StringsAreUtf8, 0,0,0}, - {"testUtf8", TestUtf8, 1,0,0}, - {"throwError", ThrowError, 0,0,0}, + {"version", Version, 0}, + {"options", Options, 0}, + {"load", Load, 1}, + {"readline", ReadLine, 0}, + {"print", Print, 0}, + {"help", Help, 0}, + {"quit", Quit, 0}, + {"gc", GC, 0}, + {"trap", Trap, 3}, + {"untrap", Untrap, 2}, + {"line2pc", LineToPC, 0}, + {"pc2line", PCToLine, 0}, + {"stringsAreUtf8", StringsAreUtf8, 0}, + {"testUtf8", TestUtf8, 1}, #ifdef DEBUG - {"dis", Disassemble, 1,0,0}, - {"dissrc", DisassWithSrc, 1,0,0}, - {"notes", Notes, 1,0,0}, - {"tracing", Tracing, 0,0,0}, - {"stats", DumpStats, 1,0,0}, + {"dis", Disassemble, 1}, + {"dissrc", DisassWithSrc, 1}, + {"notes", Notes, 1}, + {"tracing", Tracing, 0}, + {"stats", DumpStats, 1}, #endif #ifdef TEST_EXPORT - {"xport", DoExport, 2,0,0}, + {"xport", DoExport, 2}, #endif #ifdef TEST_CVTARGS - {"cvtargs", ConvertArgs, 0,0,12}, + {"cvtargs", ConvertArgs, 0, 0, 12}, #endif - {"build", BuildDate, 0,0,0}, - {"clear", Clear, 0,0,0}, - {"intern", Intern, 1,0,0}, - {"clone", Clone, 1,0,0}, - {"seal", Seal, 1,0,1}, - {"getpda", GetPDA, 1,0,0}, - {"getslx", GetSLX, 1,0,0}, - {"toint32", ToInt32, 1,0,0}, - {"evalcx", EvalInContext, 1,0,0}, - {NULL,NULL,0,0,0} + {"build", BuildDate, 0}, + {"clear", Clear, 0}, + {"intern", Intern, 1}, + {"clone", Clone, 1}, + {"seal", Seal, 1, 0, 1}, + {"getpda", GetPDA, 1}, + {"getslx", GetSLX, 1}, + {"toint32", ToInt32, 1}, + {0} }; /* NOTE: These must be kept in sync with the above. */ @@ -2181,7 +1668,6 @@ static char *shell_help_messages[] = { "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)", - "throwError() Throw an error from JS_ReportError", #ifdef DEBUG "dis([fun]) Disassemble functions into bytecodes", "dissrc([fun]) Disassemble functions with source lines", @@ -2203,9 +1689,6 @@ static char *shell_help_messages[] = { "getpda(obj) Get the property descriptors for obj", "getslx(obj) Get script line extent", "toint32(n) Testing hook for JS_ValueToInt32", - "evalcx(s[, o]) Evaluate s in optional sandbox object o\n" - " if (s == '' && !o) return new o with eager standard classes\n" - " if (s == 'lazy' && !o) return new o with lazy standard classes", 0 }; @@ -2222,40 +1705,6 @@ ShowHelpForCommand(uintN n) fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]); } -static JSObject * -split_setup(JSContext *cx) -{ - JSObject *outer, *inner, *arguments; - - outer = split_create_outer(cx); - if (!outer) - return NULL; - JS_SetGlobalObject(cx, outer); - - inner = split_create_inner(cx, outer); - if (!inner) - return NULL; - - if (!JS_DefineFunctions(cx, inner, shell_functions)) - return NULL; - JS_ClearScope(cx, outer); - - /* Create a dummy arguments object. */ - arguments = JS_NewArrayObject(cx, 0, NULL); - if (!arguments || - !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments), - NULL, NULL, 0)) { - return NULL; - } - -#ifndef LAZY_STANDARD_CLASSES - if (!JS_InitStandardClasses(cx, inner)) - return NULL; -#endif - - return inner; -} - static JSBool Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -2319,13 +1768,13 @@ enum its_tinyid { }; static JSPropertySpec its_props[] = { - {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL}, - {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL}, - {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL}, - {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL}, - {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL}, - {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL}, - {NULL,0,0,NULL,NULL} + {"color", ITS_COLOR, JSPROP_ENUMERATE}, + {"height", ITS_HEIGHT, JSPROP_ENUMERATE}, + {"width", ITS_WIDTH, JSPROP_ENUMERATE}, + {"funny", ITS_FUNNY, JSPROP_ENUMERATE}, + {"array", ITS_ARRAY, JSPROP_ENUMERATE}, + {"rdonly", ITS_RDONLY, JSPROP_READONLY}, + {0} }; static JSBool @@ -2365,9 +1814,9 @@ its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } static JSFunctionSpec its_methods[] = { - {"item", its_item, 0,0,0}, - {"bindMethod", its_bindMethod, 2,0,0}, - {NULL,NULL,0,0,0} + {"item", its_item, 0}, + {"bindMethod", its_bindMethod, 2}, + {0} }; #ifdef JSD_LOWLEVEL_SOURCE @@ -2501,13 +1950,17 @@ static JSClass its_class = { "It", JSCLASS_NEW_RESOLVE, its_addProperty, its_delProperty, its_getProperty, its_setProperty, its_enumerate, (JSResolveOp)its_resolve, - its_convert, its_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS + its_convert, its_finalize }; JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = { +#if JS_HAS_DFLT_MSG_STRINGS #define MSG_DEF(name, number, count, exception, format) \ - { format, count, JSEXN_ERR } , + { format, count } , +#else +#define MSG_DEF(name, number, count, exception, format) \ + { NULL, count } , +#endif #include "jsshell.msg" #undef MSG_DEF }; @@ -2520,6 +1973,22 @@ my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) return NULL; } +static void +my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) +{ + if (!report) { + fprintf(gErrFile, "%s\n", message); + return; + } + + /* Ignore any exceptions */ + if (JSREPORT_IS_EXCEPTION(report->flags)) + return; + + /* Otherwise, fall back to the ordinary error reporter. */ + my_ErrorReporter(cx, message, report); +} + static void my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { @@ -2651,6 +2120,8 @@ Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } #endif +#define LAZY_STANDARD_CLASSES + static JSBool global_enumerate(JSContext *cx, JSObject *obj) { @@ -2666,13 +2137,15 @@ global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { #ifdef LAZY_STANDARD_CLASSES - JSBool resolved; + if ((flags & JSRESOLVE_ASSIGNING) == 0) { + JSBool resolved; - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return JS_FALSE; - if (resolved) { - *objp = obj; - return JS_TRUE; + if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) + return JS_FALSE; + if (resolved) { + *objp = obj; + return JS_TRUE; + } } #endif @@ -2729,12 +2202,11 @@ global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, } JSClass global_class = { - "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, + "global", JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, global_enumerate, (JSResolveOp) global_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS + JS_ConvertStub, JS_FinalizeStub }; static JSBool @@ -2848,8 +2320,7 @@ static JSClass env_class = { JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, env_setProperty, env_enumerate, (JSResolveOp) env_resolve, - JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS + JS_ConvertStub, JS_FinalizeStub }; #ifdef NARCISSUS @@ -2907,7 +2378,7 @@ Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_GetStringLength(source), filename, lineno, rval); JS_SetOptions(cx, oldopts); - + return ok; } @@ -2964,16 +2435,17 @@ snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) const char *filename; char *pathname; JSStackFrame *fp; + int fd, cc; JSBool ok; - off_t cc, len; + size_t len; char *buf; - FILE *file; + struct stat sb; str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; filename = JS_GetStringBytes(str); - + /* Get the currently executing script's name. */ fp = JS_GetScriptedCaller(cx, NULL); JS_ASSERT(fp && fp->script->filename); @@ -2981,43 +2453,32 @@ snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!pathname) return JS_FALSE; - ok = JS_FALSE; + fd = open(pathname, O_RDONLY); + ok = JS_TRUE; len = 0; buf = NULL; - file = fopen(pathname, "rb"); - if (!file) { + if (fd < 0) { 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", pathname); + ok = JS_FALSE; } else { - if (fseek(file, 0, SEEK_END) == EOF) { - JS_ReportError(cx, "can't seek end of %s", pathname); - } else { - len = ftell(file); - if (fseek(file, 0, SEEK_SET) == EOF) { - JS_ReportError(cx, "can't seek start of %s", pathname); - } else { - buf = JS_malloc(cx, len + 1); - if (buf) { - cc = fread(buf, 1, len, file); - if (cc != len) { - JS_free(cx, buf); - JS_ReportError(cx, "can't read %s: %s", pathname, - (cc < 0) ? strerror(errno) - : "short read"); - } else { - len = (size_t)cc; - ok = JS_TRUE; - } - } - } + len = sb.st_size; + buf = JS_malloc(cx, len + 1); + if (!buf) { + ok = JS_FALSE; + } else if ((cc = read(fd, buf, len)) != len) { + JS_free(cx, buf); + JS_ReportError(cx, "can't read %s: %s", pathname, + (cc < 0) ? strerror(errno) : "short read"); + ok = JS_FALSE; } - fclose(file); } + close(fd); JS_free(cx, pathname); - if (!ok) { - JS_free(cx, buf); + if (!ok) return ok; - } - buf[len] = '\0'; str = JS_NewString(cx, buf, len); if (!str) { @@ -3047,8 +2508,6 @@ main(int argc, char **argv, char **envp) gStackBase = (jsuword)&stackDummy; - setlocale(LC_ALL, ""); - #ifdef XP_OS2 /* these streams are normally line buffered on OS/2 and need a \n, * * so we need to unbuffer then to get a reasonable prompt */ @@ -3071,10 +2530,6 @@ main(int argc, char **argv, char **envp) return 1; JS_SetErrorReporter(cx, my_ErrorReporter); -#ifdef JS_THREADSAFE - JS_BeginRequest(cx); -#endif - glob = JS_NewObject(cx, &global_class, NULL, NULL); if (!glob) return 1; @@ -3170,10 +2625,6 @@ main(int argc, char **argv, char **envp) JSD_DebuggerOff(_jsdc); #endif /* JSDEBUGGER */ -#ifdef JS_THREADSAFE - JS_EndRequest(cx); -#endif - JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); diff --git a/lib/javascript/js.mak b/lib/javascript/js.mak index f0f32b88..dfcfdeab 100644 --- a/lib/javascript/js.mak +++ b/lib/javascript/js.mak @@ -12,7 +12,6 @@ CFG=jsshell - Win32 Debug !IF "$(CFG)" != "js - Win32 Release" && "$(CFG)" != "js - Win32 Debug" &&\ "$(CFG)" != "jsshell - Win32 Release" && "$(CFG)" != "jsshell - Win32 Debug" &&\ - "$(CFG)" != "jskwgen - Win32 Release" && "$(CFG)" != "jskwgen - 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 @@ -26,8 +25,6 @@ CFG=jsshell - Win32 Debug !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 "jskwgen - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "jskwgen - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE "fdlibm - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "fdlibm - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE @@ -58,7 +55,7 @@ NULL=nul OUTDIR=.\Release INTDIR=.\Release -ALL : "fdlibm - Win32 Release" "jskwgen - Win32 Release" "$(OUTDIR)\js32.dll" +ALL : "fdlibm - Win32 Release" "$(OUTDIR)\js32.dll" CLEAN : -@erase "$(INTDIR)\jsapi.obj" @@ -95,22 +92,18 @@ CLEAN : -@erase "$(INTDIR)\jsxdrapi.obj" -@erase "$(INTDIR)\jsxml.obj" -@erase "$(INTDIR)\prmjtime.obj" - -@erase "$(INTDIR)\js.pch" - -@erase "$(INTDIR)\jsautokw.h" -@erase "$(OUTDIR)\js32.dll" -@erase "$(OUTDIR)\js32.exp" -@erase "$(OUTDIR)\js32.lib" - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" clean - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Release" clean "$(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" /I"$(INTDIR)" /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" /I"$(INTDIR)" /YX\ + "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /Fp"$(INTDIR)/js.pch" /YX\ /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. @@ -212,7 +205,7 @@ LINK32_OBJS= \ OUTDIR=.\Debug INTDIR=.\Debug -ALL : "fdlibm - Win32 Debug" "jskwgen - Win32 Debug" "$(OUTDIR)\js32.dll" +ALL : "fdlibm - Win32 Debug" "$(OUTDIR)\js32.dll" CLEAN : -@erase "$(INTDIR)\jsapi.obj" @@ -249,24 +242,20 @@ CLEAN : -@erase "$(INTDIR)\jsxdrapi.obj" -@erase "$(INTDIR)\jsxml.obj" -@erase "$(INTDIR)\prmjtime.obj" - -@erase "$(INTDIR)\js.pch" - -@erase "$(INTDIR)\jsautokw.h" -@erase "$(OUTDIR)\js32.dll" -@erase "$(OUTDIR)\js32.exp" -@erase "$(OUTDIR)\js32.ilk" -@erase "$(OUTDIR)\js32.lib" -@erase "$(OUTDIR)\js32.pdb" - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" clean - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Debug" clean "$(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" /I"$(INTDIR)" /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" /I"$(INTDIR)" /YX\ + /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=.\. @@ -353,156 +342,6 @@ LINK32_OBJS= \ $(LINK32_FLAGS) $(LINK32_OBJS) << -!ELSEIF "$(CFG)" == "jskwgen - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "jsshell\Release" -# PROP BASE Intermediate_Dir "jskwgen\Release" -# PROP BASE Target_Dir "jskwgen" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "jskwgen" -OUTDIR=.\Release -INTDIR=.\Release - -ALL : "$(INTDIR)" "$(INTDIR)\host_jskwgen.exe" - -CLEAN : - -@erase "$(INTDIR)\jskwgen.obj" - -@erase "$(INTDIR)\jskwgen.pch" - -@erase "$(INTDIR)\host_jskwgen.exe" - -"$(INTDIR)" : - if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)" - -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)/jskwgen.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"$(INTDIR)/jskwgen.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:"$(INTDIR)/jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" -LINK32_OBJS= \ - "$(INTDIR)\jskwgen.obj" \ - -"$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - -!ELSEIF "$(CFG)" == "jskwgen - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "jsshell\Debug" -# PROP BASE Intermediate_Dir "jskwgen\Debug" -# PROP BASE Target_Dir "jskwgen" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "jskwgen" -OUTDIR=.\Debug -INTDIR=.\Debug - -ALL : "$(INTDIR)" "$(INTDIR)\host_jskwgen.exe" - -CLEAN : - -@erase "$(INTDIR)\jskwgen.obj" - -@erase "$(INTDIR)\jskwgen.pch" - -@erase "$(INTDIR)\host_jskwgen.exe" - -"$(INTDIR)" : - if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)" - -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)/jskwgen.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) $< - -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"$(INTDIR)/jskwgen.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:"$(INTDIR)/jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" -LINK32_OBJS= \ - "$(INTDIR)\jskwgen.obj" \ - -"$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - !ELSEIF "$(CFG)" == "jsshell - Win32 Release" # PROP BASE Use_MFC 0 @@ -522,19 +361,16 @@ ALL : "js - Win32 Release" "$(OUTDIR)\jsshell.exe" CLEAN : -@erase "$(INTDIR)\js.obj" - -@erase "$(INTDIR)\jsshell.pch" -@erase "$(OUTDIR)\jsshell.exe" - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" clean - "$(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" /I"$(INTDIR)" /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" /I"$(INTDIR)" /YX /Fo"$(INTDIR)/" /c + "XP_WIN" /D "JSFILE" /Fp"$(INTDIR)/jsshell.pch" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. @@ -600,11 +436,9 @@ ALL : "js - Win32 Debug" "$(OUTDIR)\jsshell.exe" CLEAN : -@erase "$(INTDIR)\js.obj" - -@erase "$(INTDIR)\jsshell.pch" -@erase "$(OUTDIR)\jsshell.exe" -@erase "$(OUTDIR)\jsshell.ilk" -@erase "$(OUTDIR)\jsshell.pdb" - -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" clean "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" @@ -694,7 +528,6 @@ CLEAN : -@erase "$(INTDIR)\w_atan2.obj" -@erase "$(INTDIR)\w_pow.obj" -@erase "$(INTDIR)\w_sqrt.obj" - -@erase "$(INTDIR)\fdlibm.pch" -@erase "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)" : @@ -791,7 +624,6 @@ CLEAN : -@erase "$(INTDIR)\w_atan2.obj" -@erase "$(INTDIR)\w_pow.obj" -@erase "$(INTDIR)\w_sqrt.obj" - -@erase "$(INTDIR)\fdlibm.pch" -@erase "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)" : @@ -983,6 +815,7 @@ NODEP_CPP_JSAPI=\ "$(INTDIR)\jsapi.obj" : $(SOURCE) $(DEP_CPP_JSAPI) "$(INTDIR)" + !ENDIF # End Source File @@ -3129,10 +2962,8 @@ NODEP_CPP_JSSCA=\ ".\prlock.h"\ -"$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" "$(INTDIR)\jsautokw.h" +"$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" -"$(INTDIR)\jsautokw.h" : $(INTDIR)\host_jskwgen.exe jskeyword.tbl - $(INTDIR)\host_jskwgen.exe $(INTDIR)\jsautokw.h !ELSEIF "$(CFG)" == "js - Win32 Debug" @@ -3171,7 +3002,6 @@ DEP_CPP_JSSCA=\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ - $(INTDIR)\jsautokw.h \ NODEP_CPP_JSSCA=\ ".\jsautocfg.h"\ @@ -3181,46 +3011,6 @@ NODEP_CPP_JSSCA=\ "$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" -"$(INTDIR)\jsautokw.h" : $(INTDIR)\host_jskwgen.exe jskeyword.tbl - $(INTDIR)\host_jskwgen.exe $(INTDIR)\jsautokw.h - -!ENDIF - -# End Source File -################################################################################ -# Begin Source File - -SOURCE=.\jskwgen.c - -!IF "$(CFG)" == "js - Win32 Release" - -DEP_CPP_JSSCO=\ - ".\jskwgen.c"\ - {$(INCLUDE)}"\sys\types.h"\ - - -"$(INTDIR)\jskwgen.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" - - -!ELSEIF "$(CFG)" == "js - Win32 Debug" - -DEP_CPP_JSSCO=\ - ".\jskwgen.c"\ - {$(INCLUDE)}"\sys\types.h"\ - -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:"$(INTDIR)/host_jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" - -LINK32_OBJS= \ - "$(INTDIR)\jskwgen.obj" - -"$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - !ENDIF @@ -3789,12 +3579,12 @@ NODEP_CPP_PRMJT=\ !IF "$(CFG)" == "js - Win32 Debug" "fdlibm - Win32 Debug" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" !ELSEIF "$(CFG)" == "js - Win32 Release" "fdlibm - Win32 Release" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" !ENDIF @@ -3872,36 +3662,17 @@ NODEP_CPP_JS_C42=\ ################################################################################ # Begin Project Dependency -# Project_Dep_Name "jskwgen" - -!IF "$(CFG)" == "js - Win32 Release" - -"jskwgen - Win32 Release" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Release" - -!ELSEIF "$(CFG)" == "js - Win32 Debug" - -"jskwgen - Win32 Debug" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Debug" - -!ENDIF - -# End Project Dependency -# End Target -################################################################################ -# Begin Project Dependency - # Project_Dep_Name "js" !IF "$(CFG)" == "jsshell - Win32 Release" "js - Win32 Release" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" !ELSEIF "$(CFG)" == "jsshell - Win32 Debug" "js - Win32 Debug" : - @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" + $(MAKE) /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" !ENDIF diff --git a/lib/javascript/js.msg b/lib/javascript/js.msg index 2686af03..5df1ead9 100644 --- a/lib/javascript/js.msg +++ b/lib/javascript/js.msg @@ -81,46 +81,46 @@ MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined") -MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context") -MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}") -MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") -MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") -MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_INTERNALERR, "can't lock memory") -MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") +MSG_DEF(JSMSG_NO_REG_EXPS, 2, 1, JSEXN_INTERNALERR, "sorry, regular expression are not supported") +MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_NONE, "{0} requires more than {1} argument{2}") +MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_NONE, "invalid format character {0}") +MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_NONE, "unknown type {0}") +MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_NONE, "can't lock memory") +MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_NONE, "can't unlock memory") MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") -MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") -MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") +MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_NONE, "{0} has no constructor") +MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_NONE, "can't alias {0} to {1} in class {2}") MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") -MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") +MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_NONE, "can't watch non-native objects of class {0}") MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}") MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large") MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") -MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}") +MSG_DEF(JSMSG_SAME_FORMAL, 21, 1, JSEXN_NONE, "duplicate formal argument {0}") MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") MSG_DEF(JSMSG_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}") -MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_TYPEERR, "{0} is not exported") +MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_NONE, "{0} is not exported") MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") -MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") +MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_NONE, "invalid new expression result {0}") MSG_DEF(JSMSG_BAD_SHARP_DEF, 29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=") MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#") MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}") -MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head") -MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer") +MSG_DEF(JSMSG_NAN, 34, 1, JSEXN_ERR, "{0} is not a number") +MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_NONE, "can't convert {0} to an integer") MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent") MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties") -MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}") -MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_TYPEERR, "can't XDR class {0}") +MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_NONE, "can't find class id {0}") +MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_NONE, "can't XDR class {0}") MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})") MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") @@ -129,25 +129,25 @@ MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifi MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") -MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 50, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") -MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 51, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator") -MSG_DEF(JSMSG_PAREN_AFTER_LET, 52, 0, JSEXN_SYNTAXERR, "missing ) after let head") -MSG_DEF(JSMSG_CURLY_AFTER_LET, 53, 0, JSEXN_SYNTAXERR, "missing } after let block") +MSG_DEF(JSMSG_ZERO_QUANTIFIER, 50, 1, JSEXN_SYNTAXERR, "zero quantifier {0}") +MSG_DEF(JSMSG_UNTERM_QUANTIFIER, 51, 1, JSEXN_SYNTAXERR, "unterminated quantifier {0}") +MSG_DEF(JSMSG_EMPTY_BEFORE_STAR, 52, 0, JSEXN_SYNTAXERR, "regular expression before * could be empty") +MSG_DEF(JSMSG_EMPTY_BEFORE_PLUS, 53, 0, JSEXN_SYNTAXERR, "regular expression before + could be empty") MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}") MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression") MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") MSG_DEF(JSMSG_BAD_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") MSG_DEF(JSMSG_NO_INPUT, 59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}") -MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") +MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_NONE, "can't open {0}: {1}") MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") -MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") -MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") -MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") -MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") -MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") -MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") +MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_NONE, "unexpected end of data") +MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_NONE, "illegal seek beyond start") +MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_NONE, "illegal seek beyond end") +MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_NONE, "illegal end-based seek") +MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_NONE, "unknown seek whence: {0}") +MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_NONE, "bad script XDR magic number") MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") @@ -191,7 +191,7 @@ MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in par MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}") -MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") +MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_NONE, "test for equality (==) mistyped as assignment (=)?{0}") MSG_DEF(JSMSG_BAD_IMPORT, 113, 0, JSEXN_SYNTAXERR, "invalid import expression") MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") @@ -203,7 +203,7 @@ MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "invalid break") MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue") -MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function") +MSG_DEF(JSMSG_BAD_RETURN, 124, 0, JSEXN_SYNTAXERR, "invalid return") MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label") MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label") MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} hides argument") @@ -226,14 +226,14 @@ MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") -MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}") +MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_NONE, "uncaught exception: {0}") MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") -MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") -MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") +MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_NONE, "can't describe non-native properties of class {0}") +MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 0, JSEXN_TYPEERR, "second argument to Function.prototype.apply must be an array") MSG_DEF(JSMSG_REDECLARED_VAR, 155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") MSG_DEF(JSMSG_UNDECLARED_VAR, 156, 1, JSEXN_TYPEERR, "assignment to undeclared variable {0}") MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE, 157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value") @@ -241,30 +241,30 @@ MSG_DEF(JSMSG_DEPRECATED_USAGE, 158, 1, JSEXN_REFERENCEERR, "deprecated {0 MSG_DEF(JSMSG_BAD_URI, 159, 0, JSEXN_URIERR, "malformed URI sequence") MSG_DEF(JSMSG_GETTER_ONLY, 160, 0, JSEXN_TYPEERR, "setting a property that has only a getter") MSG_DEF(JSMSG_TRAILING_COMMA, 161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers") -MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}") +MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_TYPEERR, "reference to undefined property {0}") MSG_DEF(JSMSG_USELESS_EXPR, 163, 0, JSEXN_TYPEERR, "useless expression") MSG_DEF(JSMSG_REDECLARED_PARAM, 164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") -MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables") +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_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}") +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_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") +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, 1, JSEXN_SYNTAXERR, "XML tag name mismatch (expected {0})") +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}") @@ -281,21 +281,10 @@ MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attr 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_BUFFER_TOO_SMALL, 202, 0, JSEXN_INTERNALERR, "buffer too small") -MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate character {0}") -MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") -MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") -MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called") -MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") -MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") -MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") -MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") -MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value") -MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") -MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") -MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") -MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") -MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate") -MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 218, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") -MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements") +MSG_DEF(JSMSG_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/lib/javascript/jsapi.c b/lib/javascript/jsapi.c index f03fa364..344fc246 100644 --- a/lib/javascript/jsapi.c +++ b/lib/javascript/jsapi.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -86,10 +86,6 @@ #include "jsxml.h" #endif -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - #ifdef HAVE_VA_LIST_AS_ARRAY #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) #else @@ -617,7 +613,7 @@ JS_TypeOfValue(JSContext *cx, jsval v) .callAtom), &v)) { JS_ClearPendingException(cx); - } else if (VALUE_IS_FUNCTION(cx, v)) { + } else if (JSVAL_IS_FUNCTION(cx, v)) { type = JSTYPE_FUNCTION; } #endif @@ -641,7 +637,7 @@ JS_GetTypeName(JSContext *cx, JSType type) { if ((uintN)type >= (uintN)JSTYPE_LIMIT) return NULL; - return js_type_strs[type]; + return js_type_str[type]; } /************************************************************************/ @@ -652,38 +648,23 @@ JS_NewRuntime(uint32 maxbytes) JSRuntime *rt; #ifdef DEBUG - static JSBool didFirstChecks; - - if (!didFirstChecks) { - /* - * This code asserts that the numbers associated with the error names - * in jsmsg.def are monotonically increasing. It uses values for the - * error names enumerated in jscntxt.c. It's not a compile-time check - * but it's better than nothing. - */ - int errorNumber = 0; -#define MSG_DEF(name, number, count, exception, format) \ + JS_BEGIN_MACRO + /* + * This code asserts that the numbers associated with the error names in + * jsmsg.def are monotonically increasing. It uses values for the error + * names enumerated in jscntxt.c. It's not a compiletime check, but it's + * better than nothing. + */ + int errorNumber = 0; +#define MSG_DEF(name, number, count, exception, format) \ JS_ASSERT(name == errorNumber++); #include "js.msg" #undef MSG_DEF - -#define MSG_DEF(name, number, count, exception, format) \ - JS_BEGIN_MACRO \ - uintN numfmtspecs = 0; \ - const char *fmt; \ - for (fmt = format; *fmt != '\0'; fmt++) { \ - if (*fmt == '{' && isdigit(fmt[1])) \ - ++numfmtspecs; \ - } \ - JS_ASSERT(count == numfmtspecs); \ JS_END_MACRO; -#include "js.msg" -#undef MSG_DEF - - didFirstChecks = JS_TRUE; - } #endif /* DEBUG */ + if (!js_InitStringGlobals()) + return NULL; rt = (JSRuntime *) malloc(sizeof(JSRuntime)); if (!rt) return NULL; @@ -697,10 +678,6 @@ JS_NewRuntime(uint32 maxbytes) if (!js_InitGC(rt, maxbytes)) goto bad; #ifdef JS_THREADSAFE - if (PR_FAILURE == PR_NewThreadPrivateIndex(&rt->threadTPIndex, - js_ThreadDestructorCB)) { - goto bad; - } rt->gcLock = JS_NEW_LOCK(); if (!rt->gcLock) goto bad; @@ -776,8 +753,6 @@ JS_DestroyRuntime(JSRuntime *rt) JS_DESTROY_CONDVAR(rt->setSlotDone); if (rt->scopeSharingDone) JS_DESTROY_CONDVAR(rt->scopeSharingDone); -#else - GSN_CACHE_CLEAR(&rt->gsnCache); #endif js_FinishPropertyTree(rt); free(rt); @@ -786,7 +761,9 @@ JS_DestroyRuntime(JSRuntime *rt) JS_PUBLIC_API(void) JS_ShutDown(void) { + JS_ArenaShutDown(); js_FinishDtoa(); + js_FreeStringGlobals(); #ifdef JS_THREADSAFE js_CleanupLocks(); #endif @@ -811,13 +788,13 @@ JS_BeginRequest(JSContext *cx) { JSRuntime *rt; - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); + JS_ASSERT(cx->thread); if (!cx->requestDepth) { /* Wait until the GC is finished. */ rt = cx->runtime; JS_LOCK_GC(rt); - /* NB: we use cx->thread here, not js_GetCurrentThread(). */ + /* NB: we use cx->thread here, not js_CurrentThreadId(). */ if (rt->gcThread != cx->thread) { while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); @@ -907,10 +884,6 @@ JS_YieldRequest(JSContext *cx) /* XXXbe give the GC or another request calling it a chance to run here? Assumes FIFO scheduling */ JS_LOCK_GC(rt); - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - } rt->requestCount++; JS_UNLOCK_GC(rt); } @@ -947,16 +920,6 @@ JS_Unlock(JSRuntime *rt) JS_UNLOCK_RUNTIME(rt); } -JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) -{ - JSContextCallback old; - - old = rt->cxCallback; - rt->cxCallback = cxCallback; - return old; -} - JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stackChunkSize) { @@ -966,19 +929,19 @@ JS_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_PUBLIC_API(void) JS_DestroyContext(JSContext *cx) { - js_DestroyContext(cx, JSDCM_FORCE_GC); + js_DestroyContext(cx, JS_FORCE_GC); } JS_PUBLIC_API(void) JS_DestroyContextNoGC(JSContext *cx) { - js_DestroyContext(cx, JSDCM_NO_GC); + js_DestroyContext(cx, JS_NO_GC); } JS_PUBLIC_API(void) JS_DestroyContextMaybeGC(JSContext *cx) { - js_DestroyContext(cx, JSDCM_MAYBE_GC); + js_DestroyContext(cx, JS_MAYBE_GC); } JS_PUBLIC_API(void *) @@ -1023,10 +986,6 @@ JS_SetVersion(JSContext *cx, JSVersion version) if (version == oldVersion) return oldVersion; - /* We no longer support 1.4 or below. */ - if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) - return oldVersion; - cx->version = (cx->version & ~JSVERSION_MASK) | version; js_OnVersionChange(cx); return oldVersion; @@ -1044,7 +1003,6 @@ static struct v2smap { {JSVERSION_ECMA_3, "ECMAv3"}, {JSVERSION_1_5, "1.5"}, {JSVERSION_1_6, "1.6"}, - {JSVERSION_1_7, "1.7"}, {JSVERSION_DEFAULT, js_default_str}, {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ }; @@ -1106,7 +1064,7 @@ JS_ToggleOptions(JSContext *cx, uint32 options) JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void) { - return "JavaScript-C 1.7.0 2007-10-03"; + return "JavaScript-C 1.6 2006-11-19"; } @@ -1120,14 +1078,13 @@ JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj) { cx->globalObject = obj; - #if JS_HAS_XML_SUPPORT cx->xmlSettingFlags = 0; #endif } -JSObject * -js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) +static JSObject * +InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) { JSDHashTable *table; JSBool resolving; @@ -1143,16 +1100,16 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) /* Record Function and Object in cx->resolvingTable, if we are resolving. */ table = cx->resolvingTable; resolving = (table && table->entryCount); - rt = cx->runtime; - key.obj = obj; if (resolving) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); + rt = cx->runtime; + key.obj = obj; + 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 = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + key.id = ATOM_TO_JSID(rt->atomState.ObjectAtom); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); } @@ -1163,19 +1120,6 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) JS_ASSERT(!entry->key.obj && entry->flags == 0); entry->key = key; entry->flags = JSRESFLAG_LOOKUP; - } else { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) - return NULL; - - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - return NULL; - } - - table = cx->resolvingTable; } /* Initialize the function class first so constructors can be made. */ @@ -1197,75 +1141,109 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) out: /* If resolving, remove the other entry (Object or Function) from table. */ - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - if (!resolving) { - /* If not resolving, remove the first entry added above, for Object. */ - JS_ASSERT(key.id == \ - ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); + if (resolving) JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - } return fun_proto; } JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj) { - JSAtom *atom; - CHECK_REQUEST(cx); +#if JS_HAS_UNDEFINED +{ /* Define a top-level property 'undefined' with the undefined value. */ - atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; + JSAtom *atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } +} +#endif /* Function and Object require cooperative bootstrapping magic. */ - if (!js_InitFunctionAndObjectClasses(cx, obj)) + if (!InitFunctionAndObjectClasses(cx, obj)) return JS_FALSE; /* Initialize the rest of the standard objects and functions. */ return js_InitArrayClass(cx, obj) && - js_InitBlockClass(cx, obj) && js_InitBooleanClass(cx, obj) && - js_InitCallClass(cx, obj) && - js_InitExceptionClasses(cx, obj) && js_InitMathClass(cx, obj) && js_InitNumberClass(cx, obj) && - js_InitRegExpClass(cx, obj) && js_InitStringClass(cx, obj) && +#if JS_HAS_CALL_OBJECT + js_InitCallClass(cx, obj) && +#endif +#if JS_HAS_REGEXPS + js_InitRegExpClass(cx, obj) && +#endif #if JS_HAS_SCRIPT_OBJECT js_InitScriptClass(cx, obj) && #endif +#if JS_HAS_ERROR_EXCEPTIONS + js_InitExceptionClasses(cx, obj) && +#endif #if JS_HAS_XML_SUPPORT js_InitXMLClasses(cx, obj) && #endif #if JS_HAS_FILE_OBJECT js_InitFileClass(cx, obj) && -#endif -#if JS_HAS_GENERATORS - js_InitIteratorClasses(cx, obj) && #endif js_InitDateClass(cx, obj); } -#define ATOM_OFFSET(name) offsetof(JSAtomState,name##Atom) -#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) +#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) #define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) -#define CLASP(name) (JSClass *)&js_##name##Class -#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL -#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL -#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) -#define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str +/* + * Table of class initializers and their atom offsets in rt->atomState. + * If you add a "standard" class, remember to update this table. + */ +static struct { + JSObjectOp init; + size_t atomOffset; +} standard_class_atoms[] = { + {InitFunctionAndObjectClasses, ATOM_OFFSET(Function)}, + {InitFunctionAndObjectClasses, ATOM_OFFSET(Object)}, + {js_InitArrayClass, ATOM_OFFSET(Array)}, + {js_InitBooleanClass, ATOM_OFFSET(Boolean)}, + {js_InitDateClass, ATOM_OFFSET(Date)}, + {js_InitMathClass, ATOM_OFFSET(Math)}, + {js_InitNumberClass, ATOM_OFFSET(Number)}, + {js_InitStringClass, ATOM_OFFSET(String)}, +#if JS_HAS_CALL_OBJECT + {js_InitCallClass, ATOM_OFFSET(Call)}, +#endif +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, ATOM_OFFSET(Error)}, +#endif +#if JS_HAS_REGEXPS + {js_InitRegExpClass, ATOM_OFFSET(RegExp)}, +#endif +#if JS_HAS_SCRIPT_OBJECT + {js_InitScriptClass, ATOM_OFFSET(Script)}, +#endif +#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} +}; +/* + * Table of top-level function and constant names and their init functions. + * If you add a "standard" global function or property, remember to update + * this table. + */ typedef struct JSStdName { JSObjectOp init; size_t atomOffset; /* offset of atom pointer in JSAtomState */ const char *name; /* null if atom is pre-pinned, else name */ - JSClass *clasp; } JSStdName; static JSAtom * @@ -1287,121 +1265,87 @@ StdNameToAtom(JSContext *cx, JSStdName *stdn) return atom; } -/* - * Table of class initializers and their atom offsets in rt->atomState. - * If you add a "standard" class, remember to update this table. - */ -static JSStdName standard_class_atoms[] = { - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, - {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, - {js_InitBlockClass, EAGER_ATOM_AND_CLASP(Block)}, - {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, - {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, - {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, - {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, - {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, - {js_InitCallClass, EAGER_ATOM_AND_CLASP(Call)}, - {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, - {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, -#if JS_HAS_SCRIPT_OBJECT - {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, -#endif -#if JS_HAS_XML_SUPPORT - {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, - {js_InitNamespaceClass, EAGER_ATOM_AND_CLASP(Namespace)}, - {js_InitQNameClass, EAGER_ATOM_AND_CLASP(QName)}, -#endif -#if JS_HAS_FILE_OBJECT - {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, -#endif -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, -#endif - {NULL, 0, NULL, NULL} -}; +#define EAGERLY_PINNED_ATOM(name) ATOM_OFFSET(name), NULL +#define LAZILY_PINNED_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str -/* - * Table of top-level function and constant names and their init functions. - * If you add a "standard" global function or property, remember to update - * this table. - */ static JSStdName standard_class_names[] = { /* ECMA requires that eval be a direct property of the global object. */ - {js_InitObjectClass, EAGER_ATOM(eval), NULL}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(eval)}, /* Global properties and functions defined by the Number class. */ - {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, - {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(NaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(Infinity)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isNaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isFinite)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseFloat)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseInt)}, /* String global functions. */ - {js_InitStringClass, LAZY_ATOM(escape), NULL}, - {js_InitStringClass, LAZY_ATOM(unescape), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, + {js_InitStringClass, LAZILY_PINNED_ATOM(escape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(unescape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURIComponent)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURIComponent)}, #if JS_HAS_UNEVAL - {js_InitStringClass, LAZY_ATOM(uneval), NULL}, + {js_InitStringClass, LAZILY_PINNED_ATOM(uneval)}, #endif /* Exception constructors. */ - {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, EAGERLY_PINNED_ATOM(Error)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(InternalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(EvalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(RangeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(ReferenceError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(SyntaxError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(TypeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(URIError)}, +#endif #if JS_HAS_XML_SUPPORT - {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, - {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, - {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, - {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, + {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 -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, -#endif - - {NULL, 0, NULL, NULL} + {NULL, 0, NULL} }; static JSStdName object_prototype_names[] = { /* Object.prototype properties (global delegates to Object.prototype). */ - {js_InitObjectClass, EAGER_ATOM(proto), NULL}, - {js_InitObjectClass, EAGER_ATOM(parent), NULL}, - {js_InitObjectClass, EAGER_ATOM(count), NULL}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(proto)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(parent)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(count)}, #if JS_HAS_TOSOURCE - {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toSource)}, #endif - {js_InitObjectClass, EAGER_ATOM(toString), NULL}, - {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, - {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toLocaleString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(valueOf)}, #if JS_HAS_OBJ_WATCHPOINT - {js_InitObjectClass, LAZY_ATOM(watch), NULL}, - {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(watch)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(unwatch)}, +#endif +#if JS_HAS_NEW_OBJ_METHODS + {js_InitObjectClass, LAZILY_PINNED_ATOM(hasOwnProperty)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(isPrototypeOf)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(propertyIsEnumerable)}, #endif - {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, - {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, - {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, #if JS_HAS_GETTER_SETTER - {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineSetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupSetter)}, #endif - {NULL, 0, NULL, NULL} + {NULL, 0, NULL} }; +#undef EAGERLY_PINNED_ATOM +#undef LAZILY_PINNED_ATOM + JS_PUBLIC_API(JSBool) JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, JSBool *resolved) @@ -1409,7 +1353,7 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, JSString *idstr; JSRuntime *rt; JSAtom *atom; - JSStdName *stdnm; + JSObjectOp init; uintN i; CHECK_REQUEST(cx); @@ -1420,6 +1364,7 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, idstr = JSVAL_TO_STRING(id); rt = cx->runtime; +#if JS_HAS_UNDEFINED /* Check whether we're resolving 'undefined', and define it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (idstr == ATOM_TO_STRING(atom)) { @@ -1427,30 +1372,31 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL); } +#endif /* Try for class constructors/prototypes named by well-known atoms. */ - stdnm = NULL; + init = NULL; for (i = 0; standard_class_atoms[i].init; i++) { atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_atoms[i]; + init = standard_class_atoms[i].init; break; } } - if (!stdnm) { + if (!init) { /* Try less frequently used top-level functions and constants. */ for (i = 0; standard_class_names[i].init; i++) { atom = StdNameToAtom(cx, &standard_class_names[i]); if (!atom) return JS_FALSE; if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; + init = standard_class_names[i].init; break; } } - if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { + if (!init && !OBJ_GET_PROTO(cx, obj)) { /* * Try even less frequently used names delegated from the global * object to Object.prototype, but only if the Object class hasn't @@ -1461,30 +1407,15 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, if (!atom) return JS_FALSE; if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; + init = standard_class_names[i].init; break; } } } } - if (stdnm) { - /* - * If this standard class is anonymous and obj advertises itself as a - * global object (in order to reserve slots for standard class object - * pointers), then we don't want to resolve by name. - * - * If inversely, either id does not name a class, or id does not name - * an anonymous class, or the global does not reserve slots for class - * objects, then we must call the init hook here. - */ - if (stdnm->clasp && - (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - return JS_TRUE; - } - - if (!stdnm->init(cx, obj)) + if (init) { + if (!init(cx, obj)) return JS_FALSE; *resolved = JS_TRUE; } @@ -1492,17 +1423,10 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, } static JSBool -AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) +AlreadyHasOwnProperty(JSObject *obj, JSAtom *atom) { - JSScopeProperty *sprop; - JSScope *scope; - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - JS_UNLOCK_SCOPE(cx, scope); - return sprop != NULL; + return SCOPE_GET_PROPERTY(OBJ_SCOPE(obj), ATOM_TO_JSID(atom)) != NULL; } JS_PUBLIC_API(JSBool) @@ -1515,18 +1439,20 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) CHECK_REQUEST(cx); rt = cx->runtime; +#if JS_HAS_UNDEFINED /* Check whether we need to bind 'undefined' and define it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (!AlreadyHasOwnProperty(cx, obj, atom) && + if (!AlreadyHasOwnProperty(obj, atom) && !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } +#endif /* Initialize any classes that have not been resolved yet. */ for (i = 0; standard_class_atoms[i].init; i++) { atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); - if (!AlreadyHasOwnProperty(cx, obj, atom) && + if (!AlreadyHasOwnProperty(obj, atom) && !standard_class_atoms[i].init(cx, obj)) { return JS_FALSE; } @@ -1539,7 +1465,7 @@ static JSIdArray * AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) { jsint i, length; - + i = *ip; length = ida->length; if (i >= length) { @@ -1557,7 +1483,7 @@ static JSIdArray * EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, jsint *ip, JSBool *foundp) { - *foundp = AlreadyHasOwnProperty(cx, obj, atom); + *foundp = AlreadyHasOwnProperty(obj, atom); if (*foundp) ida = AddAtomToArray(cx, atom, ida, ip); return ida; @@ -1584,11 +1510,13 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, 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++) { @@ -1625,34 +1553,12 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, } #undef ATOM_OFFSET -#undef CLASS_ATOM_OFFSET #undef OFFSET_TO_ATOM -#undef CLASP - -#undef EAGER_ATOM -#undef EAGER_CLASS_ATOM -#undef EAGER_ATOM_CLASP -#undef LAZY_ATOM - -JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) -{ - CHECK_REQUEST(cx); - return js_GetClassObject(cx, obj, key, objp); -} JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx) { - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - return js_GetScopeChain(cx, fp); + return cx->fp ? cx->fp->scopeChain : NULL; } JS_PUBLIC_API(void *) @@ -1663,14 +1569,10 @@ JS_malloc(JSContext *cx, size_t nbytes) JS_ASSERT(nbytes != 0); if (nbytes == 0) nbytes = 1; - + cx->runtime->gcMallocBytes += nbytes; p = malloc(nbytes); - if (!p) { + if (!p) JS_ReportOutOfMemory(cx); - return NULL; - } - js_UpdateMallocCounter(cx, nbytes); - return p; } @@ -1761,7 +1663,12 @@ JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) JS_PUBLIC_API(void) JS_ClearNewbornRoots(JSContext *cx) { - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); + uintN i; + + for (i = 0; i < GCX_NTYPES; i++) + cx->newborn[i] = NULL; + cx->lastAtom = NULL; + cx->lastInternalResult = JSVAL_NULL; } JS_PUBLIC_API(JSBool) @@ -1778,13 +1685,6 @@ JS_LeaveLocalRootScope(JSContext *cx) js_LeaveLocalRootScope(cx); } -JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScopeWithResult(cx, rval); -} - JS_PUBLIC_API(void) JS_ForgetLocalRoot(JSContext *cx, void *thing) { @@ -1792,22 +1692,84 @@ JS_ForgetLocalRoot(JSContext *cx, void *thing) js_ForgetLocalRoot(cx, (jsval) thing); } +#include "jshash.h" /* Added by JSIFY */ + #ifdef DEBUG +typedef struct NamedRootDumpArgs { + void (*dump)(const char *name, void *rp, void *data); + void *data; +} NamedRootDumpArgs; + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + + if (rhe->name) + args->dump(rhe->name, rhe->root, args->data); + return JS_DHASH_NEXT; +} + JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data) { - js_DumpNamedRoots(rt, dump, data); + NamedRootDumpArgs args; + + args.dump = dump; + args.data = data; + JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); } #endif /* DEBUG */ +typedef struct GCRootMapArgs { + JSGCRootMapFun map; + void *data; +} GCRootMapArgs; + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + GCRootMapArgs *args = (GCRootMapArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + intN mapflags; + JSDHashOperator op; + + mapflags = args->map(rhe->root, rhe->name, args->data); + +#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ + JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ + JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE + op = (JSDHashOperator)mapflags; +#else + op = JS_DHASH_NEXT; + if (mapflags & JS_MAP_GCROOT_STOP) + op |= JS_DHASH_STOP; + if (mapflags & JS_MAP_GCROOT_REMOVE) + op |= JS_DHASH_REMOVE; +#endif + + return op; +} + JS_PUBLIC_API(uint32) JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { - return js_MapGCRoots(rt, map, data); + GCRootMapArgs args; + uint32 rv; + + args.map = map; + args.data = data; + JS_LOCK_GC(rt); + rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); + JS_UNLOCK_GC(rt); + return rv; } JS_PUBLIC_API(JSBool) @@ -1851,33 +1813,21 @@ JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) { JS_ASSERT(cx->runtime->gcLevel > 0); #ifdef JS_THREADSAFE - JS_ASSERT(cx->runtime->gcThread->id == js_CurrentThreadId()); + JS_ASSERT(cx->runtime->gcThread == js_CurrentThreadId()); #endif - GC_MARK(cx, thing, name); + GC_MARK(cx, thing, name, arg); } JS_PUBLIC_API(void) JS_GC(JSContext *cx) { -#if JS_HAS_GENERATORS - /* Run previously scheduled but delayed close hooks. */ - js_RunCloseHooks(cx); -#endif - /* Don't nuke active arenas if executing or compiling. */ if (cx->stackPool.current == &cx->stackPool.first) JS_FinishArenaPool(&cx->stackPool); if (cx->tempPool.current == &cx->tempPool.first) JS_FinishArenaPool(&cx->tempPool); - js_GC(cx, GC_NORMAL); - -#if JS_HAS_GENERATORS - /* - * Run close hooks for objects that became unreachable after the last GC. - */ - js_RunCloseHooks(cx); -#endif + js_ForceGC(cx, 0); } JS_PUBLIC_API(void) @@ -1892,64 +1842,15 @@ JS_MaybeGC(JSContext *cx) rt = cx->runtime; bytes = rt->gcBytes; lastBytes = rt->gcLastBytes; - - /* - * We run the GC if we used all available free GC cells and had to - * allocate extra 1/5 of GC arenas since the last run of GC, or if - * we have malloc'd more bytes through JS_malloc than we were told - * to allocate by JS_NewRuntime. - * - * The reason for - * bytes > 6/5 lastBytes - * condition is the following. Bug 312238 changed bytes and lastBytes - * to mean the total amount of memory that the GC uses now and right - * after the last GC. - * - * Before the bug the variables meant the size of allocated GC things - * now and right after the last GC. That size did not include the - * memory taken by free GC cells and the condition was - * bytes > 3/2 lastBytes. - * That is, we run the GC if we have half again as many bytes of - * GC-things as the last time we GC'd. To be compatible we need to - * express that condition through the new meaning of bytes and - * lastBytes. - * - * We write the original condition as - * B*(1-F) > 3/2 Bl*(1-Fl) - * where B is the total memory size allocated by GC and F is the free - * cell density currently and Sl and Fl are the size and the density - * right after GC. The density by definition is memory taken by free - * cells divided by total amount of memory. In other words, B and Bl - * are bytes and lastBytes with the new meaning and B*(1-F) and - * Bl*(1-Fl) are bytes and lastBytes with the original meaning. - * - * Our task is to exclude F and Fl from the last statement. According - * the stats from bug 331770 Fl is about 20-30% for GC allocations - * that contribute to S and Sl for a typical run of the browser. It - * means that the original condition implied that we did not run GC - * unless we exhausted the pool of free cells. Indeed if we still - * have free cells, then B == Bl since we did not yet allocated any - * new arenas and the condition means - * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F - * That implies 3/2 Fl > 1/2 or Fl > 1/3. That can not be fulfilled - * for the state described by the stats. So we can write the original - * condition as: - * F == 0 && B > 3/2 Bl(1-Fl) - * Again using the stats we see that Fl is about 20% when the browser - * starts up and when we are far from hitting rt->gcMaxBytes. With - * this F we have - * F == 0 && B > 3/2 Bl(1-0.8) or just B > 6/5 Bl. - */ - if ((bytes > 8192 && bytes > lastBytes + lastBytes / 5) || - rt->gcMallocBytes >= rt->gcMaxMallocBytes) { + if ((bytes > 8192 && bytes > lastBytes + lastBytes / 2) || + 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 + * JS_malloc than we were told to allocate by JS_NewRuntime. + */ JS_GC(cx); } -#if JS_HAS_GENERATORS - else { - /* Run scheduled but not yet executed close hooks. */ - js_RunCloseHooks(cx); - } -#endif #endif } @@ -2053,14 +1954,8 @@ JS_ValueToId(JSContext *cx, jsval v, jsid *idp) CHECK_REQUEST(cx); if (JSVAL_IS_INT(v)) { - *idp = INT_JSVAL_TO_JSID(v); + *idp = v; } else { -#if JS_HAS_XML_SUPPORT - if (JSVAL_IS_OBJECT(v)) { - *idp = OBJECT_JSVAL_TO_JSID(v); - return JS_TRUE; - } -#endif atom = js_ValueToStringAtom(cx, v); if (!atom) return JS_FALSE; @@ -2098,7 +1993,12 @@ JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { - return js_TryValueOf(cx, obj, type, vp); +#if JS_BUG_EAGER_TOSTRING + if (type == JSTYPE_STRING) + return JS_TRUE; +#endif + js_TryValueOf(cx, obj, type, vp); + return JS_TRUE; } JS_PUBLIC_API(void) @@ -2113,7 +2013,6 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { JSAtom *atom; - JSProtoKey key; JSObject *proto, *ctor; JSTempValueRooter tvr; jsval cval, rval; @@ -2125,59 +2024,21 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, if (!atom) return NULL; - /* - * When initializing a standard class, if no parent_proto (grand-proto of - * instances of the class, parent-proto of the class's prototype object) - * is given, we must use Object.prototype if it is available. Otherwise, - * we could look up the wrong binding for a class name in obj. Example: - * - * String = Array; - * print("hi there".join); - * - * should print undefined, not Array.prototype.join. This is required by - * ECMA-262, alas. It might have been better to make String readonly and - * permanent in the global object, instead -- but that's too big a change - * to swallow at this point. - */ - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null && - !parent_proto && - !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &parent_proto)) { - return NULL; - } - /* Create a prototype object for this class. */ proto = js_NewObject(cx, clasp, parent_proto, obj); if (!proto) return NULL; /* After this point, control must exit via label bad or out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(proto), &tvr); if (!constructor) { - /* - * Lacking a constructor, name the prototype (e.g., Math) unless this - * class (a) is anonymous, i.e. for internal use only; (b) the class - * of obj (the global object) is has a reserved slot indexed by key; - * and (c) key is not the null key. - */ - if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && - key != JSProto_Null) { - named = JS_FALSE; - } else { - named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(proto), - NULL, NULL, - (clasp->flags & JSCLASS_IS_ANONYMOUS) - ? JSPROP_READONLY | JSPROP_PERMANENT - : 0, - NULL); - if (!named) - goto bad; - } - + /* Lacking a constructor, name the prototype (e.g., Math). */ + named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(proto), + NULL, NULL, 0, NULL); + if (!named) + goto bad; ctor = proto; } else { /* Define the constructor function in obj's scope. */ @@ -2216,7 +2077,10 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ if (OBJ_GET_CLASS(cx, ctor) == clasp) { - JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); + /* XXXMLM - this fails in framesets that are writing over + * themselves! + * JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); + */ OBJ_SET_PROTO(cx, ctor, proto); } } @@ -2229,10 +2093,6 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, goto bad; } - /* If this is a standard class, cache its prototype. */ - if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) - goto bad; - out: JS_POP_TEMP_ROOT(cx, &tvr); return proto; @@ -2368,7 +2228,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) &cval)) { return NULL; } - if (!VALUE_IS_FUNCTION(cx, cval)) { + if (!JSVAL_IS_FUNCTION(cx, cval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, OBJ_GET_CLASS(cx, proto)->name); return NULL; @@ -2435,7 +2295,7 @@ JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) scope = js_GetMutableScope(cx, obj); if (scope) SCOPE_SET_SEALED(scope); - JS_UNLOCK_OBJ(cx, obj); + JS_UNLOCK_SCOPE(cx, scope); if (!scope) return JS_FALSE; @@ -2537,7 +2397,7 @@ JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, return NULL; if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, 0, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } return nobj; @@ -2851,10 +2711,17 @@ JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) } JS_PUBLIC_API(JSBool) -JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp) +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)) { @@ -2875,18 +2742,6 @@ JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, return JS_TRUE; } -JS_PUBLIC_API(JSBool) -JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - jsval *vp) -{ - JSAtom *atom; - - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); -} - JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { @@ -3201,14 +3056,6 @@ JS_ClearScope(JSContext *cx, JSObject *obj) if (obj->map->ops->clear) obj->map->ops->clear(cx, obj); - - /* Clear cached class objects on the global object. */ - if (JS_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) { - JSProtoKey key; - - for (key = JSProto_Null; key < JSProto_LIMIT; key++) - JS_SetReservedSlot(cx, obj, key, JSVAL_VOID); - } } JS_PUBLIC_API(JSIdArray *) @@ -3321,13 +3168,16 @@ prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) /* Native case: just mark the next property to visit. */ sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); if (sprop) - MARK_SCOPE_PROPERTY(cx, 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]; - MARK_ID(cx, id); + 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; @@ -3350,7 +3200,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) void *pdata; jsint index; JSIdArray *ida; - + CHECK_REQUEST(cx); iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); if (!iterobj) @@ -3384,8 +3234,8 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) iterobj->slots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); return iterobj; - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; +bad: + cx->newborn[GCX_OBJECT] = NULL; return NULL; } @@ -3435,7 +3285,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); JS_ASSERT(i <= ida->length); if (i == 0) { - *idp = JSVAL_VOID; + *idp = JSVAL_VOID; } else { *idp = ida->vector[--i]; OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); @@ -3597,17 +3447,7 @@ JS_GetFunctionId(JSFunction *fun) JS_PUBLIC_API(uintN) JS_GetFunctionFlags(JSFunction *fun) { -#ifdef MOZILLA_1_8_BRANCH - uintN flags = fun->flags; - - return JSFUN_DISJOINT_FLAGS(flags) | - (JSFUN_GETTER_TEST(flags) ? JSFUN_GETTER : 0) | - (JSFUN_SETTER_TEST(flags) ? JSFUN_SETTER : 0) | - (JSFUN_BOUND_METHOD_TEST(flags) ? JSFUN_BOUND_METHOD : 0) | - (JSFUN_HEAVYWEIGHT_TEST(flags) ? JSFUN_HEAVYWEIGHT : 0); -#else return fun->flags; -#endif } JS_PUBLIC_API(uint16) @@ -3663,10 +3503,8 @@ js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, * the 'this' param if no args. */ JS_ASSERT(cx->fp->argv == argv); - tmp = js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), argv); - if (!tmp) + if (!js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), cx->fp)) return JS_FALSE; - cx->fp->thisp = tmp; /* * Protect against argc - 1 underflowing below. By calling js_ComputeThis, @@ -3688,9 +3526,6 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) CHECK_REQUEST(cx); ctor = NULL; for (; fs->name; fs++) { - - /* High bits of fs->extra are reserved. */ - JS_ASSERT((fs->extra & 0xFFFF0000) == 0); flags = fs->flags; /* @@ -3710,7 +3545,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) fs->nargs + 1, flags); if (!fun) return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; + fun->extra = fs->extra; /* * As jsapi.h notes, fs must point to storage that lives as long @@ -3723,7 +3558,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); if (!fun) return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; + fun->extra = fs->extra; } return JS_TRUE; } @@ -3837,16 +3672,20 @@ JS_CompileUCScript(JSContext *cx, JSObject *obj, filename, lineno); } -#define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ +#if JS_HAS_EXCEPTIONS +# define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ JS_BEGIN_MACRO \ - if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT)) \ + 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)->weakRoots.lastInternalResult = JSVAL_NULL; \ + (cx)->lastInternalResult = JSVAL_NULL; \ LAST_FRAME_EXCEPTION_CHECK(cx, result); \ } \ JS_END_MACRO @@ -4221,7 +4060,6 @@ JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, return ok; } -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN nbytes, @@ -4241,7 +4079,6 @@ JS_EvaluateScript(JSContext *cx, JSObject *obj, return ok; } -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, @@ -4397,60 +4234,30 @@ JS_SetCallReturnValue2(JSContext *cx, jsval v) #endif } -JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx) -{ - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) - return fp; - - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - cx->fp = NULL; - return fp; -} - -JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) -{ - JS_ASSERT(!cx->fp); - if (!fp) - return; - - JS_ASSERT(cx->dormantFrameChain == fp); - cx->fp = fp; - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; -} - /************************************************************************/ JS_PUBLIC_API(JSString *) -JS_NewString(JSContext *cx, char *bytes, size_t nbytes) +JS_NewString(JSContext *cx, char *bytes, size_t length) { - size_t length = nbytes; jschar *chars; JSString *str; + size_t charsLength = length; CHECK_REQUEST(cx); - - /* Make a UTF-16 vector from the 8-bit char codes in bytes. */ - chars = js_InflateString(cx, bytes, &length); + /* Make a Unicode vector from the 8-bit char codes in bytes. */ + 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; } /* Hand off bytes to the deflated string cache, if possible. */ - if (!js_SetStringBytes(cx->runtime, str, bytes, nbytes)) + if (!js_SetStringBytes(str, bytes, length)) JS_free(cx, bytes); return str; } @@ -4547,11 +4354,9 @@ JS_InternUCString(JSContext *cx, const jschar *s) JS_PUBLIC_API(char *) JS_GetStringBytes(JSString *str) { - JSRuntime *rt; char *bytes; - rt = js_GetGCStringRuntime(str); - bytes = js_GetStringBytes(rt, str); + bytes = js_GetStringBytes(str); return bytes ? bytes : ""; } @@ -4643,7 +4448,7 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, } JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8() +JS_StringsAreUTF8 () { #ifdef JS_C_STRINGS_ARE_UTF8 return JS_TRUE; @@ -4733,7 +4538,7 @@ JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext *cx) { - js_ReportOutOfMemory(cx); + js_ReportOutOfMemory(cx, js_GetErrorMessage); } JS_PUBLIC_API(JSErrorReporter) @@ -4754,6 +4559,7 @@ JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) { +#if JS_HAS_REGEXPS jschar *chars; JSObject *obj; @@ -4764,13 +4570,22 @@ JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) obj = js_NewRegExpObject(cx, NULL, chars, length, flags); JS_free(cx, chars); return obj; +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif } JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) { CHECK_REQUEST(cx); +#if JS_HAS_REGEXPS return js_NewRegExpObject(cx, NULL, chars, length, flags); +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif } JS_PUBLIC_API(void) @@ -4833,37 +4648,50 @@ JS_GetLocaleCallbacks(JSContext *cx) JS_PUBLIC_API(JSBool) JS_IsExceptionPending(JSContext *cx) { +#if JS_HAS_EXCEPTIONS return (JSBool) cx->throwing; +#else + return JS_FALSE; +#endif } JS_PUBLIC_API(JSBool) JS_GetPendingException(JSContext *cx, jsval *vp) { +#if JS_HAS_EXCEPTIONS CHECK_REQUEST(cx); if (!cx->throwing) return JS_FALSE; *vp = cx->exception; return JS_TRUE; +#else + return JS_FALSE; +#endif } JS_PUBLIC_API(void) JS_SetPendingException(JSContext *cx, jsval v) { CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS cx->throwing = JS_TRUE; cx->exception = v; +#endif } JS_PUBLIC_API(void) JS_ClearPendingException(JSContext *cx) { +#if JS_HAS_EXCEPTIONS cx->throwing = JS_FALSE; cx->exception = JSVAL_VOID; +#endif } JS_PUBLIC_API(JSBool) JS_ReportPendingException(JSContext *cx) { +#if JS_HAS_EXCEPTIONS JSBool save, ok; CHECK_REQUEST(cx); @@ -4879,16 +4707,22 @@ JS_ReportPendingException(JSContext *cx) ok = js_ReportUncaughtException(cx); cx->creatingException = save; return ok; +#else + return JS_TRUE; +#endif } +#if JS_HAS_EXCEPTIONS struct JSExceptionState { JSBool throwing; jsval exception; }; +#endif JS_PUBLIC_API(JSExceptionState *) JS_SaveExceptionState(JSContext *cx) { +#if JS_HAS_EXCEPTIONS JSExceptionState *state; CHECK_REQUEST(cx); @@ -4899,11 +4733,15 @@ JS_SaveExceptionState(JSContext *cx) js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); } return state; +#else + return NULL; +#endif } JS_PUBLIC_API(void) JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) { +#if JS_HAS_EXCEPTIONS CHECK_REQUEST(cx); if (state) { if (state->throwing) @@ -4912,24 +4750,31 @@ JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) JS_ClearPendingException(cx); JS_DropExceptionState(cx, state); } +#endif } JS_PUBLIC_API(void) JS_DropExceptionState(JSContext *cx, JSExceptionState *state) { +#if JS_HAS_EXCEPTIONS CHECK_REQUEST(cx); if (state) { if (state->throwing && JSVAL_IS_GCTHING(state->exception)) JS_RemoveRoot(cx, &state->exception); JS_free(cx, state); } +#endif } JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v) { +#if JS_HAS_ERROR_EXCEPTIONS CHECK_REQUEST(cx); return js_ErrorFromException(cx, v); +#else + return NULL; +#endif } JS_PUBLIC_API(JSBool) @@ -4940,34 +4785,25 @@ JS_ThrowReportedError(JSContext *cx, const char *message, } #ifdef JS_THREADSAFE -/* - * Get the owning thread id of a context. Returns 0 if the context is not - * owned by any thread. - */ JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx) { - return JS_THREAD_ID(cx); + return cx->thread; } -/* - * Set the current thread as the owning thread of a context. Returns the - * old owning thread id, or -1 if the operation failed. - */ JS_PUBLIC_API(jsword) JS_SetContextThread(JSContext *cx) { - jsword old = JS_THREAD_ID(cx); - if (!js_SetContextThread(cx)) - return -1; + jsword old = cx->thread; + cx->thread = js_CurrentThreadId(); return old; } JS_PUBLIC_API(jsword) JS_ClearContextThread(JSContext *cx) { - jsword old = JS_THREAD_ID(cx); - js_ClearContextThread(cx); + jsword old = cx->thread; + cx->thread = 0; return old; } #endif diff --git a/lib/javascript/jsapi.h b/lib/javascript/jsapi.h index 464f19ff..9fe07ac6 100644 --- a/lib/javascript/jsapi.h +++ b/lib/javascript/jsapi.h @@ -132,59 +132,8 @@ JS_BEGIN_EXTERN_C #define JSFUN_SETTER JSPROP_SETTER #define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ #define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ - -#define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) -#define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) - -#ifdef MOZILLA_1_8_BRANCH - -/* - * Squeeze three more bits into existing 8-bit flags by taking advantage of - * the invalid combination (JSFUN_GETTER | JSFUN_SETTER). - */ -#define JSFUN_GETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_SETTER) -#define JSFUN_FLAGS_TEST(f,t) (JSFUN_GSFLAGS(~(f)) ? (f) & (t) : 0) -#define JSFUN_BOUND_METHOD_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) (JSFUN_GETTER_TEST(f) ? JSPROP_GETTER : \ - JSFUN_SETTER_TEST(f) ? JSPROP_SETTER : 0) - -#define JSFUN_THISP_FLAGS(f) (JSFUN_GSFLAGS(~(f)) ? 0 : \ - (f) & JSFUN_THISP_PRIMITIVE) -#define JSFUN_THISP_TEST(f,t) ((f) == (t) || (f) == JSFUN_THISP_PRIMITIVE) - -#define JSFUN_THISP_STRING 0x30 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x70 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0xb0 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0xf0 /* |this| may be any primitive value */ - #define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ -#else - -#define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) -#define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) - -#define JSFUN_THISP_FLAGS(f) (f) -#define JSFUN_THISP_TEST(f,t) ((f) & t) - -#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ - -#define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes -- - note that bit #15 is used internally - to flag interpreted functions */ - -#endif - /* * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in * JSFunctionSpec arrays that specify generic native prototype methods, i.e., @@ -441,39 +390,6 @@ JS_SuspendRequest(JSContext *cx); extern JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoRequest { - public: - JSAutoRequest(JSContext *cx) : mContext(cx), mSaveDepth(0) { - JS_BeginRequest(mContext); - } - ~JSAutoRequest() { - JS_EndRequest(mContext); - } - - void suspend() { - mSaveDepth = JS_SuspendRequest(mContext); - } - void resume() { - JS_ResumeRequest(mContext, mSaveDepth); - } - - protected: - JSContext *mContext; - jsrefcount mSaveDepth; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - #endif /* JS_THREADSAFE */ extern JS_PUBLIC_API(void) @@ -482,9 +398,6 @@ JS_Lock(JSRuntime *rt); extern JS_PUBLIC_API(void) JS_Unlock(JSRuntime *rt); -extern JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); - extern JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stackChunkSize); @@ -557,12 +470,6 @@ JS_StringToVersion(const char *string); called with a null script parameter, by native code that loops intensively */ -#define JSOPTION_DONT_REPORT_UNCAUGHT \ - JS_BIT(8) /* When returning from the - outermost API call, prevent - uncaught exceptions from - being converted to error - reports */ extern JS_PUBLIC_API(uint32) JS_GetOptions(JSContext *cx); @@ -621,10 +528,6 @@ extern JS_PUBLIC_API(JSIdArray *) JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida); -extern JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - extern JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx); @@ -735,10 +638,6 @@ JS_ClearNewbornRoots(JSContext *cx); * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must * not make the matching JS_LeaveLocalRootScope call. * - * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave - * a local root scope that protects a result or return value, by effectively - * pushing it in the caller's local root scope. - * * In case a native hook allocates many objects or other GC-things, but the * native protects some of those GC-things by storing them as property values * in an object that is itself protected, the hook can call JS_ForgetLocalRoot @@ -759,41 +658,9 @@ JS_EnterLocalRootScope(JSContext *cx); extern JS_PUBLIC_API(void) JS_LeaveLocalRootScope(JSContext *cx); -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - extern JS_PUBLIC_API(void) JS_ForgetLocalRoot(JSContext *cx, void *thing); -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoLocalRootScope { - public: - JSAutoLocalRootScope(JSContext *cx) : mContext(cx) { - JS_EnterLocalRootScope(mContext); - } - ~JSAutoLocalRootScope() { - JS_LeaveLocalRootScope(mContext); - } - - void forget(void *thing) { - JS_ForgetLocalRoot(mContext, thing); - } - - protected: - JSContext *mContext; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - #ifdef DEBUG extern JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, @@ -971,11 +838,11 @@ struct JSExtendedClass { JSEqualityOp equality; JSObjectOp outerObject; JSObjectOp innerObject; - void (*reserved0)(); - void (*reserved1)(); - void (*reserved2)(); - void (*reserved3)(); - void (*reserved4)(); + jsword reserved0; + jsword reserved1; + jsword reserved2; + jsword reserved3; + jsword reserved4; }; #define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ @@ -1010,33 +877,6 @@ struct JSExtendedClass { /* True if JSClass is really a JSExtendedClass. */ #define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) -#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) -#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) - -/* - * ECMA-262 requires that most constructors used internally create objects - * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) - * member initial value. The "original ... value" verbiage is there because - * in ECMA-262, global properties naming class objects are read/write and - * deleteable, for the most part. - * - * Implementing this efficiently requires that global objects have classes - * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break - * anything except the ECMA-262 "original prototype value" behavior, which was - * broken for years in SpiderMonkey. In other words, without these flags you - * get backward compatibility. - */ -#define JSCLASS_GLOBAL_FLAGS \ - (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) - -/* Fast access to the original value of each standard class's prototype. */ -#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) -#define JSCLASS_CACHED_PROTO_WIDTH 8 -#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) -#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) -#define JSCLASS_CACHED_PROTO_KEY(clasp) (((clasp)->flags \ - >> JSCLASS_CACHED_PROTO_SHIFT) \ - & JSCLASS_CACHED_PROTO_MASK) /* Initializer for unused members of statically initialized JSClass structs. */ #define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 @@ -1164,18 +1004,9 @@ struct JSPropertySpec { struct JSFunctionSpec { const char *name; JSNative call; -#ifdef MOZILLA_1_8_BRANCH uint8 nargs; uint8 flags; - uint16 extra; -#else - uint16 nargs; - uint16 flags; - uint32 extra; /* extra & 0xFFFF: - number of arg slots for local GC roots - extra >> 16: - reserved, must be zero */ -#endif + uint16 extra; /* number of arg slots for local GC roots */ }; extern JS_PUBLIC_API(JSObject *) @@ -1318,10 +1149,6 @@ 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_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp); - extern JS_PUBLIC_API(JSBool) JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp); @@ -1832,25 +1659,6 @@ JS_IsAssigning(JSContext *cx); extern JS_PUBLIC_API(void) JS_SetCallReturnValue2(JSContext *cx, jsval v); -/* - * Saving and restoring frame chains. - * - * These two functions are used to set aside cx->fp while that frame is - * inactive. After a call to JS_SaveFrameChain, it looks as if there is no - * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack - * must be balanced and all nested calls to JS_SaveFrameChain must have had - * matching JS_RestoreFrameChain calls. - * - * JS_SaveFrameChain deals with cx not having any code running on it. A null - * return does not signify an error and JS_RestoreFrameChain handles null - * frames. - */ -extern JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); - /************************************************************************/ /* @@ -1962,7 +1770,7 @@ JS_MakeStringImmutable(JSContext *cx, JSString *str); * to get UTF-8 support. */ JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8(); +JS_StringsAreUTF8(); /* * Character encoding support. @@ -1997,14 +1805,13 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, /************************************************************************/ /* - * Locale specific string conversion and error message callbacks. + * Locale specific string conversion callback. */ struct JSLocaleCallbacks { JSLocaleToUpperCase localeToUpperCase; JSLocaleToLowerCase localeToLowerCase; JSLocaleCompare localeCompare; JSLocaleToUnicode localeToUnicode; - JSErrorCallback localeGetErrorMessage; }; /* diff --git a/lib/javascript/jsarena.c b/lib/javascript/jsarena.c index ef6ccd19..8b2c8a54 100644 --- a/lib/javascript/jsarena.c +++ b/lib/javascript/jsarena.c @@ -49,6 +49,13 @@ #include "jsbit.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ +#include "jslock.h" + +static JSArena *arena_freelist; + +#ifdef JS_THREADSAFE +static JSLock *arena_freelist_lock; +#endif #ifdef JS_ARENAMETER static JSArenaStats *arena_stats_list; @@ -63,6 +70,13 @@ static JSArenaStats *arena_stats_list; JS_PUBLIC_API(void) JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) { +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!arena_freelist_lock) { + arena_freelist_lock = JS_NEW_LOCK(); + JS_ASSERT(arena_freelist_lock); + } +#endif if (align == 0) align = JS_ARENA_DEFAULT_ALIGN; pool->mask = JS_BITMASK(JS_CeilingLog2(align)); @@ -92,15 +106,27 @@ JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) * that (jsuword)(a + 1) is on a pointer boundary. * * By how much must we pad? Let M be the alignment modulus for pool and P - * the modulus for a pointer. Given M >= P, the base of an oversized arena - * that satisfies M is well-aligned for P. + * the modulus for a pointer. Given M >= P, the greatest distance between a + * pointer aligned on an M boundary and one aligned on a P boundary is M-P. + * If M and P are powers of two, then M-P = (pool->mask - POINTER_MASK). * - * On the other hand, if M < P, we must include enough space in the header - * size to align the back-pointer on a P boundary so that it can be found by - * subtracting P from a->base. This means a->base must be on a P boundary, - * even though subsequent allocations from a may be aligned on a lesser (M) - * boundary. Given powers of two M and P as above, the extra space needed - * when M < P is P-M or POINTER_MASK - pool->mask. + * How much extra padding might spill over unused into the remainder of the + * allocation, in the worst case (where M > P)? + * + * If we add M-P to the nominal back-pointer address and then round down to + * align on a P boundary, we will use at most M-P bytes of padding, and at + * least P (M > P => M >= 2P; M == 2P gives the least padding, P). So if we + * use P bytes of padding, then we will overallocate a by P+M-1 bytes, as we + * also add M-1 to the estimated size in case malloc returns an odd pointer. + * a->limit must include this overestimation to satisfy a->avail in [a->base, + * a->limit]. + * + * Similarly, if pool->mask is less than POINTER_MASK, we must include enough + * space in the header size to align the back-pointer on a P boundary so that + * it can be found by subtracting P from a->base. This means a->base must be + * on a P boundary, even though subsequent allocations from a may be aligned + * on a lesser (M) boundary. Given powers of two M and P as above, the extra + * space needed when P > M is P-M or POINTER_MASK - pool->mask. * * The size of a header including padding is given by the HEADER_SIZE macro, * below, for any pool (for any value of M). @@ -117,7 +143,7 @@ JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) #define HEADER_SIZE(pool) (sizeof(JSArena **) \ + (((pool)->mask < POINTER_MASK) \ ? POINTER_MASK - (pool)->mask \ - : 0)) + : (pool)->mask - POINTER_MASK)) #define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) #define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ & HEADER_BASE_MASK(pool)) \ @@ -129,8 +155,8 @@ JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) JS_PUBLIC_API(void *) JS_ArenaAllocate(JSArenaPool *pool, size_t nb) { - JSArena **ap, *a, *b; - jsuword extra, hdrsz, gross; + JSArena **ap, **bp, *a, *b; + jsuword extra, hdrsz, gross, sz; void *p; /* @@ -149,12 +175,33 @@ JS_ArenaAllocate(JSArenaPool *pool, size_t nb) pool->current = a) { ap = &a->next; if (!*ap) { - /* Not enough space in pool, so we must malloc. */ + /* 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 to avoid leaving alloc'able + * space after an oversized allocation as it grows. + */ + sz = JS_UPTRDIFF(b->limit, b); + if (sz == gross) { + *bp = b->next; + JS_RELEASE_LOCK(arena_freelist_lock); + b->next = NULL; + COUNT(pool, nreclaims); + goto claim; + } + bp = &b->next; + } + + /* Nothing big enough on the freelist, so we must malloc. */ + JS_RELEASE_LOCK(arena_freelist_lock); b = (JSArena *) malloc(gross); if (!b) return NULL; @@ -163,6 +210,7 @@ JS_ArenaAllocate(JSArenaPool *pool, size_t nb) JS_COUNT_ARENA(pool,++); COUNT(pool, nmallocs); + claim: /* If oversized, store ap in the header, just before a->base. */ *ap = a = b; JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); @@ -205,7 +253,7 @@ JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) JS_ASSERT(a->base == (jsuword)p); boff = JS_UPTRDIFF(a->base, a); - aoff = JS_ARENA_ALIGN(pool, size + incr); + aoff = size + incr; JS_ASSERT(aoff > pool->arenasize); extra = HEADER_SIZE(pool); /* oversized header holds ap */ hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ @@ -234,7 +282,7 @@ JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); a->limit = (jsuword)a + gross; - a->avail = a->base + aoff; + a->avail = JS_ARENA_ALIGN(pool, a->base + aoff); JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); /* Check whether realloc aligned differently, and copy if necessary. */ @@ -269,7 +317,7 @@ JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) * Reset pool->current to point to head in case it pointed at a tail arena. */ static void -FreeArenaList(JSArenaPool *pool, JSArena *head) +FreeArenaList(JSArenaPool *pool, JSArena *head, JSBool reallyFree) { JSArena **ap, *a; @@ -287,12 +335,24 @@ FreeArenaList(JSArenaPool *pool, JSArena *head) a = *ap; #endif - do { - *ap = a->next; - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - free(a); - } while ((a = *ap) != NULL); + if (reallyFree) { + do { + *ap = a->next; + JS_CLEAR_ARENA(a); + JS_COUNT_ARENA(pool,--); + free(a); + } while ((a = *ap) != NULL); + } else { + /* Insert the whole arena chain at the front of the freelist. */ + do { + ap = &(*ap)->next; + } while (*ap); + JS_ACQUIRE_LOCK(arena_freelist_lock); + *ap = arena_freelist; + arena_freelist = a; + JS_RELEASE_LOCK(arena_freelist_lock); + head->next = NULL; + } pool->current = head; } @@ -308,7 +368,7 @@ JS_ArenaRelease(JSArenaPool *pool, char *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); + FreeArenaList(pool, a, JS_TRUE); return; } } @@ -381,14 +441,14 @@ JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size) JS_PUBLIC_API(void) JS_FreeArenaPool(JSArenaPool *pool) { - FreeArenaList(pool, &pool->first); + FreeArenaList(pool, &pool->first, JS_FALSE); COUNT(pool, ndeallocs); } JS_PUBLIC_API(void) JS_FinishArenaPool(JSArenaPool *pool) { - FreeArenaList(pool, &pool->first); + FreeArenaList(pool, &pool->first, JS_TRUE); #ifdef JS_ARENAMETER { JSArenaStats *stats, **statsp; @@ -409,11 +469,28 @@ JS_FinishArenaPool(JSArenaPool *pool) JS_PUBLIC_API(void) JS_ArenaFinish() { + JSArena *a, *next; + + JS_ACQUIRE_LOCK(arena_freelist_lock); + a = arena_freelist; + arena_freelist = NULL; + JS_RELEASE_LOCK(arena_freelist_lock); + for (; a; a = next) { + next = a->next; + free(a); + } } JS_PUBLIC_API(void) JS_ArenaShutDown(void) { +#ifdef JS_THREADSAFE + /* Must come through here once in the process's last thread! */ + if (arena_freelist_lock) { + JS_DESTROY_LOCK(arena_freelist_lock); + arena_freelist_lock = NULL; + } +#endif } #ifdef JS_ARENAMETER diff --git a/lib/javascript/jsarena.h b/lib/javascript/jsarena.h index 8be15d08..5370f8f8 100644 --- a/lib/javascript/jsarena.h +++ b/lib/javascript/jsarena.h @@ -70,6 +70,7 @@ struct JSArenaStats { char *name; /* name for debugging */ uint32 narenas; /* number of arenas in pool */ uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ + uint32 nreclaims; /* number of reclaims from freeArenas */ uint32 nmallocs; /* number of malloc() calls */ uint32 ndeallocs; /* number of lifetime deallocations */ uint32 ngrows; /* number of JS_ARENA_GROW() calls */ @@ -233,13 +234,22 @@ extern JS_PUBLIC_API(void) JS_FinishArenaPool(JSArenaPool *pool); /* - * Deprecated do-nothing function. + * Finish using arenas, freeing all memory associated with them except for + * any locks needed for thread safety. */ extern JS_PUBLIC_API(void) JS_ArenaFinish(void); /* - * Deprecated do-nothing function. + * Free any locks or other memory needed for thread safety, just before + * shutting down. At that point, we must be called by a single thread. + * + * After shutting down, the next thread to call JS_InitArenaPool must not + * race with any other thread. Once a pool has been initialized, threads + * may safely call jsarena.c functions on thread-local pools. The upshot + * is that pools are per-thread, but the underlying global freelist is + * thread-safe, provided that both the first pool initialization and the + * shut-down call are single-threaded. */ extern JS_PUBLIC_API(void) JS_ArenaShutDown(void); diff --git a/lib/javascript/jsarray.c b/lib/javascript/jsarray.c index 532a1be3..ef94be4e 100644 --- a/lib/javascript/jsarray.c +++ b/lib/javascript/jsarray.c @@ -199,140 +199,59 @@ IndexToValue(JSContext *cx, jsuint index, jsval *vp) } static JSBool -BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, - jsid *idp) +IndexToId(JSContext *cx, jsuint index, jsid *idp) { - jschar buf[10], *start; - JSClass *clasp; + JSString *str; JSAtom *atom; - JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); - JS_ASSERT(index > JSVAL_INT_MAX); - - start = JS_ARRAY_END(buf); - do { - --start; - *start = (jschar)('0' + index % 10); - index /= 10; - } while (index != 0); - - /* - * Skip the atomization if the class is known to store atoms corresponding - * to big indexes together with elements. In such case we know that the - * array does not have an element at the given index if its atom does not - * exist. - */ - if (!createAtom && - ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_ArrayClass || - clasp == &js_ArgumentsClass || - clasp == &js_ObjectClass)) { - atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); - if (!atom) { - *idp = JSVAL_VOID; - return JS_TRUE; - } + if (index <= JSVAL_INT_MAX) { + *idp = INT_TO_JSID(index); } else { - atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); + str = js_NumberToString(cx, (jsdouble)index); + if (!str) + return JS_FALSE; + atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; - } + *idp = ATOM_TO_JSID(atom); - *idp = ATOM_TO_JSID(atom); + } return JS_TRUE; } -/* - * If the property at the given index exists, get its value into location - * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp - * to JSVAL_VOID. This function assumes that the location pointed by vp is - * properly rooted and can be used as GC-protected storage for temporaries. - */ static JSBool -GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole, - jsval *vp) +PropertyExists(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp) { - jsid id; JSObject *obj2; JSProperty *prop; - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - return JS_TRUE; - } - } - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; - if (!prop) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - } else { + + *foundp = prop != NULL; + if (*foundp) { OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) - return JS_FALSE; - *hole = JS_FALSE; } + return JS_TRUE; } -/* - * Set the value of the property at the given index to v assuming v is rooted. - */ -static JSBool -SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v) -{ - jsid id; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_TRUE, &id)) - return JS_FALSE; - JS_ASSERT(id != JSVAL_VOID); - } - return OBJ_SET_PROPERTY(cx, obj, id, &v); -} +#define JSID_HOLE JSVAL_NULL static JSBool -DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index) +IndexToExistingId(JSContext *cx, JSObject *obj, jsuint index, jsid *idp) { - jsid id; - jsval junk; + JSBool exists; - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) - return JS_TRUE; - } - return OBJ_DELETE_PROPERTY(cx, obj, id, &junk); + 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; } -/* - * When hole is true, delete the property at the given index. Otherwise set - * its value to v assuming v is rooted. - */ -static JSBool -SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index, - JSBool hole, jsval v) -{ - if (hole) { - JS_ASSERT(v == JSVAL_VOID); - return DeleteArrayElement(cx, obj, index); - } else { - return SetArrayElement(cx, obj, index, v); - } -} - - JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) { @@ -364,20 +283,6 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) return ok; } -JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, obj); - *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass); - if (!*answerp) { - *lengthp = 0; - return JS_TRUE; - } - return js_GetLengthProperty(cx, obj, lengthp); -} - /* * This get function is specific to Array.prototype.length and other array * instance length properties. It calls back through the class get function @@ -392,55 +297,21 @@ array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) static JSBool array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - jsuint newlen, oldlen, gap, index; + jsuint newlen, oldlen, slot; jsid id2; jsval junk; - JSObject *iter; - JSTempValueRooter tvr; - JSBool ok; if (!ValueIsLength(cx, *vp, &newlen)) return JS_FALSE; if (!js_GetLengthProperty(cx, obj, &oldlen)) return JS_FALSE; - if (oldlen > newlen) { - if (oldlen - newlen < (1 << 24)) { - do { - --oldlen; - if (!DeleteArrayElement(cx, obj, oldlen)) - return JS_FALSE; - } while (oldlen != newlen); - } else { - /* - * We are going to remove a lot of indexes in a presumably sparse - * array. So instead of looping through indexes between newlen and - * oldlen, we iterate through all properties and remove those that - * correspond to indexes from the [newlen, oldlen) range. - * See bug 322135. - */ - iter = JS_NewPropertyIterator(cx, obj); - if (!iter) - return JS_FALSE; - - /* Protect iter against GC in OBJ_DELETE_PROPERTY. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); - gap = oldlen - newlen; - for (;;) { - ok = JS_NextProperty(cx, iter, &id2); - if (!ok) - break; - if (id2 == JSVAL_VOID) - break; - if (js_IdIsIndex(id2, &index) && index - newlen < gap) { - ok = OBJ_DELETE_PROPERTY(cx, obj, id2, &junk); - if (!ok) - break; - } - } - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - } + slot = oldlen; + while (slot > newlen) { + --slot; + if (!IndexToId(cx, slot, &id2)) + return JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk)) + return JS_FALSE; } return IndexToValue(cx, newlen, vp); } @@ -464,36 +335,40 @@ array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) static JSBool array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { + jsuint length; + + if (JS_VERSION_IS_1_2(cx)) { + if (!js_GetLengthProperty(cx, obj, &length)) + return JS_FALSE; + switch (type) { + case JSTYPE_NUMBER: + return IndexToValue(cx, length, vp); + case JSTYPE_BOOLEAN: + *vp = BOOLEAN_TO_JSVAL(length > 0); + return JS_TRUE; + default: + return JS_TRUE; + } + } return js_TryValueOf(cx, obj, type, vp); } JSClass js_ArrayClass = { "Array", - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), + 0, array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; -enum ArrayToStringOp { - TO_STRING, - TO_LOCALE_STRING, - TO_SOURCE -}; - -/* - * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use - * or "," when sep is NULL. - * When op is TO_SOURCE sep must be NULL. - */ static JSBool -array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, - JSString *sep, jsval *rval) +array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, + jsval *rval, JSBool localeString) { - JSBool ok, hole; + JSBool ok; jsuint length, index; jschar *chars, *ochars; - size_t nchars, growth, seplen, tmplen, extratail; + size_t nchars, growth, seplen, tmplen; const jschar *sepstr; JSString *str; JSHashEntry *he; @@ -513,11 +388,7 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, he = js_EnterSharpObject(cx, obj, NULL, &chars); if (!he) return JS_FALSE; -#ifdef DEBUG - growth = (size_t) -1; -#endif - - if (op == TO_SOURCE) { + if (literalize) { if (IS_SHARP(he)) { #if JS_HAS_SHARP_VARS nchars = js_strlen(chars); @@ -531,11 +402,10 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, } /* - * Always allocate 2 extra chars for closing ']' and terminating 0 - * and then preallocate 1 + extratail to include starting '['. + * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the + * terminating 0. */ - extratail = 2; - growth = (1 + extratail) * sizeof(jschar); + growth = (1 + 3 + 1) * sizeof(jschar); if (!chars) { nchars = 0; chars = (jschar *) malloc(growth); @@ -544,29 +414,25 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, } else { MAKE_SHARP(he); nchars = js_strlen(chars); - growth += nchars * sizeof(jschar); - chars = (jschar *)realloc((ochars = chars), growth); + chars = (jschar *) + realloc((ochars = chars), nchars * sizeof(jschar) + growth); if (!chars) { free(ochars); goto done; } } chars[nchars++] = '['; - JS_ASSERT(sep == NULL); - sepstr = NULL; /* indicates to use ", " as separator */ - seplen = 2; } else { /* * Free any sharp variable definition in chars. Normally, we would * MAKE_SHARP(he) so that only the first sharp variable annotation is * a definition, and all the rest are references, but in the current - * case of (op != TO_SOURCE), we don't need chars at all. + * case of (!literalize), we don't need chars at all. */ if (chars) JS_free(cx, chars); chars = NULL; nchars = 0; - extratail = 1; /* allocate extra char for terminating 0 */ /* Return the empty string on a cycle as well as on empty join. */ if (IS_BUSY(he) || length == 0) { @@ -577,28 +443,24 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ MAKE_BUSY(he); - - if (sep) { - sepstr = JSSTRING_CHARS(sep); - seplen = JSSTRING_LENGTH(sep); - } else { - sepstr = NULL; /* indicates to use "," as separator */ - seplen = 1; - } } + 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 = GetArrayElement(cx, obj, index, &hole, &v); + ok = JS_GetElement(cx, obj, index, &v); if (!ok) goto done; - if (hole || - (op != TO_SOURCE && (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 (op == TO_LOCALE_STRING) { + if (localeString) { atom = cx->runtime->atomState.toLocaleStringAtom; JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); ok = js_ValueToObject(cx, v, &tvr.u.object) && @@ -607,11 +469,8 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, if (!ok) goto done; str = js_ValueToString(cx, v); - } else if (op == TO_STRING) { - str = js_ValueToString(cx, v); } else { - JS_ASSERT(op == TO_SOURCE); - str = js_ValueToSource(cx, v); + str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); } if (!str) { ok = JS_FALSE; @@ -619,22 +478,16 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, } } - /* - * Do not append separator after the last element unless it is a hole - * and we are in toSource. In that case we append single ",". - */ - if (index + 1 == length) - seplen = (hole && op == TO_SOURCE) ? 1 : 0; - - /* Allocate 1 at end for closing bracket and zero. */ + /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ tmplen = JSSTRING_LENGTH(str); - growth = nchars + tmplen + seplen + extratail; + 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); @@ -650,26 +503,25 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, } } - js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); - nchars += tmplen; - - if (seplen) { - if (sepstr) { - js_strncpy(&chars[nchars], sepstr, seplen); - } else { - JS_ASSERT(seplen == 1 || seplen == 2); - chars[nchars] = ','; - if (seplen == 2) - chars[nchars + 1] = ' '; - } + if (sepstr) { + js_strncpy(&chars[nchars], sepstr, seplen); nchars += seplen; } + sepstr = JSSTRING_CHARS(sep); + + js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); + nchars += tmplen; } done: - if (op == TO_SOURCE) { - if (chars) + if (literalize) { + if (chars) { + if (JSVAL_IS_VOID(v)) { + chars[nchars++] = ','; + chars[nchars++] = ' '; + } chars[nchars++] = ']'; + } } else { CLEAR_BUSY(he); } @@ -688,7 +540,6 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, return JS_FALSE; } chars[nchars] = 0; - JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth); str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); @@ -698,12 +549,17 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, return JS_TRUE; } +static jschar comma_space_ucstr[] = {',', ' ', 0}; +static jschar comma_ucstr[] = {',', 0}; +static JSString comma_space = {2, comma_space_ucstr}; +static JSString comma = {1, comma_ucstr}; + #if JS_HAS_TOSOURCE static JSBool array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - return array_join_sub(cx, obj, TO_SOURCE, NULL, rval); + return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval, JS_FALSE); } #endif @@ -711,7 +567,15 @@ static JSBool array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - return array_join_sub(cx, obj, TO_STRING, NULL, rval); + JSBool literalize; + + /* + * JS1.2 arrays convert to array literals, with a comma followed by a space + * between each element. + */ + literalize = JS_VERSION_IS_1_2(cx); + return array_join_sub(cx, obj, literalize ? &comma_space : &comma, + literalize, rval, JS_FALSE); } static JSBool @@ -722,15 +586,21 @@ array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * Passing comma here as the separator. Need a way to get a * locale-specific version. */ - return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, rval); + return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_TRUE); } static JSBool -InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end, - jsval *vector) +InitArrayElements(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) { - while (start != end) { - if (!SetArrayElement(cx, obj, start++, *vector++)) + jsuint index; + 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])) return JS_FALSE; } return JS_TRUE; @@ -753,9 +623,10 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) } if (!vector) return JS_TRUE; - return InitArrayElements(cx, obj, 0, length, vector); + return InitArrayElements(cx, obj, length, vector); } +#if JS_HAS_SOME_PERL_FUN /* * Perl-inspired join, reverse, and sort. */ @@ -764,15 +635,13 @@ array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; - if (JSVAL_IS_VOID(argv[0])) { - str = NULL; - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - } - return array_join_sub(cx, obj, TO_STRING, str, rval); + if (JSVAL_IS_VOID(argv[0])) + return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE); + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE); } static JSBool @@ -780,12 +649,24 @@ array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint len, half, i; - JSBool hole, hole2; + jsid id, id2; 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. @@ -794,15 +675,51 @@ array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, tmproot2 = argv + argc + 1; half = len / 2; for (i = 0; i < half; i++) { - if (!GetArrayElement(cx, obj, i, &hole, tmproot) || - !GetArrayElement(cx, obj, len - i - 1, &hole2, tmproot2) || - !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, *tmproot) || - !SetOrDeleteArrayElement(cx, obj, i, hole2, *tmproot2)) { - return JS_FALSE; + /* + * Get both values while checking for holes to make sure they don't + * get filled. + */ + if (!IndexToId(cx, i, &id)) + goto bad; + if (!PropertyExists(cx, obj, id, &idexists)) + goto bad; + if (idexists && !OBJ_GET_PROPERTY(cx, obj, id, tmproot)) + goto bad; + + 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; } } + 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 { @@ -814,13 +731,13 @@ typedef struct HSortArgs { JSBool fastcopy; } HSortArgs; -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result); +static int +sort_compare(const void *a, const void *b, void *arg); static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result); +sort_compare_strings(const void *a, const void *b, void *arg); -static JSBool +static void HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) { void *pivot, *vec, *vec2, *arg, *a, *b; @@ -828,7 +745,6 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) JSComparator cmp; JSBool fastcopy; size_t j, hiDiv2; - int cmp_result; pivot = hsa->pivot; vec = hsa->vec; @@ -840,17 +756,12 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) fastcopy = hsa->fastcopy; #define MEMCPY(p,q,n) \ (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) -#define CALL_CMP(a, b) \ - if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; if (lo == 1) { j = 2; b = (char *)vec + elsize; - if (j < hi) { - CALL_CMP(vec, b); - if (cmp_result < 0) - j++; - } + if (j < hi && cmp(vec, b, arg) < 0) + j++; a = (char *)vec + (hi - 1) * elsize; b = (char *)vec2 + j * elsize; @@ -859,11 +770,8 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 * always holds. */ - if (building || hi == 2) { - CALL_CMP(a, b); - if (cmp_result >= 0) - return JS_TRUE; - } + if ((building || hi == 2) && cmp(a, b, arg) >= 0) + return; MEMCPY(pivot, a, elsize); MEMCPY(a, b, elsize); @@ -878,14 +786,10 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) j = lo + lo; a = (char *)vec2 + j * elsize; b = (char *)vec + (j - 1) * elsize; - if (j < hi) { - CALL_CMP(a, b); - if (cmp_result < 0) - j++; - } + if (j < hi && cmp(a, b, arg) < 0) + j++; b = (char *)vec2 + j * elsize; - CALL_CMP(pivot, b); - if (cmp_result >= 0) + if (cmp(pivot, b, arg) >= 0) break; a = (char *)vec2 + lo * elsize; @@ -895,15 +799,10 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) a = (char *)vec2 + lo * elsize; MEMCPY(a, pivot, elsize); - - return JS_TRUE; - -#undef CALL_CMP #undef MEMCPY - } -JSBool +void js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, JSComparator cmp, void *arg) { @@ -917,47 +816,55 @@ js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, hsa.arg = arg; hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); - for (i = nel/2; i != 0; i--) { - if (!HeapSortHelper(JS_TRUE, &hsa, i, nel)) - return JS_FALSE; - } - while (nel > 2) { - if (!HeapSortHelper(JS_FALSE, &hsa, 1, --nel)) - return JS_FALSE; - } - - return JS_TRUE; + for (i = nel/2; i != 0; i--) + HeapSortHelper(JS_TRUE, &hsa, i, nel); + while (nel > 2) + HeapSortHelper(JS_FALSE, &hsa, 1, --nel); } typedef struct CompareArgs { JSContext *context; jsval fval; jsval *localroot; /* need one local root, for sort_compare */ + JSBool status; } CompareArgs; -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result) +static int +sort_compare(const void *a, const void *b, void *arg) { jsval av = *(const jsval *)a, bv = *(const jsval *)b; CompareArgs *ca = (CompareArgs *) arg; JSContext *cx = ca->context; - jsval fval; + jsdouble cmp = -1; + jsval fval, argv[2], special; JSBool ok; - /** - * array_sort deals with holes and undefs on its own and they should not - * come here. - */ - JS_ASSERT(av != JSVAL_VOID); - JS_ASSERT(bv != JSVAL_VOID); - - *result = 0; - ok = JS_TRUE; 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) { + if (av == bv) { + cmp = 0; + } else { /* * Set our local root to astr in case the second js_ValueToString * displaces the newborn root in cx, and the GC nests under that @@ -968,14 +875,11 @@ sort_compare(void *arg, const void *a, const void *b, int *result) astr = js_ValueToString(cx, av); *ca->localroot = STRING_TO_JSVAL(astr); if (astr && (bstr = js_ValueToString(cx, bv))) - *result = js_CompareStrings(astr, bstr); + cmp = js_CompareStrings(astr, bstr); else - ok = JS_FALSE; + ca->status = JS_FALSE; } } else { - jsdouble cmp; - jsval argv[2]; - argv[0] = av; argv[1] = bv; ok = js_InternalCall(cx, @@ -993,22 +897,24 @@ sort_compare(void *arg, const void *a, const void *b, int *result) * but is silent about what the result should be. So we * currently ignore it. */ + cmp = 0; } else if (cmp != 0) { - *result = cmp > 0 ? 1 : -1; + cmp = cmp > 0 ? 1 : -1; } } } + if (!ok) + ca->status = ok; } - return ok; + return (int)cmp; } static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result) +sort_compare_strings(const void *a, const void *b, void *arg) { jsval av = *(const jsval *)a, bv = *(const jsval *)b; - *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); - return JS_TRUE; + return (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); } static JSBool @@ -1016,9 +922,10 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, *vec, *pivotroot; CompareArgs ca; - jsuint len, newlen, i, undefs; - JSTempValueRooter tvr; - JSBool hole, ok; + jsuint len, newlen, i; + JSStackFrame *fp; + jsid id; + size_t nbytes; /* * Optimize the default compare function case if all of obj's elements @@ -1055,118 +962,112 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_ReportOutOfMemory(cx); return JS_FALSE; } + nbytes = ((size_t) len) * sizeof(jsval); - vec = (jsval *) JS_malloc(cx, ((size_t) len) * sizeof(jsval)); + vec = (jsval *) JS_malloc(cx, nbytes); if (!vec) return JS_FALSE; - /* - * Initialize vec as a root. We will clear elements of vec one by - * one while increasing tvr.count when we know that the property at - * the corresponding index exists and its value must be rooted. - * - * In this way when sorting a huge mostly sparse array we will not - * access the tail of vec corresponding to properties that do not - * exist, allowing OS to avoiding committing RAM. See bug 330812. - * - * After this point control must flow through label out: to exit. - */ - JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr); + /* 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; - /* - * By ECMA 262, 15.4.4.11, a property that does not exist (which we - * call a "hole") is always greater than an existing property with - * value undefined and that is always greater than any other property. - * Thus to sort holes and undefs we simply count them, sort the rest - * of elements, append undefs after them and then make holes after - * undefs. - */ - undefs = 0; newlen = 0; for (i = 0; i < len; i++) { - /* Clear vec[newlen] before including it in the rooted set. */ - vec[newlen] = JSVAL_NULL; - tvr.count = newlen + 1; - ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]); - if (!ok) + ca.status = IndexToExistingId(cx, obj, i, &id); + if (!ca.status) goto out; - if (hole) - continue; - - if (vec[newlen] == JSVAL_VOID) { - ++undefs; + if (id == JSID_HOLE) { + vec[i] = JSVAL_HOLE; + all_strings = JS_FALSE; continue; } + 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[newlen]); - - ++newlen; + all_strings &= JSVAL_IS_STRING(vec[i]); } - /* Here len == newlen + undefs + number_of_holes. */ 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 */ - ok = js_HeapSort(vec, (size_t) newlen, pivotroot, sizeof(jsval), - all_strings ? sort_compare_strings : sort_compare, - &ca); - if (!ok) - goto out; + ca.status = JS_TRUE; + js_HeapSort(vec, (size_t) len, pivotroot, sizeof(jsval), + all_strings ? sort_compare_strings : sort_compare, + &ca); - ok = InitArrayElements(cx, obj, 0, newlen, vec); - if (!ok) - goto out; + if (ca.status) { + ca.status = InitArrayElements(cx, obj, newlen, vec); + if (ca.status) + *rval = OBJECT_TO_JSVAL(obj); - out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_free(cx, vec); - if (!ok) - return JS_FALSE; + /* Re-create any holes that sorted to the end of the array. */ + while (len > newlen) { + jsval junk; - /* Set undefs that sorted after the rest of elements. */ - while (undefs != 0) { - --undefs; - if (!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) - return JS_FALSE; + if (!IndexToId(cx, --len, &id)) + return JS_FALSE; + if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk)) + return JS_FALSE; + } } - /* Re-create any holes that sorted to the end of the array. */ - while (len > newlen) { - if (!DeleteArrayElement(cx, obj, --len)) - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; +out: + if (vec) + JS_free(cx, vec); + return ca.status; } +#endif /* JS_HAS_SOME_PERL_FUN */ +#if JS_HAS_MORE_PERL_FUN /* * Perl-inspired push, pop, shift, unshift, and splice methods. */ static JSBool array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsuint length, newlength; + jsuint length; + uintN i; + jsid id; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; - newlength = length + argc; - if (!InitArrayElements(cx, obj, length, newlength, argv)) - return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, length + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } - /* Per ECMA-262, return the new array length. */ - if (!IndexToValue(cx, newlength, rval)) - return JS_FALSE; - return js_SetLengthProperty(cx, obj, newlength); + /* + * If JS1.2, follow Perl4 by returning the last thing pushed. Otherwise, + * return the new array length. + */ + length += argc; + if (JS_VERSION_IS_1_2(cx)) { + *rval = argc ? argv[argc-1] : JSVAL_VOID; + } else { + if (!IndexToValue(cx, length, rval)) + return JS_FALSE; + } + return js_SetLengthProperty(cx, obj, length); } static JSBool array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint index; - JSBool hole; + jsid id; + JSBool ok; + jsval junk; if (!js_GetLengthProperty(cx, obj, &index)) return JS_FALSE; @@ -1174,9 +1075,17 @@ array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) index--; /* Get the to-be-deleted property's value into rval. */ - if (!GetArrayElement(cx, obj, index, &hole, rval)) + if (!IndexToId(cx, index, &id)) return JS_FALSE; - if (!hole && !DeleteArrayElement(cx, obj, index)) + + /* 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); @@ -1186,31 +1095,44 @@ static JSBool array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, i; - JSBool hole; + jsid id, id2; + jsval junk; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; - if (length == 0) { - *rval = JSVAL_VOID; - } else { + if (length > 0) { length--; + id = JSVAL_ZERO; /* Get the to-be-deleted property's value into rval ASAP. */ - if (!GetArrayElement(cx, obj, 0, &hole, rval)) + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) return JS_FALSE; /* * Slide down the array above the first element. */ - for (i = 0; i != length; i++) { - if (!GetArrayElement(cx, obj, i + 1, &hole, &argv[0])) - return JS_FALSE; - if (!SetOrDeleteArrayElement(cx, obj, i, hole, argv[0])) - return JS_FALSE; + if (length > 0) { + for (i = 1; i <= length; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, &argv[0])) + return JS_FALSE; + + /* 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, &argv[0])) + return JS_FALSE; + } } - /* Delete the only or last element when it exist. */ - if (!hole && !DeleteArrayElement(cx, obj, length)) + /* + * 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; } return js_SetLengthProperty(cx, obj, length); @@ -1221,8 +1143,9 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, last; - jsval *vp; - JSBool hole; + uintN i; + jsid id, id2; + jsval *vp, junk; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1230,37 +1153,53 @@ 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; /* local root */ - do { - --last; - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + argc, hole, *vp)) { + vp = argv + argc; + while (last--) { + if (!IndexToExistingId(cx, obj, last, &id)) return JS_FALSE; + if (id != JSID_HOLE) { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; } - } while (last != 0); + + /* Get id after value to avoid nested GC hazards. */ + if (!IndexToId(cx, last + argc, &id2)) + return JS_FALSE; + 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; + } + } } /* Copy from argv to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, argc, argv)) - return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } + /* Follow Perl by returning the new array length. */ length += argc; if (!js_SetLengthProperty(cx, obj, length)) return JS_FALSE; } - - /* Follow Perl by returning the new array length. */ return IndexToValue(cx, length, rval); } static JSBool array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - jsval *vp; + jsval *vp, junk; jsuint length, begin, end, count, delta, last; jsdouble d; - JSBool hole; + jsid id, id2; JSObject *obj2; + uintN i; /* * Nothing to do if no args. Otherwise point vp at our one explicit local @@ -1306,31 +1245,56 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) argv++; } - - /* - * Create a new array value to return. Our ECMA v2 proposal specs - * that splice always returns an array value, even when given no - * arguments. We think this is best because it eliminates the need - * for callers to do an extra test to handle the empty splice case. - */ - obj2 = js_NewArrayObject(cx, 0, NULL); - if (!obj2) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj2); - - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - for (last = begin; last < end; last++) { - if (!GetArrayElement(cx, obj, last, &hole, vp)) - return JS_FALSE; - - /* Copy *vp to new array unless it's a hole. */ - if (!hole && !SetArrayElement(cx, obj2, last - begin, *vp)) - return JS_FALSE; - } - - if (!js_SetLengthProperty(cx, obj2, end - begin)) + 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 + * @single instead of $single, or by using it as Perl push's first + * argument, for instance. + * + * JS1.2 emulated Perl too closely and returned a non-Array for + * the single-splice-out case, requiring callers to test and wrap + * in [] if necessary. So JS1.3, default, and other versions all + * return an array of length 1 for uniformity. + */ + if (!IndexToId(cx, begin, &id)) return JS_FALSE; + if (!OBJ_GET_PROPERTY(cx, obj, id, rval)) + return JS_FALSE; + } else { + if (!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 + * arguments. We think this is best because it eliminates the need + * for callers to do an extra test to handle the empty splice case. + */ + obj2 = js_NewArrayObject(cx, 0, NULL); + if (!obj2) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj2); + + /* If there are elements to remove, put them into the return value. */ + if (count > 0) { + for (last = begin; last < end; last++) { + if (!IndexToExistingId(cx, obj, last, &id)) + return JS_FALSE; + if (id == JSID_HOLE) + continue; /* don't fill holes in the new array */ + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + + /* 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, vp)) + return JS_FALSE; + } + + if (!js_SetLengthProperty(cx, obj2, end - begin)) + return JS_FALSE; + } + } } /* Find the direction (up or down) to copy and make way for argv. */ @@ -1339,31 +1303,63 @@ 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 (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + delta, hole, *vp)) { + 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 (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 (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last - delta, hole, *vp)) { + 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 (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; } /* Copy from argv into the hole to complete the splice. */ - if (!InitArrayElements(cx, obj, begin, begin + argc, argv)) - return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!IndexToId(cx, begin + i, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) + return JS_FALSE; + } /* Update length in case we deleted elements from the end. */ return js_SetLengthProperty(cx, obj, length); } +#endif /* JS_HAS_MORE_PERL_FUN */ +#if JS_HAS_SEQUENCE_OPS /* * Python-esque sequence operations. */ @@ -1374,7 +1370,7 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *nobj, *aobj; jsuint length, alength, slot; uintN i; - JSBool hole; + jsid id, id2; /* Hoist the explicit local root address computation. */ vp = argv + argc; @@ -1405,14 +1401,22 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!ValueIsLength(cx, *vp, &alength)) return JS_FALSE; for (slot = 0; slot < alength; slot++) { - if (!GetArrayElement(cx, aobj, slot, &hole, vp)) + if (!IndexToExistingId(cx, aobj, slot, &id)) + return JS_FALSE; + 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; - /* - * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent - * properties. - */ - if (!hole && !SetArrayElement(cx, nobj, length + slot, *vp)) + /* 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, vp)) return JS_FALSE; } length += alength; @@ -1420,7 +1424,10 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } } - if (!SetArrayElement(cx, nobj, length, v)) + *vp = v; + if (!IndexToId(cx, length, &id)) + return JS_FALSE; + if (!OBJ_SET_PROPERTY(cx, nobj, id, vp)) return JS_FALSE; length++; } @@ -1435,7 +1442,7 @@ array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *nobj; jsuint length, begin, end, slot; jsdouble d; - JSBool hole; + jsid id, id2; /* Hoist the explicit local root address computation. */ vp = argv + argc; @@ -1483,13 +1490,22 @@ array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) begin = end; for (slot = begin; slot < end; slot++) { - if (!GetArrayElement(cx, obj, slot, &hole, vp)) + if (!IndexToExistingId(cx, obj, slot, &id)) return JS_FALSE; - if (!hole && !SetArrayElement(cx, nobj, slot - begin, *vp)) + 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_SET_PROPERTY(cx, nobj, id2, vp)) return JS_FALSE; } return js_SetLengthProperty(cx, nobj, end - begin); } +#endif /* JS_HAS_SEQUENCE_OPS */ #if JS_HAS_ARRAY_EXTRAS @@ -1499,7 +1515,6 @@ array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { jsuint length, i, stop; jsint direction; - JSBool hole; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -1516,16 +1531,8 @@ array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, start = js_DoubleToInteger(start); if (start < 0) { start += length; - if (start < 0) { - if (isLast) - goto not_found; - i = 0; - } else { - i = (jsuint)start; - } + i = (start < 0) ? 0 : (jsuint)start; } else if (start >= length) { - if (!isLast) - goto not_found; i = length - 1; } else { i = (jsuint)start; @@ -1541,10 +1548,18 @@ array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } for (;;) { - if (!GetArrayElement(cx, obj, (jsuint)i, &hole, rval)) + jsid id; + jsval v; + + if (!IndexToExistingId(cx, obj, (jsuint)i, &id)) return JS_FALSE; - if (!hole && js_StrictlyEqual(*rval, argv[0])) - return js_NewNumberValue(cx, i, rval); + 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; @@ -1587,7 +1602,7 @@ array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSObject *callable, *thisp, *newarr; void *mark; JSStackFrame *fp; - JSBool ok, cond, hole; + JSBool ok, b; /* Hoist the explicit local root address computation. */ vp = argv + argc; @@ -1599,7 +1614,7 @@ array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, * First, get or compute our callee, so that we error out consistently * when passed a non-callable object. */ - callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); + callable = js_ValueToCallableObject(cx, &argv[0], 0); if (!callable) return JS_FALSE; @@ -1652,11 +1667,17 @@ array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, oldsp = fp->sp; for (i = 0; i < length; i++) { - ok = GetArrayElement(cx, obj, i, &hole, vp); + jsid id; + jsval rval2; + + ok = IndexToExistingId(cx, obj, i, &id); if (!ok) break; - if (hole) + 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 @@ -1673,18 +1694,18 @@ array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, /* Do the call. */ fp->sp = sp; ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); - vp[1] = fp->sp[-1]; + rval2 = fp->sp[-1]; fp->sp = oldsp; if (!ok) break; if (mode > MAP) { - if (vp[1] == JSVAL_NULL) { - cond = JS_FALSE; - } else if (JSVAL_IS_BOOLEAN(vp[1])) { - cond = JSVAL_TO_BOOLEAN(vp[1]); + if (rval2 == JSVAL_NULL) { + b = JS_FALSE; + } else if (JSVAL_IS_BOOLEAN(rval2)) { + b = JSVAL_TO_BOOLEAN(rval2); } else { - ok = js_ValueToBoolean(cx, vp[1], &cond); + ok = js_ValueToBoolean(cx, rval2, &b); if (!ok) goto out; } @@ -1694,26 +1715,38 @@ array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, case FOREACH: break; case MAP: - ok = SetArrayElement(cx, newarr, i, vp[1]); + /* + * 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 (!cond) + if (!b) break; /* Filter passed *vp, push as result. */ - ok = SetArrayElement(cx, newarr, newlen++, *vp); + 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 (cond) { + if (b) { *rval = JSVAL_TRUE; goto out; } break; case EVERY: - if (!cond) { + if (!b) { *rval = JSVAL_FALSE; goto out; } @@ -1772,27 +1805,33 @@ static JSFunctionSpec array_methods[] = { {js_toLocaleString_str, array_toLocaleString, 0,0,0}, /* Perl-ish methods. */ +#if JS_HAS_SOME_PERL_FUN {"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,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, 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,2}, - {"map", array_map, 1,JSFUN_GENERIC_NATIVE,2}, - {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,2}, - {"some", array_some, 1,JSFUN_GENERIC_NATIVE,2}, - {"every", array_every, 1,JSFUN_GENERIC_NATIVE,2}, + {"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} @@ -1815,6 +1854,9 @@ Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (argc == 0) { length = 0; vector = NULL; + } else if (JS_VERSION_IS_1_2(cx)) { + length = (jsuint) argc; + vector = argv; } else if (argc > 1) { length = (jsuint) argc; vector = argv; @@ -1846,19 +1888,14 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) JSObject * js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) { - JSTempValueRooter tvr; JSObject *obj; obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); if (!obj) return NULL; - - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - if (!InitArrayObject(cx, obj, length, vector)) - obj = NULL; - JS_POP_TEMP_ROOT(cx, &tvr); - - /* Set/clear newborn root, in case we lost it. */ - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; + if (!InitArrayObject(cx, obj, length, vector)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } return obj; } diff --git a/lib/javascript/jsarray.h b/lib/javascript/jsarray.h index a89561b4..0f43bee0 100644 --- a/lib/javascript/jsarray.h +++ b/lib/javascript/jsarray.h @@ -47,9 +47,6 @@ JS_BEGIN_EXTERN_C -/* Generous sanity-bound on length (in elements) of array initialiser. */ -#define ARRAY_INIT_LIMIT JS_BIT(24) - extern JSBool js_IdIsIndex(jsval id, jsuint *indexp); @@ -70,23 +67,12 @@ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); extern JSBool js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); -/* - * Test whether an object is "array-like". Currently this means whether obj - * is an Array or an arguments object. We would like an API, and probably a - * way in the language, to bless other objects as array-like: having indexed - * properties, and a 'length' property of uint32 value equal to one more than - * the greatest index. - */ -extern JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp); - /* * JS-specific heap sort function. */ -typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, - int *result); +typedef int (*JSComparator)(const void *a, const void *b, void *arg); -extern JSBool +extern void js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, JSComparator cmp, void *arg); diff --git a/lib/javascript/jsatom.c b/lib/javascript/jsatom.c index 02ee2506..6de628ae 100644 --- a/lib/javascript/jsatom.c +++ b/lib/javascript/jsatom.c @@ -54,7 +54,7 @@ #include "jsgc.h" #include "jslock.h" #include "jsnum.h" -#include "jsscan.h" +#include "jsopcode.h" #include "jsstr.h" JS_FRIEND_API(const char *) @@ -63,11 +63,15 @@ js_AtomToPrintableString(JSContext *cx, JSAtom *atom) 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 * its length match the JSType enum's JSTYPE_LIMIT limit value. */ -const char *js_type_strs[] = { +const char *js_type_str[] = { "undefined", js_object_str, "function", @@ -78,24 +82,27 @@ const char *js_type_strs[] = { "xml", }; -JS_STATIC_ASSERT(JSTYPE_LIMIT == - sizeof js_type_strs / sizeof js_type_strs[0]); - -const char *js_boolean_strs[] = { +const char *js_boolean_str[] = { js_false_str, js_true_str }; -#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; -#include "jsproto.tbl" -#undef JS_PROTO - -const char *js_proto_strs[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) js_##name##_str, -#include "jsproto.tbl" -#undef JS_PROTO -}; - +const char js_Arguments_str[] = "Arguments"; +const char js_Array_str[] = "Array"; +const char js_Boolean_str[] = "Boolean"; +const char js_Call_str[] = "Call"; +const char js_Date_str[] = "Date"; +const char js_Function_str[] = "Function"; +const char js_Math_str[] = "Math"; +const char js_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"; @@ -106,24 +113,19 @@ const char js_constructor_str[] = "constructor"; const char js_count_str[] = "__count__"; const char js_each_str[] = "each"; const char js_eval_str[] = "eval"; -const char js_fileName_str[] = "fileName"; -const char js_get_str[] = "get"; const char js_getter_str[] = "getter"; +const char js_get_str[] = "get"; const char js_index_str[] = "index"; const char js_input_str[] = "input"; -const char js_iterator_str[] = "__iterator__"; const char js_length_str[] = "length"; -const char js_lineNumber_str[] = "lineNumber"; -const char js_message_str[] = "message"; const char js_name_str[] = "name"; -const char js_next_str[] = "next"; const char js_noSuchMethod_str[] = "__noSuchMethod__"; const char js_object_str[] = "object"; const char js_parent_str[] = "__parent__"; +const char js_private_str[] = "private"; const char js_proto_str[] = "__proto__"; const char js_setter_str[] = "setter"; const char js_set_str[] = "set"; -const char js_stack_str[] = "stack"; const char js_toSource_str[] = "toSource"; const char js_toString_str[] = "toString"; const char js_toLocaleString_str[] = "toLocaleString"; @@ -142,11 +144,6 @@ const char js_tagc_str[] = ">"; const char js_xml_str[] = "xml"; #endif -#if JS_HAS_GENERATORS -const char js_close_str[] = "close"; -const char js_send_str[] = "send"; -#endif - #ifdef NARCISSUS const char js_call_str[] = "__call__"; const char js_construct_str[] = "__construct__"; @@ -190,7 +187,7 @@ js_compare_atom_keys(const void *k1, const void *k2) v1 = (jsval)k1, v2 = (jsval)k2; if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) - return js_EqualStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); + return !js_CompareStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { double d1 = *JSVAL_TO_DOUBLE(v1); double d2 = *JSVAL_TO_DOUBLE(v2); @@ -297,16 +294,33 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) return JS_FALSE; \ JS_END_MACRO + JS_ASSERT(sizeof js_type_str / sizeof js_type_str[0] == JSTYPE_LIMIT); for (i = 0; i < JSTYPE_LIMIT; i++) - FROB(typeAtoms[i], js_type_strs[i]); - - for (i = 0; i < JSProto_LIMIT; i++) - FROB(classAtoms[i], js_proto_strs[i]); + FROB(typeAtoms[i], js_type_str[i]); FROB(booleanAtoms[0], js_false_str); FROB(booleanAtoms[1], js_true_str); FROB(nullAtom, js_null_str); + FROB(ArgumentsAtom, js_Arguments_str); + FROB(ArrayAtom, js_Array_str); + FROB(BooleanAtom, js_Boolean_str); + FROB(CallAtom, js_Call_str); + FROB(DateAtom, js_Date_str); +#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); @@ -317,23 +331,17 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(countAtom, js_count_str); FROB(eachAtom, js_each_str); FROB(evalAtom, js_eval_str); - FROB(fileNameAtom, js_fileName_str); FROB(getAtom, js_get_str); FROB(getterAtom, js_getter_str); FROB(indexAtom, js_index_str); FROB(inputAtom, js_input_str); - FROB(iteratorAtom, js_iterator_str); FROB(lengthAtom, js_length_str); - FROB(lineNumberAtom, js_lineNumber_str); - FROB(messageAtom, js_message_str); FROB(nameAtom, js_name_str); - FROB(nextAtom, js_next_str); FROB(noSuchMethodAtom, js_noSuchMethod_str); FROB(parentAtom, js_parent_str); FROB(protoAtom, js_proto_str); FROB(setAtom, js_set_str); FROB(setterAtom, js_setter_str); - FROB(stackAtom, js_stack_str); FROB(toSourceAtom, js_toSource_str); FROB(toStringAtom, js_toString_str); FROB(toLocaleStringAtom, js_toLocaleString_str); @@ -352,10 +360,6 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(xmlAtom, js_xml_str); #endif -#if JS_HAS_GENERATORS - FROB(closeAtom, js_close_str); -#endif - #ifdef NARCISSUS FROB(callAtom, js_call_str); FROB(constructAtom, js_construct_str); @@ -425,7 +429,7 @@ js_FinishAtomState(JSAtomState *state) } typedef struct MarkArgs { - JSBool keepAtoms; + uintN gcflags; JSGCThingMarker mark; void *data; } MarkArgs; @@ -439,7 +443,8 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg) atom = (JSAtom *)he; args = (MarkArgs *)arg; - if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) { + if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || + (args->gcflags & GC_KEEP_ATOMS)) { atom->flags |= ATOM_MARK; key = ATOM_KEY(atom); if (JSVAL_IS_GCTHING(key)) @@ -449,14 +454,14 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg) } void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, +js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark, void *data) { MarkArgs args; if (!state->table) return; - args.keepAtoms = keepAtoms; + args.gcflags = gcflags; args.mark = mark; args.data = data; JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args); @@ -529,7 +534,7 @@ js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) atom = (JSAtom *)he; atom->flags |= flags; - cx->weakRoots.lastAtom = atom; + cx->lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; @@ -620,7 +625,7 @@ js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) atom = (JSAtom *)he; atom->flags |= flags; - cx->weakRoots.lastAtom = atom; + cx->lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; @@ -691,7 +696,7 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) atom = (JSAtom *)he; atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); - cx->weakRoots.lastAtom = atom; + cx->lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; @@ -750,30 +755,6 @@ js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); } -JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) -{ - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry **hep; - - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - keyHash = js_HashString(str); - key = STRING_TO_JSVAL(str); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - JS_UNLOCK(&state->lock, cx); - return (hep) ? (JSAtom *)*hep : NULL; -} - JSAtom * js_AtomizeValue(JSContext *cx, jsval value, uintN flags) { @@ -940,8 +921,8 @@ js_map_atom(JSHashEntry *he, intN i, void *arg) } #ifdef DEBUG -static jsrefcount js_atom_map_count; -static jsrefcount js_atom_map_hash_table_count; +jsrefcount js_atom_map_count; +jsrefcount js_atom_map_hash_table_count; #endif JS_FRIEND_API(JSBool) diff --git a/lib/javascript/jsatom.h b/lib/javascript/jsatom.h index 4fb3d8d5..f8ce3f90 100644 --- a/lib/javascript/jsatom.h +++ b/lib/javascript/jsatom.h @@ -63,8 +63,8 @@ JS_BEGIN_EXTERN_C #define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ struct JSAtom { - JSHashEntry entry; /* key is jsval or unhidden atom - if ATOM_HIDDEN */ + 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 */ }; @@ -89,6 +89,9 @@ struct JSAtom { extern JS_FRIEND_API(const char *) js_AtomToPrintableString(JSContext *cx, JSAtom *atom); +#define ATOM_KEYWORD(atom) ((struct keyword *)(atom)->entry.value) +#define ATOM_SET_KEYWORD(atom,kw) ((atom)->entry.value = (kw)) + struct JSAtomListElement { JSHashEntry entry; }; @@ -160,34 +163,42 @@ struct JSAtomState { JSAtom *booleanAtoms[2]; JSAtom *nullAtom; - /* Standard class constructor or prototype names. */ - JSAtom *classAtoms[JSProto_LIMIT]; - /* Various built-in or commonly-used atoms, pinned on first context. */ + JSAtom *ArgumentsAtom; + JSAtom *ArrayAtom; + JSAtom *BooleanAtom; + JSAtom *CallAtom; + JSAtom *DateAtom; + JSAtom *ErrorAtom; + JSAtom *FunctionAtom; + JSAtom *MathAtom; + JSAtom *NamespaceAtom; + JSAtom *NumberAtom; + JSAtom *ObjectAtom; + JSAtom *QNameAtom; + JSAtom *RegExpAtom; + JSAtom *ScriptAtom; + JSAtom *StringAtom; + JSAtom *XMLAtom; + JSAtom *FileAtom; JSAtom *anonymousAtom; JSAtom *argumentsAtom; JSAtom *arityAtom; JSAtom *calleeAtom; JSAtom *callerAtom; JSAtom *classPrototypeAtom; - JSAtom *closeAtom; JSAtom *constructorAtom; JSAtom *countAtom; JSAtom *eachAtom; JSAtom *etagoAtom; JSAtom *evalAtom; - JSAtom *fileNameAtom; JSAtom *getAtom; JSAtom *getterAtom; JSAtom *indexAtom; JSAtom *inputAtom; - JSAtom *iteratorAtom; JSAtom *lengthAtom; - JSAtom *lineNumberAtom; - JSAtom *messageAtom; JSAtom *nameAtom; JSAtom *namespaceAtom; - JSAtom *nextAtom; JSAtom *noSuchMethodAtom; JSAtom *parentAtom; JSAtom *protoAtom; @@ -196,7 +207,6 @@ struct JSAtomState { JSAtom *setAtom; JSAtom *setterAtom; JSAtom *spaceAtom; - JSAtom *stackAtom; JSAtom *stagoAtom; JSAtom *starAtom; JSAtom *starQualifierAtom; @@ -209,8 +219,17 @@ struct JSAtomState { /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { + JSAtom *AnyNameAtom; + JSAtom *AttributeNameAtom; + JSAtom *EvalErrorAtom; JSAtom *InfinityAtom; + JSAtom *InternalErrorAtom; JSAtom *NaNAtom; + JSAtom *RangeErrorAtom; + JSAtom *ReferenceErrorAtom; + JSAtom *SyntaxErrorAtom; + JSAtom *TypeErrorAtom; + JSAtom *URIErrorAtom; JSAtom *XMLListAtom; JSAtom *decodeURIAtom; JSAtom *decodeURIComponentAtom; @@ -249,42 +268,44 @@ struct JSAtomState { #endif }; -#define CLASS_ATOM(cx,name) \ - ((cx)->runtime->atomState.classAtoms[JSProto_##name]) - /* Well-known predefined strings and their atoms. */ -extern const char *js_type_strs[]; -extern const char *js_boolean_strs[]; -extern const char *js_proto_strs[]; - -#define JS_PROTO(name,code,init) extern const char js_##name##_str[]; -#include "jsproto.tbl" -#undef JS_PROTO +extern const char *js_type_str[]; +extern const char *js_boolean_str[]; +extern const char js_Arguments_str[]; +extern const char js_Array_str[]; +extern const char js_Boolean_str[]; +extern const char js_Call_str[]; +extern const char js_Date_str[]; +extern const char js_Function_str[]; +extern const char js_Math_str[]; +extern const char js_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[]; extern const char js_callee_str[]; extern const char js_caller_str[]; extern const char js_class_prototype_str[]; -extern const char js_close_str[]; extern const char js_constructor_str[]; extern const char js_count_str[]; extern const char js_etago_str[]; extern const char js_each_str[]; extern const char js_eval_str[]; -extern const char js_fileName_str[]; -extern const char js_get_str[]; extern const char js_getter_str[]; +extern const char js_get_str[]; extern const char js_index_str[]; extern const char js_input_str[]; -extern const char js_iterator_str[]; extern const char js_length_str[]; -extern const char js_lineNumber_str[]; -extern const char js_message_str[]; extern const char js_name_str[]; extern const char js_namespace_str[]; -extern const char js_next_str[]; extern const char js_noSuchMethod_str[]; extern const char js_object_str[]; extern const char js_parent_str[]; @@ -292,11 +313,9 @@ 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_send_str[]; extern const char js_setter_str[]; extern const char js_set_str[]; extern const char js_space_str[]; -extern const char js_stack_str[]; extern const char js_stago_str[]; extern const char js_star_str[]; extern const char js_starQualifier_str[]; @@ -351,7 +370,7 @@ typedef void (*JSGCThingMarker)(void *thing, void *data); extern void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, +js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark, void *data); extern void @@ -404,13 +423,6 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); extern JS_FRIEND_API(JSAtom *) js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); -/* - * Return an existing atom for the given char array or null if the char - * sequence is currently not atomized. - */ -extern JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); - /* * This variant handles all value tag types. */ diff --git a/lib/javascript/jsbit.h b/lib/javascript/jsbit.h index 87bb0476..db41acf9 100644 --- a/lib/javascript/jsbit.h +++ b/lib/javascript/jsbit.h @@ -40,8 +40,6 @@ #define jsbit_h___ #include "jstypes.h" -#include "jsutil.h" - JS_BEGIN_EXTERN_C /* @@ -67,33 +65,11 @@ extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); */ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); -/* - * Check if __builtin_clz is available which apeared first in GCC 3.4. - * The built-in allows to speedup calculations of ceiling/floor log2, - * see bug 327129. - */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# define JS_HAS_GCC_BUILTIN_CLZ -#endif - /* ** Macro version of JS_CeilingLog2: Compute the log of the least power of ** 2 greater than or equal to _n. The result is returned in _log2. */ -#ifdef JS_HAS_GCC_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate ceil(log2(_n)). - * The macro checks for "n <= 1" and not "n != 0" as __builtin_clz(0) is - * undefined. - */ -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - unsigned int j_ = (unsigned int)(_n); \ - (_log2) = (j_ <= 1 ? 0 : 32 - __builtin_clz(j_ - 1)); \ - JS_END_MACRO -#else -# define JS_CEILING_LOG2(_log2,_n) \ +#define JS_CEILING_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JSUint32 j_ = (JSUint32)(_n); \ (_log2) = 0; \ @@ -110,7 +86,6 @@ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); if ((j_) >> 1) \ (_log2) += 1; \ JS_END_MACRO -#endif /* ** Macro version of JS_FloorLog2: Compute the log of the greatest power of @@ -118,19 +93,7 @@ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); ** ** This is equivalent to finding the highest set bit in the word. */ -#if JS_GCC_HAS_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate floor(log2(_n)). - * Since __builtin_clz(0) is undefined, the macro set the loweset bit to 1 - * to ensure 0 result when _n == 0. - */ -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - (_log2) = 31 - __builtin_clz(((unsigned int)(_n)) | 1); \ - JS_END_MACRO -#else -# define JS_FLOOR_LOG2(_log2,_n) \ +#define JS_FLOOR_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JSUint32 j_ = (JSUint32)(_n); \ (_log2) = 0; \ @@ -145,51 +108,6 @@ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); if ((j_) >> 1) \ (_log2) += 1; \ JS_END_MACRO -#endif - -/* - * Internal function. - * Compute the log of the least power of 2 greater than or equal to n. - * This is a version of JS_CeilingLog2 that operates on jsuword with - * CPU-dependant size. - */ -#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) - -/* - * Internal function. - * Compute the log of the greatest power of 2 less than or equal to n. - * This is a version of JS_FloorLog2 that operates on jsuword with - * CPU-dependant size and requires that n != 0. - */ -#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) - -#ifdef JS_HAS_GCC_BUILTIN_CLZ - -# if JS_BYTES_PER_WORD == 4 -JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clz(n))) -# elif JS_BYTES_PER_WORD == 8 -JS_STATIC_ASSERT(sizeof(unsigned long long) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clzll(n))) -# else -# error "NOT SUPPORTED" -# endif - -#else - -# if JS_BYTES_PER_WORD == 4 -# define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n)) -# elif JS_BYTES_PER_WORD == 8 -extern JSUword -js_FloorLog2wImpl(JSUword n); -# else -# error "NOT SUPPORTED" -# endif - -#endif - JS_END_EXTERN_C #endif /* jsbit_h___ */ diff --git a/lib/javascript/jsbool.c b/lib/javascript/jsbool.c index 543b4f3f..33d5c507 100644 --- a/lib/javascript/jsbool.c +++ b/lib/javascript/jsbool.c @@ -56,7 +56,7 @@ JSClass js_BooleanClass = { "Boolean", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), + JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS @@ -73,18 +73,14 @@ bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, char buf[32]; JSString *str; - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - 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); - } + 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); JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_BooleanClass.name, - js_boolean_strs[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); + js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; @@ -101,15 +97,11 @@ bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSAtom *atom; JSString *str; - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - 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); - } + 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); atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; str = ATOM_TO_STRING(atom); if (!str) @@ -121,10 +113,6 @@ 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 (JSVAL_IS_BOOLEAN((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); @@ -133,10 +121,10 @@ bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSFunctionSpec boolean_methods[] = { #if JS_HAS_TOSOURCE - {js_toSource_str, bool_toSource, 0,JSFUN_THISP_BOOLEAN,0}, + {js_toSource_str, bool_toSource, 0,0,0}, #endif - {js_toString_str, bool_toString, 0,JSFUN_THISP_BOOLEAN,0}, - {js_valueOf_str, bool_valueOf, 0,JSFUN_THISP_BOOLEAN,0}, + {js_toString_str, bool_toString, 0,0,0}, + {js_valueOf_str, bool_valueOf, 0,0,0}, {0,0,0,0,0} }; diff --git a/lib/javascript/jsbool.h b/lib/javascript/jsbool.h index 8dbd2181..770b94c6 100644 --- a/lib/javascript/jsbool.h +++ b/lib/javascript/jsbool.h @@ -51,11 +51,8 @@ JS_BEGIN_EXTERN_C * 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". - * - * JSVAL_ARETURN is used to throw asynchronous return for generator.close(). */ #define JSVAL_HOLE BOOLEAN_TO_JSVAL(2) -#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(3) extern JSClass js_BooleanClass; diff --git a/lib/javascript/jscntxt.c b/lib/javascript/jscntxt.c index 139ad9b8..43041f97 100644 --- a/lib/javascript/jscntxt.c +++ b/lib/javascript/jscntxt.c @@ -1,5 +1,4 @@ /* -*- 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,124 +60,21 @@ #include "jsobj.h" #include "jsopcode.h" #include "jsscan.h" -#include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#ifdef JS_THREADSAFE - -/* - * Callback function to delete a JSThread info when the thread that owns it - * is destroyed. - */ -void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr) -{ - JSThread *thread = (JSThread *)ptr; - - if (!thread) - return; - while (!JS_CLIST_IS_EMPTY(&thread->contextList)) { - /* NB: use a temporary, as the macro evaluates its args many times. */ - JSCList *link = thread->contextList.next; - - JS_REMOVE_AND_INIT_LINK(link); - } - GSN_CACHE_CLEAR(&thread->gsnCache); - free(thread); -} - -/* - * Get current thread-local JSThread info, creating one if it doesn't exist. - * Each thread has a unique JSThread pointer. - * - * Since we are dealing with thread-local data, no lock is needed. - * - * Return a pointer to the thread local info, NULL if the system runs out - * of memory, or it failed to set thread private data (neither case is very - * likely; both are probably due to out-of-memory). It is up to the caller - * to report an error, if possible. - */ -JSThread * -js_GetCurrentThread(JSRuntime *rt) -{ - JSThread *thread; - - thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex); - if (!thread) { - thread = (JSThread *) calloc(1, sizeof(JSThread)); - if (!thread) - return NULL; - - if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) { - free(thread); - return NULL; - } - - JS_INIT_CLIST(&thread->contextList); - thread->id = js_CurrentThreadId(); - - /* js_SetContextThread initialize gcFreeLists as necessary. */ -#ifdef DEBUG - memset(thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(thread->gcFreeLists)); -#endif - } - return thread; -} - -/* - * Sets current thread as owning thread of a context by assigning the - * thread-private info to the context. If the current thread doesn't have - * private JSThread info, create one. - */ -JSBool -js_SetContextThread(JSContext *cx) -{ - JSThread *thread = js_GetCurrentThread(cx->runtime); - - if (!thread) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* - * Clear gcFreeLists on each transition from 0 to 1 context active on the - * current thread. See bug 351602. - */ - if (JS_CLIST_IS_EMPTY(&thread->contextList)) - memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists)); - - cx->thread = thread; - JS_REMOVE_LINK(&cx->threadLinks); - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - return JS_TRUE; -} - -/* Remove the owning thread info of a context. */ -void -js_ClearContextThread(JSContext *cx) -{ - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); -#ifdef DEBUG - if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) { - memset(cx->thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(cx->thread->gcFreeLists)); - } -#endif - cx->thread = NULL; -} - -#endif /* JS_THREADSAFE */ - void js_OnVersionChange(JSContext *cx) { -#ifdef DEBUG - JSVersion version = JSVERSION_NUMBER(cx); - - JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3); -#endif +#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 @@ -193,7 +89,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) { JSContext *cx; JSBool ok, first; - JSContextCallback cxCallback; cx = (JSContext *) malloc(sizeof *cx); if (!cx) @@ -205,8 +100,7 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) cx->stackLimit = (jsuword)-1; #endif #ifdef JS_THREADSAFE - JS_INIT_CLIST(&cx->threadLinks); - js_SetContextThread(cx); + js_InitContextForLocking(cx); #endif JS_LOCK_GC(rt); @@ -239,10 +133,15 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval)); JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble)); +#if JS_HAS_REGEXPS if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); + js_DestroyContext(cx, JS_NO_GC); return NULL; } +#endif +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_FALSE; +#endif /* * If cx is the first context on this runtime, initialize well-known atoms, @@ -253,9 +152,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) * as well as "first". */ if (first) { -#ifdef JS_THREADSAFE - JS_BeginRequest(cx); -#endif /* * 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 @@ -266,17 +162,16 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) 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_InitRuntimeStringState(cx); -#ifdef JS_THREADSAFE - JS_EndRequest(cx); -#endif if (!ok) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); + js_DestroyContext(cx, JS_NO_GC); return NULL; } @@ -286,19 +181,13 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_UNLOCK_GC(rt); } - cxCallback = rt->cxCallback; - if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } return cx; } void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) +js_DestroyContext(JSContext *cx, JSGCMode gcmode) { JSRuntime *rt; - JSContextCallback cxCallback; JSBool last; JSArgumentFormatMap *map; JSLocalRootStack *lrs; @@ -306,21 +195,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) rt = cx->runtime; - if (mode != JSDCM_NEW_FAILED) { - cxCallback = rt->cxCallback; - if (cxCallback) { - /* - * JSCONTEXT_DESTROY callback is not allowed to fail and must - * return true. - */ -#ifdef DEBUG - JSBool callbackStatus = -#endif - cxCallback(cx, JSCONTEXT_DESTROY); - JS_ASSERT(callbackStatus); - } - } - /* Remove cx from context list first. */ JS_LOCK_GC(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); @@ -358,6 +232,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JS_ClearAllWatchPoints(cx); } +#if JS_HAS_REGEXPS /* * Remove more GC roots in regExpStatics, then collect garbage. * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within @@ -365,6 +240,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) * XXX case where JS_DestroyContext is called outside of a request on cx */ js_FreeRegExpStatics(cx, &cx->regExpStatics); +#endif #ifdef JS_THREADSAFE /* @@ -383,7 +259,12 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) #endif if (last) { - js_GC(cx, GC_LAST_CONTEXT); + /* Always force, so we wait for any racing GC to finish. */ + js_ForceGC(cx, GC_LAST_CONTEXT); + + /* Iterate until no finalizer removes a GC root or lock. */ + while (rt->gcPoke) + js_GC(cx, GC_LAST_CONTEXT); /* Try to free atom state, now that no unrooted scripts survive. */ if (rt->atomState.liveAtoms == 0) @@ -393,28 +274,21 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) js_FinishRuntimeScriptState(rt); - /* - * Free the deflated string cache, but only after the last GC has - * collected all unleaked strings. - */ - js_FinishDeflatedStringCache(rt); - /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_GC(rt); rt->state = JSRTS_DOWN; JS_NOTIFY_ALL_CONDVAR(rt->stateChange); JS_UNLOCK_GC(rt); } else { - if (mode == JSDCM_FORCE_GC) - js_GC(cx, GC_NORMAL); - else if (mode == JSDCM_MAYBE_GC) + if (gcmode == JS_FORCE_GC) + js_ForceGC(cx, 0); + else if (gcmode == JS_MAYBE_GC) JS_MaybeGC(cx); } /* Free the stuff hanging off of cx. */ JS_FinishArenaPool(&cx->stackPool); JS_FinishArenaPool(&cx->tempPool); - if (cx->lastMessage) free(cx->lastMessage); @@ -441,10 +315,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) JS_free(cx, lrs); } -#ifdef JS_THREADSAFE - js_ClearContextThread(cx); -#endif - /* Finally, free cx itself. */ free(cx); } @@ -618,10 +488,10 @@ js_EnterLocalRootScope(JSContext *cx) } void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) +js_LeaveLocalRootScope(JSContext *cx) { JSLocalRootStack *lrs; - uint32 mark, m, n; + unsigned mark, m, n; JSLocalRootChunk *lrc; /* Defend against buggy native callers. */ @@ -646,29 +516,11 @@ js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) --n; } - /* - * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push - * it on the caller's scope, or store it in lastInternalResult if we are - * leaving the outermost scope. We don't need to allocate a new lrc - * because we can overwrite the old mark's slot with rval. - */ + /* Pop the scope, restoring lrs->scopeMark. */ lrc = lrs->topChunk; m = mark & JSLRS_CHUNK_MASK; lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); - if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { - if (mark == 0) { - cx->weakRoots.lastInternalResult = rval; - } else { - /* - * Increment m to avoid the "else if (m == 0)" case below. If - * rval is not a GC-thing, that case would take care of freeing - * any chunk that contained only the old mark. Since rval *is* - * a GC-thing here, we want to reuse that old mark's slot. - */ - lrc->roots[m++] = rval; - ++mark; - } - } + lrc->roots[m] = JSVAL_NULL; lrs->rootCount = (uint32) mark; /* @@ -693,7 +545,7 @@ void js_ForgetLocalRoot(JSContext *cx, jsval v) { JSLocalRootStack *lrs; - uint32 i, j, m, n, mark; + unsigned i, j, m, n, mark; JSLocalRootChunk *lrc, *lrc2; jsval top; @@ -751,7 +603,7 @@ js_ForgetLocalRoot(JSContext *cx, jsval v) int js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) { - uint32 n, m; + unsigned n, m; JSLocalRootChunk *lrc; n = lrs->rootCount; @@ -787,7 +639,7 @@ js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) void js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) { - uint32 n, m, mark; + unsigned n, m, mark; JSLocalRootChunk *lrc; n = lrs->rootCount; @@ -801,10 +653,12 @@ js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) #ifdef GC_MARK_DEBUG char name[22]; JS_snprintf(name, sizeof name, "", n); +#else + const char *name = NULL; #endif m = n & JSLRS_CHUNK_MASK; JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); - GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name); + JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL); if (m == 0) lrc = lrc->down; } @@ -825,10 +679,10 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) * exception is thrown, then the JSREPORT_EXCEPTION flag will be set * on the error report, and exception-aware hosts should ignore it. */ - JS_ASSERT(reportp); - if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) + if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) reportp->flags |= JSREPORT_EXCEPTION; +#if JS_HAS_ERROR_EXCEPTIONS /* * Call the error reporter only if an exception wasn't raised. * @@ -845,6 +699,9 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) if (hook) hook(cx, message, reportp, cx->runtime->debugErrorHookData); } +#else + js_ReportErrorAgain(cx, message, reportp); +#endif } /* @@ -855,15 +712,14 @@ ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) * type message, and then hope the process ends swiftly. */ void -js_ReportOutOfMemory(JSContext *cx) +js_ReportOutOfMemory(JSContext *cx, JSErrorCallback callback) { JSStackFrame *fp; JSErrorReport report; JSErrorReporter onError = cx->errorReporter; /* Get the message for this error, but we won't expand any arguments. */ - const JSErrorFormatString *efs = - js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); + const JSErrorFormatString *efs = callback(NULL, NULL, JSMSG_OUT_OF_MEMORY); const char *msg = efs ? efs->format : "Out of memory"; /* Fill out the report, but don't do anything that requires allocation. */ @@ -902,9 +758,7 @@ js_ReportOutOfMemory(JSContext *cx) JSBool js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) { - char *message; - jschar *ucmessage; - size_t messagelen; + char *last; JSStackFrame *fp; JSErrorReport report; JSBool warning; @@ -912,15 +766,12 @@ js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; - message = JS_vsmprintf(format, ap); - if (!message) + last = JS_vsmprintf(format, ap); + if (!last) return JS_FALSE; - messagelen = strlen(message); memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; - report.errorNumber = JSMSG_USER_DEFINED_ERROR; - report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); /* Find the top-most active script frame, for best line number blame. */ for (fp = cx->fp; fp; fp = fp->down) { @@ -937,9 +788,8 @@ js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) warning = JS_FALSE; } - ReportError(cx, message, &report); - free(message); - JS_free(cx, ucmessage); + ReportError(cx, last, &report); + free(last); return warning; } @@ -971,112 +821,110 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, } *messagep = NULL; - - /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ - if (!callback || callback == js_GetErrorMessage) - efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); - else + if (callback) { efs = callback(userRef, NULL, errorNumber); - if (efs) { - size_t totalArgsLength = 0; - size_t argLengths[10]; /* only {0} thru {9} supported */ - argCount = efs->argCount; - JS_ASSERT(argCount <= 10); - if (argCount > 0) { - /* - * Gather the arguments into an array, and accumulate - * their sizes. We allocate 1 more than necessary and - * null it out to act as the caboose when we free the - * pointers later. - */ - reportp->messageArgs = (const jschar **) - JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); - if (!reportp->messageArgs) - return JS_FALSE; - reportp->messageArgs[argCount] = NULL; - for (i = 0; i < argCount; i++) { - if (charArgs) { - char *charArg = va_arg(ap, char *); - size_t charArgLength = strlen(charArg); - reportp->messageArgs[i] - = js_InflateString(cx, charArg, &charArgLength); - if (!reportp->messageArgs[i]) - goto error; - } else { - reportp->messageArgs[i] = va_arg(ap, jschar *); - } - argLengths[i] = js_strlen(reportp->messageArgs[i]); - totalArgsLength += argLengths[i]; - } - /* NULL-terminate for easy copying. */ - reportp->messageArgs[i] = NULL; - } - /* - * Parse the error format, substituting the argument X - * for {X} in the format. - */ - if (argCount > 0) { - if (efs->format) { - jschar *buffer, *fmt, *out; - int expandedArgs = 0; - size_t expandedLength; - size_t len = strlen(efs->format); - - buffer = fmt = js_InflateString (cx, efs->format, &len); - if (!buffer) - goto error; - expandedLength = len - - (3 * argCount) /* exclude the {n} */ - + totalArgsLength; - + if (efs) { + size_t totalArgsLength = 0; + size_t argLengths[10]; /* only {0} thru {9} supported */ + argCount = efs->argCount; + JS_ASSERT(argCount <= 10); + if (argCount > 0) { /* - * Note - the above calculation assumes that each argument - * is used once and only once in the expansion !!! - */ - reportp->ucmessage = out = (jschar *) - JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); - if (!out) { - JS_free (cx, buffer); - goto error; - } - while (*fmt) { - if (*fmt == '{') { - if (isdigit(fmt[1])) { - int d = JS7_UNDEC(fmt[1]); - JS_ASSERT(d < argCount); - js_strncpy(out, reportp->messageArgs[d], - argLengths[d]); - out += argLengths[d]; - fmt += 3; - expandedArgs++; - continue; - } + * Gather the arguments into an array, and accumulate + * their sizes. We allocate 1 more than necessary and + * null it out to act as the caboose when we free the + * pointers later. + */ + reportp->messageArgs = (const jschar **) + JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); + if (!reportp->messageArgs) + return JS_FALSE; + reportp->messageArgs[argCount] = NULL; + for (i = 0; i < argCount; i++) { + if (charArgs) { + char *charArg = va_arg(ap, char *); + size_t charArgLength = strlen(charArg); + reportp->messageArgs[i] + = js_InflateString(cx, charArg, &charArgLength); + if (!reportp->messageArgs[i]) + goto error; } - *out++ = *fmt++; + else + reportp->messageArgs[i] = va_arg(ap, jschar *); + argLengths[i] = js_strlen(reportp->messageArgs[i]); + totalArgsLength += argLengths[i]; } - JS_ASSERT(expandedArgs == argCount); - *out = 0; - JS_free (cx, buffer); - *messagep = - js_DeflateString(cx, reportp->ucmessage, - (size_t)(out - reportp->ucmessage)); - if (!*messagep) - goto error; + /* NULL-terminate for easy copying. */ + reportp->messageArgs[i] = NULL; } - } else { /* - * Zero arguments: the format string (if it exists) is the - * entire message. + * Parse the error format, substituting the argument X + * for {X} in the format. */ - if (efs->format) { - size_t len; - *messagep = JS_strdup(cx, efs->format); - if (!*messagep) - goto error; - len = strlen(*messagep); - reportp->ucmessage = js_InflateString(cx, *messagep, &len); - if (!reportp->ucmessage) - goto error; + if (argCount > 0) { + if (efs->format) { + jschar *buffer, *fmt, *out; + const jschar *arg; + int expandedArgs = 0; + size_t expandedLength; + size_t len = strlen (efs->format); + buffer = fmt = js_InflateString (cx, efs->format, &len); + if (!buffer) + goto error; + expandedLength + = len + - (3 * argCount) /* exclude the {n} */ + + totalArgsLength; + /* + * Note - the above calculation assumes that each argument + * is used once and only once in the expansion !!! + */ + reportp->ucmessage = out = (jschar *) + JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); + if (!out) { + JS_free (cx, buffer); + goto error; + } + while (*fmt) { + if (*fmt == '{') { + if (isdigit(fmt[1])) { + int d = JS7_UNDEC(fmt[1]); + JS_ASSERT(d < argCount); + arg = reportp->messageArgs[d]; + js_strncpy(out, arg, argLengths[d]); + out += argLengths[d]; + fmt += 3; + expandedArgs++; + continue; + } + } + *out++ = *fmt++; + } + JS_ASSERT(expandedArgs == argCount); + *out = 0; + JS_free (cx, buffer); + *messagep = + js_DeflateString(cx, reportp->ucmessage, + (size_t)(out - reportp->ucmessage)); + if (!*messagep) + goto error; + } + } else { + /* + * Zero arguments: the format string (if it exists) is the + * entire message. + */ + if (efs->format) { + size_t len; + *messagep = JS_strdup(cx, efs->format); + if (!*messagep) + goto error; + len = strlen(*messagep); + reportp->ucmessage + = js_InflateString(cx, *messagep, &len); + if (!reportp->ucmessage) + goto error; + } } } } @@ -1094,12 +942,9 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, error: if (reportp->messageArgs) { - /* free the arguments only if we allocated them */ - if (charArgs) { - i = 0; - while (reportp->messageArgs[i]) - JS_free(cx, (void *)reportp->messageArgs[i++]); - } + i = 0; + while (reportp->messageArgs[i]) + JS_free(cx, (void *)reportp->messageArgs[i++]); JS_free(cx, (void *)reportp->messageArgs); reportp->messageArgs = NULL; } @@ -1153,15 +998,9 @@ js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, if (message) JS_free(cx, message); if (report.messageArgs) { - /* - * js_ExpandErrorArguments owns its messageArgs only if it had to - * inflate the arguments (from regular |char *|s). - */ - if (charArgs) { - int i = 0; - while (report.messageArgs[i]) - JS_free(cx, (void *)report.messageArgs[i++]); - } + int i = 0; + while (report.messageArgs[i]) + JS_free(cx, (void *)report.messageArgs[i++]); JS_free(cx, (void *)report.messageArgs); } if (report.ucmessage) @@ -1214,8 +1053,13 @@ void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } #endif JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { +#if JS_HAS_DFLT_MSG_STRINGS #define MSG_DEF(name, number, count, exception, format) \ - { format, count, exception } , + { format, count } , +#else +#define MSG_DEF(name, number, count, exception, format) \ + { NULL, count } , +#endif #include "js.msg" #undef MSG_DEF }; diff --git a/lib/javascript/jscntxt.h b/lib/javascript/jscntxt.h index 7ca678e5..49ca4384 100644 --- a/lib/javascript/jscntxt.h +++ b/lib/javascript/jscntxt.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -59,97 +58,7 @@ JS_BEGIN_EXTERN_C -/* - * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a - * given pc in a script. - */ -typedef struct JSGSNCache { - JSScript *script; - JSDHashTable table; -#ifdef JS_GSNMETER - uint32 hits; - uint32 misses; - uint32 fills; - uint32 clears; -# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) -#else -# define GSN_CACHE_METER(cache,cnt) /* nothing */ -#endif -} JSGSNCache; - -#define GSN_CACHE_CLEAR(cache) \ - JS_BEGIN_MACRO \ - (cache)->script = NULL; \ - if ((cache)->table.ops) { \ - JS_DHashTableFinish(&(cache)->table); \ - (cache)->table.ops = NULL; \ - } \ - GSN_CACHE_METER(cache, clears); \ - JS_END_MACRO - -/* These helper macros take a cx as parameter and operate on its GSN cache. */ -#define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx)) -#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) - -#ifdef JS_THREADSAFE - -/* - * Structure uniquely representing a thread. It holds thread-private data - * that can be accessed without a global lock. - */ -struct JSThread { - /* Linked list of all contexts active on this thread. */ - JSCList contextList; - - /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - jsword id; - - /* Thread-local gc free lists array. */ - JSGCThing *gcFreeLists[GC_NUM_FREELISTS]; - - /* - * Thread-local version of JSRuntime.gcMallocBytes to avoid taking - * locks on each JS_malloc. - */ - uint32 gcMallocBytes; - -#if JS_HAS_GENERATORS - /* Flag indicating that the current thread is executing close hooks. */ - JSBool gcRunningCloseHooks; -#endif - - /* - * Store the GSN cache in struct JSThread, not struct JSContext, both to - * save space and to simplify cleanup in js_GC. Any embedding (Firefox - * or another Gecko application) that uses many contexts per thread is - * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler - * among two or more contexts running script in one thread. - */ - JSGSNCache gsnCache; -}; - -#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache) - -extern void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr); - -extern JSBool -js_SetContextThread(JSContext *cx); - -extern void -js_ClearContextThread(JSContext *cx); - -extern JSThread * -js_GetCurrentThread(JSRuntime *rt); - -#endif /* JS_THREADSAFE */ - -typedef enum JSDestroyContextMode { - JSDCM_NO_GC, - JSDCM_MAYBE_GC, - JSDCM_FORCE_GC, - JSDCM_NEW_FAILED -} JSDestroyContextMode; +typedef enum JSGCMode { JS_NO_GC, JS_MAYBE_GC, JS_FORCE_GC } JSGCMode; typedef enum JSRuntimeState { JSRTS_DOWN, @@ -172,11 +81,9 @@ struct JSRuntime { /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ JSRuntimeState state; - /* Context create/destroy callback. */ - JSContextCallback cxCallback; - /* Garbage collector state, used by jsgc.c. */ - JSGCArenaList gcArenaList[GC_NUM_FREELISTS]; + JSArenaPool gcArenaPool[GC_NUM_FREELISTS]; + JSGCThing *gcFreeList[GC_NUM_FREELISTS]; JSDHashTable gcRootsHash; JSDHashTable *gcLocksHash; jsrefcount gcKeepAtoms; @@ -186,23 +93,10 @@ struct JSRuntime { uint32 gcMaxMallocBytes; uint32 gcLevel; uint32 gcNumber; - - /* - * NB: do not pack another flag here by claiming gcPadding unless the new - * flag is written only by the GC thread. Atomic updates to packed bytes - * are not guaranteed, so stores issued by one thread may be lost due to - * unsynchronized read-modify-write cycles on other threads. - */ JSPackedBool gcPoke; JSPackedBool gcRunning; - uint16 gcPadding; - JSGCCallback gcCallback; uint32 gcMallocBytes; - JSGCArena *gcUnscannedArenaStackTop; -#ifdef DEBUG - size_t gcUnscannedBagSize; -#endif /* * API compatibility requires keeping GCX_PRIVATE bytes separate from the @@ -217,17 +111,12 @@ struct JSRuntime { */ uint32 gcPrivateBytes; - /* - * Table for tracking iterators to ensure that we close iterator's state - * before finalizing the iterable object. - */ - JSPtrTable gcIteratorTable; - -#if JS_HAS_GENERATORS - /* Runtime state to support close hooks. */ - JSGCCloseState gcCloseState; +#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 @@ -248,14 +137,6 @@ struct JSRuntime { jsdouble *jsNegativeInfinity; jsdouble *jsPositiveInfinity; -#ifdef JS_THREADSAFE - JSLock *deflatedStringCacheLock; -#endif - JSHashTable *deflatedStringCache; -#ifdef DEBUG - uint32 deflatedStringCacheBytes; -#endif - /* Empty string held for use by this runtime's contexts. */ JSString *emptyString; @@ -301,7 +182,7 @@ struct JSRuntime { PRCondVar *gcDone; PRCondVar *requestDone; uint32 requestCount; - JSThread *gcThread; + jsword gcThread; /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ PRLock *rtLock; @@ -339,13 +220,6 @@ struct JSRuntime { * value. */ #define NO_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef) - - /* - * The index for JSThread info, returned by PR_NewThreadPrivateIndex. - * The value is visible and shared by all threads, but the data is - * private to each thread. - */ - PRUintn threadTPIndex; #endif /* JS_THREADSAFE */ /* @@ -360,16 +234,10 @@ struct JSRuntime { /* Optional hook to find principals for an object in this runtime. */ JSObjectPrincipalsFinder findObjectPrincipals; - /* - * Shared scope property tree, and arena-pool for allocating its nodes. - * The propertyRemovals counter is incremented for every js_ClearScope, - * and for each js_RemoveScopeProperty that frees a slot in an object. - * See js_NativeGet and js_NativeSet in jsobj.c. - */ + /* Shared scope property tree, and allocator for its nodes. */ JSDHashTable propertyTreeHash; JSScopeProperty *propertyFreeList; JSArenaPool propertyArenaPool; - int32 propertyRemovals; /* Script filename table. */ struct JSHashTable *scriptFilenameTable; @@ -399,18 +267,6 @@ struct JSRuntime { */ JSNativeIteratorState *nativeIteratorStates; -#ifndef JS_THREADSAFE - /* - * For thread-unsafe embeddings, the GSN cache lives in the runtime and - * not each context, since we expect it to be filled once when decompiling - * a longer script, then hit repeatedly as js_GetSrcNote is called during - * the decompiler activation that filled it. - */ - JSGSNCache gsnCache; - -#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache) -#endif - #ifdef DEBUG /* Function invocation metering. */ jsrefcount inlineCalls; @@ -526,46 +382,31 @@ typedef void typedef union JSTempValueUnion { jsval value; JSObject *object; - JSString *string; - void *gcthing; JSTempValueMarker marker; - JSScopeProperty *sprop; - JSWeakRoots *weakRoots; jsval *array; } JSTempValueUnion; -/* - * The following allows to reinterpret JSTempValueUnion.object as jsval using - * the tagging property of a generic jsval described below. - */ -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(JSObject *)); - /* * Context-linked stack of temporary GC roots. * - * If count is -1, then u.value contains the single value or GC-thing to root. - * If count is -2, then u.marker holds a mark hook called to mark the values. - * If count is -3, then u.sprop points to the property tree node to mark. - * If count is -4, then u.weakRoots points to saved weak 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_TEMP_ROOT_GCTHING. The macro reinterprets an arbitrary - * GC-thing as jsval. It 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). + * 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). * - * JS_PUSH_TEMP_ROOT_OBJECT and JS_PUSH_TEMP_ROOT_STRING are type-safe - * alternatives to JS_PUSH_TEMP_ROOT_GCTHING for JSObject and JSString. They - * also provide a simple way to get a single pointer to rooted JSObject or - * JSString via JS_PUSH_TEMP_ROOT_(OBJECT|STRTING)(cx, NULL, &tvr). Then - * &tvr.u.object or tvr.u.string gives the necessary pointer, which puns - * tvr.u.value safely because JSObject * and JSString * are GC-things and, as - * such, their tag bits are all zeroes. + * 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 @@ -577,11 +418,6 @@ struct JSTempValueRooter { JSTempValueUnion u; }; -#define JSTVU_SINGLE (-1) -#define JSTVU_MARKER (-2) -#define JSTVU_SPROP (-3) -#define JSTVU_WEAK_ROOTS (-4) - #define JS_PUSH_TEMP_ROOT_COMMON(cx,tvr) \ JS_BEGIN_MACRO \ JS_ASSERT((cx)->tempValueRooters != (tvr)); \ @@ -591,46 +427,31 @@ struct JSTempValueRooter { #define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.value = val; \ 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_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_MARKER(cx,marker_,tvr) \ JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_MARKER; \ - (tvr)->u.marker = (marker_); \ 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 \ - (tvr)->count = JSTVU_SINGLE; \ + JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ + (tvr)->count = -1; \ (tvr)->u.object = (obj); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.string = (str); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_GCTHING(cx,thing,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)thing)); \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.gcthing = (thing); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_POP_TEMP_ROOT(cx,tvr) \ @@ -647,22 +468,7 @@ struct JSTempValueRooter { JS_POP_TEMP_ROOT(cx, &tvr); \ JS_END_MACRO -#define JS_PUSH_TEMP_ROOT_SPROP(cx,sprop_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SPROP; \ - (tvr)->u.sprop = (sprop_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_WEAK_ROOTS; \ - (tvr)->u.weakRoots = (weakRoots_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - struct JSContext { - /* JSRuntime contextList linkage. */ JSCList links; /* Interpreter activation count. */ @@ -689,8 +495,14 @@ struct JSContext { /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; - /* Storage to root recently allocated GC things and script result. */ - JSWeakRoots weakRoots; + /* Most recently created things by type, members of the GC's root set. */ + JSGCThing *newborn[GCX_NTYPES]; + + /* Atom root for the last-looked-up atom on this context. */ + JSAtom *lastAtom; + + /* Root for the result of the most recent js_InternalInvoke call. */ + jsval lastInternalResult; /* Regular expression class statics (XXX not shared globally). */ JSRegExpStatics regExpStatics; @@ -717,15 +529,11 @@ struct JSContext { /* GC and thread-safe state. */ JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ #ifdef JS_THREADSAFE - JSThread *thread; + jsword thread; jsrefcount requestDepth; JSScope *scopeToShare; /* weak reference, see jslock.c */ JSScope *lockedSealedScope; /* weak ref, for low-cost sealed scope locking */ - JSCList threadLinks; /* JSThread contextList linkage */ - -#define CX_FROM_THREAD_LINKS(tl) \ - ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif #if JS_HAS_LVALUE_RETURN @@ -762,8 +570,6 @@ struct JSContext { */ JSPackedBool throwing; /* is there a pending exception? */ jsval exception; /* most-recently-thrown exception */ - /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */ - JSPackedBool insideGCMarkCallback; /* Per-context options. */ uint32 options; /* see jsapi.h for JSOPTION_* */ @@ -787,17 +593,8 @@ struct JSContext { /* Stack of thread-stack-allocated temporary GC roots. */ JSTempValueRooter *tempValueRooters; - -#ifdef GC_MARK_DEBUG - /* Top of the GC mark stack. */ - void *gcCurrentMarkNode; -#endif }; -#ifdef JS_THREADSAFE -# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) -#endif - #ifdef __cplusplus /* FIXME(bug 332648): Move this into a public header. */ class JSAutoTempValueRooter @@ -817,8 +614,8 @@ class JSAutoTempValueRooter } private: - static void *operator new(size_t); - static void operator delete(void *, size_t); + // static void *operator new(size_t) { return 0; } + // static void operator delete(void *, size_t) { } JSContext *mContext; JSTempValueRooter mTvr; @@ -873,6 +670,7 @@ class JSAutoTempValueRooter * and masking off the XML flag and any other high order bits. */ #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 @@ -896,7 +694,7 @@ extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); extern void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); +js_DestroyContext(JSContext *cx, JSGCMode gcmode); /* * Return true if cx points to a context in rt->contextList, else return false. @@ -925,20 +723,12 @@ js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, /* * Local root set management. - * - * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing - * pointers cast to (jsval). This relies on JSObject's tag being zero, but - * on the up side it lets us push int-jsval-encoded scopeMark values on the - * local root stack. */ extern JSBool js_EnterLocalRootScope(JSContext *cx); -#define js_LeaveLocalRootScope(cx) \ - js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) - extern void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); +js_LeaveLocalRootScope(JSContext *cx); extern void js_ForgetLocalRoot(JSContext *cx, jsval v); @@ -981,7 +771,7 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, #endif extern void -js_ReportOutOfMemory(JSContext *cx); +js_ReportOutOfMemory(JSContext *cx, JSErrorCallback errorCallback); /* * Report an exception using a previously composed JSErrorReport. diff --git a/lib/javascript/jscompat.h b/lib/javascript/jscompat.h index 80d86056..6ba036a5 100644 --- a/lib/javascript/jscompat.h +++ b/lib/javascript/jscompat.h @@ -36,7 +36,7 @@ * ***** END LICENSE BLOCK ***** */ /* -*- Mode: C; tab-width: 8 -*- - * Copyright (C) 1996-1999 Netscape Communications Corporation, All Rights Reserved. + * Copyright © 1996-1999 Netscape Communications Corporation, All Rights Reserved. */ #ifndef jscompat_h___ #define jscompat_h___ diff --git a/lib/javascript/jsconfig.h b/lib/javascript/jsconfig.h index d61e8029..5902499f 100644 --- a/lib/javascript/jsconfig.h +++ b/lib/javascript/jsconfig.h @@ -41,7 +41,7 @@ * JS configuration macros. */ #ifndef JS_VERSION -#define JS_VERSION 170 +#define JS_VERSION 160 #endif /* @@ -58,19 +58,15 @@ * <= JSVERSION_1_4 to mean "before the Third Edition of ECMA-262" and version * > JSVERSION_1_4 to mean "at or after the Third Edition". * - * In the (likely?) event that SpiderMonkey grows to implement JavaScript 2.0, - * or ECMA-262 Edition 4 (JS2 without certain extensions), the version number - * to use would be near 200, or greater. + * In the unlikely event that SpiderMonkey ever implements JavaScript 2.0, or + * ECMA-262 Edition 4 (JS2 without certain extensions), the version number to + * use would be near 200, or greater. * * The JS_VERSION_ECMA_3 version is the minimal configuration conforming to * the ECMA-262 Edition 3 specification. Use it for minimal embeddings, where * you're sure you don't need any of the extensions disabled in this version. * In order to facilitate testing, JS_HAS_OBJ_PROTO_PROP is defined as part of * the JS_VERSION_ECMA_3_TEST version. - * - * To keep things sane in the modern age, where we need exceptions in order to - * implement, e.g., iterators and generators, we are dropping support for all - * versions <= 1.4. */ #define JS_VERSION_ECMA_3 148 #define JS_VERSION_ECMA_3_TEST 149 @@ -78,24 +74,59 @@ #if JS_VERSION == JS_VERSION_ECMA_3 || \ JS_VERSION == JS_VERSION_ECMA_3_TEST +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, array.pop, etc */ #define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has fun.apply(obj, argArray) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ #if JS_VERSION == JS_VERSION_ECMA_3_TEST #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #else #define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ #endif +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ #define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ #define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ #define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ #define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 1 /* has error object hierarchy */ #define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 1 /* has Object.prototype query methods */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 1 /* numbers have formatting methods */ #define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ #define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ #define JS_HAS_CONST 0 /* has JS2 const as alternative var */ @@ -104,30 +135,358 @@ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ -#elif JS_VERSION < 150 +#elif JS_VERSION == 100 -#error "unsupported JS_VERSION" +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 1 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ -#elif JS_VERSION == 150 +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 0 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 0 /* has fun.apply(obj, argArray) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#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 + +#define JS_BUG_NULL_INDEX_PROPS 1 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 1 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 1 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 1 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 1 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 1 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 1 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 0 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 0 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 0 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 0 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 0 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 0 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 0 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 0 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 0 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 0 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 0 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 0 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 0 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#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 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 1 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 0 /* has fun.call(obj, arg1, ... argN) */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 0 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 0 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 0 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 0 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 0 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#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 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ #define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 0 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 0 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 0 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ #define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* has error object hierarchy */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#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 + +#define JS_BUG_NULL_INDEX_PROPS 0 /* o[0] defaults to null, not void */ +#define JS_BUG_EMPTY_INDEX_ZERO 0 /* o[""] is equivalent to o[0] */ +#define JS_BUG_EAGER_TOSTRING 0 /* o.toString() trumps o.valueOf() */ +#define JS_BUG_VOID_TOSTRING 0 /* void 0 + 0 == "undefined0" */ +#define JS_BUG_EVAL_THIS_FUN 0 /* eval('this') in function f is f */ +#define JS_BUG_EVAL_THIS_SCOPE 0 /* Math.eval('sin(x)') vs. local x */ +#define JS_BUG_FALLIBLE_EQOPS 0 /* fallible/intransitive equality ops */ +#define JS_BUG_FALLIBLE_TONUM 0 /* fallible ValueToNumber primitive */ +#define JS_BUG_WITH_CLOSURE 1 /* with(o)function f(){} sets o.f */ + +#define JS_HAS_PROP_DELETE 1 /* delete o.p removes p from o */ +#define JS_HAS_CALL_OBJECT 1 /* fun.caller is stack frame obj */ +#define JS_HAS_LABEL_STATEMENT 1 /* has break/continue to label: */ +#define JS_HAS_DO_WHILE_LOOP 1 /* has do {...} while (b) */ +#define JS_HAS_SWITCH_STATEMENT 1 /* has switch (v) {case c: ...} */ +#define JS_HAS_SOME_PERL_FUN 1 /* has array.join/reverse/sort */ +#define JS_HAS_MORE_PERL_FUN 1 /* has array.push, str.substr, etc */ +#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ +#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ +#define JS_HAS_VALUEOF_HINT 1 /* valueOf(hint) where hint is typeof */ +#define JS_HAS_LEXICAL_CLOSURE 1 /* nested functions, lexically closed */ +#define JS_HAS_APPLY_FUNCTION 1 /* has apply(fun, arg1, ... argN) */ +#define JS_HAS_CALL_FUNCTION 1 /* has fun.call(obj, arg1, ... argN) */ +#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ +#define JS_HAS_REGEXPS 1 /* has perl r.e.s via RegExp, /pat/ */ +#define JS_HAS_SEQUENCE_OPS 1 /* has array.slice, string.concat */ +#define JS_HAS_INITIALIZERS 1 /* has var o = {'foo': 42, 'bar':3} */ +#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ +#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ +#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ +#define JS_HAS_TRIPLE_EQOPS 1 /* has === and !== identity eqops */ +#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ +#define JS_HAS_REPLACE_LAMBDA 1 /* has string.replace(re, lambda) */ +#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ +#define JS_HAS_XDR 1 /* has XDR API and internal support */ +#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ +#define JS_HAS_EXCEPTIONS 1 /* has exception handling */ +#define JS_HAS_UNDEFINED 1 /* has global "undefined" property */ +#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ +#define JS_HAS_IN_OPERATOR 1 /* has in operator ('p' in {p:1}) */ +#define JS_HAS_INSTANCEOF 1 /* has {p:1} instanceof Object */ +#define JS_HAS_ARGS_OBJECT 1 /* has minimal ECMA arguments object */ +#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ +#define JS_HAS_ERROR_EXCEPTIONS 0 /* rt errors reflected as exceptions */ +#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ +#define JS_HAS_NEW_OBJ_METHODS 0 /* has Object.prototype query methods */ +#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ +#define JS_HAS_DFLT_MSG_STRINGS 1 /* provides English error messages */ +#define JS_HAS_NUMBER_FORMATS 0 /* numbers have formatting methods */ +#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ +#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ +#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ +#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ +#define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ +#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#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 + +#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 */ @@ -136,26 +495,58 @@ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ #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 */ @@ -164,45 +555,9 @@ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION == 170 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_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_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_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 */ -#define JS_HAS_GENERATORS 1 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ #else #error "unknown JS_VERSION" #endif - -/* Features that are present in all versions. */ -#define JS_HAS_RESERVED_JAVA_KEYWORDS 1 -#define JS_HAS_RESERVED_ECMA_KEYWORDS 1 - diff --git a/lib/javascript/jscpucfg.c b/lib/javascript/jscpucfg.c index daa91218..e02f3361 100644 --- a/lib/javascript/jscpucfg.c +++ b/lib/javascript/jscpucfg.c @@ -79,11 +79,6 @@ #define NS_NEVER_INLINE #endif -#ifdef __SUNPRO_C -static int StackGrowthDirection(int *dummy1addr); -#pragma no_inline(StackGrowthDirection) -#endif - typedef void *prword; struct align_short { diff --git a/lib/javascript/jscpucfg.h b/lib/javascript/jscpucfg.h index 63ef932a..7b8b2add 100644 --- a/lib/javascript/jscpucfg.h +++ b/lib/javascript/jscpucfg.h @@ -44,62 +44,11 @@ #if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) -#if defined(_WIN64) - -#if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define JS_BYTES_PER_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 8L -#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 64L - -#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 6L - -#define JS_ALIGN_OF_SHORT 2L -#define JS_ALIGN_OF_INT 4L -#define JS_ALIGN_OF_LONG 4L -#define JS_ALIGN_OF_INT64 8L -#define JS_ALIGN_OF_FLOAT 4L -#define JS_ALIGN_OF_DOUBLE 8L -#define JS_ALIGN_OF_POINTER 8L -#define JS_ALIGN_OF_WORD 8L - -#define JS_BYTES_PER_WORD_LOG2 3L -#define JS_BYTES_PER_DWORD_LOG2 3L -#define PR_WORDS_PER_DWORD_LOG2 0L -#else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ -#error "CPU type is unknown" -#endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ - -#elif defined(_WIN32) || defined(XP_OS2) || defined(WINCE) - #ifdef __WATCOMC__ #define HAVE_VA_LIST_AS_ARRAY #endif +#if defined(_WIN32) || defined(XP_OS2) || defined(WINCE) #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN @@ -146,7 +95,6 @@ #endif /* _WIN32 || XP_OS2 || WINCE*/ #if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ - #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN @@ -190,7 +138,6 @@ #define JS_BYTES_PER_WORD_LOG2 2L #define JS_BYTES_PER_DWORD_LOG2 3L #define PR_WORDS_PER_DWORD_LOG2 1L - #endif /* defined(_WINDOWS) && !defined(_WIN32) */ #elif defined(XP_UNIX) || defined(XP_BEOS) diff --git a/lib/javascript/jsdate.c b/lib/javascript/jsdate.c index 9e6697f0..a76502c3 100644 --- a/lib/javascript/jsdate.c +++ b/lib/javascript/jsdate.c @@ -337,8 +337,8 @@ MakeDay(jsdouble year, jsdouble month, jsdouble date) #define MakeDate(day, time) ((day) * msPerDay + (time)) -/* - * Years and leap years on which Jan 1 is a Sunday, Monday, etc. +/* + * 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. @@ -359,15 +359,14 @@ static jsint yearStartingWith[2][7] = { * incorrect year for times near year boundaries. */ static jsint -EquivalentYearForDST(jsint year) -{ +EquivalentYearForDST(jsint year) { jsint day; JSBool isLeapYear; day = (jsint) DayFromYear(year) + 4; day = day % 7; if (day < 0) - day += 7; + day += 7; isLeapYear = (DaysInYear(year) == 366); @@ -473,9 +472,9 @@ msFromTime(jsdouble t) * Other Support routines and definitions */ -JSClass js_DateClass = { +static JSClass date_class = { js_Date_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), + JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS @@ -779,7 +778,7 @@ date_parseString(JSString *str, jsdouble *result) /* 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. + 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. @@ -787,7 +786,7 @@ date_parseString(JSString *str, jsdouble *result) 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 + 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 @@ -819,7 +818,7 @@ date_parseString(JSString *str, jsdouble *result) year += 1900; } } else if (mon < 100) { /* (b) year/month/day */ - if (mday < 70) { + if (mday < 70) { temp = year; year = mon + 1900; mon = mday; @@ -902,7 +901,7 @@ date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static jsdouble * date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) { - if (!JS_InstanceOf(cx, obj, &js_DateClass, argv)) + if (!JS_InstanceOf(cx, obj, &date_class, argv)) return NULL; return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); } @@ -925,6 +924,7 @@ date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date; jsdouble result; + JSVersion version; date = date_getProlog(cx, obj, argv); if (!date) @@ -936,8 +936,25 @@ date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) result = YearFromTime(LocalTime(result)); - /* Follow ECMA-262 to the letter, contrary to IE JScript. */ - result -= 1900; + /* + * During the great date rewrite of 1.3, we tried to track the evolving ECMA + * standard, which then had a definition of getYear which always subtracted + * 1900. Which we implemented, not realizing that it was incompatible with + * the old behavior... now, rather than thrash the behavior yet again, + * we've decided to leave it with the - 1900 behavior and point people to + * the getFullYear method. But we try to protect existing scripts that + * have specified a version... + */ + 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; + } else { + result -= 1900; + } return js_NewNumberValue(cx, result, rval); } @@ -1396,7 +1413,7 @@ date_makeDate(JSContext *cx, JSObject *obj, uintN argc, /* return NaN if date is NaN and we're not setting the year, * If we are, use 0 as the time. */ if (!(JSDOUBLE_IS_FINITE(result))) { - if (maxargs < 3) + if (argc < 3) return js_NewNumberValue(cx, result, rval); else lorutime = +0.; @@ -1751,14 +1768,8 @@ date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, return date_format(cx, *date, FORMATSPEC_FULL, rval); /* Hacked check against undesired 2-digit year 00/00/00 form. */ - if (strcmp(format, "%x") == 0 && result_len >= 6 && - /* Format %x means use OS settings, which may have 2-digit yr, so - hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ - !isdigit(buf[result_len - 3]) && - isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && - /* ...but not if starts with 4-digit year, like 2022/3/11. */ - !(isdigit(buf[0]) && isdigit(buf[1]) && - isdigit(buf[2]) && isdigit(buf[3]))) { + if (buf[result_len - 3] == '/' && + isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) { JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), "%d", js_DateGetYear(cx, obj)); } @@ -1875,7 +1886,7 @@ date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; } - bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); + bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr); if (!bytes) { JS_ReportOutOfMemory(cx); return JS_FALSE; @@ -1901,6 +1912,7 @@ date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 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) @@ -1922,11 +1934,14 @@ date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!str) return JS_FALSE; str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - if (js_EqualStrings(str, str2)) + if (!js_CompareStrings(str, str2)) return date_getTime(cx, obj, argc, argv, rval); } return date_toString(cx, obj, argc, argv, rval); } +#else +#define date_valueOf date_getTime +#endif /* @@ -2045,7 +2060,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!JSVAL_IS_STRING(argv[0])) { /* the argument is a millisecond number */ if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; + return JS_FALSE; date = date_constructor(cx, obj); if (!date) return JS_FALSE; @@ -2119,7 +2134,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj) /* set static LocalTZA */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS, + proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS, NULL, date_methods, NULL, date_static_methods); if (!proto) return NULL; @@ -2143,7 +2158,7 @@ js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) JSObject *obj; jsdouble *date; - obj = js_NewObject(cx, &js_DateClass, NULL, NULL); + obj = js_NewObject(cx, &date_class, NULL, NULL); if (!obj) return NULL; diff --git a/lib/javascript/jsdate.h b/lib/javascript/jsdate.h index 88bd5f58..343314fa 100644 --- a/lib/javascript/jsdate.h +++ b/lib/javascript/jsdate.h @@ -46,8 +46,6 @@ JS_BEGIN_EXTERN_C -extern JSClass js_DateClass; - extern JSObject * js_InitDateClass(JSContext *cx, JSObject *obj); diff --git a/lib/javascript/jsdbgapi.c b/lib/javascript/jsdbgapi.c index 8fa0e684..cddbd480 100644 --- a/lib/javascript/jsdbgapi.c +++ b/lib/javascript/jsdbgapi.c @@ -1,5 +1,4 @@ /* -*- 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 @@ -247,24 +246,17 @@ typedef struct JSWatchPoint { JSPropertyOp setter; JSWatchPointHandler handler; void *closure; - uintN flags; + jsrefcount nrefs; } JSWatchPoint; -#define JSWP_LIVE 0x1 /* live because set and not cleared */ -#define JSWP_HELD 0x2 /* held while running handler/setter */ +#define HoldWatchPoint(wp) ((wp)->nrefs++) static JSBool -DropWatchPoint(JSContext *cx, JSWatchPoint *wp, uintN flag) +DropWatchPoint(JSContext *cx, JSWatchPoint *wp) { - JSBool ok; JSScopeProperty *sprop; - JSObject *pobj; - JSProperty *prop; - JSPropertyOp setter; - ok = JS_TRUE; - wp->flags &= ~flag; - if (wp->flags != 0) + if (--wp->nrefs != 0) return JS_TRUE; /* @@ -273,38 +265,16 @@ DropWatchPoint(JSContext *cx, JSWatchPoint *wp, uintN flag) */ JS_REMOVE_LINK(&wp->links); sprop = wp->sprop; - - /* - * If js_ChangeNativePropertyAttrs fails, propagate failure after removing - * wp->closure's root and freeing wp. - */ - setter = js_GetWatchedSetter(cx->runtime, NULL, sprop); - if (!setter) { - ok = js_LookupProperty(cx, wp->object, sprop->id, &pobj, &prop); - - /* - * If the property wasn't found on wp->object or didn't exist, then - * someone else has dealt with this sprop, and we don't need to change - * the property attributes. - */ - if (ok && prop) { - if (pobj == wp->object) { - JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj); - - sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(pobj), sprop, - 0, sprop->attrs, - sprop->getter, - wp->setter); - if (!sprop) - ok = JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } + if (!js_GetWatchedSetter(cx->runtime, NULL, sprop)) { + sprop = js_ChangeNativePropertyAttrs(cx, wp->object, sprop, + 0, sprop->attrs, + sprop->getter, wp->setter); + if (!sprop) + return JS_FALSE; } - js_RemoveRoot(cx->runtime, &wp->closure); JS_free(cx, wp); - return ok; + return JS_TRUE; } void @@ -317,7 +287,7 @@ js_MarkWatchPoints(JSContext *cx) for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { - MARK_SCOPE_PROPERTY(cx, wp->sprop); + MARK_SCOPE_PROPERTY(wp->sprop); if (wp->sprop->attrs & JSPROP_SETTER) JS_MarkGCThing(cx, wp->setter, "wp->setter", NULL); } @@ -378,10 +348,7 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { sprop = wp->sprop; - if (wp->object == obj && SPROP_USERID(sprop) == id && - !(wp->flags & JSWP_HELD)) { - wp->flags |= JSWP_HELD; - + if (wp->object == obj && SPROP_USERID(sprop) == id) { JS_LOCK_OBJ(cx, obj); propid = ID_TO_VALUE(sprop->id); userid = (sprop->flags & SPROP_HAS_SHORTID) @@ -389,20 +356,17 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) : propid; scope = OBJ_SCOPE(obj); JS_UNLOCK_OBJ(cx, obj); - - /* NB: wp is held, so we can safely dereference it still. */ + HoldWatchPoint(wp); ok = wp->handler(cx, obj, propid, SPROP_HAS_VALID_SLOT(sprop, scope) - ? OBJ_GET_SLOT(cx, obj, sprop->slot) + ? OBJ_GET_SLOT(cx, obj, wp->sprop->slot) : JSVAL_VOID, vp, wp->closure); if (ok) { /* - * Create a pseudo-frame for the setter invocation so that any - * stack-walking security code under the setter will correctly - * identify the guilty party. So that the watcher appears to - * be active to obj_eval and other such code, point frame.pc - * at the JSOP_STOP at the end of the script. + * Create pseudo-frame for call to setter so that any + * stack-walking security code in the setter will correctly + * identify the guilty party. */ JSObject *closure; JSClass *clasp; @@ -430,15 +394,15 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (fun) { nslots += fun->nargs; if (FUN_NATIVE(fun)) - nslots += fun->u.n.extra; + nslots += fun->extra; } - if (nslots <= JS_ARRAY_LENGTH(smallv)) { + if (nslots <= sizeof (smallv) / sizeof (smallv[0])) { argv = smallv; } else { argv = JS_malloc(cx, nslots * sizeof(jsval)); if (!argv) { - DropWatchPoint(cx, wp, JSWP_HELD); + DropWatchPoint(cx, wp); return JS_FALSE; } } @@ -449,11 +413,8 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) memset(&frame, 0, sizeof(frame)); frame.script = script; - if (script) { - JS_ASSERT(script->length >= JSOP_STOP_LENGTH); - frame.pc = script->code + script->length - - JSOP_STOP_LENGTH; - } + if (script) + frame.pc = script->code; frame.fun = fun; frame.argv = argv + 2; frame.down = cx->fp; @@ -469,10 +430,11 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (argv != smallv) JS_free(cx, argv); } - return DropWatchPoint(cx, wp, JSWP_HELD) && ok; + return DropWatchPoint(cx, wp); } } - return JS_TRUE; + JS_ASSERT(0); /* XXX can't happen */ + return JS_FALSE; } JSBool JS_DLL_CALLBACK @@ -627,26 +589,21 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, wp->object = obj; JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); wp->setter = sprop->setter; - wp->flags = JSWP_LIVE; + wp->nrefs = 1; /* XXXbe nest in obj lock here */ sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, sprop->getter, watcher); if (!sprop) { - /* Self-link so DropWatchPoint can JS_REMOVE_LINK it. */ + /* Self-link wp->links so DropWatchPoint can JS_REMOVE_LINK it. */ JS_INIT_CLIST(&wp->links); - DropWatchPoint(cx, wp, JSWP_LIVE); + DropWatchPoint(cx, wp); ok = JS_FALSE; goto out; } wp->sprop = sprop; - /* - * Now that wp is fully initialized, append it to rt's wp list. - * Because obj is locked we know that no other thread could have added - * a watchpoint for (obj, propid). - */ - JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid)); + /* Now that wp is fully initialized, append it to rt's wp list. */ JS_APPEND_LINK(&wp->links, &rt->watchPointList); } wp->handler = handler; @@ -673,7 +630,7 @@ JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, *handlerp = wp->handler; if (closurep) *closurep = wp->closure; - return DropWatchPoint(cx, wp, JSWP_LIVE); + return DropWatchPoint(cx, wp); } } if (handlerp) @@ -694,7 +651,7 @@ JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) wp != (JSWatchPoint *)&rt->watchPointList; wp = next) { next = (JSWatchPoint *)wp->links.next; - if (wp->object == obj && !DropWatchPoint(cx, wp, JSWP_LIVE)) + if (wp->object == obj && !DropWatchPoint(cx, wp)) return JS_FALSE; } return JS_TRUE; @@ -711,7 +668,7 @@ JS_ClearAllWatchPoints(JSContext *cx) wp != (JSWatchPoint *)&rt->watchPointList; wp = next) { next = (JSWatchPoint *)wp->links.next; - if (!DropWatchPoint(cx, wp, JSWP_LIVE)) + if (!DropWatchPoint(cx, wp)) return JS_FALSE; } return JS_TRUE; @@ -880,7 +837,7 @@ JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp) { /* Force creation of argument and call objects if not yet created */ (void) JS_GetFrameCallObject(cx, fp); - return js_GetScopeChain(cx, fp); + return fp->scopeChain; } JS_PUBLIC_API(JSObject *) @@ -888,15 +845,19 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) { if (! fp->fun) return NULL; - +#if JS_HAS_ARGS_OBJECT /* Force creation of argument object if not yet created */ (void) js_GetArgsObject(cx, fp); - +#endif +#if JS_HAS_CALL_OBJECT /* * XXX ill-defined: null return here means error was reported, unlike a * null returned above or in the #else */ return js_GetCallObject(cx, fp, NULL); +#else + return NULL; +#endif /* JS_HAS_CALL_OBJECT */ } @@ -995,19 +956,14 @@ JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, JS_PUBLIC_API(JSBool) JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, + const jschar *bytes, uintN length, const char *filename, uintN lineno, jsval *rval) { - JSObject *scobj; uint32 flags, options; JSScript *script; JSBool ok; - scobj = JS_GetFrameScopeChain(cx, fp); - if (!scobj) - return JS_FALSE; - /* * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL * flags to the code generator (see js_EmitTree's TOK_SEMI case). @@ -1016,34 +972,33 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; options = cx->options; cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, scobj, + script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain, JS_StackFramePrincipals(cx, fp), - chars, length, filename, lineno); + bytes, length, filename, lineno); fp->flags = flags; cx->options = options; if (!script) return JS_FALSE; - ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, - rval); + ok = js_Execute(cx, fp->scopeChain, script, fp, + JSFRAME_DEBUGGER | JSFRAME_EVAL, rval); js_DestroyScript(cx, script); return ok; } JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, + const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { + size_t length = nbytes; jschar *chars; JSBool ok; - size_t len = length; - chars = js_InflateString(cx, bytes, &len); + chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; - length = (uintN) len; ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, rval); JS_free(cx, chars); @@ -1124,10 +1079,12 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) +#if JS_HAS_CALL_OBJECT | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) +#endif /* JS_HAS_CALL_OBJECT */ | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); - +#if JS_HAS_CALL_OBJECT /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && getter == js_CallClass.getProperty) { @@ -1142,7 +1099,7 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, ? JSPD_ARGUMENT : JSPD_VARIABLE; } - +#endif /* JS_HAS_CALL_OBJECT */ pd->spare = 0; pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) ? sprop->shortid @@ -1331,15 +1288,24 @@ GetAtomTotalSize(JSContext *cx, JSAtom *atom) JS_PUBLIC_API(size_t) JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) { - size_t nbytes; + size_t nbytes, obytes; + JSObject *obj; + JSAtom *atom; nbytes = sizeof *fun; - if (fun->object) - nbytes += JS_GetObjectTotalSize(cx, fun->object); - if (FUN_INTERPRETED(fun)) - nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); - if (fun->atom) - nbytes += GetAtomTotalSize(cx, fun->atom); + JS_ASSERT(fun->nrefs); + obj = fun->object; + if (obj) { + obytes = JS_GetObjectTotalSize(cx, obj); + if (fun->nrefs > 1) + obytes = JS_HOWMANY(obytes, fun->nrefs); + nbytes += obytes; + } + if (fun->interpreted) + nbytes += JS_GetScriptTotalSize(cx, fun->u.script); + atom = fun->atom; + if (atom) + nbytes += GetAtomTotalSize(cx, atom); return nbytes; } @@ -1412,7 +1378,7 @@ JS_GetScriptFilenameFlags(JSScript *script) JS_ASSERT(script); if (!script->filename) return JSFILENAME_NULL; - return js_GetScriptFilenameFlags(script->filename); + return js_GetScriptFilenameFlags(script->filename); } JS_PUBLIC_API(JSBool) @@ -1433,7 +1399,7 @@ JS_PUBLIC_API(void) JS_FlagSystemObject(JSContext *cx, JSObject *obj) { uint8 *flagp; - + flagp = js_GetGCThingFlags(obj); *flagp |= GCF_SYSTEM; } diff --git a/lib/javascript/jsdbgapi.h b/lib/javascript/jsdbgapi.h index d2e1f1c6..d1b13d9d 100644 --- a/lib/javascript/jsdbgapi.h +++ b/lib/javascript/jsdbgapi.h @@ -266,7 +266,7 @@ JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, extern JS_PUBLIC_API(JSBool) JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, + const jschar *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); @@ -359,8 +359,8 @@ 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. + * 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); diff --git a/lib/javascript/jsdhash.c b/lib/javascript/jsdhash.c index 295883b2..cd300668 100644 --- a/lib/javascript/jsdhash.c +++ b/lib/javascript/jsdhash.c @@ -56,32 +56,6 @@ # define METER(x) /* nothing */ #endif -/* - * The following DEBUG-only code is used to assert that calls to one of - * table->ops or to an enumerator do not cause re-entry into a call that - * can mutate the table. The recursion level is stored in additional - * space allocated at the end of the entry store to avoid changing - * JSDHashTable, which could cause issues when mixing DEBUG and - * non-DEBUG components. - */ -#ifdef DEBUG - -#define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ - JS_DHASH_TABLE_SIZE(table_) * \ - table_->entrySize)) - -#define ENTRY_STORE_EXTRA sizeof(uint32) -#define INCREMENT_RECURSION_LEVEL(table_) (++RECURSION_LEVEL(table_)) -#define DECREMENT_RECURSION_LEVEL(table_) (--RECURSION_LEVEL(table_)) - -#else - -#define ENTRY_STORE_EXTRA 0 -#define INCREMENT_RECURSION_LEVEL(table_) ((void)1) -#define DECREMENT_RECURSION_LEVEL(table_) ((void)0) - -#endif /* defined(DEBUG) */ - JS_PUBLIC_API(void *) JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) { @@ -247,16 +221,11 @@ JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, table->generation = 0; nbytes = capacity * entrySize; - table->entryStore = ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); + table->entryStore = ops->allocTable(table, nbytes); if (!table->entryStore) return JS_FALSE; memset(table->entryStore, 0, nbytes); METER(memset(&table->stats, 0, sizeof table->stats)); - -#ifdef DEBUG - RECURSION_LEVEL(table) = 0; -#endif - return JS_TRUE; } @@ -361,8 +330,6 @@ JS_DHashTableFinish(JSDHashTable *table) } #endif - INCREMENT_RECURSION_LEVEL(table); - /* Call finalize before clearing entries, so it can enumerate them. */ table->ops->finalize(table); @@ -379,9 +346,6 @@ JS_DHashTableFinish(JSDHashTable *table) entryAddr += entrySize; } - DECREMENT_RECURSION_LEVEL(table); - JS_ASSERT(RECURSION_LEVEL(table) == 0); - /* Free entry storage last. */ table->ops->freeTable(table, table->entryStore); } @@ -471,9 +435,6 @@ ChangeTable(JSDHashTable *table, int deltaLog2) JSDHashEntryHdr *oldEntry, *newEntry; JSDHashGetKey getKey; JSDHashMoveEntry moveEntry; -#ifdef DEBUG - uint32 recursionLevel; -#endif /* Look, but don't touch, until we succeed in getting new entry store. */ oldLog2 = JS_DHASH_BITS - table->hashShift; @@ -485,14 +446,11 @@ ChangeTable(JSDHashTable *table, int deltaLog2) entrySize = table->entrySize; nbytes = newCapacity * entrySize; - newEntryStore = table->ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); + newEntryStore = table->ops->allocTable(table, nbytes); if (!newEntryStore) return JS_FALSE; /* We can't fail from here on, so update table parameters. */ -#ifdef DEBUG - recursionLevel = RECURSION_LEVEL(table); -#endif table->hashShift = JS_DHASH_BITS - newLog2; table->removedCount = 0; table->generation++; @@ -503,9 +461,6 @@ ChangeTable(JSDHashTable *table, int deltaLog2) table->entryStore = newEntryStore; getKey = table->ops->getKey; moveEntry = table->ops->moveEntry; -#ifdef DEBUG - RECURSION_LEVEL(table) = recursionLevel; -#endif /* Copy only live entries, leaving removed ones behind. */ for (i = 0; i < oldCapacity; i++) { @@ -533,9 +488,6 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) uint32 size; int deltaLog2; - JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); - INCREMENT_RECURSION_LEVEL(table); - keyHash = table->ops->hashKey(table, key); keyHash *= JS_DHASH_GOLDEN_RATIO; @@ -573,8 +525,7 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) if (!ChangeTable(table, deltaLog2) && table->entryCount + table->removedCount == size - 1) { METER(table->stats.addFailures++); - entry = NULL; - break; + return NULL; } } @@ -595,8 +546,7 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) !table->ops->initEntry(table, entry, key)) { /* We haven't claimed entry yet; fail with null return. */ memset(entry + 1, 0, table->entrySize - sizeof *entry); - entry = NULL; - break; + return NULL; } entry->keyHash = keyHash; table->entryCount++; @@ -628,8 +578,6 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) entry = NULL; } - DECREMENT_RECURSION_LEVEL(table); - return entry; } @@ -660,8 +608,6 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) JSDHashEntryHdr *entry; JSDHashOperator op; - INCREMENT_RECURSION_LEVEL(table); - entryAddr = table->entryStore; entrySize = table->entrySize; capacity = JS_DHASH_TABLE_SIZE(table); @@ -683,8 +629,6 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) entryAddr += entrySize; } - JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1); - /* * Shrink or compress if a quarter or more of all entries are removed, or * if the table is underloaded according to the configured minimum alpha, @@ -707,9 +651,6 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) (void) ChangeTable(table, ceiling); } - - DECREMENT_RECURSION_LEVEL(table); - return i; } diff --git a/lib/javascript/jsdhash.h b/lib/javascript/jsdhash.h index 76867e52..6beecadd 100644 --- a/lib/javascript/jsdhash.h +++ b/lib/javascript/jsdhash.h @@ -47,8 +47,6 @@ JS_BEGIN_EXTERN_C #if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) #define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) -#elif defined(XP_WIN) -#define JS_DHASH_FASTCALL __fastcall #else #define JS_DHASH_FASTCALL #endif diff --git a/lib/javascript/jsdtoa.c b/lib/javascript/jsdtoa.c index 5b0b09ff..ff6731bf 100644 --- a/lib/javascript/jsdtoa.c +++ b/lib/javascript/jsdtoa.c @@ -1360,16 +1360,16 @@ dig_done: case 'i': case 'I': if (match(&s,"nfinity")) { - set_word0(rv, 0x7ff00000); - set_word1(rv, 0); + word0(rv) = 0x7ff00000; + word1(rv) = 0; goto ret; } break; case 'n': case 'N': if (match(&s, "an")) { - set_word0(rv, NAN_WORD0); - set_word1(rv, NAN_WORD1); + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; goto ret; } } @@ -1439,8 +1439,8 @@ dig_done: rv = HUGE_VAL; #else /* Can't trust HUGE_VAL */ - set_word0(rv, Exp_mask); - set_word1(rv, 0); + word0(rv) = Exp_mask; + word1(rv) = 0; #endif if (bd0) goto retfree; @@ -1826,7 +1826,6 @@ dig_done: } #ifdef Avoid_Underflow if (scale) { - rv0 = 0.; set_word0(rv0, Exp_1 - P*Exp_msk1); set_word1(rv0, 0); if ((word0(rv) & Exp_mask) <= P*Exp_msk1 @@ -1865,7 +1864,6 @@ nomem: Bfree(bs); Bfree(bd0); Bfree(delta); - RELEASE_DTOA_LOCK(); *err = JS_DTOA_ENOMEM; return 0; } @@ -2972,8 +2970,6 @@ JS_dtobasestr(int base, double d) if (!b) { nomem1: Bfree(b); - RELEASE_DTOA_LOCK(); - free(buffer); return NULL; } do { @@ -3008,8 +3004,6 @@ JS_dtobasestr(int base, double d) if (mlo != mhi) Bfree(mlo); Bfree(mhi); - RELEASE_DTOA_LOCK(); - free(buffer); return NULL; } JS_ASSERT(e < 0); diff --git a/lib/javascript/jsemit.c b/lib/javascript/jsemit.c index f8a06beb..c7e32f20 100644 --- a/lib/javascript/jsemit.c +++ b/lib/javascript/jsemit.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -233,20 +233,18 @@ js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ const char js_with_statement_str[] = "with statement"; -const char js_finally_block_str[] = "finally block"; const char js_script_str[] = "script"; static const char *statementName[] = { + "block", /* BLOCK */ "label statement", /* LABEL */ "if statement", /* IF */ "else statement", /* ELSE */ "switch statement", /* SWITCH */ - "block", /* BLOCK */ js_with_statement_str, /* WITH */ + "try statement", /* TRY */ "catch block", /* CATCH */ - "try block", /* TRY */ - js_finally_block_str, /* FINALLY */ - js_finally_block_str, /* SUBROUTINE */ + "finally statement", /* FINALLY */ "do loop", /* DO_LOOP */ "for loop", /* FOR_LOOP */ "for/in loop", /* FOR_IN_LOOP */ @@ -561,6 +559,7 @@ BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) return JS_FALSE; break; +#if JS_HAS_SWITCH_STATEMENT case JOF_TABLESWITCH: { jsbytecode *pc2; @@ -608,6 +607,7 @@ BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) len = 1 + pc2 - pc; break; } +#endif /* JS_HAS_SWITCH_STATEMENT */ } JS_ASSERT(len > 0); @@ -805,9 +805,7 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; - default: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; + default: JS_ASSERT(0); } *pc = (jsbytecode) op; @@ -1197,41 +1195,27 @@ js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, } JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type) +js_InWithStatement(JSTreeContext *tc) { JSStmtInfo *stmt; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == type) + if (stmt->type == STMT_WITH) return JS_TRUE; } return JS_FALSE; } JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp) +js_InCatchBlock(JSTreeContext *tc, JSAtom *atom) { JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - *loopyp = JS_FALSE; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_WITH) - return JS_FALSE; - if (STMT_IS_LOOP(stmt)) { - *loopyp = JS_TRUE; - continue; - } - if (stmt->flags & SIF_SCOPE) { - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom))) - return JS_FALSE; - } + if (stmt->type == STMT_CATCH && stmt->label == atom) + return JS_TRUE; } - return JS_TRUE; + return JS_FALSE; } void @@ -1239,33 +1223,10 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top) { stmt->type = type; - stmt->flags = 0; SET_STATEMENT_TOP(stmt, top); - stmt->atom = NULL; + stmt->label = NULL; stmt->down = tc->topStmt; tc->topStmt = stmt; - if (STMT_LINKS_SCOPE(stmt)) { - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - } else { - stmt->downScope = NULL; - } -} - -void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top) -{ - JSObject *blockObj; - - js_PushStatement(tc, stmt, STMT_BLOCK, top); - stmt->flags |= SIF_SCOPE; - blockObj = ATOM_TO_OBJECT(blockAtom); - blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - tc->blockChain = blockObj; - stmt->atom = blockAtom; } /* @@ -1284,18 +1245,6 @@ EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) return EmitJump(cx, cg, op, delta); } -/* - * Macro to emit a bytecode followed by a uint16 immediate operand stored in - * big-endian order, used for arg and var numbers as well as for atomIndexes. - * NB: We use cx and cg from our caller's lexical environment, and return - * false on error. - */ -#define EMIT_UINT16_IMM_OP(op, i) \ - JS_BEGIN_MACRO \ - if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ - return JS_FALSE; \ - JS_END_MACRO - /* Emit additional bytecode(s) for non-local jumps. */ static JSBool EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, @@ -1324,9 +1273,7 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, JS_ASSERT(*returnop == JSOP_RETURN); for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { - if (stmt->type == STMT_FINALLY || - ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) && - STMT_MAYBE_SCOPE(stmt))) { + if (stmt->type == STMT_FINALLY) { if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) return JS_FALSE; *returnop = JSOP_RETRVAL; @@ -1355,12 +1302,13 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, case STMT_FINALLY: if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmt->gosub); if (jmp < 0) return JS_FALSE; break; case STMT_WITH: + case STMT_CATCH: /* There's a With object on the stack that we need to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; @@ -1371,10 +1319,9 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, case STMT_FOR_IN_LOOP: /* * The iterator and the object being iterated need to be popped. + * JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) + if (js_Emit1(cx, cg, JSOP_POP2) < 0) return JS_FALSE; break; @@ -1391,16 +1338,6 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, default:; } - - if (stmt->flags & SIF_SCOPE) { - uintN i; - - /* There is a Block object with locals on the stack to pop. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom)); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); - } } cg->stackDepth = depth; @@ -1416,14 +1353,18 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) return -1; - if (label) - index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); - else if (noteType != SRC_NULL) + if (label) { index = js_NewSrcNote(cx, cg, noteType); - else - index = 0; - if (index < 0) - return -1; + if (index < 0) + return -1; + if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0, + (ptrdiff_t) ALE_INDEX(label))) { + return -1; + } + } else if (noteType != SRC_NULL) { + if (js_NewSrcNote(cx, cg, noteType) < 0) + return -1; + } return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); } @@ -1456,18 +1397,7 @@ BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, void js_PopStatement(JSTreeContext *tc) { - JSStmtInfo *stmt; - JSObject *blockObj; - - stmt = tc->topStmt; - tc->topStmt = stmt->down; - if (STMT_LINKS_SCOPE(stmt)) { - tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) { - blockObj = ATOM_TO_OBJECT(stmt->atom); - tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]); - } - } + tc->topStmt = tc->topStmt->down; } JSBool @@ -1476,10 +1406,9 @@ js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) JSStmtInfo *stmt; stmt = cg->treeContext.topStmt; - if (!STMT_IS_TRYING(stmt) && - (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || - !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), - JSOP_GOTO))) { + if (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || + !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), + JSOP_GOTO)) { return JS_FALSE; } js_PopStatement(&cg->treeContext); @@ -1511,60 +1440,12 @@ js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, return JS_TRUE; } -JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl) -{ - JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - jsval v; - - for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { - if (stmt->type == STMT_WITH) { - /* Ignore with statements enclosing a single let declaration. */ - if (letdecl) - continue; - break; - } - - /* Skip "maybe scope" statements that don't contain let bindings. */ - if (!(stmt->flags & SIF_SCOPE)) - continue; - - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - - if (slotp) { - /* - * Use LOCKED_OBJ_GET_SLOT since we know obj is single- - * threaded and owned by this compiler activation. - */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH); - JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0); - *slotp = JSVAL_TO_INT(v) + sprop->shortid; - } - return stmt; - } - } - - if (slotp) - *slotp = -1; - return stmt; -} - JSBool js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp) { JSBool ok; JSStackFrame *fp; - JSStmtInfo *stmt; - jsint slot; JSAtomListElement *ale; JSObject *obj, *pobj; JSProperty *prop; @@ -1584,12 +1465,9 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, JS_ASSERT(fp->flags & JSFRAME_COMPILING); obj = fp->varobj; - if (obj == fp->scopeChain) { - /* XXX this will need revising when 'let const' is added. */ - stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE); - if (stmt) - return JS_TRUE; - + if (obj == fp->scopeChain && + !js_InWithStatement(&cg->treeContext) && + !js_InCatchBlock(&cg->treeContext, atom)) { ATOM_LIST_SEARCH(ale, &cg->constList, atom); if (ale) { *vp = ALE_VALUE(ale); @@ -1604,22 +1482,16 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * nor can prop be deleted. */ prop = NULL; - if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { + if (OBJ_IS_NATIVE(obj)) { ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (!ok) break; if (prop) { -#ifdef DEBUG - JSScopeProperty *sprop = (JSScopeProperty *)prop; - /* * Any hidden property must be a formal arg or local var, * which will shadow a global const of the same name. */ - JS_ASSERT(sprop->getter == js_GetArgument || - sprop->getter == js_GetLocalVariable); -#endif OBJ_DROP_PROPERTY(cx, pobj, prop); break; } @@ -1715,7 +1587,7 @@ IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, clasp = OBJ_GET_CLASS(cx, varobj); if (clasp == &js_FunctionClass) { fun = (JSFunction *) JS_GetPrivate(cx, varobj); - countPtr = &fun->u.i.nregexps; + countPtr = &fun->nregexps; cloneIndex = *countPtr; } else { JS_ASSERT(clasp != &js_CallClass); @@ -1738,6 +1610,18 @@ IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, return JS_TRUE; } +/* + * 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 @@ -1767,16 +1651,11 @@ EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) if (atomIndex >= JS_BIT(16)) { mode = (js_CodeSpec[op].format & JOF_MODEMASK); if (op != JSOP_SETNAME) { - prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) || -#if JS_HAS_XML_SUPPORT - op == JSOP_GETMETHOD || - op == JSOP_SETMETHOD || -#endif - op == JSOP_SETCONST) - ? JSOP_LITOPX - : (mode == JOF_NAME) + prefixOp = (mode == JOF_NAME) ? JSOP_FINDNAME - : JSOP_LITERAL; + : (mode == JOF_PROP) + ? JSOP_LITERAL + : JSOP_LITOPX; off = js_EmitN(cx, cg, prefixOp, 3); if (off < 0) return JS_FALSE; @@ -1792,7 +1671,6 @@ EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) case JSOP_FORNAME: op = JSOP_FORELEM; break; case JSOP_FORPROP: op = JSOP_FORELEM; break; case JSOP_GETPROP: op = JSOP_GETELEM; break; - case JSOP_GETXPROP: op = JSOP_GETXELEM; break; case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; case JSOP_INCNAME: op = JSOP_INCELEM; break; case JSOP_INCPROP: op = JSOP_INCELEM; break; @@ -1805,19 +1683,7 @@ EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) case JSOP_BINDNAME: return JS_TRUE; case JSOP_SETNAME: op = JSOP_SETELEM; break; case JSOP_SETPROP: op = JSOP_SETELEM; break; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; -#endif - default: -#if JS_HAS_XML_SUPPORT - JS_ASSERT(mode == 0 || op == JSOP_SETCONST || - op == JSOP_GETMETHOD || op == JSOP_SETMETHOD); -#else - JS_ASSERT(mode == 0 || op == JSOP_SETCONST); -#endif - break; + default: JS_ASSERT(mode == 0); break; } return js_Emit1(cx, cg, op) >= 0; @@ -1858,34 +1724,28 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) * error, true on success. * * The caller can inspect pn->pn_slot for a non-negative slot number to tell - * whether optimization occurred, in which case BindNameToSlot also updated + * whether optimization occurred, in which case LookupArgOrVar also updated * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether * or not pn->pn_op was modified, if this function finds an argument or local * variable name, pn->pn_attrs will contain the property's attributes after a * successful return. - * - * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget - * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases - * in js_EmitTree. */ static JSBool -BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, - JSBool letdecl) +LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) { - JSAtom *atom; - JSStmtInfo *stmt; - jsint slot; - JSOp op; JSStackFrame *fp; JSObject *obj, *pobj; JSClass *clasp; JSBool optimizeGlobals; - JSPropertyOp getter; - uintN attrs; - JSAtomListElement *ale; + JSAtom *atom; JSProperty *prop; JSScopeProperty *sprop; + JSOp op; + JSPropertyOp getter; + uintN attrs; + jsint slot; + JSAtomListElement *ale; JS_ASSERT(pn->pn_type == TOK_NAME); if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) @@ -1895,39 +1755,6 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, if (pn->pn_op == JSOP_QNAMEPART) return JS_TRUE; - /* - * We can't optimize if we are compiling a with statement and its body, - * or we're in a catch block whose exception variable has the same name - * as this node. FIXME: we should be able to optimize catch vars to be - * block-locals. - */ - atom = pn->pn_atom; - stmt = js_LexicalLookup(tc, atom, &slot, letdecl); - if (stmt) { - if (stmt->type == STMT_WITH) - return JS_TRUE; - - JS_ASSERT(stmt->flags & SIF_SCOPE); - JS_ASSERT(slot >= 0); - op = pn->pn_op; - switch (op) { - case JSOP_NAME: op = JSOP_GETLOCAL; break; - case JSOP_SETNAME: op = JSOP_SETLOCAL; break; - case JSOP_INCNAME: op = JSOP_INCLOCAL; break; - case JSOP_NAMEINC: op = JSOP_LOCALINC; break; - case JSOP_DECNAME: op = JSOP_DECLOCAL; break; - case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; - case JSOP_FORNAME: op = JSOP_FORLOCAL; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - if (op != pn->pn_op) { - pn->pn_op = op; - pn->pn_slot = slot; - } - 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 @@ -1936,8 +1763,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, * ensures that its caller's frame has a Call object, so arg and var name * lookups will succeed. */ - fp = cx->fp; - if (fp->flags & JSFRAME_SCRIPT_OBJECT) + if (cx->fp->flags & JSFRAME_SCRIPT_OBJECT) return JS_TRUE; /* @@ -1952,13 +1778,10 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, * We can't optimize if we're not compiling a function body, whether via * eval, or directly when compiling a function statement or expression. */ + fp = cx->fp; obj = fp->varobj; clasp = OBJ_GET_CLASS(cx, obj); if (clasp != &js_FunctionClass && clasp != &js_CallClass) { - /* Check for an eval or debugger frame. */ - if (fp->flags & JSFRAME_SPECIAL) - return JS_TRUE; - /* * Optimize global variable accesses if there are at least 100 uses * in unambiguous contexts, or failing that, if least half of all the @@ -1974,10 +1797,16 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, } /* - * We can't optimize if we are in an eval called inside a with statement. + * We can't optimize if we're in an eval called inside a with statement, + * or we're compiling a with statement and its body, or we're in a catch + * block whose exception variable has the same name as pn. */ - if (fp->scopeChain != obj) + atom = pn->pn_atom; + if (fp->scopeChain != obj || + js_InWithStatement(tc) || + js_InCatchBlock(tc, atom)) { return JS_TRUE; + } op = pn->pn_op; getter = NULL; @@ -2144,9 +1973,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, case PN_LIST: if (pn->pn_type == TOK_NEW || pn->pn_type == TOK_LP || - pn->pn_type == TOK_LB || - pn->pn_type == TOK_RB || - pn->pn_type == TOK_RC) { + pn->pn_type == TOK_LB) { /* * All invocation operations (construct: TOK_NEW, call: TOK_LP) * are presumed to be useful, because they may have side effects @@ -2157,10 +1984,6 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, * to be useful because each index operation could invoke a getter * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, * does not apply here: arguments[i][j] might invoke a getter). - * - * Array and object initializers (TOK_RB and TOK_RC lists) must be - * considered useful, because they are sugar for constructor calls - * (to Array and Object, respectively). */ *answer = JS_TRUE; } else { @@ -2182,30 +2005,13 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, * is another assignment overwriting this one's ostensible effect, * because the left operand may be a property with a setter that * has side effects. - * - * The only exception is assignment of a useless value to a const - * declared in the function currently being compiled. */ - pn2 = pn->pn_left; - if (pn2->pn_type != TOK_NAME) { - *answer = JS_TRUE; - } else { - if (!BindNameToSlot(cx, tc, pn2, JS_FALSE)) - return JS_FALSE; - if (!CheckSideEffects(cx, tc, pn->pn_right, answer)) - return JS_FALSE; - if (!*answer && - (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) { - *answer = JS_TRUE; - } - } + *answer = JS_TRUE; } else { if (pn->pn_type == TOK_LB) { pn2 = pn->pn_left; - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { + if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2)) return JS_FALSE; - } if (pn2->pn_op != JSOP_ARGUMENTS) { /* * Any indexed property reference could call a getter with @@ -2222,45 +2028,19 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, case PN_UNARY: if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || + pn->pn_type == TOK_DELETE || pn->pn_type == TOK_THROW || -#if JS_HAS_GENERATORS - pn->pn_type == TOK_YIELD || -#endif pn->pn_type == TOK_DEFSHARP) { /* All these operations have effects that we must commit. */ *answer = JS_TRUE; - } else if (pn->pn_type == TOK_DELETE) { - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - case TOK_DOT: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - case TOK_LB: - /* All these delete addressing modes have effects too. */ - *answer = JS_TRUE; - break; - default: - ok = CheckSideEffects(cx, tc, pn2, answer); - break; - } } else { ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); } break; case PN_NAME: - /* - * Take care to avoid trying to bind a label name (labels, both for - * statements and property values in object initialisers, have pn_op - * defaulted to JSOP_NOP). - */ - if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { - if (!BindNameToSlot(cx, tc, pn, JS_FALSE)) + if (pn->pn_type == TOK_NAME) { + if (!LookupArgOrVar(cx, tc, pn)) return JS_FALSE; if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { /* @@ -2271,11 +2051,9 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, } } pn2 = pn->pn_expr; - if (pn->pn_type == TOK_DOT) { - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { + if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { + if (!LookupArgOrVar(cx, tc, pn2)) return JS_FALSE; - } if (!(pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom)) { /* @@ -2298,10 +2076,10 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, /* * 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's name - * with a function:: prefix. All other JSOP_GETMETHOD and JSOP_SETMETHOD uses - * must be explicit, so we need a distinct source note (SRC_METHODBASE rather - * than SRC_PCBASE) for round-tripping through the beloved decompiler. + * 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 @@ -2311,7 +2089,7 @@ SrcNoteForPropOp(JSParseNode *pn, JSOp op) return ((op == JSOP_GETMETHOD && !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) || op == JSOP_SETMETHOD) - ? SRC_METHODBASE + ? SRC_PCDELTA : SRC_PCBASE; } @@ -2326,7 +2104,7 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { /* Try to optimize arguments.length into JSOP_ARGCNT. */ - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; if (pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom) { @@ -2396,13 +2174,13 @@ static JSBool EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) { ptrdiff_t top; - JSParseNode *left, *right, *next, ltmp, rtmp; + JSParseNode *left, *right, *next, temp; jsint slot; top = CG_OFFSET(cg); if (pn->pn_arity == PN_LIST) { /* Left-associative operator chain to avoid too much recursion. */ - JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM); + JS_ASSERT(pn->pn_op == JSOP_GETELEM); JS_ASSERT(pn->pn_count >= 3); left = pn->pn_head; right = PN_LAST(pn); @@ -2414,7 +2192,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) * one or more index expression and JSOP_GETELEM op pairs. */ if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, left)) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(next->pn_dval, slot) && @@ -2451,28 +2229,13 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) if (pn->pn_arity == PN_NAME) { /* * Set left and right so pn appears to be a TOK_LB node, instead - * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and - * EmitDestructuringOps nearer below. In the destructuring case, - * the base expression (pn_expr) of the name may be null, which - * means we have to emit a JSOP_BINDNAME. + * of a TOK_DOT node (see the TOK_FOR/IN case in js_EmitTree). */ left = pn->pn_expr; - if (!left) { - left = <mp; - left->pn_type = TOK_OBJECT; - left->pn_op = JSOP_BINDNAME; - left->pn_arity = PN_NULLARY; - left->pn_pos = pn->pn_pos; - left->pn_atom = pn->pn_atom; - } - right = &rtmp; + right = &temp; right->pn_type = TOK_STRING; - JS_ASSERT(ATOM_IS_STRING(pn->pn_atom)); - right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom)) - ? JSOP_QNAMEPART - : JSOP_STRING; right->pn_arity = PN_NULLARY; - right->pn_pos = pn->pn_pos; + right->pn_op = JSOP_STRING; right->pn_atom = pn->pn_atom; } else { JS_ASSERT(pn->pn_arity == PN_BINARY); @@ -2484,7 +2247,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) if (op == JSOP_GETELEM && left->pn_type == TOK_NAME && right->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, left)) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(right->pn_dval, slot) && @@ -2498,10 +2261,6 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) if (!js_EmitTree(cx, cg, left)) return JS_FALSE; } - - /* The right side of the descendant operator is implicitly quoted. */ - JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || - right->pn_op == JSOP_QNAMEPART); if (!js_EmitTree(cx, cg, right)) return JS_FALSE; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) @@ -2553,6 +2312,7 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg); } +#if JS_HAS_SWITCH_STATEMENT static JSBool EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, JSStmtInfo *stmtInfo) @@ -2571,10 +2331,6 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, intN noteIndex; size_t switchSize, tableSize; jsbytecode *pc, *savepc; -#if JS_HAS_BLOCK_SCOPE - JSObject *obj; - jsint count; -#endif /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ switchOp = JSOP_TABLESWITCH; @@ -2582,83 +2338,15 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, hasDefault = constPropagated = JS_FALSE; defaultOffset = -1; - /* - * If the switch contains let variables scoped by its body, model the - * resulting block on the stack first, before emitting the discriminant's - * bytecode (in case the discriminant contains a stack-model dependency - * such as a let expression). - */ - pn2 = pn->pn_right; -#if JS_HAS_BLOCK_SCOPE - if (pn2->pn_type == TOK_LEXICALSCOPE) { - atom = pn2->pn_atom; - obj = ATOM_TO_OBJECT(atom); - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - - /* - * Push the body's block scope before discriminant code-gen for proper - * static block scope linkage in case the discriminant contains a let - * expression. The block's locals must lie under the discriminant on - * the stack so that case-dispatch bytecodes can find the discriminant - * on top of stack. - */ - js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1); - stmtInfo->type = STMT_SWITCH; - - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - /* - * Pop the switch's statement info around discriminant code-gen. Note - * how this leaves cg->treeContext.blockChain referencing the switch's - * block scope object, which is necessary for correct block parenting - * in the case where the discriminant contains a let expression. - */ - cg->treeContext.topStmt = stmtInfo->down; - cg->treeContext.topScopeStmt = stmtInfo->downScope; - } -#ifdef __GNUC__ - else { - atom = NULL; - count = -1; - } -#endif -#endif - - /* - * Emit code for the discriminant first (or nearly first, in the case of a - * switch whose body is a block scope). - */ - if (!js_EmitTree(cx, cg, pn->pn_left)) + /* Emit code for the discriminant first. */ + if (!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; /* Switch bytecodes run from here till end of final case. */ top = CG_OFFSET(cg); -#if !JS_HAS_BLOCK_SCOPE js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); -#else - if (pn2->pn_type == TOK_LC) { - js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); - } else { - /* Re-push the switch's statement info record. */ - cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo; - - /* Set the statement info record's idea of top. */ - stmtInfo->update = top; - - /* Advance pn2 to refer to the switch case list. */ - pn2 = pn2->pn_expr; - } -#endif + pn2 = pn->pn_kid2; caseCount = pn2->pn_count; tableLength = 0; table = NULL; @@ -2801,15 +2489,6 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, tableLength = (uint32)(high - low + 1); if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) switchOp = JSOP_LOOKUPSWITCH; - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* - * Lookup switch supports only atom indexes below 64K limit. - * Conservatively estimate the maximum possible index during - * switch generation and use conditional switch if it exceeds - * the limit. - */ - if (caseCount + cg->atomList.count > JS_BIT(16)) - switchOp = JSOP_CONDSWITCH; } } @@ -2884,34 +2563,15 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, return JS_FALSE; pn3->pn_offset = off; if (beforeCases) { - uintN noteCount, noteCountDelta; - /* Switch note's second offset is to first JSOP_CASE. */ - noteCount = CG_NOTE_COUNT(cg); if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, off - top)) { return JS_FALSE; } - noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; - if (noteCountDelta != 0) - caseNoteIndex += noteCountDelta; beforeCases = JS_FALSE; } } - /* - * If we didn't have an explicit default (which could fall in between - * cases, preventing us from fusing this js_SetSrcNoteOffset with the - * call in the loop above), link the last case to the implicit default - * for the decompiler. - */ - if (!hasDefault && - caseNoteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - /* Emit default even if no explicit default statement. */ defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); if (defaultOffset < 0) @@ -3091,41 +2751,13 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, out: if (table) JS_free(cx, table); - if (ok) { - ok = js_PopStatementCG(cx, cg); - -#if JS_HAS_BLOCK_SCOPE - if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) { - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); - cg->stackDepth -= count; - } -#endif - } - return ok; + return ok && js_PopStatementCG(cx, cg); bad: ok = JS_FALSE; goto out; } - -JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) -{ - if (!js_AllocTryNotes(cx, cg)) - return JS_FALSE; - - if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ - CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); - if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) - return JS_FALSE; - CG_SWITCH_TO_MAIN(cg); - } - - return js_EmitTree(cx, cg, body) && - js_Emit1(cx, cg, JSOP_STOP) >= 0; -} +#endif /* JS_HAS_SWITCH_STATEMENT */ JSBool js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, @@ -3135,6 +2767,9 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, JSObject *funobj; JSBool ok; + if (!js_AllocTryNotes(cx, cg)) + return JS_FALSE; + fp = cx->fp; funobj = fun->object; JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && @@ -3147,15 +2782,17 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; - ok = js_EmitFunctionBytecode(cx, cg, body); + ok = js_EmitTree(cx, cg, body); cx->fp = fp; if (!ok) return JS_FALSE; - if (!js_NewScriptFromCG(cx, cg, fun)) + fun->u.script = js_NewScriptFromCG(cx, cg, fun); + if (!fun->u.script) return JS_FALSE; - - JS_ASSERT(FUN_INTERPRETED(fun)); + JS_ASSERT(fun->interpreted); + if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; return JS_TRUE; } @@ -3197,702 +2834,6 @@ UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_TRUE; } -static JSBool -MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn, jsatomid *result) -{ - jsatomid atomIndex; - JSAtomListElement *ale; - - if (pn->pn_slot >= 0) { - atomIndex = (jsatomid) pn->pn_slot; - } else { - ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - } - - if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST && - (!(cg->treeContext.flags & TCF_IN_FUNCTION) || - (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { - /* Emit a prolog bytecode to predefine the variable. */ - CG_SWITCH_TO_PROLOG(cg); - if (!UpdateLineNumberNotes(cx, cg, pn)) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(prologOp, atomIndex); - CG_SWITCH_TO_MAIN(cg); - } - - if (result) - *result = atomIndex; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING - -typedef JSBool -(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn); - -static JSBool -EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JS_ASSERT(pn->pn_type == TOK_NAME); - if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP)) - return JS_FALSE; - - JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); - return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); -} - -static JSBool -EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - DestructuringDeclEmitter emitter; - - if (pn->pn_type == TOK_RB) { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_COMMA) - continue; - emitter = (pn2->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn2)) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pn3 = pn2->pn_right; - emitter = (pn3->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn3)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -static JSBool -EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool wantpop) -{ - jsuint slot; - - /* Skip any parenthesization. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - /* - * Now emit the lvalue opcode sequence. If the lvalue is a nested - * destructuring initialiser-form, call ourselves to handle it, then - * pop the matched value. Otherwise emit an lvalue bytecode sequence - * ending with a JSOP_ENUMELEM or equivalent op. - */ - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (!EmitDestructuringOpsHelper(cx, cg, pn)) - return JS_FALSE; - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (pn->pn_type == TOK_NAME && - !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) { - return JS_FALSE; - } - - switch (pn->pn_op) { - case JSOP_SETNAME: - /* - * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, - * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. - * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. - */ - if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETCONST: - if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETLOCAL: - if (wantpop) { - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); - break; - } - /* FALL THROUGH */ - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(pn->pn_op, slot); - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - - default: -#if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT - { - ptrdiff_t top; - - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - break; - } -#endif - case JSOP_ENUMELEM: - JS_ASSERT(0); - } - } - - return JS_TRUE; -} - -/* - * Recursive helper for EmitDestructuringOps. - * - * Given a value to destructure on the stack, walk over an object or array - * initialiser at pn, emitting bytecodes to match property values and store - * them in the lvalues identified by the matched property names. - */ -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - jsuint index; - JSParseNode *pn2, *pn3; - JSBool doElemOp; - -#ifdef DEBUG - intN stackDepth = cg->stackDepth; - JS_ASSERT(stackDepth != 0); - JS_ASSERT(pn->pn_arity == PN_LIST); - JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); -#endif - - if (pn->pn_count == 0) { - /* Emit a DUP;POP sequence for the decompiler. */ - return js_Emit1(cx, cg, JSOP_DUP) >= 0 && - js_Emit1(cx, cg, JSOP_POP) >= 0; - } - - index = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Duplicate the value being destructured to use as a reference base. - */ - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - - /* - * Now push the property name currently being matched, which is either - * the array initialiser's current index, or the current property name - * "label" on the left of a colon in the object initialiser. Set pn3 - * to the lvalue node, which is in the value-initializing position. - */ - doElemOp = JS_TRUE; - if (pn->pn_type == TOK_RB) { - if (!EmitNumberOp(cx, index, cg)) - return JS_FALSE; - pn3 = pn2; - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - JS_ASSERT(pn2->pn_type == TOK_COLON); - pn3 = pn2->pn_left; - if (pn3->pn_type == TOK_NUMBER) { - /* - * If we are emitting an object destructuring initialiser, - * annotate the index op with SRC_INITPROP so we know we are - * not decompiling an array initialiser. - */ - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - } else { - JS_ASSERT(pn3->pn_type == TOK_STRING || - pn3->pn_type == TOK_NAME); - if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) - return JS_FALSE; - doElemOp = JS_FALSE; - } - pn3 = pn2->pn_right; - } - - if (doElemOp) { - /* - * Ok, get the value of the matching property name. This leaves - * that value on top of the value being destructured, so the stack - * is one deeper than when we started. - */ - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == stackDepth + 1); - } - - /* Nullary comma node makes a hole in the array destructurer. */ - if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { - JS_ASSERT(pn->pn_type == TOK_RB); - JS_ASSERT(pn2 == pn3); - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE)) - return JS_FALSE; - } - - JS_ASSERT(cg->stackDepth == stackDepth); - ++index; - } - - return JS_TRUE; -} - -static ptrdiff_t -OpToDeclType(JSOp op) -{ - switch (op) { - case JSOP_NOP: - return SRC_DECL_LET; - case JSOP_DEFCONST: - return SRC_DECL_CONST; - case JSOP_DEFVAR: - return SRC_DECL_VAR; - default: - return SRC_DECL_NONE; - } -} - -static JSBool -EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn) -{ - /* - * If we're called from a variable declaration, help the decompiler by - * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. - * If the destructuring initialiser is empty, our helper will emit a - * JSOP_DUP followed by a JSOP_POP for the decompiler. - */ - if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - /* - * Call our recursive helper to emit the destructuring assignments and - * related stack manipulations. - */ - return EmitDestructuringOpsHelper(cx, cg, pn); -} - -static JSBool -EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *lhs, JSParseNode *rhs) -{ - jsuint depth, limit, slot; - JSParseNode *pn; - - depth = limit = (uintN) cg->stackDepth; - for (pn = rhs->pn_head; pn; pn = pn->pn_next) { - if (limit == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, rhs, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_INIT_TOO_BIG); - return JS_FALSE; - } - - if (pn->pn_type == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - JS_ASSERT(pn->pn_type != TOK_DEFSHARP); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - } - ++limit; - } - - if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - slot = depth; - for (pn = lhs->pn_head; pn; pn = pn->pn_next) { - if (slot < limit) { - EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL)) - return JS_FALSE; - } - ++slot; - } - - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - cg->stackDepth = (uintN) depth; - return JS_TRUE; -} - -/* - * Helper called with pop out param initialized to a JSOP_POP* opcode. If we - * can emit a group assignment sequence, which results in 0 stack depth delta, - * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. - */ -static JSBool -MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn, JSOp *pop) -{ - JSParseNode *lhs, *rhs; - - JS_ASSERT(pn->pn_type == TOK_ASSIGN); - JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); - lhs = pn->pn_left; - rhs = pn->pn_right; - if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && - lhs->pn_count <= rhs->pn_count && - (rhs->pn_count == 0 || - rhs->pn_head->pn_type != TOK_DEFSHARP)) { - if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs)) - return JS_FALSE; - *pop = JSOP_NOP; - } - return JS_TRUE; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -static JSBool -EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool inLetHead, ptrdiff_t *headNoteIndex) -{ - JSTreeContext *tc; - JSBool let, forInVar; -#if JS_HAS_BLOCK_SCOPE - JSBool forInLet, popScope; - JSStmtInfo *stmt, *scopeStmt; -#endif - ptrdiff_t off, noteIndex, tmp; - JSParseNode *pn2, *pn3; - JSOp op; - jsatomid atomIndex; - uintN oldflags; - - /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ - *headNoteIndex = -1; - - /* - * Let blocks and expressions have a parenthesized head in which the new - * scope is not yet open. Initializer evaluation uses the parent node's - * lexical scope. If popScope is true below, then we hide the top lexical - * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that - * it won't find any names in the new let block. - * - * The same goes for let declarations in the head of any kind of for loop. - * Unlike a let declaration 'let x = i' within a block, where x is hoisted - * to the start of the block, a 'for (let x = i...) ...' loop evaluates i - * in the containing scope, and puts x in the loop body's scope. - */ - tc = &cg->treeContext; - let = (pn->pn_op == JSOP_NOP); - forInVar = (pn->pn_extra & PNX_FORINVAR) != 0; -#if JS_HAS_BLOCK_SCOPE - forInLet = let && forInVar; - popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); - JS_ASSERT(!popScope || let); -#endif - - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { -#if JS_HAS_DESTRUCTURING - if (pn2->pn_type != TOK_NAME) { - if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { - /* - * Emit variable binding ops, but not destructuring ops. - * The parser (see Variables, jsparse.c) has ensured that - * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, - * and that case will emit the destructuring code only after - * emitting an enumerating opcode and a branch that tests - * whether the enumeration ended. - */ - JS_ASSERT(forInVar); - JS_ASSERT(pn->pn_count == 1); - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2)) - return JS_FALSE; - break; - } - - /* - * A destructuring initialiser assignment preceded by var is - * always evaluated promptly, even if it is to the left of 'in' - * in a for-in loop. As with 'for (var x = i in o)...', this - * will cause the entire 'var [a, b] = i' to be hoisted out of - * the head of the loop. - */ - JS_ASSERT(pn2->pn_type == TOK_ASSIGN); - if (pn->pn_count == 1 && !forInLet) { - /* - * If this is the only destructuring assignment in the list, - * try to optimize to a group assignment. If we're in a let - * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP - * in pn->pn_op, to suppress a second (and misplaced) 'let'. - */ - JS_ASSERT(noteIndex < 0 && !pn2->pn_next); - op = JSOP_POP; - if (!MaybeEmitGroupAssignment(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn2, &op)) { - return JS_FALSE; - } - if (op == JSOP_NOP) { - pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; - break; - } - } - - pn3 = pn2->pn_left; - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let [x, y] = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * The expression i in 'for (let [x, y] = i in o) ...', which is - * pn2->pn_right above, appears to have side effects. We've just - * emitted code to evaluate i, but we must not destructure i yet. - * Let the TOK_FOR: code in js_EmitTree do the destructuring to - * emit the right combination of source notes and bytecode for the - * decompiler. - * - * This has the effect of hoisting the evaluation of i out of the - * for-in loop, without hoisting the let variables, which must of - * course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP - * to be emitted, just before returning from this function. - */ - if (forInVar) { - pn->pn_extra |= PNX_POPVAR; - if (forInLet) - break; - } -#endif - - /* - * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT - * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that - * we will emit at the bottom of this function. - */ - if (!EmitDestructuringOps(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn3)) { - return JS_FALSE; - } - goto emit_note_pop; - } -#else - JS_ASSERT(pn2->pn_type == TOK_NAME); -#endif - - if (!BindNameToSlot(cx, &cg->treeContext, pn2, let)) - return JS_FALSE; - JS_ASSERT(pn2->pn_slot >= 0 || !let); - - op = pn2->pn_op; - if (op == JSOP_ARGUMENTS) { - /* JSOP_ARGUMENTS => no initializer */ - JS_ASSERT(!pn2->pn_expr && !let); - pn3 = NULL; -#ifdef __GNUC__ - atomIndex = 0; /* quell GCC overwarning */ -#endif - } else { - if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex)) - return JS_FALSE; - - pn3 = pn2->pn_expr; - if (pn3) { -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let x = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn3, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (op == JSOP_SETNAME) { - JS_ASSERT(!let); - EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); - } - if (pn->pn_op == JSOP_DEFCONST && - !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, - pn3)) { - return JS_FALSE; - } - -#if JS_HAS_BLOCK_SCOPE - /* Evaluate expr in the outer lexical scope if requested. */ - if (popScope) { - stmt = tc->topStmt; - scopeStmt = tc->topScopeStmt; - - tc->topStmt = stmt->down; - tc->topScopeStmt = scopeStmt->downScope; - } -#ifdef __GNUC__ - else { - stmt = scopeStmt = NULL; /* quell GCC overwarning */ - } -#endif -#endif - - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = stmt; - tc->topScopeStmt = scopeStmt; - } -#endif - } - } - - /* - * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the - * TOK_VAR case, but only the initialized case (a strange one that - * falls out of ECMA-262's grammar) wants to run past this point. - * Both cases must conditionally emit a JSOP_DEFVAR, above. Note - * that the parser error-checks to ensure that pn->pn_count is 1. - * - * 'for (let x = i in o) ...' must evaluate i before the loop, and - * subject it to useless expression elimination. The variable list - * in pn is a single let declaration if pn_op == JSOP_NOP. We test - * the let local in order to break early in this case, as well as in - * the 'for (var x in o)' case. - * - * XXX Narcissus keeps track of variable declarations in the node - * for the script being compiled, so there's no need to share any - * conditional prolog code generation there. We could do likewise, - * but it's a big change, requiring extra allocation, so probably - * not worth the trouble for SpiderMonkey. - */ - JS_ASSERT(pn3 == pn2->pn_expr); - if (forInVar && (!pn3 || let)) { - JS_ASSERT(pn->pn_count == 1); - break; - } - - if (pn2 == pn->pn_head && - !inLetHead && - js_NewSrcNote2(cx, cg, SRC_DECL, - (pn->pn_op == JSOP_DEFCONST) - ? SRC_DECL_CONST - : (pn->pn_op == JSOP_DEFVAR) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else if (pn2->pn_slot >= 0) { - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - EMIT_ATOM_INDEX_OP(op, atomIndex); - } - -#if JS_HAS_DESTRUCTURING - emit_note_pop: -#endif - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - - /* If this is a let head, emit and return a srcnote on the pop. */ - if (inLetHead) { - *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); - if (*headNoteIndex < 0) - return JS_FALSE; - if (!(pn->pn_extra & PNX_POPVAR)) - return js_Emit1(cx, cg, JSOP_NOP) >= 0; - } - - return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; -} - -#if defined DEBUG_brendan || defined DEBUG_mrbkap -static JSBool -GettableNoteForNextOp(JSCodeGenerator *cg) -{ - ptrdiff_t offset, target; - jssrcnote *sn, *end; - - offset = 0; - target = CG_OFFSET(cg); - for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; - sn = SN_NEXT(sn)) { - if (offset == target && SN_IS_GETTABLE(sn)) - return JS_TRUE; - offset += SN_DELTA(sn); - } - return JS_FALSE; -} -#endif - JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { @@ -3907,7 +2848,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSSrcNoteType noteType; jsbytecode *pc; JSOp op; - JSTokenType type; uint32 argc; int stackDummy; @@ -3974,11 +2914,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; atomIndex = ALE_INDEX(ale); +#if JS_HAS_LEXICAL_CLOSURE /* Emit a bytecode pointing to the closure object in its immediate. */ if (pn->pn_op != JSOP_NOP) { EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); break; } +#endif /* Top-level named functions need a nop for decompilation. */ noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex); @@ -3993,6 +2935,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ CG_SWITCH_TO_PROLOG(cg); +#if JS_HAS_LEXICAL_CLOSURE if (cg->treeContext.flags & TCF_IN_FUNCTION) { JSObject *obj, *pobj; JSProperty *prop; @@ -4011,21 +2954,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) slot = sprop->shortid; OBJ_DROP_PROPERTY(cx, pobj, prop); - /* - * If this local function is declared in a body block induced by - * let declarations, reparent fun->object to the compiler-created - * body block object so that JSOP_DEFLOCALFUN can clone that block - * into the runtime scope chain. - */ - stmt = cg->treeContext.topStmt; - if (stmt && stmt->type == STMT_BLOCK && - stmt->down && stmt->down->type == STMT_BLOCK && - (stmt->down->flags & SIF_SCOPE)) { - obj = ATOM_TO_OBJECT(stmt->down->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - OBJ_SET_PARENT(cx, fun->object, obj); - } - if (atomIndex >= JS_BIT(16)) { /* * Lots of literals in the outer function, so we have to emit @@ -4048,10 +2976,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) pc += VARNO_LEN; SET_ATOM_INDEX(pc, atomIndex); } - } else { - JS_ASSERT(!cg->treeContext.topStmt); + } else +#endif EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex); - } CG_SWITCH_TO_MAIN(cg); break; @@ -4104,26 +3031,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* Emit code for the condition before pushing stmtInfo. */ if (!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; - top = CG_OFFSET(cg); if (stmtInfo.type == STMT_IF) { - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top); + js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, + CG_OFFSET(cg)); } else { /* * We came here from the goto further below that detects else-if * chains, so we must mutate stmtInfo back into a STMT_IF record. * Also (see below for why) we need a note offset for SRC_IF_ELSE - * to help the decompiler. Actually, we need two offsets, one for - * decompiling any else clause and the second for decompiling an - * else-if chain without bracing, overindenting, or incorrectly - * scoping let declarations. + * to help the decompiler. */ JS_ASSERT(stmtInfo.type == STMT_ELSE); stmtInfo.type = STMT_IF; - stmtInfo.update = top; if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp)) - return JS_FALSE; } /* Emit an annotated branch-if-false around the then part. */ @@ -4178,10 +3099,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = js_PopStatementCG(cx, cg); break; +#if JS_HAS_SWITCH_STATEMENT case TOK_SWITCH: /* Out of line to avoid bloating js_EmitTree's stack frame size. */ ok = EmitSwitch(cx, cg, pn, &stmtInfo); break; +#endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_WHILE: js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); @@ -4204,6 +3127,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = js_PopStatementCG(cx, cg); break; +#if JS_HAS_DO_WHILE_LOOP case TOK_DO: /* Emit an annotated nop so we know to decompile a 'do' keyword. */ if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || @@ -4237,6 +3161,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; ok = js_PopStatementCG(cx, cg); break; +#endif /* JS_HAS_DO_WHILE_LOOP */ case TOK_FOR: beq = 0; /* suppress gcc warnings */ @@ -4261,131 +3186,68 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * TOK_VAR case, conditioned on pn_extra flags set by the parser. * * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) - * called here will generate the proper note for the assignment + * called here will generate the SRC_VAR note for the assignment * op that sets x = i, hoisting the initialized var declaration * out of the loop: 'var x = i; for (x in o) ...'. * * In the 'for (var x in o) ...' case, nothing but the prolog op - * (if needed) should be generated here, we must emit the note + * (if needed) should be generated here, we must emit the SRC_VAR * just before the JSOP_FOR* opcode in the switch on pn3->pn_type * a bit below, so nothing is hoisted: 'for (var x in o) ...'. - * - * A 'for (let x = i in o)' loop must not be hoisted, since in - * this form the let variable is scoped by the loop body (but not - * the head). The initializer expression i must be evaluated for - * any side effects. So we hoist only i in the let case. */ pn3 = pn2->pn_left; - type = pn3->pn_type; - cg->treeContext.flags |= TCF_IN_FOR_INIT; - if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) + if (pn3->pn_type == TOK_VAR && !js_EmitTree(cx, cg, pn3)) return JS_FALSE; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; /* Emit a push to allocate the iterator. */ - if (js_Emit1(cx, cg, JSOP_STARTITER) < 0) + if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; /* Compile the object expression to the right of 'in'. */ if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; - - /* - * Emit a bytecode to convert top of stack value to the iterator - * object depending on the loop variant (for-in, for-each-in, or - * destructuring for-in). - */ -#if JS_HAS_DESTRUCTURING - JS_ASSERT(pn->pn_op == JSOP_FORIN || - pn->pn_op == JSOP_FOREACHKEYVAL || - pn->pn_op == JSOP_FOREACH); -#else - JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH); -#endif - if (js_Emit1(cx, cg, pn->pn_op) < 0) + if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0) return JS_FALSE; top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); - /* - * Compile a JSOP_FOR* bytecode based on the left hand side. - * - * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| - * or similar, to signify assignment, rather than declaration, to - * the decompiler. EmitDestructuringOps takes a prolog bytecode - * parameter and emits the appropriate source note, defaulting to - * assignment, so JSOP_SETNAME is not critical here; many similar - * ops could be used -- just not JSOP_NOP (which means 'let'). - */ +#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; - op = JSOP_SETNAME; - switch (type) { -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: -#endif + switch (pn3->pn_type) { case TOK_VAR: - JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); pn3 = pn3->pn_head; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN) { - pn3 = pn3->pn_left; - JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); - } - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - op = pn2->pn_left->pn_op; - goto destructuring_for; - } -#else JS_ASSERT(pn3->pn_type == TOK_NAME); -#endif - /* - * Always annotate JSOP_FORLOCAL if given input of the form - * 'for (let x in * o)' -- the decompiler must not hoist the - * 'let x' out of the loop head, or x will be bound in the - * wrong scope. Likewise, but in this case only for the sake - * of higher decompilation fidelity only, do not hoist 'var x' - * when given 'for (var x in o)'. But 'for (var x = i in o)' - * requires hoisting in order to preserve the initializer i. - * The decompiler can only handle so much! - */ - if (( -#if JS_HAS_BLOCK_SCOPE - type == TOK_LET || -#endif - !pn3->pn_expr) && - js_NewSrcNote2(cx, cg, SRC_DECL, - type == TOK_VAR - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { + if (!pn3->pn_expr && js_NewSrcNote(cx, cg, SRC_VAR) < 0) return JS_FALSE; - } /* FALL THROUGH */ case TOK_NAME: if (pn3->pn_slot >= 0) { op = pn3->pn_op; switch (op) { - case JSOP_GETARG: /* FALL THROUGH */ - case JSOP_SETARG: op = JSOP_FORARG; break; - case JSOP_GETVAR: /* FALL THROUGH */ - case JSOP_SETVAR: op = JSOP_FORVAR; break; - case JSOP_GETGVAR: /* FALL THROUGH */ - case JSOP_SETGVAR: op = JSOP_FORNAME; break; - case JSOP_GETLOCAL: /* FALL THROUGH */ - case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break; - default: JS_ASSERT(0); + case JSOP_GETARG: /* FALL THROUGH */ + case JSOP_SETARG: op = JSOP_FORARG; break; + case JSOP_GETVAR: /* FALL THROUGH */ + case JSOP_SETVAR: op = JSOP_FORVAR; break; + case JSOP_GETGVAR: + case JSOP_SETGVAR: op = JSOP_FORNAME; break; + default: JS_ASSERT(0); } } else { pn3->pn_op = JSOP_FORNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn3)) return JS_FALSE; op = pn3->pn_op; } if (pn3->pn_slot >= 0) { - if (pn3->pn_attrs & JSPROP_READONLY) { - JS_ASSERT(op == JSOP_FORVAR); + if (pn3->pn_attrs & JSPROP_READONLY) op = JSOP_GETVAR; - } atomIndex = (jsatomid) pn3->pn_slot; EMIT_UINT16_IMM_OP(op, atomIndex); } else { @@ -4395,7 +3257,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_DOT: - useful = JS_FALSE; + useful = JS_TRUE; if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, &useful)) { return JS_FALSE; @@ -4407,11 +3269,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } /* FALL THROUGH */ -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - destructuring_for: -#endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: #endif @@ -4443,15 +3300,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (beq < 0) return JS_FALSE; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - if (!EmitDestructuringOps(cx, cg, op, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - } -#endif #if JS_HAS_LVALUE_RETURN if (pn3->pn_type == TOK_LP) { JS_ASSERT(pn3->pn_op == JSOP_SETCALL); @@ -4494,35 +3342,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } } else { - op = JSOP_POP; if (!pn2->pn_kid1) { /* No initializer: emit an annotated nop for the decompiler. */ op = JSOP_NOP; } else { - cg->treeContext.flags |= TCF_IN_FOR_INIT; -#if JS_HAS_DESTRUCTURING - pn3 = pn2->pn_kid1; - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { + if (!js_EmitTree(cx, cg, pn2->pn_kid1)) return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) { - /* - * Check whether a destructuring-initialized var decl - * was optimized to a group assignment. If so, we do - * not need to emit a pop below, so switch to a nop, - * just for the decompiler. - */ - JS_ASSERT(pn3->pn_arity == PN_LIST); - if (pn3->pn_extra & PNX_GROUPINIT) - op = JSOP_NOP; - } - } - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; + op = JSOP_POP; } noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); if (noteIndex < 0 || @@ -4572,19 +3398,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - op = JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } /* Restore the absolute line number for source note readers. */ off = (ptrdiff_t) pn->pn_pos.end.lineno; @@ -4615,12 +3432,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } - /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ + /* Now fixup all breaks and continues (before for/in's final POP2). */ if (!js_PopStatementCG(cx, cg)) return JS_FALSE; if (pn2->pn_type == TOK_IN) { - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) + if (js_Emit1(cx, cg, JSOP_POP2) < 0) return JS_FALSE; } break; @@ -4632,7 +3449,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) + while (stmt->type != STMT_LABEL || stmt->label != atom) stmt = stmt->down; noteType = SRC_BREAK2LABEL; } else { @@ -4655,7 +3472,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) { + while (stmt->type != STMT_LABEL || stmt->label != atom) { if (STMT_IS_LOOP(stmt)) loop = stmt; stmt = stmt->down; @@ -4686,25 +3503,28 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = js_PopStatementCG(cx, cg); break; +#if JS_HAS_EXCEPTIONS + case TOK_TRY: { - ptrdiff_t start, end, catchJump, catchStart, finallyCatch; + ptrdiff_t start, end, catchStart, finallyCatch, catchJump; + JSParseNode *iter; intN depth; - JSParseNode *lastCatch; - catchJump = catchStart = finallyCatch = -1; + /* Quell GCC overwarnings. */ + end = catchStart = finallyCatch = catchJump = -1; /* * Push stmtInfo to track jumps-over-catches and gosubs-to-finally * for later fixup. * - * When a finally block is 'active' (STMT_FINALLY on the treeContext), + * 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. * EmitBackPatchOp and BackPatch). */ js_PushStatement(&cg->treeContext, &stmtInfo, - pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, + pn->pn_kid3 ? STMT_FINALLY : STMT_BLOCK, CG_OFFSET(cg)); /* @@ -4712,7 +3532,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * an unbalanced state, and this imbalance causes problems with things * like function invocation later on. * - * To fix this, we compute the 'balanced' stack depth upon try entry, + * To fix this, we compute the `balanced' stack depth upon try entry, * and then restore the stack to this depth when we hit the first catch * or finally block. We can't just zero the stack, because things like * for/in and with that are active upon entry to the block keep state @@ -4731,45 +3551,39 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (pn->pn_kid3) { if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.gosub); if (jmp < 0) return JS_FALSE; - - /* JSOP_RETSUB pops the return pc-index, balancing the stack. */ - cg->stackDepth = depth; } /* Emit (hidden) jump over catch and/or finally. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.catchJump); if (jmp < 0) return JS_FALSE; end = CG_OFFSET(cg); /* If this try has a catch block, emit it. */ - pn2 = pn->pn_kid2; - lastCatch = NULL; - if (pn2) { - jsint count = 0; /* previous catch block's population */ - + iter = pn->pn_kid2; + if (iter) { catchStart = end; /* * The emitted code for a catch block looks like: * - * [throwing] only if 2nd+ catch block - * [leaveblock] only if 2nd+ catch block - * enterblock with SRC_CATCH + * [ popscope ] only if 2nd+ catch block + * name Object + * pushobj + * newinit * exception - * [dup] only if catchguard - * setlocalpop or destructuring code + * initcatchvar + * enterwith * [< catchguard code >] if there's a catchguard * [ifeq ] " " - * [pop] only if catchguard * < catch block contents > - * leaveblock + * leavewith * goto non-local; finally applies * * If there's no catch block without a catchguard, the last @@ -4778,144 +3592,191 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * also used for the catch-all trynote for capturing exceptions * thrown from catch{} blocks. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - ptrdiff_t guardJump, catchNote; + for (;;) { + JSStmtInfo stmtInfo2; + JSParseNode *disc; + ptrdiff_t guardnote; - guardJump = GUARDJUMP(stmtInfo); - if (guardJump == -1) { + if (!UpdateLineNumberNotes(cx, cg, iter)) + 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); + + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { + return JS_FALSE; + } + } else { /* Set stack to original depth (see SETSP comment above). */ EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); cg->stackDepth = depth; - } else { - /* Fix up and clean up previous catch block. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); - - /* - * Account for the pushed exception object that we still - * have after the jumping from the previous guard. - */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Move exception back to cx->exception to prepare for - * the next catch. We hide [throwing] from the decompiler - * since it compensates for the hidden JSOP_DUP at the - * start of the previous guarded catch. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROWING) < 0) { - return JS_FALSE; - } - - /* - * Emit an unbalanced [leaveblock] for the previous catch, - * whose block object count is saved below. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - JS_ASSERT(count >= 0); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); } - /* - * Annotate the JSOP_ENTERBLOCK that's about to be generated - * by the call to js_EmitTree immediately below. Save this - * source note's index in stmtInfo for use by the TOK_CATCH: - * case, where the length of the catch guard is set as the - * note's offset. - */ - catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); - if (catchNote < 0) + /* Non-negative guardnote offset is length of catchguard. */ + guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); + if (guardnote < 0 || + js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; - CATCHNOTE(stmtInfo) = catchNote; + } + + /* Construct the scope holder and push it on. */ + ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); + + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 || + js_Emit1(cx, cg, JSOP_NEWINIT) < 0 || + js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) { + return JS_FALSE; + } + + /* initcatchvar */ + disc = iter->pn_kid1; + ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + + EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale)); + if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) { + return JS_FALSE; + } + + /* boolean_expr */ + if (disc->pn_expr) { + ptrdiff_t guardstart = CG_OFFSET(cg); + if (!js_EmitTree(cx, cg, disc->pn_expr)) + return JS_FALSE; + if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0, + CG_OFFSET(cg) - guardstart)) { + return JS_FALSE; + } + /* ifeq */ + catchJump = EmitJump(cx, cg, JSOP_IFEQ, 0); + if (catchJump < 0) + return JS_FALSE; + } + + /* Emit catch block. */ + js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH, + CG_OFFSET(cg)); + stmtInfo2.label = disc->pn_atom; + if (!js_EmitTree(cx, cg, iter->pn_kid3)) + return JS_FALSE; + js_PopStatementCG(cx, cg); /* - * Emit the lexical scope and catch body. Save the catch's - * block object population via count, for use when targeting - * guardJump at the next catch (the guard mismatch case). + * Jump over the remaining catch blocks. + * This counts as a non-local jump, so do the finally thing. */ - JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); - count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom)); - if (!js_EmitTree(cx, cg, pn3)) + + /* leavewith, annotated so the decompiler knows to pop */ + off = cg->stackDepth - 1; + if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0 || + js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) { return JS_FALSE; + } /* gosub , if required */ if (pn->pn_kid3) { jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); + &stmtInfo.gosub); if (jmp < 0) return JS_FALSE; JS_ASSERT(cg->stackDepth == depth); } - /* - * Jump over the remaining catch blocks. This will get fixed - * up to jump to after catch/finally. - */ + /* This will get fixed up to jump to after catch/finally. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, + &stmtInfo.catchJump); if (jmp < 0) return JS_FALSE; - - /* - * Save a pointer to the last catch node to handle try-finally - * and try-catch(guard)-finally special cases. - */ - lastCatch = pn3->pn_expr; + if (!iter->pn_kid2) /* leave iter at last catch */ + break; + iter = iter->pn_kid2; } } /* - * Last catch guard jumps to the rethrow code sequence if none of the - * guards match. Target guardJump at the beginning of the rethrow - * sequence, just in case a guard expression throws and leaves the - * stack unbalanced. + * 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 (lastCatch && lastCatch->pn_kid2) { - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); - - /* Sync the stack to take into account pushed exception. */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - + if (pn->pn_kid3 || + (catchJump != -1 && iter->pn_kid1->pn_expr)) { /* - * Rethrow the exception, delegating executing of finally if any - * to the exception handler. + * 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 (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROW) < 0) { - return JS_FALSE; + 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; } - } - JS_ASSERT(cg->stackDepth == depth); - - /* Emit finally handler if any. */ - if (pn->pn_kid3) { /* - * We emit [setsp][gosub] to call try-finally when an exception is - * thrown from try or try-catch blocks. The [gosub] and [retsub] - * opcodes will take care of stacking and rethrowing any exception - * pending across the finally. + * Emit another stack fixup, because the catch could itself + * throw an exception in an unbalanced state, and the finally + * may need to call functions. If there is no finally, only + * guarded catches, the rethrow code below nevertheless needs + * stack fixup. */ finallyCatch = CG_OFFSET(cg); EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); + cg->stackDepth = depth; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; + if (pn->pn_kid3) { + jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.gosub); + if (jmp < 0) + return JS_FALSE; - JS_ASSERT(cg->stackDepth == depth); - JS_ASSERT((uintN)depth <= cg->maxStackDepth); + 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; + } + } + } - /* - * Fix up the gosubs that might have been emitted before non-local - * jumps to the finally code. - */ - if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) + /* + * If we have a finally, it belongs here, and we have to fix up the + * gosubs that might have been emitted before non-local jumps. + */ + if (pn->pn_kid3) { + if (!BackPatch(cx, cg, stmtInfo.gosub, CG_NEXT(cg), JSOP_GOSUB)) return JS_FALSE; /* @@ -4952,7 +3813,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } /* Fix up the end-of-try/catch jumps to come here. */ - if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) + if (!BackPatch(cx, cg, stmtInfo.catchJump, CG_NEXT(cg), JSOP_GOTO)) return JS_FALSE; /* @@ -4978,99 +3839,104 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; } - case TOK_CATCH: - { - ptrdiff_t catchStart, guardJump; +#endif /* JS_HAS_EXCEPTIONS */ - /* - * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, - * and save the block object atom. - */ - stmt = cg->treeContext.topStmt; - JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); - stmt->type = STMT_CATCH; - catchStart = stmt->update; - atom = stmt->atom; - - /* Go up one statement info record to the TRY or FINALLY record. */ - stmt = stmt->down; - JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); - - /* Pick up the pending exception and bind it to the catch variable. */ - if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) - return JS_FALSE; - - /* - * Dup the exception object if there is a guard for rethrowing to use - * it later when rethrowing or in other catches. - */ - if (pn->pn_kid2) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_DUP) < 0) { + case TOK_VAR: + off = noteIndex = -1; + for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + JS_ASSERT(pn2->pn_type == TOK_NAME); + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; - } - } - - pn2 = pn->pn_kid1; - switch (pn2->pn_type) { -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; + op = pn2->pn_op; + if (op == JSOP_ARGUMENTS) { + JS_ASSERT(!pn2->pn_expr); /* JSOP_ARGUMENTS => no initializer */ +#ifdef __GNUC__ + atomIndex = 0; /* quell GCC overwarning */ #endif + } else { + if (pn2->pn_slot >= 0) { + atomIndex = (jsatomid) pn2->pn_slot; + } else { + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + } - case TOK_NAME: - /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */ - pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom)); - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot); - break; + if ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST && + (!(cg->treeContext.flags & TCF_IN_FUNCTION) || + (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { + /* Emit a prolog bytecode to predefine the variable. */ + CG_SWITCH_TO_PROLOG(cg); + if (!UpdateLineNumberNotes(cx, cg, pn2)) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); + CG_SWITCH_TO_MAIN(cg); + } - default: - JS_ASSERT(0); - } + if (pn2->pn_expr) { + if (op == JSOP_SETNAME) + EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); + pn3 = pn2->pn_expr; + if (pn->pn_op == JSOP_DEFCONST && + !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, + pn3)) { + return JS_FALSE; + } + if (!js_EmitTree(cx, cg, pn3)) + return JS_FALSE; + } + } - /* Emit the guard expression, if there is one. */ - if (pn->pn_kid2) { - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, - CG_OFFSET(cg) - catchStart)) { + /* + * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the + * TOK_VAR case, but only the initialized case (a strange one that + * falls out of ECMA-262's grammar) wants to run past this point. + * Both cases must conditionally emit a JSOP_DEFVAR, above. Note + * that the parser error-checks to ensure that pn->pn_count is 1. + * + * XXX Narcissus keeps track of variable declarations in the node + * for the script being compiled, so there's no need to share any + * conditional prolog code generation there. We could do likewise, + * but it's a big change, requiring extra allocation, so probably + * not worth the trouble for SpiderMonkey. + */ + if ((pn->pn_extra & PNX_FORINVAR) && !pn2->pn_expr) + break; + + if (pn2 == pn->pn_head && + js_NewSrcNote(cx, cg, + (pn->pn_op == JSOP_DEFCONST) + ? SRC_CONST + : SRC_VAR) < 0) { return JS_FALSE; } - /* ifeq */ - guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (guardJump < 0) - return JS_FALSE; - GUARDJUMP(*stmt) = guardJump; - - /* Pop duplicated exception object as we no longer need it. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || + 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); + } + tmp = CG_OFFSET(cg); + if (noteIndex >= 0) { + if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) + return JS_FALSE; + } + if (!pn2->pn_next) + break; + off = tmp; + noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); + if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) { return JS_FALSE; } } - - /* Emit the catch body. */ - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - - /* - * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via - * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. - */ - off = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) - return JS_FALSE; - break; - } - - case TOK_VAR: - if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) - return JS_FALSE; + if (pn->pn_extra & PNX_POPVAR) { + if (js_Emit1(cx, cg, JSOP_POP) < 0) + return JS_FALSE; + } break; case TOK_RETURN: @@ -5099,20 +3965,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; break; -#if JS_HAS_GENERATORS - case TOK_YIELD: - if (pn->pn_kid) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_YIELD) < 0) - return JS_FALSE; - break; -#endif - case TOK_LC: #if JS_HAS_XML_SUPPORT if (pn->pn_arity == PN_UNARY) { @@ -5125,37 +3977,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #endif JS_ASSERT(pn->pn_arity == PN_LIST); - - noteIndex = -1; - tmp = CG_OFFSET(cg); - if (pn->pn_extra & PNX_NEEDBRACES) { - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; - } - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_BODY: - JS_ASSERT(pn->pn_arity == PN_LIST); - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } ok = js_PopStatementCG(cx, cg); break; @@ -5169,23 +3995,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * that it appears useless to the compiler. */ useful = wantval = !cx->fp->fun || - !FUN_INTERPRETED(cx->fp->fun) || + !cx->fp->fun->interpreted || (cx->fp->flags & JSFRAME_SPECIAL); if (!useful) { if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) return JS_FALSE; } - - /* - * Don't eliminate apparently useless expressions if they are - * labeled expression statements. The tc->topStmt->update test - * catches the case where we are nesting in js_EmitTree for a - * labeled compound statement. - */ - if (!useful && - (!cg->treeContext.topStmt || - cg->treeContext.topStmt->type != STMT_LABEL || - cg->treeContext.topStmt->update < CG_OFFSET(cg))) { + if (!useful) { CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; if (!js_ReportCompileErrorNumber(cx, cg, JSREPORT_CG | @@ -5195,20 +4011,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } } else { - op = wantval ? JSOP_POPV : JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (!wantval && - pn2->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, wantval ? JSOP_POPV : JSOP_POP) < 0) return JS_FALSE; - } -#endif - if (op != JSOP_NOP) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } } } break; @@ -5220,12 +4026,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!ale) return JS_FALSE; pn2 = pn->pn_expr; - noteType = (pn2->pn_type == TOK_LC || - (pn2->pn_type == TOK_LEXICALSCOPE && - pn2->pn_expr->pn_type == TOK_LC)) - ? SRC_LABELBRACE - : SRC_LABEL; - noteIndex = js_NewSrcNote2(cx, cg, noteType, + noteIndex = js_NewSrcNote2(cx, cg, + (pn2->pn_type == TOK_LC) + ? SRC_LABELBRACE + : SRC_LABEL, (ptrdiff_t) ALE_INDEX(ale)); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { @@ -5235,14 +4039,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* Emit code for the labeled statement. */ js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); - stmtInfo.atom = atom; + stmtInfo.label = atom; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (!js_PopStatementCG(cx, cg)) return JS_FALSE; /* If the statement was compound, emit a note for the end brace. */ - if (noteType == SRC_LABELBRACE) { + if (pn2->pn_type == TOK_LC) { if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; @@ -5284,10 +4088,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ pn2 = pn->pn_left; JS_ASSERT(pn2->pn_type != TOK_RP); - atomIndex = (jsatomid) -1; + atomIndex = (jsatomid) -1; /* Suppress warning. */ switch (pn2->pn_type) { case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; if (pn2->pn_slot >= 0) { atomIndex = (jsatomid) pn2->pn_slot; @@ -5308,17 +4112,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) atomIndex = ALE_INDEX(ale); break; case TOK_LB: - JS_ASSERT(pn2->pn_arity == PN_BINARY); + JS_ASSERT(pn->pn_arity == PN_BINARY); if (!js_EmitTree(cx, cg, pn2->pn_left)) return JS_FALSE; if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - break; -#endif #if JS_HAS_LVALUE_RETURN case TOK_LP: if (!js_EmitTree(cx, cg, pn2)) @@ -5342,10 +4141,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #if JS_HAS_GETTER_SETTER if (op == JSOP_GETTER || op == JSOP_SETTER) { /* We'll emit these prefix bytecodes after emitting the r.h.s. */ - if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } } else #endif /* If += or similar, dup the left operand and get its value. */ @@ -5357,8 +4152,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ? JSOP_GETGVAR : (pn2->pn_op == JSOP_SETARG) ? JSOP_GETARG - : (pn2->pn_op == JSOP_SETLOCAL) - ? JSOP_GETLOCAL : JSOP_GETVAR, atomIndex); break; @@ -5367,10 +4160,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_DOT: if (js_Emit1(cx, cg, JSOP_DUP) < 0) return JS_FALSE; - EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME) - ? JSOP_GETXPROP - : JSOP_GETPROP, - atomIndex); + EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex); break; case TOK_LB: #if JS_HAS_LVALUE_RETURN @@ -5394,28 +4184,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* If += etc., emit the binary operator with a decompiler note. */ if (op != JSOP_NOP) { - /* - * Take care to avoid SRC_ASSIGNOP if the left-hand side is a - * const declared in a function (i.e., with non-negative pn_slot - * and JSPROP_READONLY in pn_attrs), as in this case (just a bit - * further below) we will avoid emitting the assignment op. - */ - if (pn2->pn_type != TOK_NAME || - pn2->pn_slot < 0 || - !(pn2->pn_attrs & JSPROP_READONLY)) { - if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) + if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 || + js_Emit1(cx, cg, op) < 0) { return JS_FALSE; + } } /* Left parts such as a.b.c and a[b].c need a decompiler note. */ if (pn2->pn_type != TOK_NAME && -#if JS_HAS_DESTRUCTURING - pn2->pn_type != TOK_RB && - pn2->pn_type != TOK_RC && -#endif js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op), CG_OFFSET(cg) - top) < 0) { return JS_FALSE; @@ -5440,21 +4216,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) return JS_FALSE; break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) - return JS_FALSE; - break; -#endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) return JS_FALSE; break; #endif - default: - JS_ASSERT(0); + default:; } break; @@ -5474,26 +4242,22 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (jmp < 0) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - - /* - * Because each branch pushes a single value, but our stack budgeting - * analysis ignores branches, we now have to adjust cg->stackDepth to - * ignore the value pushed by the first branch. Execution will follow - * only one path, so we must decrement cg->stackDepth. - * - * Failing to do this will foil code, such as the try/catch/finally - * exception handling code generator, that samples cg->stackDepth for - * use at runtime (JSOP_SETSP), or in let expression and block code - * generation, which must use the stack depth to compute local stack - * indexes correctly. - */ - JS_ASSERT(cg->stackDepth > 0); - cg->stackDepth--; if (!js_EmitTree(cx, cg, pn->pn_kid3)) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; + + /* + * Because each branch pushes a single value, but our stack budgeting + * analysis ignores branches, we now have two values accounted for in + * cg->stackDepth. Execution will follow only one path, so we must + * decrement cg->stackDepth here. Failing to do this will foil code, + * such as the try/catch/finally exception handling code generator, + * that samples cg->stackDepth for use at runtime (JSOP_SETSP). + */ + JS_ASSERT(cg->stackDepth > 1); + cg->stackDepth--; break; case TOK_OR: @@ -5550,8 +4314,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_BITAND: case TOK_EQOP: case TOK_RELOP: +#if JS_HAS_IN_OPERATOR case TOK_IN: +#endif +#if JS_HAS_INSTANCEOF case TOK_INSTANCEOF: +#endif case TOK_SHOP: case TOK_PLUS: case TOK_MINUS: @@ -5571,8 +4339,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } } else { #if JS_HAS_XML_SUPPORT - uintN oldflags; - case TOK_DBLCOLON: if (pn->pn_arity == PN_NAME) { if (!js_EmitTree(cx, cg, pn->pn_expr)) @@ -5581,30 +4347,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; break; } - - /* - * Binary :: has a right operand that brackets arbitrary code, - * possibly including a let (a = b) ... expression. We must clear - * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; #endif - /* Binary operators that evaluate both operands unconditionally. */ if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; -#if JS_HAS_XML_SUPPORT - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; -#endif if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; } break; +#if JS_HAS_EXCEPTIONS case TOK_THROW: +#endif #if JS_HAS_XML_SUPPORT case TOK_AT: case TOK_DEFAULT: @@ -5612,23 +4368,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* FALL THROUGH */ #endif case TOK_UNARYOP: - { - uintN oldflags; - /* Unary op, including unary +/-. */ pn2 = pn->pn_kid; - op = pn->pn_op; - if (op == JSOP_TYPEOF) { - for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid) - continue; - if (pn3->pn_type != TOK_NAME) - op = JSOP_TYPEOFEXPR; - } - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; + op = pn->pn_op; #if JS_HAS_XML_SUPPORT if (op == JSOP_XMLNAME && js_NewSrcNote2(cx, cg, SRC_PCBASE, @@ -5639,22 +4383,31 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; - } case TOK_INC: case TOK_DEC: - { - intN depth; - /* Emit lvalue-specialized code for ++/-- operators. */ pn2 = pn->pn_kid; JS_ASSERT(pn2->pn_type != TOK_RP); op = pn->pn_op; - depth = cg->stackDepth; + + /* + * 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; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; op = pn2->pn_op; if (pn2->pn_slot >= 0) { @@ -5674,18 +4427,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_DOT: if (!EmitPropOp(cx, pn2, op, cg)) return JS_FALSE; - ++depth; break; case TOK_LB: if (!EmitElemOp(cx, pn2, op, cg)) return JS_FALSE; - depth += 2; break; #if JS_HAS_LVALUE_RETURN case TOK_LP: if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - depth = cg->stackDepth; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0) { return JS_FALSE; @@ -5701,7 +4451,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) return JS_FALSE; - depth = cg->stackDepth; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; @@ -5710,20 +4459,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JS_ASSERT(0); } - /* - * 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)depth == cg->maxStackDepth) { - ++cg->maxStackDepth; - } + if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & JOF_POST)) + --cg->stackDepth; break; - } case TOK_DELETE: /* @@ -5734,7 +4472,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) switch (pn2->pn_type) { case TOK_NAME: pn2->pn_op = JSOP_DELNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; op = pn2->pn_op; if (op == JSOP_FALSE) { @@ -5754,51 +4492,23 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) return JS_FALSE; break; -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (pn2->pn_op != JSOP_SETCALL) { - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); - pn2->pn_op = JSOP_SETCALL; - } - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_DELELEM) < 0) - return JS_FALSE; - break; #endif case TOK_LB: if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) return JS_FALSE; break; default: - /* - * If useless, just emit JSOP_TRUE; otherwise convert delete foo() - * to foo(), true (a comma expression, requiring SRC_PCDELTA). - */ useful = JS_FALSE; if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) return JS_FALSE; - if (!useful) { - off = noteIndex = -1; - } else { + if (useful) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - off = CG_OFFSET(cg); - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) + if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } if (js_Emit1(cx, cg, JSOP_TRUE) < 0) return JS_FALSE; - if (noteIndex >= 0) { - tmp = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } } break; @@ -5843,9 +4553,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_NEW: case TOK_LP: - { - uintN oldflags; - /* * Emit function call or operator new (constructor call) code. * First, emit code for the left operand to evaluate the callable or @@ -5878,17 +4585,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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. */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) return JS_FALSE; @@ -5896,129 +4600,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) return JS_FALSE; break; - } - - case TOK_LEXICALSCOPE: - { - JSObject *obj; - jsint count; - - atom = pn->pn_atom; - obj = ATOM_TO_OBJECT(atom); - js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg)); - - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* - * If this lexical scope is not for a catch block, let block or let - * expression, or any kind of for loop (where the scope starts in the - * head after the first part if for (;;), else in the body if for-in); - * and if our container is top-level but not a function body, or else - * a block statement; then emit a SRC_BRACE note. All other container - * statements get braces by default from the decompiler. - */ - noteIndex = -1; - type = pn->pn_expr->pn_type; - if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && - (!(stmt = stmtInfo.down) - ? !(cg->treeContext.flags & TCF_IN_FUNCTION) - : stmt->type == STMT_BLOCK)) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap - /* There must be no source note already output for the next op. */ - JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || - CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || - !GettableNoteForNextOp(cg)); -#endif - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0) - return JS_FALSE; - } - - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - JS_ASSERT(CG_OFFSET(cg) == top); - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - - op = pn->pn_op; - if (op == JSOP_LEAVEBLOCKEXPR) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - } else { - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - EMIT_UINT16_IMM_OP(op, count); - cg->stackDepth -= count; - - ok = js_PopStatementCG(cx, cg); - break; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - /* Let statements have their variable declarations on the left. */ - if (pn->pn_arity == PN_BINARY) { - pn2 = pn->pn_right; - pn = pn->pn_left; - } else { - pn2 = NULL; - } - - /* Non-null pn2 means that pn is the variable list from a let head. */ - JS_ASSERT(pn->pn_arity == PN_LIST); - if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) - return JS_FALSE; - - /* Thus non-null pn2 is the body of the let block or expression. */ - tmp = CG_OFFSET(cg); - if (pn2 && !js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - break; -#endif /* JS_HAS_BLOCK_SCOPE */ - -#if JS_HAS_GENERATORS - case TOK_ARRAYPUSH: - /* - * The array object's stack index is in cg->arrayCompSlot. See below - * under the array initialiser code generator for array comprehension - * special casing. - */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot); - break; -#endif +#if JS_HAS_INITIALIZERS case TOK_RB: -#if JS_HAS_GENERATORS - case TOK_ARRAYCOMP: -#endif /* * Emit code for [a, b, c] of the form: * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; * but use a stack slot for t and avoid dup'ing and popping it via * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList); + ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom, + &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); @@ -6035,29 +4627,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } #endif -#if JS_HAS_GENERATORS - if (pn->pn_type == TOK_ARRAYCOMP) { - uintN saveSlot; - - /* - * Pass the new array's stack index to the TOK_ARRAYPUSH case by - * storing it in pn->pn_extra, then simply traverse the TOK_FOR - * node and its kids under pn2 to generate this comprehension. - */ - JS_ASSERT(cg->stackDepth > 0); - saveSlot = cg->arrayCompSlot; - cg->arrayCompSlot = (uint32) (cg->stackDepth - 1); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->arrayCompSlot = saveSlot; - - /* Emit the usual op needed for decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - } -#endif /* JS_HAS_GENERATORS */ - for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { if (!EmitNumberOp(cx, atomIndex, cg)) return JS_FALSE; @@ -6093,7 +4662,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * but use a stack slot for t and avoid dup'ing and popping it via * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList); + ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom, + &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); @@ -6136,18 +4706,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #if JS_HAS_GETTER_SETTER op = pn2->pn_op; if (op == JSOP_GETTER || op == JSOP_SETTER) { - if (pn3->pn_type != TOK_NUMBER && - ALE_INDEX(ale) >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } #endif /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ if (pn3->pn_type == TOK_NUMBER) { - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) + if (js_NewSrcNote2(cx, cg, SRC_LABEL, 0) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) return JS_FALSE; @@ -6172,28 +4737,22 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); break; #endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ case TOK_RP: - { - uintN oldflags; - /* * The node for (e) has e as its kid, enabling users who want to nest * assignment expressions in conditions to avoid the error correction * done by Condition (from x = y to x == y) by double-parenthesizing. */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; if (js_Emit1(cx, cg, JSOP_GROUP) < 0) return JS_FALSE; break; - } case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) + if (!LookupArgOrVar(cx, &cg->treeContext, pn)) return JS_FALSE; op = pn->pn_op; if (op == JSOP_ARGUMENTS) { @@ -6385,7 +4944,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); if (!ale) return JS_FALSE; - if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) + if (!EmitAtomIndexOp(cx, JSOP_STRING, ALE_INDEX(ale), cg)) return JS_FALSE; if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) return JS_FALSE; @@ -6402,19 +4961,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return ok; } -/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { {"null", 0, 0, 0}, {"if", 0, 0, 0}, - {"if-else", 2, 0, 1}, + {"if-else", 1, 0, 1}, {"while", 1, 0, 1}, {"for", 3, 1, 1}, {"continue", 0, 0, 0}, - {"decl", 1, 1, 1}, + {"var", 0, 0, 0}, {"pcdelta", 1, 0, 1}, {"assignop", 0, 0, 0}, {"cond", 1, 0, 1}, - {"brace", 1, 0, 1}, + {"reserved0", 0, 0, 0}, {"hidden", 0, 0, 0}, {"pcbase", 1, 0, -1}, {"label", 1, 0, 0}, @@ -6424,8 +4982,8 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { {"cont2label", 1, 0, 0}, {"switch", 2, 0, 1}, {"funcdef", 1, 0, 0}, - {"catch", 1, 0, 1}, - {"extended", -1, 0, 0}, + {"catch", 1, 11, 1}, + {"const", 0, 0, 0}, {"newline", 0, 0, 0}, {"setline", 1, 0, 0}, {"xdelta", 0, 0, 0}, @@ -6676,11 +5234,7 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, return JS_TRUE; } -#ifdef DEBUG_notme -#define DEBUG_srcnotesize -#endif - -#ifdef DEBUG_srcnotesize +#ifdef DEBUG_brendan #define NBINS 10 static uint32 hist[NBINS]; @@ -6738,7 +5292,7 @@ js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) */ offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; JS_ASSERT(offset >= 0); - if (offset > 0 && cg->main.noteCount != 0) { + if (offset > 0) { /* NB: Use as much of the first main note's delta as we can. */ sn = cg->main.notes; delta = SN_IS_XDELTA(sn) @@ -6765,7 +5319,7 @@ js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); SN_MAKE_TERMINATOR(¬es[totalCount]); -#ifdef DEBUG_notme +#ifdef DEBUG_brendan { int bin = JS_CeilingLog2(totalCount); if (bin >= NBINS) bin = NBINS - 1; diff --git a/lib/javascript/jsemit.h b/lib/javascript/jsemit.h index 90709c22..6ec4600b 100644 --- a/lib/javascript/jsemit.h +++ b/lib/javascript/jsemit.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -54,104 +53,44 @@ JS_BEGIN_EXTERN_C /* - * NB: If you add enumerators for scope statements, add them between STMT_WITH - * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add - * non-looping statement enumerators, add them before STMT_DO_LOOP or you will - * break the STMT_TYPE_IS_LOOP macro. - * - * Also remember to keep the statementName array in jsemit.c in sync. + * NB: If you add non-loop STMT_* enumerators, do so before STMT_DO_LOOP or + * you will break the STMT_IS_LOOP macro, just below this enum. */ typedef enum JSStmtType { - STMT_LABEL, /* labeled statement: L: s */ - STMT_IF, /* if (then) statement */ - STMT_ELSE, /* else clause of if statement */ - STMT_BODY, /* synthetic body of function with - destructuring formal parameters */ - STMT_BLOCK, /* compound statement: { s1[;... sN] } */ - STMT_SWITCH, /* switch statement */ - STMT_WITH, /* with statement */ - STMT_CATCH, /* catch block */ - STMT_TRY, /* try block */ - STMT_FINALLY, /* finally block */ - STMT_SUBROUTINE, /* gosub-target subroutine body */ - STMT_DO_LOOP, /* do/while loop statement */ - STMT_FOR_LOOP, /* for loop statement */ - STMT_FOR_IN_LOOP, /* for/in loop statement */ - STMT_WHILE_LOOP /* while loop statement */ + STMT_BLOCK = 0, /* compound statement: { s1[;... sN] } */ + STMT_LABEL = 1, /* labeled statement: L: s */ + STMT_IF = 2, /* if (then) statement */ + STMT_ELSE = 3, /* else clause of if statement */ + STMT_SWITCH = 4, /* switch statement */ + STMT_WITH = 5, /* with statement */ + STMT_TRY = 6, /* try statement */ + STMT_CATCH = 7, /* catch block */ + STMT_FINALLY = 8, /* finally statement */ + STMT_SUBROUTINE = 9, /* gosub-target subroutine body */ + STMT_DO_LOOP = 10, /* do/while loop statement */ + STMT_FOR_LOOP = 11, /* for loop statement */ + STMT_FOR_IN_LOOP = 12, /* for/in loop statement */ + STMT_WHILE_LOOP = 13 /* while loop statement */ } JSStmtType; -#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) - -/* - * A comment on the encoding of the JSStmtType enum and type-testing macros: - * - * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may - * become, a lexical scope. It therefore includes block and switch (the two - * low-numbered "maybe" scope types) and excludes with (with has dynamic scope - * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally - * types, which are high-numbered maybe-scope types. - * - * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly - * links to other scoping statement info records. It excludes the two early - * "maybe" types, block and switch, as well as the try and both finally types, - * since try and the other trailing maybe-scope types don't need block scope - * unless they contain let declarations. - * - * We treat with as a static scope because it prevents lexical binding from - * continuing further up the static scope chain. With the "reformed with" - * proposal for JS2, we'll be able to model it statically, too. - */ -#define STMT_TYPE_MAYBE_SCOPE(type) \ - (type != STMT_WITH && \ - STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) - -#define STMT_TYPE_LINKS_SCOPE(type) \ - STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) - -#define STMT_TYPE_IS_TRYING(type) \ - STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) - -#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) - -#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) -#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ - ((stmt)->flags & SIF_SCOPE)) -#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) -#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) +#define STMT_IS_LOOP(stmt) ((stmt)->type >= STMT_DO_LOOP) typedef struct JSStmtInfo JSStmtInfo; struct JSStmtInfo { - uint16 type; /* statement type */ - uint16 flags; /* flags, see below */ + JSStmtType type; /* statement type */ ptrdiff_t update; /* loop update offset (top if none) */ ptrdiff_t breaks; /* offset of last break in loop */ ptrdiff_t continues; /* offset of last continue in loop */ - JSAtom *atom; /* name of LABEL, or block scope object */ + ptrdiff_t gosub; /* offset of last GOSUB for this finally */ + ptrdiff_t catchJump; /* offset of last end-of-catch jump */ + JSAtom *label; /* name of LABEL or CATCH var */ JSStmtInfo *down; /* info for enclosing statement */ - JSStmtInfo *downScope; /* next enclosing lexical scope */ }; -#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ -#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ - -/* - * To reuse space in JSStmtInfo, rename breaks and continues for use during - * try/catch/finally code generation and backpatching. To match most common - * use cases, the macro argument is a struct, not a struct pointer. Only a - * loop, switch, or label statement info record can have breaks and continues, - * and only a for loop has an update backpatch chain, so it's safe to overlay - * these for the "trying" JSStmtTypes. - */ -#define CATCHNOTE(stmt) ((stmt).update) -#define GOSUBS(stmt) ((stmt).breaks) -#define GUARDJUMP(stmt) ((stmt).continues) - -#define AT_TOP_LEVEL(tc) \ - (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) - #define SET_STATEMENT_TOP(stmt, top) \ - ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) + ((stmt)->update = (top), (stmt)->breaks = \ + (stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1)) struct JSTreeContext { /* tree context for semantic checks */ uint16 flags; /* statement state flags, see below */ @@ -160,12 +99,6 @@ struct JSTreeContext { /* tree context for semantic checks */ uint32 globalUses; /* optimizable global var uses in total */ uint32 loopyGlobalUses;/* optimizable global var uses in loops */ JSStmtInfo *topStmt; /* top of statement info stack */ - JSStmtInfo *topScopeStmt; /* top lexical scope statement */ - JSObject *blockChain; /* compile time block scope chain (NB: one - deeper than the topScopeStmt/downScope - chain when in head of let block/expr) */ - JSParseNode *blockNode; /* parse node for a lexical scope. - XXX combine with blockChain? */ JSAtomList decls; /* function, const, and var declarations */ JSParseNode *nodeList; /* list of recyclable parse-node structs */ }; @@ -174,23 +107,18 @@ struct JSTreeContext { /* tree context for semantic checks */ #define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ #define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ #define TCF_RETURN_VOID 0x08 /* function has 'return;' */ -#define TCF_RETURN_FLAGS 0x0C /* propagate these out of blocks */ #define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ #define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */ #define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ #define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ -#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */ -#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */ -#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ -#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */ +#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, \ (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \ - (tc)->topStmt = (tc)->topScopeStmt = NULL, \ - (tc)->blockChain = NULL, \ - ATOM_LIST_INIT(&(tc)->decls), \ - (tc)->nodeList = NULL, (tc)->blockNode = NULL) + (tc)->topStmt = NULL, ATOM_LIST_INIT(&(tc)->decls), \ + (tc)->nodeList = NULL) #define TREE_CONTEXT_FINISH(tc) \ ((void)0) @@ -296,8 +224,6 @@ struct JSCodeGenerator { ptrdiff_t spanDepTodo; /* offset from main.base of potentially unoptimized spandeps */ - uintN arrayCompSlot; /* stack slot of array in comprehension */ - uintN emitLevel; /* js_EmitTree recursion level */ JSAtomList constList; /* compile time constants */ JSCodeGenerator *parent; /* Enclosing function or global context */ @@ -388,20 +314,13 @@ extern JSBool js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off); -/* Test whether we're in a statement of given type. */ -extern JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type); - /* Test whether we're in a with statement. */ -#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) - -/* - * Test whether atom refers to a global variable (or is a reference error). - * Return true in *loopyp if any loops enclose the lexical reference, false - * otherwise. - */ extern JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp); +js_InWithStatement(JSTreeContext *tc); + +/* Test whether we're in a catch block with exception named by atom. */ +extern JSBool +js_InCatchBlock(JSTreeContext *tc, JSAtom *atom); /* * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. @@ -410,15 +329,6 @@ extern void js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top); -/* - * Push a block scope statement and link blockAtom's object-valued key into - * tc->blockChain. To pop this statement info record, use js_PopStatement as - * usual, or if appropriate (if generating code), js_PopStatementCG. - */ -extern void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top); - /* * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it * is up to the caller to free it. @@ -427,8 +337,7 @@ extern void js_PopStatement(JSTreeContext *tc); /* - * Like js_PopStatement(&cg->treeContext), also patch breaks and continues - * unless the top statement info record represents a try-catch-finally suite. + * Like js_PopStatement(&cg->treeContext), also patch breaks and continues. * May fail if a jump offset overflows. */ extern JSBool @@ -454,36 +363,12 @@ extern JSBool js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp); -/* - * Find a lexically scoped variable (one declared by let, catch, or an array - * comprehension) named by atom, looking in tc's compile-time scopes. - * - * If a WITH statement is reached along the scope stack, return its statement - * info record, so callers can tell that atom is ambiguous. If slotp is not - * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. - * This means that if slotp is not null, all the block objects on the lexical - * scope chain must have had their depth slots computed by the code generator, - * so the caller must be under js_EmitTree. - * - * In any event, directly return the statement info record in which atom was - * found. Otherwise return null. - */ -extern JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, - JSBool letdecl); - /* * Emit code into cg for the tree rooted at pn. */ extern JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); -/* - * Emit function code into cg for the tree rooted at body. - */ -extern JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); - /* * Emit code into cg for the tree rooted at body, then create a persistent * script for fun from cg. @@ -510,48 +395,27 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, * * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its * initializers need to match the order here. - * - * Note on adding new source notes: every pair of bytecodes (A, B) where A and - * B have disjoint sets of source notes that could apply to each bytecode may - * reuse the same note type value for two notes (snA, snB) that have the same - * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is - * why SRC_IF and SRC_INITPROP have the same value below. For bad historical - * reasons, some bytecodes below that could be overlayed have not been, but - * before using SRC_EXTENDED, consider compressing the existing note types. - * - * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such - * incompatible source note or other bytecode changes. */ typedef enum JSSrcNoteType { SRC_NULL = 0, /* terminates a note vector */ SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ - SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or - to an index label in a regular (structuring) - or a destructuring object initialiser */ SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */ SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */ SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; also used on JSOP_ENDINIT if extra comma at end of array literal: [1,2,,] */ - SRC_DECL = 6, /* type of a declaration (var, const, let*) */ - SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment - operation, with SRC_DECL_* offset operand */ - SRC_PCDELTA = 7, /* distance forward from comma-operator to - next POP, or from CONDSWITCH to first CASE - opcode, etc. -- always a forward delta */ - SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ + SRC_VAR = 6, /* JSOP_NAME/SETNAME/FORNAME in a var decl */ + 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_BRACE = 10, /* mandatory brace, for scope or to avoid - dangling else */ + SRC_RESERVED0 = 10, /* reserved for future use */ SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ - SRC_PCBASE = 12, /* distance back from annotated getprop or - setprop op to left-most obj.prop.subprop - bytecode -- always a backward delta */ - SRC_METHODBASE = 13, /* SRC_PCBASE variant for obj.function::foo - gets and sets; disjoint from SRC_LABEL by - bytecode to which it applies */ + 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 */ @@ -561,27 +425,12 @@ typedef enum JSSrcNoteType { 2nd off to first JSOP_CASE if condswitch */ SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ SRC_CATCH = 20, /* catch block has guard */ - SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ + SRC_CONST = 21, /* JSOP_SETCONST in a const decl */ SRC_NEWLINE = 22, /* bytecode follows a source newline */ SRC_SETLINE = 23, /* a file-absolute source line number note */ SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ } JSSrcNoteType; -/* - * Constants for the SRC_DECL source note. Note that span-dependent bytecode - * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need - * to be adjusted, but these "offsets" are too small to span a span-dependent - * instruction, so can be used to denote distinct declaration syntaxes to the - * decompiler. - * - * NB: the var_prefix array in jsopcode.c depends on these dense indexes from - * SRC_DECL_VAR through SRC_DECL_LET. - */ -#define SRC_DECL_VAR 0 -#define SRC_DECL_CONST 1 -#define SRC_DECL_LET 2 -#define SRC_DECL_NONE 3 - #define SN_TYPE_BITS 5 #define SN_DELTA_BITS 3 #define SN_XDELTA_BITS 6 diff --git a/lib/javascript/jsexn.c b/lib/javascript/jsexn.c index e60f85e4..c637a563 100644 --- a/lib/javascript/jsexn.c +++ b/lib/javascript/jsexn.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -60,56 +60,40 @@ #include "jsopcode.h" #include "jsscript.h" -/* Forward declarations for js_ErrorClass's initializer. */ +#if JS_HAS_ERROR_EXCEPTIONS +#if !JS_HAS_EXCEPTIONS +# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS" +#endif + +/* XXX consider adding rt->atomState.messageAtom */ +static char js_message_str[] = "message"; +static char js_filename_str[] = "fileName"; +static char js_lineno_str[] = "lineNumber"; +static char js_stack_str[] = "stack"; + +/* Forward declarations for ExceptionClass's initializer. */ static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static void exn_finalize(JSContext *cx, JSObject *obj); -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg); - -static void -exn_finalize(JSContext *cx, JSObject *obj); - -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj); - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp); - -JSClass js_ErrorClass = { - js_Error_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Error), +static JSClass ExceptionClass = { + "Error", + JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize, NULL, NULL, NULL, Exception, - NULL, NULL, exn_mark, NULL + NULL, NULL, NULL, 0 }; -typedef struct JSStackTraceElem { - JSString *funName; - size_t argc; - const char *filename; - uintN ulineno; -} JSStackTraceElem; - +/* + * A copy of the JSErrorReport originally generated. + */ typedef struct JSExnPrivate { - /* A copy of the JSErrorReport originally generated. */ - JSErrorReport *errorReport; - JSString *message; - JSString *filename; - uintN lineno; - size_t stackDepth; - JSStackTraceElem stackElems[1]; + JSErrorReport *errorReport; } JSExnPrivate; -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv); - static JSErrorReport * CopyErrorReport(JSContext *cx, JSErrorReport *report) { @@ -123,12 +107,8 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) * jschar array with characters for uclinebuf and uctokenptr * char array with characters for linebuf and tokenptr * char array with characters for filename - * Such layout together with the properties enforced by the following - * asserts does not need any extra alignment padding. + * Such layout does not need any extra alignment padding. */ - JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); - JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); - size_t filenameSize; size_t linebufSize; size_t uclinebufSize; @@ -228,314 +208,97 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report) return copy; } -static jsval * -GetStackTraceValueBuffer(JSExnPrivate *priv) -{ - /* - * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals - * that helps to produce more informative stack traces. The following - * assert allows us to assume that no gap after stackElems is necessary to - * align the buffer properly. - */ - JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); - - return (jsval *)(priv->stackElems + priv->stackDepth); -} - -static JSBool -InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, - JSString *filename, uintN lineno, JSErrorReport *report) -{ - JSCheckAccessOp checkAccess; - JSErrorReporter older; - JSExceptionState *state; - jsval callerid, v; - JSStackFrame *fp, *fpstop; - size_t stackDepth, valueCount, size; - JSBool overflow; - JSExnPrivate *priv; - JSStackTraceElem *elem; - jsval *values; - - JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); - - /* - * Prepare stack trace data. - * - * Set aside any error reporter for cx and save its exception state - * so we can suppress any checkAccess failures. Such failures should stop - * the backtrace procedure, not result in a failure of this constructor. - */ - checkAccess = cx->runtime->checkObjectAccess; - older = JS_SetErrorReporter(cx, NULL); - state = JS_SaveExceptionState(cx); - - callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); - stackDepth = 0; - valueCount = 0; - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->fun && fp->argv) { - if (checkAccess) { - v = fp->argv[-2]; - if (!JSVAL_IS_PRIMITIVE(v) && - !checkAccess(cx, JSVAL_TO_OBJECT(v), callerid, - JSACC_READ, &v /* ignored */)) { - break; - } - } - valueCount += fp->argc; - } - ++stackDepth; - } - JS_RestoreExceptionState(cx, state); - JS_SetErrorReporter(cx, older); - fpstop = fp; - - size = offsetof(JSExnPrivate, stackElems); - overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); - size += stackDepth * sizeof(JSStackTraceElem); - overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); - size += valueCount * sizeof(jsval); - if (overflow) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - priv = (JSExnPrivate *)JS_malloc(cx, size); - if (!priv) - return JS_FALSE; - - /* - * We initialize errorReport with a copy of report after setting the - * private slot, to prevent GC accessing a junk value we clear the field - * here. - */ - priv->errorReport = NULL; - priv->message = message; - priv->filename = filename; - priv->lineno = lineno; - priv->stackDepth = stackDepth; - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (fp = cx->fp; fp != fpstop; fp = fp->down) { - if (!fp->fun) { - elem->funName = NULL; - elem->argc = 0; - } else { - elem->funName = fp->fun->atom - ? ATOM_TO_STRING(fp->fun->atom) - : cx->runtime->emptyString; - elem->argc = fp->argc; - memcpy(values, fp->argv, fp->argc * sizeof(jsval)); - values += fp->argc; - } - elem->ulineno = 0; - elem->filename = NULL; - if (fp->script) { - elem->filename = fp->script->filename; - if (fp->pc) - elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); - } - ++elem; - } - JS_ASSERT(priv->stackElems + stackDepth == elem); - JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); - - OBJ_SET_SLOT(cx, exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); - - if (report) { - /* - * Construct a new copy of the error report struct. We can't use the - * error report struct that was passed in, because it's allocated on - * the stack, and also because it may point to transient data in the - * JSTokenStream. - */ - priv->errorReport = CopyErrorReport(cx, report); - if (!priv->errorReport) { - /* The finalizer realeases priv since it is in the private slot. */ - return JS_FALSE; - } - } - - return JS_TRUE; -} - +/* + * Copy everything interesting about an error into allocated memory. + */ static JSExnPrivate * -GetExnPrivate(JSContext *cx, JSObject *obj) +exn_newPrivate(JSContext *cx, JSErrorReport *report) { - jsval privateValue; - JSExnPrivate *priv; + JSExnPrivate *newPrivate; - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass); - privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (JSVAL_IS_VOID(privateValue)) + newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate)); + if (!newPrivate) + return NULL; + newPrivate->errorReport = CopyErrorReport(cx, report); + if (!newPrivate->errorReport) { + JS_free(cx, newPrivate); return NULL; - priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue); - JS_ASSERT(priv); - return priv; -} - -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSExnPrivate *priv; - JSStackTraceElem *elem; - size_t vcount, i; - jsval *vp, v; - - priv = GetExnPrivate(cx, obj); - if (priv) { - GC_MARK(cx, priv->message, "exception message"); - GC_MARK(cx, priv->filename, "exception filename"); - elem = priv->stackElems; - for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { - if (elem->funName) - GC_MARK(cx, elem->funName, "stack trace function name"); - if (elem->filename) - js_MarkScriptFilename(elem->filename); - vcount += elem->argc; - } - vp = GetStackTraceValueBuffer(priv); - for (i = 0; i != vcount; ++i, ++vp) { - v = *vp; - if (JSVAL_IS_GCTHING(v)) - GC_MARK(cx, JSVAL_TO_GCTHING(v), "stack trace argument"); - } } - return 0; + return newPrivate; } static void exn_finalize(JSContext *cx, JSObject *obj) { - JSExnPrivate *priv; + JSExnPrivate *privateData; + jsval privateValue; - priv = GetExnPrivate(cx, obj); - if (priv) { - if (priv->errorReport) - JS_free(cx, priv->errorReport); - JS_free(cx, priv); - } -} + privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj) -{ - JSAtomState *atomState; - uintN i; - JSAtom *atom; - JSObject *pobj; - JSProperty *prop; - - JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); - static const uint16 offsets[] = { - (uint16)offsetof(JSAtomState, messageAtom), - (uint16)offsetof(JSAtomState, fileNameAtom), - (uint16)offsetof(JSAtomState, lineNumberAtom), - (uint16)offsetof(JSAtomState, stackAtom), - }; - - atomState = &cx->runtime->atomState; - for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { - atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSExnPrivate *priv; - JSString *str; - JSAtom *atom; - JSString *stack; - const char *prop; - jsval v; - - *objp = NULL; - priv = GetExnPrivate(cx, obj); - if (priv && JSVAL_IS_STRING(id)) { - str = JSVAL_TO_STRING(id); - - atom = cx->runtime->atomState.messageAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_message_str; - v = STRING_TO_JSVAL(priv->message); - goto define; - } - - atom = cx->runtime->atomState.fileNameAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_fileName_str; - v = STRING_TO_JSVAL(priv->filename); - goto define; - } - - atom = cx->runtime->atomState.lineNumberAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_lineNumber_str; - v = INT_TO_JSVAL(priv->lineno); - goto define; - } - - atom = cx->runtime->atomState.stackAtom; - if (str == ATOM_TO_STRING(atom)) { - stack = StackTraceToString(cx, priv); - if (!stack) - return JS_FALSE; - - /* Allow to GC all things that were used to build stack trace. */ - priv->stackDepth = 0; - prop = js_stack_str; - v = STRING_TO_JSVAL(stack); - goto define; + if (!JSVAL_IS_VOID(privateValue)) { + privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); + if (privateData) { + if (privateData->errorReport) + JS_free(cx, privateData->errorReport); + JS_free(cx, privateData); } } - return JS_TRUE; - - define: - if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE)) - return JS_FALSE; - *objp = obj; - return JS_TRUE; } JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn) { JSObject *obj; - JSExnPrivate *priv; + JSExnPrivate *privateData; + jsval privateValue; if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); - if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) + if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass) return NULL; - priv = GetExnPrivate(cx, obj); - if (!priv) + privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (JSVAL_IS_VOID(privateValue)) return NULL; - return priv->errorReport; + privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue); + if (!privateData) + return NULL; + + JS_ASSERT(privateData->errorReport); + return privateData->errorReport; } +/* + * This must be kept in synch with the exceptions array below. + * XXX use a jsexn.tbl file a la jsopcode.tbl + */ +typedef enum JSExnType { + JSEXN_NONE = -1, + JSEXN_ERR, + JSEXN_INTERNALERR, + JSEXN_EVALERR, + JSEXN_RANGEERR, + JSEXN_REFERENCEERR, + JSEXN_SYNTAXERR, + JSEXN_TYPEERR, + JSEXN_URIERR, + JSEXN_LIMIT +} JSExnType; + struct JSExnSpec { int protoIndex; const char *name; - JSProtoKey key; JSNative native; }; /* - * All *Error constructors share the same JSClass, js_ErrorClass. But each + * All *Error constructors share the same JSClass, ExceptionClass. But each * constructor function for an *Error class must have a distinct native 'call' * function pointer, in order for instanceof to work properly across multiple * standard class sets. See jsfun.c:fun_hasInstance. */ #define MAKE_EXCEPTION_CTOR(name) \ +const char js_##name##_str[] = #name; \ static JSBool \ name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ { \ @@ -554,66 +317,83 @@ MAKE_EXCEPTION_CTOR(URIError) #undef MAKE_EXCEPTION_CTOR static struct JSExnSpec exceptions[] = { - {JSEXN_NONE, js_Error_str, JSProto_Error, Error}, - {JSEXN_ERR, js_InternalError_str, JSProto_InternalError, InternalError}, - {JSEXN_ERR, js_EvalError_str, JSProto_EvalError, EvalError}, - {JSEXN_ERR, js_RangeError_str, JSProto_RangeError, RangeError}, - {JSEXN_ERR, js_ReferenceError_str, JSProto_ReferenceError, ReferenceError}, - {JSEXN_ERR, js_SyntaxError_str, JSProto_SyntaxError, SyntaxError}, - {JSEXN_ERR, js_TypeError_str, JSProto_TypeError, TypeError}, - {JSEXN_ERR, js_URIError_str, JSProto_URIError, URIError}, - {0, NULL, JSProto_Null, NULL} + { JSEXN_NONE, js_Error_str, Error }, + { JSEXN_ERR, js_InternalError_str, InternalError }, + { JSEXN_ERR, js_EvalError_str, EvalError }, + { JSEXN_ERR, js_RangeError_str, RangeError }, + { JSEXN_ERR, js_ReferenceError_str, ReferenceError }, + { JSEXN_ERR, js_SyntaxError_str, SyntaxError }, + { JSEXN_ERR, js_TypeError_str, TypeError }, + { JSEXN_ERR, js_URIError_str, URIError }, + {0,NULL,NULL} }; -static JSString * -ValueToShortSource(JSContext *cx, jsval v) -{ - JSString *str; - - /* Avoid toSource bloat and fallibility for object types. */ - if (JSVAL_IS_PRIMITIVE(v)) { - str = js_ValueToSource(cx, v); - } else if (VALUE_IS_FUNCTION(cx, v)) { - /* - * XXX Avoid function decompilation bloat for now. - */ - str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); - if (!str && !(str = js_ValueToSource(cx, v))) { - /* - * Continue to soldier on if the function couldn't be - * converted into a string. - */ - JS_ClearPendingException(cx); - str = JS_NewStringCopyZ(cx, "[unknown function]"); - } - } else { - /* - * XXX Avoid toString on objects, it takes too long and uses too much - * memory, for too many classes (see Mozilla bug 166743). - */ - char buf[100]; - JS_snprintf(buf, sizeof buf, "[object %s]", - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); - str = JS_NewStringCopyZ(cx, buf); - } - return str; -} - -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv) +static JSBool +InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, + JSString *filename, uintN lineno) { + JSCheckAccessOp checkAccess; + JSErrorReporter older; + JSExceptionState *state; jschar *stackbuf; size_t stacklen, stackmax; - JSStackTraceElem *elem, *endElem; - jsval *values; - size_t i; - JSString *str; + JSStackFrame *fp; + jsval callerid, v; + JSBool ok; + JSString *argsrc, *stack; + uintN i, ulineno; const char *cp; char ulnbuf[11]; - /* After this point, failing control flow must goto bad. */ + if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, js_filename_str, + STRING_TO_JSVAL(filename), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + if (!JS_DefineProperty(cx, obj, js_lineno_str, + INT_TO_JSVAL(lineno), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + + /* + * Set the 'stack' property. + * + * First, set aside any error reporter for cx and save its exception state + * so we can suppress any checkAccess failures. Such failures should stop + * the backtrace procedure, not result in a failure of this constructor. + */ + checkAccess = cx->runtime->checkObjectAccess; + if (checkAccess) { + older = JS_SetErrorReporter(cx, NULL); + state = JS_SaveExceptionState(cx); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else { + older = NULL; + state = NULL; + } +#endif + callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); + + /* + * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes + * the next free jschar slot, and with room for at most stackmax non-null + * jschars. If stackbuf is non-null, it always contains an extra slot for + * the null terminator we'll store at the end, as a backstop. + * + * All early returns must goto done after this point, till the after-loop + * cleanup code has run! + */ stackbuf = NULL; stacklen = stackmax = 0; + ok = JS_TRUE; /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ #define STACK_LENGTH_LIMIT JS_BIT(20) @@ -626,8 +406,10 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) goto done; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ + if (!ptr_) { \ + ok = JS_FALSE; \ + goto done; \ + } \ stackbuf = ptr_; \ } \ stackbuf[stacklen++] = (c); \ @@ -645,70 +427,123 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) } \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ + if (!ptr_) { \ + ok = JS_FALSE; \ + goto done; \ + } \ stackbuf = ptr_; \ } \ js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ stacklen += length_; \ JS_END_MACRO - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { - if (elem->funName) { - APPEND_STRING_TO_STACK(elem->funName); + for (fp = cx->fp; fp; fp = fp->down) { + if (checkAccess) { + v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + ok = checkAccess(cx, JSVAL_TO_OBJECT(fp->argv[-2]), callerid, + JSACC_READ, &v /* ignored */); + if (!ok) { + ok = JS_TRUE; + break; + } + } + } + + if (fp->fun) { + if (fp->fun->atom) + APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom)); + APPEND_CHAR_TO_STACK('('); - for (i = 0; i != elem->argc; i++, values++) { + for (i = 0; i < fp->argc; i++) { + /* Avoid toSource bloat and fallibility for object types. */ + v = fp->argv[i]; + if (JSVAL_IS_PRIMITIVE(v)) { + argsrc = js_ValueToSource(cx, v); + } else if (JSVAL_IS_FUNCTION(cx, v)) { + /* XXX Avoid function decompilation bloat for now. */ + argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v)); + if (!argsrc) + argsrc = js_ValueToSource(cx, v); + } else { + /* XXX Avoid toString on objects, it takes too long and + uses too much memory, for too many classes (see + Mozilla bug 166743). */ + char buf[100]; + JS_snprintf(buf, sizeof buf, "[object %s]", + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); + argsrc = JS_NewStringCopyZ(cx, buf); + } + if (!argsrc) { + ok = JS_FALSE; + goto done; + } if (i > 0) APPEND_CHAR_TO_STACK(','); - str = ValueToShortSource(cx, *values); - if (!str) - goto bad; - APPEND_STRING_TO_STACK(str); + APPEND_STRING_TO_STACK(argsrc); } APPEND_CHAR_TO_STACK(')'); } + APPEND_CHAR_TO_STACK('@'); - if (elem->filename) { - for (cp = elem->filename; *cp; cp++) + if (fp->script && fp->script->filename) { + for (cp = fp->script->filename; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } APPEND_CHAR_TO_STACK(':'); - JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); - for (cp = ulnbuf; *cp; cp++) - APPEND_CHAR_TO_STACK(*cp); + if (fp->script && fp->pc) { + ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); + JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno); + for (cp = ulnbuf; *cp; cp++) + APPEND_CHAR_TO_STACK(*cp); + } else { + APPEND_CHAR_TO_STACK('0'); + } APPEND_CHAR_TO_STACK('\n'); } + #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK #undef STACK_LENGTH_LIMIT - done: - if (stacklen == 0) { - JS_ASSERT(!stackbuf); - return cx->runtime->emptyString; +done: + if (checkAccess) { + if (ok) + JS_RestoreExceptionState(cx, state); + else + JS_DropExceptionState(cx, state); + JS_SetErrorReporter(cx, older); } - if (stacklen < stackmax) { - /* - * Realloc can fail when shrinking on some FreeBSD versions, so - * don't use JS_realloc here; simply let the oversized allocation - * be owned by the string in that rare case. - */ - void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar)); - if (shrunk) - stackbuf = shrunk; - } - - stackbuf[stacklen] = 0; - str = js_NewString(cx, stackbuf, stacklen, 0); - if (str) - return str; - - bad: - if (stackbuf) + if (!ok) { JS_free(cx, stackbuf); - return NULL; + return JS_FALSE; + } + + if (!stackbuf) { + stack = cx->runtime->emptyString; + } else { + /* NB: if stackbuf was allocated, it has room for the terminator. */ + JS_ASSERT(stacklen <= stackmax); + if (stacklen < stackmax) { + /* + * Realloc can fail when shrinking on some FreeBSD versions, so + * don't use JS_realloc here; simply let the oversized allocation + * be owned by the string in that rare case. + */ + void *shrunk = realloc(stackbuf, (stacklen+1) * sizeof(jschar)); + if (shrunk) + stackbuf = shrunk; + } + stackbuf[stacklen] = 0; + stack = js_NewString(cx, stackbuf, stacklen, 0); + if (!stack) { + JS_free(cx, stackbuf); + return JS_FALSE; + } + } + return JS_DefineProperty(cx, obj, js_stack_str, + STRING_TO_JSVAL(stack), + NULL, NULL, JSPROP_ENUMERATE); } /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 @@ -751,7 +586,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) rval); if (!ok) goto out; - obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); + obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(*rval), NULL); if (!obj) { ok = JS_FALSE; goto out; @@ -763,7 +598,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ - if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) + if (OBJ_GET_CLASS(cx, obj) == &ExceptionClass) OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ @@ -811,10 +646,9 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0; } - ok = (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || - InitExnPrivate(cx, obj, message, filename, lineno, NULL); + ok = InitExceptionObject(cx, obj, message, filename, lineno); - out: +out: cx->creatingException = JS_FALSE; return ok; } @@ -908,13 +742,13 @@ exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } vp[0] = STRING_TO_JSVAL(message); - if (!JS_GetProperty(cx, obj, js_fileName_str, &vp[1]) || + 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_lineNumber_str, &vp[2]) || + if (!JS_GetProperty(cx, obj, js_lineno_str, &vp[2]) || !js_ValueToECMAUint32 (cx, vp[2], &lineno)) { return JS_FALSE; } @@ -1009,23 +843,8 @@ static JSFunctionSpec exception_methods[] = { JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj) { - JSObject *obj_proto, *protos[JSEXN_LIMIT]; int i; - - /* - * If lazy class initialization occurs for any Error subclass, then all - * classes are initialized, starting with Error. To avoid reentry and - * redundant initialization, we must not pass a null proto parameter to - * js_NewObject below, when called for the Error superclass. We need to - * ensure that Object.prototype is the proto of Error.prototype. - * - * See the equivalent code to ensure that parent_proto is non-null when - * JS_InitClass calls js_NewObject, in jsapi.c. - */ - if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &obj_proto)) { - return NULL; - } + JSObject *protos[JSEXN_LIMIT]; if (!js_EnterLocalRootScope(cx)) return NULL; @@ -1034,15 +853,14 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) for (i = 0; exceptions[i].name != 0; i++) { JSAtom *atom; JSFunction *fun; - JSObject *funobj; JSString *nameString; int protoIndex = exceptions[i].protoIndex; /* Make the prototype for the current constructor name. */ - protos[i] = js_NewObject(cx, &js_ErrorClass, + protos[i] = js_NewObject(cx, &ExceptionClass, (protoIndex != JSEXN_NONE) ? protos[protoIndex] - : obj_proto, + : NULL, obj); if (!protos[i]) break; @@ -1050,20 +868,20 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) /* 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) + break; + /* Make a constructor function for the current name. */ - atom = cx->runtime->atomState.classAtoms[exceptions[i].key]; fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); if (!fun) break; /* Make this constructor make objects of class Exception. */ - fun->clasp = &js_ErrorClass; - - /* Extract the constructor object. */ - funobj = fun->object; + fun->clasp = &ExceptionClass; /* Make the prototype and constructor links. */ - if (!js_SetClassPrototype(cx, funobj, protos[i], + if (!js_SetClassPrototype(cx, fun->object, protos[i], JSPROP_READONLY | JSPROP_PERMANENT)) { break; } @@ -1080,10 +898,6 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) JSPROP_ENUMERATE)) { break; } - - /* Finally, stash the constructor for later uses. */ - if (!js_SetClassObject(cx, obj, exceptions[i].key, funobj)) - break; } js_LeaveLocalRootScope(cx); @@ -1100,12 +914,12 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } - if (!JS_DefineProperty(cx, protos[0], js_fileName_str, + if (!JS_DefineProperty(cx, protos[0], js_filename_str, STRING_TO_JSVAL(cx->runtime->emptyString), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } - if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str, + if (!JS_DefineProperty(cx, protos[0], js_lineno_str, INT_TO_JSVAL(0), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; @@ -1121,19 +935,12 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) return protos[0]; } -const JSErrorFormatString* -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber) -{ - const JSErrorFormatString *errorString = NULL; - - if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) { - errorString = cx->localeCallbacks - ->localeGetErrorMessage(userRef, locale, errorNumber); - } - if (!errorString) - errorString = js_GetErrorMessage(userRef, locale, errorNumber); - return errorString; -} +static JSExnType errorToExceptionNum[] = { +#define MSG_DEF(name, number, count, exception, format) \ + exception, +#include "js.msg" +#undef MSG_DEF +}; #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* For use below... get character strings for error name and exception name */ @@ -1149,13 +956,14 @@ JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; - const JSErrorFormatString *errorString; JSExnType exn; jsval tv[4]; JSTempValueRooter tvr; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; + uintN lineno; + JSExnPrivate *privateData; /* * Tell our caller to report immediately if cx has no active frames, or if @@ -1167,8 +975,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) /* Find the exception index associated with this error. */ errorNumber = (JSErrNum) reportp->errorNumber; - errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); - exn = errorString ? errorString->exnType : JSEXN_NONE; + exn = errorToExceptionNum[errorNumber]; JS_ASSERT(exn < JSEXN_LIMIT); #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) @@ -1207,13 +1014,12 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key), - &errProto); + ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto); if (!ok) goto out; tv[0] = OBJECT_TO_JSVAL(errProto); - errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); + errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL); if (!errObject) { ok = JS_FALSE; goto out; @@ -1227,17 +1033,34 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) } tv[2] = STRING_TO_JSVAL(messageStr); - filenameStr = JS_NewStringCopyZ(cx, reportp->filename); - if (!filenameStr) { + if (reportp) { + filenameStr = JS_NewStringCopyZ(cx, reportp->filename); + if (!filenameStr) { + ok = JS_FALSE; + goto out; + } + tv[3] = STRING_TO_JSVAL(filenameStr); + lineno = reportp->lineno; + } else { + filenameStr = cx->runtime->emptyString; + lineno = 0; + } + ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno); + if (!ok) + goto out; + + /* + * Construct a new copy of the error report struct, and store it in the + * exception object's private data. We can't use the error report struct + * that was passed in, because it's stack-allocated, and also because it + * may point to transient data in the JSTokenStream. + */ + privateData = exn_newPrivate(cx, reportp); + if (!privateData) { ok = JS_FALSE; goto out; } - tv[3] = STRING_TO_JSVAL(filenameStr); - - ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, - reportp->lineno, reportp); - if (!ok) - goto out; + OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData)); JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); @@ -1249,14 +1072,16 @@ out: cx->creatingException = JS_FALSE; return ok; } +#endif /* JS_HAS_ERROR_EXCEPTIONS */ + +#if JS_HAS_EXCEPTIONS JSBool js_ReportUncaughtException(JSContext *cx) { - jsval exn; + jsval exn, *vp; JSObject *exnObject; - jsval vp[5]; - JSTempValueRooter tvr; + void *mark; JSErrorReport *reportp, report; JSString *str; const char *bytes; @@ -1276,30 +1101,40 @@ js_ReportUncaughtException(JSContext *cx) */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; + vp = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + mark = NULL; +#endif } else { exnObject = JSVAL_TO_OBJECT(exn); + vp = js_AllocStack(cx, 5, &mark); + if (!vp) { + ok = JS_FALSE; + goto out; + } vp[0] = exn; - memset(vp + 1, 0, sizeof vp - sizeof vp[0]); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(vp), vp, &tvr); } - JS_ClearPendingException(cx); +#if JS_HAS_ERROR_EXCEPTIONS reportp = js_ErrorFromException(cx, exn); +#else + reportp = NULL; +#endif /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { - if (exnObject) + if (vp) vp[1] = STRING_TO_JSVAL(str); - bytes = js_GetStringBytes(cx->runtime, str); + bytes = js_GetStringBytes(str); } ok = JS_TRUE; if (!reportp && exnObject && - OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { + OBJ_GET_CLASS(cx, exnObject) == &ExceptionClass) { const char *filename; uint32 lineno; @@ -1309,7 +1144,7 @@ js_ReportUncaughtException(JSContext *cx) if (JSVAL_IS_STRING(vp[2])) bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); - ok = JS_GetProperty(cx, exnObject, js_fileName_str, &vp[3]); + ok = JS_GetProperty(cx, exnObject, js_filename_str, &vp[3]); if (!ok) goto out; str = js_ValueToString(cx, vp[3]); @@ -1319,7 +1154,7 @@ js_ReportUncaughtException(JSContext *cx) } filename = StringToFilename(cx, str); - ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &vp[4]); + ok = JS_GetProperty(cx, exnObject, js_lineno_str, &vp[4]); if (!ok) goto out; ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); @@ -1341,8 +1176,11 @@ js_ReportUncaughtException(JSContext *cx) js_ReportErrorAgain(cx, bytes, reportp); } + JS_ClearPendingException(cx); out: if (exnObject) - JS_POP_TEMP_ROOT(cx, &tvr); + js_FreeStack(cx, mark); return ok; } + +#endif /* JS_HAS_EXCEPTIONS */ diff --git a/lib/javascript/jsexn.h b/lib/javascript/jsexn.h index 58cb984d..aeaab0a5 100644 --- a/lib/javascript/jsexn.h +++ b/lib/javascript/jsexn.h @@ -46,14 +46,24 @@ JS_BEGIN_EXTERN_C -extern JSClass js_ErrorClass; - /* * Initialize the exception constructor/prototype hierarchy. */ extern JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj); +/* + * String constants naming the exception classes. + */ +extern const char js_Error_str[]; +extern const char js_InternalError_str[]; +extern const char js_EvalError_str[]; +extern const char js_RangeError_str[]; +extern const char js_ReferenceError_str[]; +extern const char js_SyntaxError_str[]; +extern const char js_TypeError_str[]; +extern const char js_URIError_str[]; + /* * Given a JSErrorReport, check to see if there is an exception associated with * the error number. If there is, then create an appropriate exception object, @@ -87,10 +97,6 @@ js_ReportUncaughtException(JSContext *cx); extern JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn); -extern const JSErrorFormatString * -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, - const uintN errorNumber); - JS_END_EXTERN_C #endif /* jsexn_h___ */ diff --git a/lib/javascript/jsfile.c b/lib/javascript/jsfile.c index ed1c4e8b..2237dbd4 100644 --- a/lib/javascript/jsfile.c +++ b/lib/javascript/jsfile.c @@ -44,7 +44,6 @@ #if JS_HAS_FILE_OBJECT #include "jsstddef.h" -#include "jsfile.h" /* ----------------- Platform-specific includes and defines ----------------- */ #if defined(XP_WIN) || defined(XP_OS2) @@ -240,6 +239,7 @@ typedef struct JSFile { } JSFile; /* a few forward declarations... */ +static JSClass file_class; JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); @@ -782,7 +782,7 @@ js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ /* Buffered version of PR_Read. Used by js_FileRead */ static int32 -js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) +js_BufferedRead(JSFile * f, char *buf, int32 len) { int32 count = 0; @@ -796,7 +796,7 @@ js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) count++; } - if (len > 0) { + if (len>0) { count += (!f->isNative) ? PR_Read(f->handle, buf, len) : fread(buf, 1, len, f->nativehandle); @@ -870,7 +870,7 @@ js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) break; case UCS2: - count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; + count = js_BufferedRead(file, (char*)buf, len*2) >> 1; if (count == -1) return 0; @@ -1129,7 +1129,7 @@ js_size(JSContext *cx, JSFile *file) : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JSVAL_VOID; + return JS_FALSE; } return INT_TO_JSVAL(info.size); @@ -1201,7 +1201,7 @@ js_name(JSContext *cx, JSFile *file, jsval *vp) static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSString *strmode, *strtype; char *ctype, *mode; int32 mask, type; @@ -1386,7 +1386,7 @@ out: static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); SECURITY_CHECK(cx, NULL, "close", file); @@ -1428,7 +1428,7 @@ out: static JSBool file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); SECURITY_CHECK(cx, NULL, "remove", file); JSFILE_CHECK_NATIVE("remove"); @@ -1453,7 +1453,7 @@ out: static JSBool file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); char *dest = NULL; PRFileDesc *handle = NULL; char *buffer; @@ -1548,7 +1548,7 @@ out: static JSBool file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); char *dest; SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ @@ -1577,7 +1577,7 @@ out: static JSBool file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); SECURITY_CHECK(cx, NULL, "flush", file); JSFILE_CHECK_NATIVE("flush"); @@ -1599,7 +1599,7 @@ out: static JSBool file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSString *str; int32 count; uintN i; @@ -1627,7 +1627,7 @@ out: static JSBool file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSString *str; SECURITY_CHECK(cx, NULL, "writeln", file); @@ -1658,7 +1658,7 @@ out: static JSBool file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); jsuint i; jsuint limit; JSObject *array; @@ -1695,7 +1695,7 @@ out: static JSBool file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSString *str; int32 want, count; jschar *buf; @@ -1733,7 +1733,7 @@ out: static JSBool file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSString *str; jschar *buf = NULL, *tmp; int32 offset, read; @@ -1818,7 +1818,7 @@ out: static JSBool file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSObject *array; jsint len; jsval line; @@ -1847,7 +1847,7 @@ out: static JSBool file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); int32 toskip; int32 pos; @@ -1889,7 +1889,7 @@ file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { PRDir *dir; PRDirEntry *entry; - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); JSObject *array; JSObject *eachFile; jsint len; @@ -1907,7 +1907,7 @@ file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (JSVAL_IS_REGEXP(cx, argv[0])) { re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); }else - if (VALUE_IS_FUNCTION(cx, argv[0])) { + if (JSVAL_IS_FUNCTION(cx, argv[0])) { func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, @@ -1989,7 +1989,7 @@ out: static JSBool file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); SECURITY_CHECK(cx, NULL, "mkdir", file); JSFILE_CHECK_ONE_ARG("mkdir"); @@ -2031,43 +2031,30 @@ out: static JSBool file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path)); return JS_TRUE; } static JSBool file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); char url[MAX_PATH_LENGTH]; jschar *urlChars; - size_t len; - JSString *str; - JSFILE_CHECK_NATIVE("toURL"); + JSFILE_CHECK_NATIVE("toURL"); sprintf(url, "file://%s", file->path); - - len = strlen(url); - urlChars = js_InflateString(cx, url, &len); - if (!urlChars) - return JS_FALSE; - str = js_NewString(cx, urlChars, len, 0); - if (!str) { - JS_free(cx, urlChars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - /* TODO: js_escape in jsstr.h may go away at some point */ - return js_str_escape(cx, obj, 0, rval, rval); + urlChars = js_InflateString(cx, url, strlen(url)); + if (urlChars == NULL) return JS_FALSE; + *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0)); + if (!js_str_escape(cx, obj, 0, rval, rval)) return JS_FALSE; + + return JS_TRUE; out: *rval = JSVAL_VOID; return JS_FALSE; @@ -2077,7 +2064,7 @@ out: static void file_finalize(JSContext *cx, JSObject *obj) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); if(file) { /* Close the file before exiting. */ @@ -2127,7 +2114,7 @@ js_NewFileObject(JSContext *cx, char *filename) JSObject *obj; JSFile *file; - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + obj = JS_NewObject(cx, &file_class, NULL, NULL); if (!obj){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); @@ -2146,7 +2133,7 @@ js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, JSObject *obj; JSFile *file; - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + obj = JS_NewObject(cx, &file_class, NULL, NULL); if (!obj){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); @@ -2182,7 +2169,7 @@ file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* Replace obj with a new File object. */ - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); + obj = JS_NewObject(cx, &file_class, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -2283,7 +2270,7 @@ static JSPropertySpec file_props[] = { static JSBool file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); char *bytes; JSString *str; jsint tiny; @@ -2557,12 +2544,10 @@ file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!strcmp(entry->name, prop_name)){ bytes = js_combinePath(cx, file->path, prop_name); *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); - PR_CloseDir(dir); JS_free(cx, bytes); - return !JSVAL_IS_NULL(*vp); + return JS_TRUE; } } - PR_CloseDir(dir); } } return JS_TRUE; @@ -2574,7 +2559,7 @@ out: static JSBool file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL); jsint slot; if (JSVAL_IS_STRING(id)){ @@ -2634,11 +2619,11 @@ file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFile *file; - file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); + 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, obj, &js_FileClass, NULL)) { + 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))) { JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); @@ -2671,8 +2656,8 @@ file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } /* Declare class */ -JSClass js_FileClass = { - "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), +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 }; @@ -2686,7 +2671,7 @@ js_InitFileClass(JSContext *cx, JSObject* obj) char *currentdir; char separator[2]; - file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, + file = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, 1, file_props, file_functions, NULL, NULL); if (!file) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, diff --git a/lib/javascript/jsfile.h b/lib/javascript/jsfile.h index 78707e8b..741c5bdd 100644 --- a/lib/javascript/jsfile.h +++ b/lib/javascript/jsfile.h @@ -41,16 +41,10 @@ #define _jsfile_h__ #if JS_HAS_FILE_OBJECT - -#include "jsobj.h" - extern JS_PUBLIC_API(JSObject*) js_InitFileClass(JSContext *cx, JSObject* obj); extern JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *bytes); - -extern JSClass js_FileClass; - #endif /* JS_HAS_FILE_OBJECT */ #endif /* _jsfile_h__ */ diff --git a/lib/javascript/jsfun.c b/lib/javascript/jsfun.c index 2a2df539..68372e12 100644 --- a/lib/javascript/jsfun.c +++ b/lib/javascript/jsfun.c @@ -66,10 +66,6 @@ #include "jsstr.h" #include "jsexn.h" -#if JS_HAS_GENERATORS -# include "jsiter.h" -#endif - /* Generic function/call/arguments tinyids -- also reflected bit numbers. */ enum { CALL_ARGUMENTS = -1, /* predefined arguments local variable */ @@ -91,6 +87,8 @@ enum { #define SET_OVERRIDE_BIT(fp, tinyid) \ ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) +#if JS_HAS_ARGS_OBJECT + JSBool js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) { @@ -236,13 +234,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, JSObject * js_GetArgsObject(JSContext *cx, JSStackFrame *fp) { - JSObject *argsobj, *global, *parent; - - /* - * We must be in a function activation; the function must be lightweight - * or else fp must have a variable object. - */ - JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj)); + JSObject *argsobj; /* Skip eval and debugger frames. */ while (fp->flags & JSFRAME_SPECIAL) @@ -256,25 +248,9 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp) /* Link the new object to fp so it can get actual argument values. */ argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } - - /* - * Give arguments an intrinsic scope chain link to fp's global object. - * Since the arguments object lacks a prototype because js_ArgumentsClass - * is not initialized, js_NewObject won't assign a default parent to it. - * - * Therefore if arguments is used as the head of an eval scope chain (via - * a direct or indirect call to eval(program, arguments)), any reference - * to a standard class object in the program will fail to resolve due to - * js_GetClassPrototype not being able to find a global object containing - * the standard prototype by starting from arguments and following parent. - */ - global = fp->scopeChain; - while ((parent = OBJ_GET_PARENT(cx, global)) != NULL) - global = parent; - argsobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(global); fp->argsobj = argsobj; return argsobj; } @@ -418,7 +394,7 @@ args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; default: - if (FUN_INTERPRETED(fp->fun) && + if (fp->fun->interpreted && (uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { fp->argv[slot] = *vp; @@ -542,25 +518,6 @@ args_enumerate(JSContext *cx, JSObject *obj) return JS_TRUE; } -#if JS_HAS_GENERATORS -/* - * If a generator-iterator's arguments or call object escapes, it needs to - * mark its generator object. - */ -static uint32 -args_or_call_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSStackFrame *fp; - - fp = JS_GetPrivate(cx, obj); - if (fp && (fp->flags & JSFRAME_GENERATOR)) - GC_MARK(cx, FRAME_TO_GENERATOR(fp)->obj, "FRAME_TO_GENERATOR(fp)->obj"); - return 0; -} -#else -# define args_or_call_mark NULL -#endif - /* * The Arguments class is not initialized via JS_InitClass, and must not be, * because its name is "Object". Per ECMA, that causes instances of it to @@ -574,18 +531,18 @@ args_or_call_mark(JSContext *cx, JSObject *obj, void *arg) */ JSClass js_ArgumentsClass = { js_Object_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, args_delProperty, - args_getProperty, args_setProperty, - args_enumerate, (JSResolveOp) args_resolve, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, args_delProperty, + args_getProperty, args_setProperty, + args_enumerate, (JSResolveOp) args_resolve, + JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS }; +#endif /* JS_HAS_ARGS_OBJECT */ + +#if JS_HAS_CALL_OBJECT + JSObject * js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) { @@ -608,13 +565,12 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) /* Create the call object and link it to its stack frame. */ callobj = js_NewObject(cx, &js_CallClass, NULL, parent); if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } fp->callobj = callobj; /* Make callobj be the scope chain and the variables object. */ - JS_ASSERT(fp->scopeChain == parent); fp->scopeChain = callobj; fp->varobj = callobj; return callobj; @@ -938,18 +894,16 @@ call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) JSClass js_CallClass = { js_Call_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Call), - JS_PropertyStub, JS_PropertyStub, - call_getProperty, call_setProperty, - call_enumerate, (JSResolveOp)call_resolve, - call_convert, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, + call_getProperty, call_setProperty, + call_enumerate, (JSResolveOp)call_resolve, + call_convert, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS }; +#endif /* JS_HAS_CALL_OBJECT */ + /* * 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 @@ -1020,6 +974,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) switch (slot) { case CALL_ARGUMENTS: +#if JS_HAS_ARGS_OBJECT /* Warn if strict about f.arguments or equivalent unqualified uses. */ if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, @@ -1035,8 +990,15 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) *vp = JSVAL_NULL; } break; +#else /* !JS_HAS_ARGS_OBJECT */ + *vp = OBJECT_TO_JSVAL(fp ? obj : NULL); + break; +#endif /* !JS_HAS_ARGS_OBJECT */ case ARGS_LENGTH: + if (!JS_VERSION_IS_ECMA(cx)) + *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); + else case FUN_ARITY: *vp = INT_TO_JSVAL((jsint)fun->nargs); break; @@ -1094,15 +1056,6 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSString *str; JSAtom *prototypeAtom; - /* - * No need to reflect fun.prototype in 'fun.prototype = ...' or in an - * unqualified reference to prototype, which the emitter looks up as a - * hidden atom when attempting to bind to a formal parameter or local - * variable slot. - */ - if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_HIDDEN)) - return JS_TRUE; - if (!JSVAL_IS_STRING(id)) return JS_TRUE; @@ -1111,6 +1064,10 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, if (!fun || !fun->object) return JS_TRUE; + /* No need to reflect fun.prototype in 'fun.prototype = ...'. */ + if (flags & JSRESOLVE_ASSIGNING) + return JS_TRUE; + /* * Ok, check whether id is 'prototype' and bootstrap the function object's * prototype property. @@ -1137,7 +1094,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * root until then to protect pval in case it is figuratively * up in the air, with no strong refs protecting it. */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); parentProto = JSVAL_TO_OBJECT(pval); } } @@ -1146,7 +1103,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * Beware of the wacky case of a user function named Object -- trying * to find a prototype for that will recur back here _ad perniciem_. */ - if (!parentProto && fun->atom == CLASS_ATOM(cx, Object)) + if (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom) return JS_TRUE; /* @@ -1168,7 +1125,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, */ if (!js_SetClassPrototype(cx, obj, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return JS_FALSE; } *objp = obj; @@ -1193,7 +1150,6 @@ static void fun_finalize(JSContext *cx, JSObject *obj) { JSFunction *fun; - JSScript *script; /* No valid function object should lack private data, but check anyway. */ fun = (JSFunction *) JS_GetPrivate(cx, obj); @@ -1201,15 +1157,13 @@ fun_finalize(JSContext *cx, JSObject *obj) return; if (fun->object == obj) fun->object = NULL; + JS_ATOMIC_DECREMENT(&fun->nrefs); + if (fun->nrefs) + return; /* Null-check required since the parser sets interpreted very early. */ - if (FUN_INTERPRETED(fun) && fun->u.i.script && - js_IsAboutToBeFinalized(cx, fun)) - { - script = fun->u.i.script; - fun->u.i.script = NULL; - js_DestroyScript(cx, script); - } + if (fun->interpreted && fun->u.script) + js_DestroyScript(cx, fun->u.script); } #if JS_HAS_XDR @@ -1228,13 +1182,13 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; JSFunction *fun; - uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ + JSString *atomstr; JSTempValueRooter tvr; uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ - uint16 extraUnused; /* variable for no longer used field */ - JSAtom *propAtom; + char *propname; JSScopeProperty *sprop; uint32 userid; /* NB: holds a signed int-tagged jsval */ + JSAtom *atom; uintN i, n, dupflag; uint32 type; JSBool ok; @@ -1252,43 +1206,36 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) fun = (JSFunction *) JS_GetPrivate(cx, *objp); if (!fun) return JS_TRUE; - if (!FUN_INTERPRETED(fun)) { + if (!fun->interpreted) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION, JS_GetFunctionName(fun)); return JS_FALSE; } - nullAtom = !fun->atom; - flagsword = ((uint32)fun->u.i.nregexps << 16) | fun->flags; - extraUnused = 0; + atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; + flagsword = ((uint32)fun->nregexps << 16) | fun->flags; } else { fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); if (!fun) return JS_FALSE; + atomstr = NULL; } /* From here on, control flow must flow through label out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, fun->object, &tvr); + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(fun->object), &tvr); ok = JS_TRUE; - if (!JS_XDRUint32(xdr, &nullAtom)) - goto bad; - if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) - goto bad; - - if (!JS_XDRUint16(xdr, &fun->nargs) || - !JS_XDRUint16(xdr, &extraUnused) || - !JS_XDRUint16(xdr, &fun->u.i.nvars) || + if (!JS_XDRStringOrNull(xdr, &atomstr) || + !JS_XDRUint16(xdr, &fun->nargs) || + !JS_XDRUint16(xdr, &fun->extra) || + !JS_XDRUint16(xdr, &fun->nvars) || !JS_XDRUint32(xdr, &flagsword)) { goto bad; } - /* Assert that all previous writes of extraUnused were writes of 0. */ - JS_ASSERT(extraUnused == 0); - /* do arguments and local vars */ if (fun->object) { - n = fun->nargs + fun->u.i.nvars; + n = fun->nargs + fun->nvars; if (xdr->mode == JSXDR_ENCODE) { JSScope *scope; JSScopeProperty **spvec, *auto_spvec[8]; @@ -1313,7 +1260,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JS_ASSERT(nargs++ <= fun->nargs); spvec[sprop->shortid] = sprop; } else if (sprop->getter == js_GetLocalVariable) { - JS_ASSERT(nvars++ <= fun->u.i.nvars); + JS_ASSERT(nvars++ <= fun->nvars); spvec[fun->nargs + sprop->shortid] = sprop; } } @@ -1326,10 +1273,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) ? JSXDR_FUNCONST : JSXDR_FUNVAR; userid = INT_TO_JSVAL(sprop->shortid); - propAtom = JSID_TO_ATOM(sprop->id); - if (!JS_XDRUint32(xdr, &type) || + /* XXX lossy conversion, need new XDR version for ECMAv3 */ + propname = JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))); + if (!propname || + !JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { + !JS_XDRCString(xdr, &propname)) { if (mark) JS_ARENA_RELEASE(&cx->tempPool, mark); goto bad; @@ -1345,7 +1294,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) if (!JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { + !JS_XDRCString(xdr, &propname)) { goto bad; } JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || @@ -1359,20 +1308,23 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) setter = js_SetLocalVariable; if (type == JSXDR_FUNCONST) attrs |= JSPROP_READONLY; - JS_ASSERT(nvars++ <= fun->u.i.nvars); + JS_ASSERT(nvars++ <= fun->nvars); } else { getter = NULL; setter = NULL; } + atom = js_Atomize(cx, propname, strlen(propname), 0); + JS_free(cx, propname); + if (!atom) + goto bad; /* Flag duplicate argument if atom is bound in fun->object. */ dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), - ATOM_TO_JSID(propAtom)) + ATOM_TO_JSID(atom)) ? SPROP_IS_DUPLICATE : 0; - if (!js_AddHiddenProperty(cx, fun->object, - ATOM_TO_JSID(propAtom), + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), getter, setter, SPROP_INVALID_SLOT, attrs | JSPROP_SHARED, dupflag | SPROP_HAS_SHORTID, @@ -1383,15 +1335,23 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) } } - if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) + if (!js_XDRScript(xdr, &fun->u.script, NULL)) goto bad; if (xdr->mode == JSXDR_DECODE) { - fun->flags = (uint16) flagsword | JSFUN_INTERPRETED; - fun->u.i.nregexps = (uint16) (flagsword >> 16); + fun->interpreted = JS_TRUE; + fun->flags = (uint8) flagsword; + fun->nregexps = (uint16) (flagsword >> 16); *objp = fun->object; - js_CallNewScriptHook(cx, fun->u.i.script, fun); + if (atomstr) { + /* XXX only if this was a top-level function! */ + fun->atom = js_AtomizeString(cx, atomstr, 0); + if (!fun->atom) + goto bad; + } + + js_CallNewScriptHook(cx, fun->u.script, fun); } out: @@ -1409,6 +1369,8 @@ bad: #endif /* !JS_HAS_XDR */ +#if JS_HAS_INSTANCEOF + /* * [[HasInstance]] internal method for Function objects: fetch the .prototype * property of its 'this' parameter, and walks the prototype chain of v (only @@ -1443,6 +1405,12 @@ fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); } +#else /* !JS_HAS_INSTANCEOF */ + +#define fun_hasInstance NULL + +#endif /* !JS_HAS_INSTANCEOF */ + static uint32 fun_mark(JSContext *cx, JSObject *obj, void *arg) { @@ -1450,11 +1418,11 @@ fun_mark(JSContext *cx, JSObject *obj, void *arg) fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) { - GC_MARK(cx, fun, "private"); + JS_MarkGCThing(cx, fun, js_private_str, arg); if (fun->atom) - GC_MARK_ATOM(cx, fun->atom); - if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_MarkScript(cx, fun->u.i.script); + GC_MARK_ATOM(cx, fun->atom, arg); + if (fun->interpreted && fun->u.script) + js_MarkScript(cx, fun->u.script, arg); } return 0; } @@ -1465,7 +1433,7 @@ fun_reserveSlots(JSContext *cx, JSObject *obj) JSFunction *fun; fun = (JSFunction *) JS_GetPrivate(cx, obj); - return (fun && FUN_INTERPRETED(fun)) ? fun->u.i.nregexps : 0; + return fun ? fun->nregexps : 0; } /* @@ -1475,8 +1443,7 @@ fun_reserveSlots(JSContext *cx, JSObject *obj) */ JS_FRIEND_DATA(JSClass) js_FunctionClass = { js_Function_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Function), + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2), JS_PropertyStub, JS_PropertyStub, fun_getProperty, JS_PropertyStub, fun_enumerate, (JSResolveOp)fun_resolve, @@ -1499,7 +1466,7 @@ js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, JS_ASSERT(JS_ObjectIsFunction(cx, obj)); } else { fval = argv[-1]; - if (!VALUE_IS_FUNCTION(cx, fval)) { + if (!JSVAL_IS_FUNCTION(cx, fval)) { /* * If we don't have a function to start off with, try converting * the object to a function. If that doesn't work, complain. @@ -1512,7 +1479,7 @@ js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, } argv[-1] = fval; } - if (!VALUE_IS_FUNCTION(cx, fval)) { + if (!JSVAL_IS_FUNCTION(cx, fval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, js_toString_str, @@ -1553,6 +1520,7 @@ fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 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) { @@ -1567,7 +1535,7 @@ fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; fval = argv[-1]; - if (!VALUE_IS_FUNCTION(cx, fval)) { + if (!JSVAL_IS_FUNCTION(cx, fval)) { str = JS_ValueToString(cx, fval); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -1612,7 +1580,9 @@ fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) js_FreeStack(cx, mark); return ok; } +#endif /* JS_HAS_CALL_FUNCTION */ +#if JS_HAS_APPLY_FUNCTION static JSBool fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -1620,9 +1590,9 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; JSObject *aobj; jsuint length; - JSBool arraylike, ok; void *mark; uintN i; + JSBool ok; JSStackFrame *fp; if (argc == 0) { @@ -1634,7 +1604,7 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; fval = argv[-1]; - if (!VALUE_IS_FUNCTION(cx, fval)) { + if (!JSVAL_IS_FUNCTION(cx, fval)) { str = JS_ValueToString(cx, fval); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -1655,17 +1625,17 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) argc = 0; } else { /* The second arg must be an array (or arguments object). */ - arraylike = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(argv[1])) { - aobj = JSVAL_TO_OBJECT(argv[1]); - if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) - return JS_FALSE; - } - if (!arraylike) { + if (JSVAL_IS_PRIMITIVE(argv[1]) || + (aobj = JSVAL_TO_OBJECT(argv[1]), + OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass && + OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass)) + { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "apply"); + JSMSG_BAD_APPLY_ARGS); return JS_FALSE; } + if (!js_GetLengthProperty(cx, aobj, &length)) + return JS_FALSE; } } @@ -1674,7 +1644,7 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; /* Allocate stack space for fval, obj, and the args. */ - argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1); + argc = (uintN)JS_MIN(length, ARGC_LIMIT - 1); sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; @@ -1702,69 +1672,18 @@ out: js_FreeStack(cx, mark); return ok; } - -#ifdef NARCISSUS -static JSBool -fun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *aobj; - uintN length, i; - void *mark; - jsval *sp, *newsp, *oldsp; - JSStackFrame *fp; - JSBool ok; - - if (JSVAL_IS_PRIMITIVE(argv[0]) || - (aobj = JSVAL_TO_OBJECT(argv[0]), - OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && - OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); - return JS_FALSE; - } - - if (!js_GetLengthProperty(cx, aobj, &length)) - return JS_FALSE; - - if (length >= ARRAY_INIT_LIMIT) - length = ARRAY_INIT_LIMIT - 1; - newsp = sp = js_AllocStack(cx, 2 + length, &mark); - if (!sp) - return JS_FALSE; - - fp = cx->fp; - oldsp = fp->sp; - *sp++ = OBJECT_TO_JSVAL(obj); - *sp++ = JSVAL_NULL; /* This is filled automagically. */ - for (i = 0; i < length; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; - } - - oldsp = fp->sp; - fp->sp = sp; - ok = js_InvokeConstructor(cx, newsp, length); - - *rval = fp->sp[-1]; - fp->sp = oldsp; -out: - js_FreeStack(cx, mark); - return ok; -} -#endif +#endif /* JS_HAS_APPLY_FUNCTION */ static JSFunctionSpec function_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, fun_toSource, 0,0,0}, #endif {js_toString_str, fun_toString, 1,0,0}, +#if JS_HAS_APPLY_FUNCTION {"apply", fun_apply, 2,0,0}, +#endif +#if JS_HAS_CALL_FUNCTION {call_str, fun_call, 1,0,0}, -#ifdef NARCISSUS - {"__applyConstructor__", fun_applyConstructor, 1,0,0}, #endif {0,0,0,0,0} }; @@ -1772,23 +1691,22 @@ static JSFunctionSpec function_methods[] = { JSBool js_IsIdentifier(JSString *str) { - size_t length; - jschar c, *chars, *end, *s; + size_t n; + jschar *s, c; - length = JSSTRING_LENGTH(str); - if (length == 0) + n = JSSTRING_LENGTH(str); + if (n == 0) return JS_FALSE; - chars = JSSTRING_CHARS(str); - c = *chars; + s = JSSTRING_CHARS(str); + c = *s; if (!JS_ISIDSTART(c)) return JS_FALSE; - end = chars + length; - for (s = chars + 1; s != end; ++s) { - c = *s; + for (n--; n != 0; n--) { + c = *++s; if (!JS_ISIDENT(c)) return JS_FALSE; } - return !js_IsKeyword(chars, length); + return JS_TRUE; } static JSBool @@ -1813,7 +1731,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSBool ok; fp = cx->fp; - if (!(fp->flags & JSFRAME_CONSTRUCTING)) { + if (fp && !(fp->flags & JSFRAME_CONSTRUCTING)) { obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); if (!obj) return JS_FALSE; @@ -1823,6 +1741,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (fun) return JS_TRUE; +#if JS_HAS_CALL_OBJECT /* * NB: (new Function) is not lexically closed by its caller, it's just an * anonymous function in the top-level scope that its constructor inhabits. @@ -1834,9 +1753,15 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * top-level reachable from scopeChain (in HTML frames, e.g.). */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); +#else + /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */ + parent = NULL; +#endif fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, - cx->runtime->atomState.anonymousAtom); + JS_VERSION_IS_ECMA(cx) + ? cx->runtime->atomState.anonymousAtom + : NULL); if (!fun) return JS_FALSE; @@ -1848,7 +1773,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * are built for Function.prototype.call or .apply activations that invoke * Function indirectly from a script. */ - JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); + JS_ASSERT(!fp->script && fp->fun && fp->fun->u.native == Function); caller = JS_GetScriptedCaller(cx, fp); if (caller) { filename = caller->script->filename; @@ -1861,10 +1786,8 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* Belt-and-braces: check that the caller has access to parent. */ - if (!js_CheckPrincipalsAccess(cx, parent, principals, - CLASS_ATOM(cx, Function))) { + if (!js_CheckPrincipalsAccess(cx, parent, principals, js_Function_str)) return JS_FALSE; - } n = argc ? argc - 1 : 0; if (n > 0) { @@ -2090,18 +2013,18 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); if (!fun) goto bad; - fun->u.i.script = js_NewScript(cx, 1, 0, 0); - if (!fun->u.i.script) + fun->u.script = js_NewScript(cx, 0, 0, 0); + if (!fun->u.script) goto bad; - fun->u.i.script->code[0] = JSOP_STOP; - fun->flags |= JSFUN_INTERPRETED; + fun->interpreted = JS_TRUE; return proto; bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } +#if JS_HAS_CALL_OBJECT JSObject * js_InitCallClass(JSContext *cx, JSObject *obj) { @@ -2119,6 +2042,7 @@ js_InitCallClass(JSContext *cx, JSObject *obj) OBJ_SET_PROTO(cx, proto, NULL); return proto; } +#endif JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, @@ -2136,30 +2060,34 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, return NULL; } - /* Protect fun from any potential GC callback. */ + /* 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 newborn[GCX_PRIVATE]. + * 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; + fun->u.native = native; fun->nargs = nargs; + fun->extra = 0; + fun->nvars = 0; fun->flags = flags & JSFUN_FLAGS_MASK; - fun->u.n.native = native; - fun->u.n.extra = 0; - fun->u.n.spare = 0; + fun->interpreted = JS_FALSE; + fun->nregexps = 0; + fun->spare = 0; fun->atom = atom; fun->clasp = NULL; /* Link fun to funobj and vice versa. */ if (!js_LinkFunctionObject(cx, fun, funobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; fun = NULL; } @@ -2180,7 +2108,7 @@ js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) return NULL; fun = (JSFunction *) JS_GetPrivate(cx, funobj); if (!js_LinkFunctionObject(cx, fun, newfunobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } return newfunobj; @@ -2191,7 +2119,10 @@ js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) { if (!fun->object) fun->object = funobj; - return JS_SetPrivate(cx, funobj, fun); + if (!JS_SetPrivate(cx, funobj, fun)) + return JS_FALSE; + JS_ATOMIC_INCREMENT(&fun->nrefs); + return JS_TRUE; } JSFunction * @@ -2229,7 +2160,7 @@ js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) return NULL; - obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; + obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; } } if (!obj) { @@ -2247,7 +2178,7 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) JSStackFrame *caller; JSPrincipals *principals; - if (VALUE_IS_FUNCTION(cx, *vp)) + if (JSVAL_IS_FUNCTION(cx, *vp)) return JSVAL_TO_OBJECT(*vp); fun = js_ValueToFunction(cx, vp, flags); @@ -2264,10 +2195,14 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) 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 - ? fun->atom - : cx->runtime->atomState.anonymousAtom)) { + ? js_AtomToPrintableString(cx, fun->atom) + : js_anonymous_str)) { return NULL; } return funobj; @@ -2293,38 +2228,31 @@ js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) void js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) { - JSStackFrame *fp; + JSType type; + JSString *fallback; JSString *str; - JSTempValueRooter tvr; - const char *bytes, *source; - for (fp = cx->fp; fp && !fp->spbase; fp = fp->down) - continue; + /* + * We provide the typename as the fallback to handle the case when + * valueOf is not a function, which prevents ValueToString from being + * called as the default case inside js_DecompileValueGenerator (and + * so recursing back to here). + */ + type = JS_TypeOfValue(cx, *vp); + fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); str = js_DecompileValueGenerator(cx, - (fp && fp->spbase <= vp && vp < fp->sp) - ? vp - fp->sp - : (flags & JSV2F_SEARCH_STACK) + (flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK + : cx->fp + ? vp - cx->fp->sp : JSDVG_IGNORE_STACK, *vp, - NULL); + fallback); if (str) { - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - bytes = JS_GetStringBytes(str); - if (flags & JSV2F_ITERATOR) { - source = js_ValueToPrintableSource(cx, *vp); - if (source) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR, - bytes, js_iterator_str, source); - } - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - (uintN)((flags & JSV2F_CONSTRUCT) - ? JSMSG_NOT_CONSTRUCTOR - : JSMSG_NOT_FUNCTION), - bytes); - } - JS_POP_TEMP_ROOT(cx, &tvr); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + (uintN)((flags & JSV2F_CONSTRUCT) + ? JSMSG_NOT_CONSTRUCTOR + : JSMSG_NOT_FUNCTION), + JS_GetStringBytes(str)); } } diff --git a/lib/javascript/jsfun.h b/lib/javascript/jsfun.h index 8d5c1850..25aa1032 100644 --- a/lib/javascript/jsfun.h +++ b/lib/javascript/jsfun.h @@ -48,41 +48,36 @@ JS_BEGIN_EXTERN_C struct JSFunction { + jsrefcount nrefs; /* number of referencing objects */ JSObject *object; /* back-pointer to GC'ed object header */ - uint16 nargs; /* minimum number of actual arguments */ - uint16 flags; /* bound method and other flags, see jsapi.h */ union { - struct { - uint16 extra; /* number of arg slots for local GC roots */ - uint16 spare; /* reserved for future use */ - JSNative native; /* native method pointer or null */ - } n; - struct { - uint16 nvars; /* number of local variables */ - uint16 nregexps; /* number of regular expressions literals */ - JSScript *script; /* interpreted bytecode descriptor or null */ - } i; + JSNative native; /* native method pointer or null */ + JSScript *script; /* interpreted bytecode descriptor or null */ } u; + uint16 nargs; /* minimum number of actual arguments */ + uint16 extra; /* number of arg slots for local GC roots */ + uint16 nvars; /* number of local variables */ + uint8 flags; /* bound method and other flags, see jsapi.h */ + JSPackedBool interpreted; /* use u.script if true, u.native if false */ + uint16 nregexps; /* number of regular expressions literals */ + uint16 spare; /* reserved for future use */ JSAtom *atom; /* name for diagnostics and decompiling */ JSClass *clasp; /* if non-null, constructor for this class */ }; -#define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */ - -#define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED) -#define FUN_NATIVE(fun) (FUN_INTERPRETED(fun) ? NULL : (fun)->u.n.native) -#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) +#define FUN_NATIVE(fun) ((fun)->interpreted ? NULL : (fun)->u.native) +#define FUN_SCRIPT(fun) ((fun)->interpreted ? (fun)->u.script : NULL) extern JSClass js_ArgumentsClass; extern JSClass js_CallClass; -/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ +/* JS_FRIEND_DATA so that JSVAL_IS_FUNCTION is callable from outside */ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; /* * NB: jsapi.h and jsobj.h must be included before any call to this macro. */ -#define VALUE_IS_FUNCTION(cx, v) \ +#define JSVAL_IS_FUNCTION(cx, v) \ (!JSVAL_IS_PRIMITIVE(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) @@ -122,8 +117,7 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, * with #if/#error in jsfun.c. */ #define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT -#define JSV2F_ITERATOR JSINVOKE_ITERATOR -#define JSV2F_SEARCH_STACK 0x10000 +#define JSV2F_SEARCH_STACK 2 extern JSFunction * js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); diff --git a/lib/javascript/jsgc.c b/lib/javascript/jsgc.c index 7fae096e..2383240c 100644 --- a/lib/javascript/jsgc.c +++ b/lib/javascript/jsgc.c @@ -1,5 +1,4 @@ /* -*- 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 @@ -41,31 +40,28 @@ /* * JS Mark-and-Sweep Garbage Collector. * - * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see - * jsgc.h). It allocates from a special GC arena pool with each arena allocated - * using malloc. It uses an ideally parallel array of flag bytes to hold the + * This GC allocates only fixed-sized things big enough to contain two words + * (pointers) on any host architecture. It allocates from an arena pool (see + * jsarena.h). It uses an ideally parallel array of flag bytes to hold the * mark bit, finalizer type index, etc. * * XXX swizzle page to freelist for better locality of reference */ #include "jsstddef.h" -#include /* for free */ -#include /* for memset used when DEBUG */ +#include /* for free, called by JS_ARENA_DESTROY */ +#include /* for memset, called by jsarena.h macros if DEBUG */ #include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsatom.h" -#include "jsbit.h" -#include "jsclist.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" -#include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" -#include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" @@ -93,57 +89,34 @@ #endif #define GC_THINGS_SIZE JS_BIT(GC_THINGS_SHIFT) #define GC_FLAGS_SIZE (GC_THINGS_SIZE / sizeof(JSGCThing)) +#define GC_ARENA_SIZE (GC_THINGS_SIZE + GC_FLAGS_SIZE) /* * A GC arena contains one flag byte for each thing in its heap, and supports * O(1) lookup of a flag given its thing's address. * * To implement this, we take advantage of the thing/flags numerology: given - * the 8K bytes worth of GC-things, there are 1K flag bytes. Within each 9K - * allocation for things+flags there are always 8 consecutive 1K-pages each - * aligned on 1K boundary. We use these pages to allocate things and the - * remaining 1K of space before and after the aligned pages to store flags. - * If we are really lucky and things+flags starts on a 1K boundary, then - * flags would consist of a single 1K chunk that comes after 8K of things. - * Otherwise there are 2 chunks of flags, one before and one after things. + * the 8K bytes worth of GC-things, there are 1K flag bytes. We mask a thing's + * address with ~1023 to find a JSGCPageInfo record at the front of a mythical + * "GC page" within the larger 8K thing arena. That JSGCPageInfo contains a + * pointer to the 128 flag bytes corresponding to the things in the page, so we + * index into this flags array using the thing's index within its page. * - * To be able to find the flag byte for a particular thing, we put a - * JSGCPageInfo record at the beginning of each 1K-aligned page to hold that - * page's offset from the beginning of things+flags allocation and we allocate - * things after this record. Thus for each thing |thing_address & ~1023| - * gives the address of a JSGCPageInfo record from which we read page_offset. - * Due to page alignment - * (page_offset & ~1023) + (thing_address & 1023) - * gives thing_offset from the beginning of 8K paged things. We then divide - * thing_offset by sizeof(JSGCThing) to get thing_index. + * To align thing pages on 1024-byte boundaries, we must allocate the 9KB of + * flags+things arena payload, then find the first 0 mod 1024 boundary after + * the first payload address. That's where things start, with a JSGCPageInfo + * taking up the first thing-slot, as usual for 0 mod 1024 byte boundaries. + * The effect of this alignment trick is to split the flags into at most 2 + * discontiguous spans, one before the things and one after (if we're really + * lucky, and the arena payload starts on a 0 mod 1024 byte boundary, no need + * to split). * - * Now |page_address - page_offset| is things+flags arena_address and - * (page_offset & 1023) is the offset of the first page from the start of - * things+flags area. Thus if - * thing_index < (page_offset & 1023) - * then - * allocation_start_address + thing_index < address_of_the_first_page - * and we use - * allocation_start_address + thing_index - * as the address to store thing's flags. If - * thing_index >= (page_offset & 1023), - * then we use the chunk of flags that comes after the pages with things - * and calculate the address for the flag byte as - * address_of_the_first_page + 8K + (thing_index - (page_offset & 1023)) - * which is just - * allocation_start_address + thing_index + 8K. - * - * When we allocate things with size equal to sizeof(JSGCThing), the overhead - * of this scheme for 32 bit platforms is (8+8*(8+1))/(8+9K) or 0.87% - * (assuming 4 bytes for each JSGCArena header, and 8 bytes for each - * JSGCThing and JSGCPageInfo). When thing_size > 8, the scheme wastes the - * flag byte for each extra 8 bytes beyond sizeof(JSGCThing) in thing_size - * and the overhead is close to 1/8 or 12.5%. - * FIXME: How can we avoid this overhead? + * The overhead of this scheme for most platforms is (16+8*(8+1))/(16+9K) or + * .95% (assuming 16 byte JSArena header size, and 8 byte JSGCThing size). * * Here's some ASCII art showing an arena: * - * split or the first 1-K aligned address. + * split * | * V * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ @@ -160,57 +133,47 @@ * * In general, one of the thing pages will have some of its things' flags on * the low side of the split, and the rest of its things' flags on the high - * side. All the other pages have flags only below or only above. + * side. All the other pages have flags only below or only above. Therefore + * we'll have to test something to decide whether the split divides flags in + * a given thing's page. So we store the split pointer (the pointer to tp0) + * in each JSGCPageInfo, along with the flags pointer for the 128 flag bytes + * ideally starting, for tp0 things, at the beginning of the arena's payload + * (at the start of fB). + * + * That is, each JSGCPageInfo's flags pointer is 128 bytes from the previous, + * or at the start of the arena if there is no previous page in this arena. + * Thus these ideal 128-byte flag pages run contiguously from the start of the + * arena (right over the split!), and the JSGCPageInfo flags pointers contain + * no discontinuities over the split created by the thing pages. So if, for a + * given JSGCPageInfo *pi, we find that + * + * pi->flags + ((jsuword)thing % 1024) / sizeof(JSGCThing) >= pi->split + * + * then we must add GC_THINGS_SIZE to the nominal flags pointer to jump over + * all the thing pages that split the flags into two discontiguous spans. * * (If we need to implement card-marking for an incremental GC write barrier, - * we can replace word-sized offsetInArena in JSGCPageInfo by pair of - * uint8 card_mark and uint16 offsetInArena fields as the offset can not exceed - * GC_THINGS_SIZE. This would gives an extremely efficient write barrier: - * when mutating an object obj, just store a 1 byte at - * (uint8 *) ((jsuword)obj & ~1023) on 32-bit platforms.) + * we can use the low byte of the pi->split pointer as the card-mark, for an + * extremely efficient write barrier: when mutating an object obj, just store + * a 1 byte at (uint8 *) ((jsuword)obj & ~1023) for little-endian platforms. + * When finding flags, we'll of course have to mask split with ~255, but it is + * guaranteed to be 1024-byte aligned, so no information is lost by overlaying + * the card-mark byte on split's low byte.) */ #define GC_PAGE_SHIFT 10 #define GC_PAGE_MASK ((jsuword) JS_BITMASK(GC_PAGE_SHIFT)) #define GC_PAGE_SIZE JS_BIT(GC_PAGE_SHIFT) -#define GC_PAGE_COUNT (1 << (GC_THINGS_SHIFT - GC_PAGE_SHIFT)) typedef struct JSGCPageInfo { - jsuword offsetInArena; /* offset from the arena start */ - jsuword unscannedBitmap; /* bitset for fast search of marked - but not yet scanned GC things */ + uint8 *split; + uint8 *flags; } JSGCPageInfo; -struct JSGCArena { - JSGCArenaList *list; /* allocation list for the arena */ - JSGCArena *prev; /* link field for allocation list */ - JSGCArena *prevUnscanned; /* link field for the list of arenas - with marked but not yet scanned - things */ - jsuword unscannedPages; /* bitset for fast search of pages - with marked but not yet scanned - things */ - uint8 base[1]; /* things+flags allocation area */ -}; - -#define GC_ARENA_SIZE \ - (offsetof(JSGCArena, base) + GC_THINGS_SIZE + GC_FLAGS_SIZE) - -#define FIRST_THING_PAGE(a) \ - (((jsuword)(a)->base + GC_FLAGS_SIZE - 1) & ~GC_PAGE_MASK) - -#define PAGE_TO_ARENA(pi) \ - ((JSGCArena *)((jsuword)(pi) - (pi)->offsetInArena \ - - offsetof(JSGCArena, base))) - -#define PAGE_INDEX(pi) \ - ((size_t)((pi)->offsetInArena >> GC_PAGE_SHIFT)) - -#define THING_TO_PAGE(thing) \ - ((JSGCPageInfo *)((jsuword)(thing) & ~GC_PAGE_MASK)) +#define FIRST_THING_PAGE(a) (((a)->base + GC_FLAGS_SIZE) & ~GC_PAGE_MASK) /* - * Given a thing size n, return the size of the gap from the page start before - * the first thing. We know that any n not a power of two packs from + * 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). @@ -218,300 +181,57 @@ struct JSGCArena { * This works because all allocations are a multiple of sizeof(JSGCThing) == * sizeof(JSGCPageInfo) in size. */ -#define PAGE_THING_GAP(n) (((n) & ((n) - 1)) ? (GC_PAGE_SIZE % (n)) : (n)) +#define FIRST_THING(p,n) (((n) & ((n) - 1)) \ + ? (p) + (uint32)(GC_PAGE_SIZE % (n)) \ + : (p) + (n)) -#ifdef JS_THREADSAFE -/* - * The maximum number of things to put to the local free list by taking - * several things from the global free list or from the tail of the last - * allocated arena to amortize the cost of rt->gcLock. - * - * We use number 8 based on benchmarks from bug 312238. - */ -#define MAX_THREAD_LOCAL_THINGS 8 - -#endif - -JS_STATIC_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); -JS_STATIC_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); -JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); - -/* - * JSPtrTable capacity growth descriptor. The table grows by powers of two - * starting from capacity JSPtrTableInfo.minCapacity, but switching to linear - * growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold. - */ -typedef struct JSPtrTableInfo { - uint16 minCapacity; - uint16 linearGrowthThreshold; -} JSPtrTableInfo; - -#define GC_ITERATOR_TABLE_MIN 4 -#define GC_ITERATOR_TABLE_LINEAR 1024 - -static const JSPtrTableInfo iteratorTableInfo = { - GC_ITERATOR_TABLE_MIN, - GC_ITERATOR_TABLE_LINEAR -}; - -/* Calculate table capacity based on the current value of JSPtrTable.count. */ -static size_t -PtrTableCapacity(size_t count, const JSPtrTableInfo *info) +static JSGCThing * +gc_new_arena(JSArenaPool *pool, size_t nbytes) { - size_t linear, log, capacity; - - linear = info->linearGrowthThreshold; - JS_ASSERT(info->minCapacity <= linear); - - if (count == 0) { - capacity = 0; - } else if (count < linear) { - log = JS_CEILING_LOG2W(count); - JS_ASSERT(log != JS_BITS_PER_WORD); - capacity = (size_t)1 << log; - if (capacity < info->minCapacity) - capacity = info->minCapacity; - } else { - capacity = JS_ROUNDUP(count, linear); - } - - JS_ASSERT(capacity >= count); - return capacity; -} - -static void -FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info) -{ - if (table->array) { - JS_ASSERT(table->count > 0); - free(table->array); - table->array = NULL; - table->count = 0; - } - JS_ASSERT(table->count == 0); -} - -static JSBool -AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info, - void *ptr) -{ - size_t count, capacity; - void **array; - - count = table->count; - capacity = PtrTableCapacity(count, info); - - if (count == capacity) { - if (capacity < info->minCapacity) { - JS_ASSERT(capacity == 0); - JS_ASSERT(!table->array); - capacity = info->minCapacity; - } else { - /* - * Simplify the overflow detection assuming pointer is bigger - * than byte. - */ - JS_STATIC_ASSERT(2 <= sizeof table->array[0]); - capacity = (capacity < info->linearGrowthThreshold) - ? 2 * capacity - : capacity + info->linearGrowthThreshold; - if (capacity > (size_t)-1 / sizeof table->array[0]) - goto bad; - } - array = (void **) realloc(table->array, - capacity * sizeof table->array[0]); - if (!array) - goto bad; -#ifdef DEBUG - memset(array + count, JS_FREE_PATTERN, - (capacity - count) * sizeof table->array[0]); -#endif - table->array = array; - } - - table->array[count] = ptr; - table->count = count + 1; - - return JS_TRUE; - - bad: - JS_ReportOutOfMemory(cx); - return JS_FALSE; -} - -static void -ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info, - size_t newCount) -{ - size_t oldCapacity, capacity; - void **array; - - JS_ASSERT(newCount <= table->count); - if (newCount == table->count) - return; - - oldCapacity = PtrTableCapacity(table->count, info); - table->count = newCount; - capacity = PtrTableCapacity(newCount, info); - - if (oldCapacity != capacity) { - array = table->array; - JS_ASSERT(array); - if (capacity == 0) { - free(array); - table->array = NULL; - return; - } - array = (void **) realloc(array, capacity * sizeof array[0]); - if (array) - table->array = array; - } -#ifdef DEBUG - memset(table->array + newCount, JS_FREE_PATTERN, - (capacity - newCount) * sizeof table->array[0]); -#endif -} - -#ifdef JS_GCMETER -# define METER(x) x -#else -# define METER(x) ((void) 0) -#endif - -static JSBool -NewGCArena(JSRuntime *rt, JSGCArenaList *arenaList) -{ - JSGCArena *a; - jsuword offset; + uint8 *flagp, *split, *pagep, *limit; + JSArena *a; + jsuword p; + JSGCThing *thing; JSGCPageInfo *pi; - uint32 *bytesptr; - /* Check if we are allowed and can allocate a new arena. */ - if (rt->gcBytes >= rt->gcMaxBytes) - return JS_FALSE; - a = (JSGCArena *)malloc(GC_ARENA_SIZE); - if (!a) - return JS_FALSE; + /* Use JS_ArenaAllocate to grab another 9K-net-size hunk of space. */ + flagp = (uint8 *) JS_ArenaAllocate(pool, GC_ARENA_SIZE); + if (!flagp) + return NULL; + a = pool->current; + + /* Reset a->avail to start at the flags split, aka the first thing page. */ + 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; + JS_ArenaCountAllocation(pool, a->avail - p); + a->avail += nbytes; /* Initialize the JSGCPageInfo records at the start of every thing page. */ - offset = (GC_PAGE_SIZE - ((jsuword)a->base & GC_PAGE_MASK)) & GC_PAGE_MASK; - JS_ASSERT((jsuword)a->base + offset == FIRST_THING_PAGE(a)); + limit = pagep + GC_THINGS_SIZE; do { - pi = (JSGCPageInfo *) (a->base + offset); - pi->offsetInArena = offset; - pi->unscannedBitmap = 0; - offset += GC_PAGE_SIZE; - } while (offset < GC_THINGS_SIZE); - - METER(++arenaList->stats.narenas); - METER(arenaList->stats.maxarenas - = JS_MAX(arenaList->stats.maxarenas, arenaList->stats.narenas)); - - a->list = arenaList; - a->prev = arenaList->last; - a->prevUnscanned = NULL; - a->unscannedPages = 0; - arenaList->last = a; - arenaList->lastLimit = 0; - - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - *bytesptr += GC_ARENA_SIZE; - - return JS_TRUE; -} - -static void -DestroyGCArena(JSRuntime *rt, JSGCArenaList *arenaList, JSGCArena **ap) -{ - JSGCArena *a; - uint32 *bytesptr; - - a = *ap; - JS_ASSERT(a); - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - JS_ASSERT(*bytesptr >= GC_ARENA_SIZE); - *bytesptr -= GC_ARENA_SIZE; - METER(rt->gcStats.afree++); - METER(--arenaList->stats.narenas); - if (a == arenaList->last) - arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0); - *ap = a->prev; - -#ifdef DEBUG - memset(a, JS_FREE_PATTERN, GC_ARENA_SIZE); -#endif - free(a); -} - -static void -InitGCArenaLists(JSRuntime *rt) -{ - uintN i, thingSize; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - thingSize = GC_FREELIST_NBYTES(i); - JS_ASSERT((size_t)(uint16)thingSize == thingSize); - arenaList->last = NULL; - arenaList->lastLimit = 0; - arenaList->thingSize = (uint16)thingSize; - arenaList->freeList = NULL; - METER(memset(&arenaList->stats, 0, sizeof arenaList->stats)); - } -} - -static void -FinishGCArenaLists(JSRuntime *rt) -{ - uintN i; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - while (arenaList->last) - DestroyGCArena(rt, arenaList, &arenaList->last); - arenaList->freeList = NULL; - } + pi = (JSGCPageInfo *) pagep; + pi->split = split; + pi->flags = flagp; + flagp += GC_PAGE_SIZE >> (GC_THINGS_SHIFT - GC_PAGE_SHIFT); + pagep += GC_PAGE_SIZE; + } while (pagep < limit); + return thing; } uint8 * js_GetGCThingFlags(void *thing) { JSGCPageInfo *pi; - jsuword offsetInArena, thingIndex; + uint8 *flagp; - pi = THING_TO_PAGE(thing); - offsetInArena = pi->offsetInArena; - JS_ASSERT(offsetInArena < GC_THINGS_SIZE); - thingIndex = ((offsetInArena & ~GC_PAGE_MASK) | - ((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing); - JS_ASSERT(thingIndex < GC_PAGE_SIZE); - if (thingIndex >= (offsetInArena & GC_PAGE_MASK)) - thingIndex += GC_THINGS_SIZE; - return (uint8 *)pi - offsetInArena + thingIndex; -} - -JSRuntime* -js_GetGCStringRuntime(JSString *str) -{ - JSGCPageInfo *pi; - JSGCArenaList *list; - - pi = THING_TO_PAGE(str); - list = PAGE_TO_ARENA(pi)->list; - - JS_ASSERT(list->thingSize == sizeof(JSGCThing)); - JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0); - - return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList)); + pi = (JSGCPageInfo *) ((jsuword)thing & ~GC_PAGE_MASK); + flagp = pi->flags + ((jsuword)thing & GC_PAGE_MASK) / sizeof(JSGCThing); + if (flagp >= pi->split) + flagp += GC_THINGS_SIZE; + return flagp; } JSBool @@ -591,12 +311,11 @@ js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, return -1; } -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCRootHashEntry { - JSDHashEntryHdr hdr; - void *root; - const char *name; -} JSGCRootHashEntry; +#ifdef JS_GCMETER +#define METER(x) x +#else +#define METER(x) /* nothing */ +#endif /* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ #define GC_ROOTS_SIZE 256 @@ -605,7 +324,17 @@ typedef struct JSGCRootHashEntry { JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) { - InitGCArenaLists(rt); + uintN i; + + JS_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); + JS_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); + JS_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); + JS_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); + + 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; @@ -618,7 +347,6 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) * for default backward API compatibility. */ rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; - return JS_TRUE; } @@ -627,58 +355,20 @@ JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp) { uintN i; - size_t totalThings, totalMaxThings, totalBytes; fprintf(fp, "\nGC allocation statistics:\n"); #define UL(x) ((unsigned long)(x)) #define ULSTAT(x) UL(rt->gcStats.x) - totalThings = 0; - totalMaxThings = 0; - totalBytes = 0; - for (i = 0; i < GC_NUM_FREELISTS; i++) { - JSGCArenaList *list = &rt->gcArenaList[i]; - JSGCArenaStats *stats = &list->stats; - if (stats->maxarenas == 0) { - fprintf(fp, "ARENA LIST %u (thing size %lu): NEVER USED\n", - i, UL(GC_FREELIST_NBYTES(i))); - continue; - } - fprintf(fp, "ARENA LIST %u (thing size %lu):\n", - i, UL(GC_FREELIST_NBYTES(i))); - fprintf(fp, " arenas: %lu\n", UL(stats->narenas)); - fprintf(fp, " max arenas: %lu\n", UL(stats->maxarenas)); - fprintf(fp, " things: %lu\n", UL(stats->nthings)); - fprintf(fp, " max things: %lu\n", UL(stats->maxthings)); - fprintf(fp, " free list: %lu\n", UL(stats->freelen)); - fprintf(fp, " free list density: %.1f%%\n", - stats->narenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->freelen / - (GC_THINGS_SIZE * (jsdouble)stats->narenas))); - fprintf(fp, " average free list density: %.1f%%\n", - stats->totalarenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->totalfreelen / - (GC_THINGS_SIZE * (jsdouble)stats->totalarenas))); - fprintf(fp, " recycles: %lu\n", UL(stats->recycle)); - fprintf(fp, " recycle/alloc ratio: %.2f\n", - (jsdouble)stats->recycle / - (jsdouble)(stats->totalnew - stats->recycle)); - totalThings += stats->nthings; - totalMaxThings += stats->maxthings; - totalBytes += GC_FREELIST_NBYTES(i) * stats->nthings; - } - fprintf(fp, "TOTAL STATS:\n"); 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)); -#ifdef JS_THREADSAFE - fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc)); -#endif - fprintf(fp, " total GC things: %lu\n", UL(totalThings)); - fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings)); - fprintf(fp, " GC things size: %lu\n", UL(totalBytes)); + 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)); @@ -688,20 +378,17 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp) fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); - fprintf(fp, " delayed scan bag adds: %lu\n", ULSTAT(unscanned)); -#ifdef DEBUG - fprintf(fp, " max delayed scan bag size: %lu\n", ULSTAT(maxunscanned)); -#endif + 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)); - fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); - fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); - fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); - fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); #undef UL #undef US @@ -712,32 +399,61 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp) #endif #ifdef DEBUG -static void -CheckLeakedRoots(JSRuntime *rt); +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) +{ + uint32 *leakedroots = (uint32 *)arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + + (*leakedroots)++; + fprintf(stderr, + "JS engine warning: leaking GC root \'%s\' at %p\n", + rhe->name ? (char *)rhe->name : "", rhe->root); + + return JS_DHASH_NEXT; +} #endif void js_FinishGC(JSRuntime *rt) { + uintN i; + #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif #ifdef JS_GCMETER js_DumpGCStats(rt, stdout); #endif - - FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); -#if JS_HAS_GENERATORS - rt->gcCloseState.reachableList = NULL; - METER(rt->gcStats.nclose = 0); - rt->gcCloseState.todoQueue = NULL; -#endif - FinishGCArenaLists(rt); + for (i = 0; i < GC_NUM_FREELISTS; i++) { + JS_FinishArenaPool(&rt->gcArenaPool[i]); + rt->gcFreeList[i] = NULL; + } + JS_ArenaFinish(); if (rt->gcRootsHash.ops) { #ifdef DEBUG - CheckLeakedRoots(rt); + uint32 leakedroots = 0; + + /* Warn (but don't assert) debug builds of any remaining roots. */ + JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, + &leakedroots); + if (leakedroots > 0) { + if (leakedroots == 1) { + fprintf(stderr, +"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n" +" This root may point to freed memory. Objects reachable\n" +" through it have not been finalized.\n"); + } else { + fprintf(stderr, +"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" +" These roots may point to freed memory. Objects reachable\n" +" through them have not been finalized.\n", + (unsigned long) leakedroots); + } + } #endif + JS_DHashTableFinish(&rt->gcRootsHash); rt->gcRootsHash.ops = NULL; } @@ -778,7 +494,7 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name) JS_LOCK_GC(rt); #ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); @@ -807,7 +523,7 @@ js_RemoveRoot(JSRuntime *rt, void *rp) JS_LOCK_GC(rt); #ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { + if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); @@ -819,514 +535,7 @@ js_RemoveRoot(JSRuntime *rt, void *rp) return JS_TRUE; } -#ifdef DEBUG - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) -{ - uint32 *leakedroots = (uint32 *)arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - (*leakedroots)++; - fprintf(stderr, - "JS engine warning: leaking GC root \'%s\' at %p\n", - rhe->name ? (char *)rhe->name : "", rhe->root); - - return JS_DHASH_NEXT; -} - -static void -CheckLeakedRoots(JSRuntime *rt) -{ - uint32 leakedroots = 0; - - /* Warn (but don't assert) debug builds of any remaining roots. */ - JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, - &leakedroots); - if (leakedroots > 0) { - if (leakedroots == 1) { - fprintf(stderr, -"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n" -" This root may point to freed memory. Objects reachable\n" -" through it have not been finalized.\n"); - } else { - fprintf(stderr, -"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" -" These roots may point to freed memory. Objects reachable\n" -" through them have not been finalized.\n", - (unsigned long) leakedroots); - } - } -} - -typedef struct NamedRootDumpArgs { - void (*dump)(const char *name, void *rp, void *data); - void *data; -} NamedRootDumpArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - if (rhe->name) - args->dump(rhe->name, rhe->root, args->data); - return JS_DHASH_NEXT; -} - -void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data) -{ - NamedRootDumpArgs args; - - args.dump = dump; - args.data = data; - JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); -} - -#endif /* DEBUG */ - -typedef struct GCRootMapArgs { - JSGCRootMapFun map; - void *data; -} GCRootMapArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - GCRootMapArgs *args = (GCRootMapArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - intN mapflags; - JSDHashOperator op; - - mapflags = args->map(rhe->root, rhe->name, args->data); - -#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ - JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ - JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE - op = (JSDHashOperator)mapflags; -#else - op = JS_DHASH_NEXT; - if (mapflags & JS_MAP_GCROOT_STOP) - op |= JS_DHASH_STOP; - if (mapflags & JS_MAP_GCROOT_REMOVE) - op |= JS_DHASH_REMOVE; -#endif - - return op; -} - -uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) -{ - GCRootMapArgs args; - uint32 rv; - - args.map = map; - args.data = data; - JS_LOCK_GC(rt); - rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); - JS_UNLOCK_GC(rt); - return rv; -} - -JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSBool ok; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - - JS_LOCK_GC(rt); - ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj); - JS_UNLOCK_GC(rt); - return ok; -} - -static void -CloseIteratorStates(JSContext *cx) -{ - JSRuntime *rt; - size_t count, newCount, i; - void **array; - JSObject *obj; - - rt = cx->runtime; - count = rt->gcIteratorTable.count; - array = rt->gcIteratorTable.array; - - newCount = 0; - for (i = 0; i != count; ++i) { - obj = (JSObject *)array[i]; - if (js_IsAboutToBeFinalized(cx, obj)) - js_CloseIteratorState(cx, obj); - else - array[newCount++] = obj; - } - ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount); -} - -#if JS_HAS_GENERATORS - -void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen) -{ - JSRuntime *rt; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - JS_ASSERT(rt->state != JSRTS_LANDING); - JS_ASSERT(gen->state == JSGEN_NEWBORN); - - JS_LOCK_GC(rt); - gen->next = rt->gcCloseState.reachableList; - rt->gcCloseState.reachableList = gen; - METER(rt->gcStats.nclose++); - METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose, - rt->gcStats.nclose)); - JS_UNLOCK_GC(rt); -} - -/* - * We do not run close hooks when the parent scope of the generator instance - * becomes unreachable to prevent denial-of-service and resource leakage from - * misbehaved generators. - * - * Called from the GC. - */ -static JSBool -CanScheduleCloseHook(JSGenerator *gen) -{ - JSObject *parent; - JSBool canSchedule; - - /* Avoid OBJ_GET_PARENT overhead as we are in GC. */ - parent = JSVAL_TO_OBJECT(gen->obj->slots[JSSLOT_PARENT]); - canSchedule = *js_GetGCThingFlags(parent) & GCF_MARK; -#ifdef DEBUG_igor - if (!canSchedule) { - fprintf(stderr, "GEN: Kill without schedule, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return canSchedule; -} - -/* - * Check if we should delay execution of the close hook. - * - * Called outside GC or any locks. - * - * XXX The current implementation is a hack that embeds the knowledge of the - * browser embedding pending the resolution of bug 352788. In the browser we - * must not close any generators that came from a page that is currently in - * the browser history. We detect that using the fact in the browser the scope - * is history if scope->outerObject->innerObject != scope. - */ -static JSBool -ShouldDeferCloseHook(JSContext *cx, JSGenerator *gen, JSBool *defer) -{ - JSObject *parent, *obj; - JSClass *clasp; - JSExtendedClass *xclasp; - - /* - * This is called outside any locks, so use thread-safe macros to access - * parent and classes. - */ - *defer = JS_FALSE; - parent = OBJ_GET_PARENT(cx, gen->obj); - clasp = OBJ_GET_CLASS(cx, parent); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *)clasp; - if (xclasp->outerObject) { - obj = xclasp->outerObject(cx, parent); - if (!obj) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - *defer = obj != parent; - } - } -#ifdef DEBUG_igor - if (*defer) { - fprintf(stderr, "GEN: deferring, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return JS_TRUE; -} - -/* - * Find all unreachable generators and move them to the todo queue from - * rt->gcCloseState.reachableList to execute thier close hooks after the GC - * cycle completes. To ensure liveness during the sweep phase we mark all - * generators we are going to close later. - */ -static void -FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind, - JSGenerator **todoQueueTail) -{ - JSRuntime *rt; - JSGenerator *todo, **genp, *gen; - - rt = cx->runtime; - todo = NULL; - genp = &rt->gcCloseState.reachableList; - while ((gen = *genp) != NULL) { - if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) { - genp = &gen->next; - } else { - /* Generator must not be executing when it becomes unreachable. */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || - gen->state == JSGEN_OPEN || - gen->state == JSGEN_CLOSED); - - *genp = gen->next; - if (gen->state == JSGEN_OPEN && - js_FindFinallyHandler(gen->frame.script, gen->frame.pc) && - CanScheduleCloseHook(gen)) { - /* - * Generator yielded inside a try with a finally block. - * Schedule it for closing. - * - * We keep generators that yielded outside try-with-finally - * with gen->state == JSGEN_OPEN. The finalizer must deal with - * open generators as we may skip the close hooks, see below. - */ - gen->next = NULL; - *todoQueueTail = gen; - todoQueueTail = &gen->next; - if (!todo) - todo = gen; - METER(JS_ASSERT(rt->gcStats.nclose)); - METER(rt->gcStats.nclose--); - METER(rt->gcStats.closelater++); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, - rt->gcStats.closelater)); - } - } - } - - if (gckind == GC_LAST_CONTEXT) { - /* - * Remove scheduled hooks on shutdown as it is too late to run them: - * we do not allow execution of arbitrary scripts at this point. - */ - rt->gcCloseState.todoQueue = NULL; - } else { - /* - * Mark just-found unreachable generators *after* we scan the global - * list to prevent a generator that refers to other unreachable - * generators from keeping them on gcCloseState.reachableList. - */ - for (gen = todo; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "newly scheduled generator"); - } -} - -/* - * Mark unreachable generators already scheduled to close and return the tail - * pointer to JSGCCloseState.todoQueue. - */ -static JSGenerator ** -MarkScheduledGenerators(JSContext *cx) -{ - JSRuntime *rt; - JSGenerator **genp, *gen; - - rt = cx->runtime; - genp = &rt->gcCloseState.todoQueue; - while ((gen = *genp) != NULL) { - if (CanScheduleCloseHook(gen)) { - GC_MARK(cx, gen->obj, "scheduled generator"); - genp = &gen->next; - } else { - /* Discard the generator from the list if its schedule is over. */ - *genp = gen->next; - METER(JS_ASSERT(rt->gcStats.closelater > 0)); - METER(rt->gcStats.closelater--); - } - } - return genp; -} - -#ifdef JS_THREADSAFE -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->thread->gcRunningCloseHooks) -#else -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->runtime->gcCloseState.runningCloseHook) -#endif - -typedef struct JSTempCloseList { - JSTempValueRooter tvr; - JSGenerator *head; -} JSTempCloseList; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_close_list(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempCloseList *list = (JSTempCloseList *)tvr; - JSGenerator *gen; - - for (gen = list->head; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "temp list generator"); -} - -#define JS_PUSH_TEMP_CLOSE_LIST(cx, tempList) \ - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_close_list, &(tempList)->tvr) - -#define JS_POP_TEMP_CLOSE_LIST(cx, tempList) \ - JS_BEGIN_MACRO \ - JS_ASSERT((tempList)->tvr.u.marker == mark_temp_close_list); \ - JS_POP_TEMP_ROOT(cx, &(tempList)->tvr); \ - JS_END_MACRO - -JSBool -js_RunCloseHooks(JSContext *cx) -{ - JSRuntime *rt; - JSTempCloseList tempList; - JSStackFrame *fp; - JSGenerator **genp, *gen; - JSBool ok, defer; -#if JS_GCMETER - uint32 deferCount; -#endif - - rt = cx->runtime; - - /* - * It is OK to access todoQueue outside the lock here. When many threads - * update the todo list, accessing some older value of todoQueue in the - * worst case just delays the excution of close hooks. - */ - if (!rt->gcCloseState.todoQueue) - return JS_TRUE; - - /* - * To prevent an infinite loop when a close hook creats more objects with - * close hooks and then triggers GC we ignore recursive invocations of - * js_RunCloseHooks and limit number of hooks to execute to the initial - * size of the list. - */ - if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx)) - return JS_TRUE; - - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE; - - JS_LOCK_GC(rt); - tempList.head = rt->gcCloseState.todoQueue; - JS_PUSH_TEMP_CLOSE_LIST(cx, &tempList); - rt->gcCloseState.todoQueue = NULL; - METER(rt->gcStats.closelater = 0); - rt->gcPoke = JS_TRUE; - JS_UNLOCK_GC(rt); - - /* - * Set aside cx->fp since we do not want a close hook using caller or - * other means to backtrace into whatever stack might be active when - * running the hook. We store the current frame on the dormant list to - * protect against GC that the hook can trigger. - */ - fp = cx->fp; - if (fp) { - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - } - cx->fp = NULL; - - genp = &tempList.head; - ok = JS_TRUE; - while ((gen = *genp) != NULL) { - ok = ShouldDeferCloseHook(cx, gen, &defer); - if (!ok) { - /* Quit ASAP discarding the hook. */ - *genp = gen->next; - break; - } - if (defer) { - genp = &gen->next; - METER(deferCount++); - continue; - } - ok = js_CloseGeneratorObject(cx, gen); - - /* - * Unlink the generator after closing it to make sure it always stays - * rooted through tempList. - */ - *genp = gen->next; - - if (cx->throwing) { - /* - * Report the exception thrown by the close hook and continue to - * execute the rest of the hooks. - */ - if (!js_ReportUncaughtException(cx)) - JS_ClearPendingException(cx); - ok = JS_TRUE; - } else if (!ok) { - /* - * Assume this is a stop signal from the branch callback or - * other quit ASAP condition. Break execution until the next - * invocation of js_RunCloseHooks. - */ - break; - } - } - - cx->fp = fp; - if (fp) { - JS_ASSERT(cx->dormantFrameChain == fp); - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; - } - - if (tempList.head) { - /* - * Some close hooks were not yet executed, put them back into the - * scheduled list. - */ - while ((gen = *genp) != NULL) { - genp = &gen->next; - METER(deferCount++); - } - - /* Now genp is a pointer to the tail of tempList. */ - JS_LOCK_GC(rt); - *genp = rt->gcCloseState.todoQueue; - rt->gcCloseState.todoQueue = tempList.head; - METER(rt->gcStats.closelater += deferCount); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); - JS_UNLOCK_GC(rt); - } - - JS_POP_TEMP_CLOSE_LIST(cx, &tempList); - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_FALSE; - - return ok; -} - -#endif /* JS_HAS_GENERATORS */ - -#if defined(DEBUG_brendan) || defined(DEBUG_timeless) -#define DEBUG_gchist -#endif - -#ifdef DEBUG_gchist +#ifdef DEBUG_brendan #define NGCHIST 64 static struct GCHist { @@ -1340,56 +549,17 @@ unsigned gchpos; void * js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) { + JSBool tried_gc; JSRuntime *rt; - uintN flindex; - JSBool doGC; - JSGCThing *thing; - uint8 *flagp, *firstPage; - JSGCArenaList *arenaList; - jsuword offset; - JSGCArena *a; + size_t nflags; + uintN i; + JSGCThing *thing, **flp; + uint8 *flagp; JSLocalRootStack *lrs; -#ifdef JS_THREADSAFE - JSBool gcLocked; - uintN localMallocBytes; - JSGCThing **flbase, **lastptr; - JSGCThing *tmpthing; - uint8 *tmpflagp; - uintN maxFreeThings; /* max to take from the global free list */ - METER(size_t nfree); -#endif + uint32 *bytesptr; rt = cx->runtime; - METER(rt->gcStats.alloc++); /* this is not thread-safe */ - nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); - flindex = GC_FREELIST_INDEX(nbytes); - -#ifdef JS_THREADSAFE - gcLocked = JS_FALSE; - JS_ASSERT(cx->thread); - flbase = cx->thread->gcFreeLists; - JS_ASSERT(flbase); - thing = flbase[flindex]; - localMallocBytes = cx->thread->gcMallocBytes; - if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { - flagp = thing->flagp; - flbase[flindex] = thing->next; - METER(rt->gcStats.localalloc++); /* this is not thread-safe */ - goto success; - } - JS_LOCK_GC(rt); - gcLocked = JS_TRUE; - - /* Transfer thread-local counter to global one. */ - if (localMallocBytes != 0) { - cx->thread->gcMallocBytes = 0; - if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) - rt->gcMallocBytes = rt->gcMaxMallocBytes; - else - rt->gcMallocBytes += localMallocBytes; - } -#endif JS_ASSERT(!rt->gcRunning); if (rt->gcRunning) { METER(rt->gcStats.finalfail++); @@ -1401,149 +571,96 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) #ifdef WAY_TOO_MUCH_GC rt->gcPoke = JS_TRUE; #endif - doGC = JS_TRUE; + js_GC(cx, GC_KEEP_ATOMS | GC_ALREADY_LOCKED); + tried_gc = JS_TRUE; #else - doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes); + tried_gc = JS_FALSE; #endif - arenaList = &rt->gcArenaList[flindex]; - for (;;) { - if (doGC) { + 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 = *flp; + if (thing) { + *flp = thing->next; + flagp = thing->flagp; + METER(rt->gcStats.freelen[i]--); + METER(rt->gcStats.recycle[i]++); + } else { + if (rt->gcBytes < rt->gcMaxBytes && + (tried_gc || rt->gcMallocBytes < rt->gcMaxMallocBytes)) + { /* - * Keep rt->gcLock across the call into js_GC so we don't starve - * and lose to racing threads who deplete the heap just after - * js_GC has replenished it (or has synchronized with a racing - * GC that collected a bunch of garbage). This unfair scheduling - * can happen on certain operating systems. For the gory details, - * see bug 162779 at https://bugzilla.mozilla.org/. + * Inline form of JS_ARENA_ALLOCATE adapted to truncate the current + * arena's limit to a GC_PAGE_SIZE boundary, and to skip over every + * GC_PAGE_SIZE-byte-aligned thing (which is actually not a thing, + * it's a JSGCPageInfo record). */ - js_GC(cx, GC_LAST_DITCH); - METER(rt->gcStats.retry++); + JSArenaPool *pool = &rt->gcArenaPool[i]; + JSArena *a = pool->current; + jsuword p = a->avail; + jsuword q = p + nbytes; + + if (q > (a->limit & ~GC_PAGE_MASK)) { + thing = gc_new_arena(pool, nbytes); + } else { + if ((p & GC_PAGE_MASK) == 0) { + /* Beware, p points to a JSGCPageInfo record! */ + p = FIRST_THING(p, nbytes); + q = p + nbytes; + JS_ArenaCountAllocation(pool, p & GC_PAGE_MASK); + } + a->avail = q; + thing = (JSGCThing *)p; + } + JS_ArenaCountAllocation(pool, nbytes); } - /* Try to get thing from the free list. */ - thing = arenaList->freeList; - if (thing) { - arenaList->freeList = thing->next; - flagp = thing->flagp; - JS_ASSERT(*flagp & GCF_FINAL); - METER(arenaList->stats.freelen--); - METER(arenaList->stats.recycle++); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking several things from the - * global free list unless we are still at rt->gcMaxMallocBytes - * barrier or the free list is already populated. The former - * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN) - * or no gcPoke. The latter is caused via allocating new things - * in gcCallback(cx, JSGC_END). - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - tmpthing = arenaList->freeList; - if (tmpthing) { - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - do { - if (!tmpthing->next) - break; - tmpthing = tmpthing->next; - } while (--maxFreeThings != 0); - - flbase[flindex] = arenaList->freeList; - arenaList->freeList = tmpthing->next; - tmpthing->next = NULL; + /* + * Consider doing a "last ditch" GC if thing couldn't be allocated. + * + * Keep rt->gcLock across the call into js_GC so we don't starve and + * lose to racing threads who deplete the heap just after js_GC has + * replenished it (or has synchronized with a racing GC that collected + * a bunch of garbage). This unfair scheduling can happen on certain + * operating systems. For the gory details, see Mozilla bug 162779 + * (http://bugzilla.mozilla.org/show_bug.cgi?id=162779). + */ + if (!thing) { + if (!tried_gc) { + rt->gcPoke = JS_TRUE; + js_GC(cx, GC_KEEP_ATOMS | GC_ALREADY_LOCKED); + tried_gc = JS_TRUE; + METER(rt->gcStats.retry++); + goto retry; } -#endif - break; - } - - /* Allocate from the tail of last arena or from new arena if we can. */ - if ((arenaList->last && arenaList->lastLimit != GC_THINGS_SIZE) || - NewGCArena(rt, arenaList)) { - - offset = arenaList->lastLimit; - if ((offset & GC_PAGE_MASK) == 0) { - /* - * Skip JSGCPageInfo record located at GC_PAGE_SIZE boundary. - */ - offset += PAGE_THING_GAP(nbytes); - } - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - arenaList->lastLimit = (uint16)(offset + nbytes); - a = arenaList->last; - firstPage = (uint8 *)FIRST_THING_PAGE(a); - thing = (JSGCThing *)(firstPage + offset); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - METER(++arenaList->stats.nthings); - METER(arenaList->stats.maxthings = - JS_MAX(arenaList->stats.nthings, - arenaList->stats.maxthings)); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking free things from the last - * arena. Prefer to order free things by ascending address in the - * (unscientific) hope of better cache locality. - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - METER(nfree = 0); - lastptr = &flbase[flindex]; - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - for (offset = arenaList->lastLimit; - offset != GC_THINGS_SIZE && maxFreeThings-- != 0; - offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - tmpflagp = a->base + offset / sizeof(JSGCThing); - if (tmpflagp >= firstPage) - tmpflagp += GC_THINGS_SIZE; - - tmpthing = (JSGCThing *)(firstPage + offset); - tmpthing->flagp = tmpflagp; - *tmpflagp = GCF_FINAL; /* signifying that thing is free */ - - *lastptr = tmpthing; - lastptr = &tmpthing->next; - METER(++nfree); - } - arenaList->lastLimit = offset; - *lastptr = NULL; - METER(arenaList->stats.freelen += nfree); -#endif - break; - } - - /* Consider doing a "last ditch" GC unless already tried. */ - if (doGC) goto fail; - rt->gcPoke = JS_TRUE; - doGC = JS_TRUE; + } + + /* Find the flags pointer given thing's address. */ + flagp = js_GetGCThingFlags(thing); } - /* We successfully allocated the thing. */ -#ifdef JS_THREADSAFE - success: -#endif lrs = cx->localRootStack; if (lrs) { /* - * If we're in a local root scope, don't set newborn[type] at all, to - * avoid entraining garbage from it for an unbounded amount of time + * If we're in a local root scope, don't set cx->newborn[type] at all, + * to avoid entraining garbage from it for an unbounded amount of time * on this context. A caller will leave the local root scope and pop * this reference, allowing thing to be GC'd if it has no other refs. * See JS_EnterLocalRootScope and related APIs. */ if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { /* - * 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. + * 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; @@ -1553,38 +670,35 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) * No local root scope, so we're stuck with the old, fragile model of * depending on a pigeon-hole newborn per type per context. */ - cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing; + cx->newborn[flags & GCF_TYPEMASK] = thing; } /* We can't fail now, so update flags and rt->gc{,Private}Bytes. */ *flagp = (uint8)flags; + 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, - * finding it via newborn[]. + * finding it via cx->newborn[]. */ thing->next = NULL; thing->flagp = NULL; -#ifdef DEBUG_gchist - gchist[gchpos].lastDitch = doGC; - gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList; +#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++); - METER(++rt->gcArenaList[flindex].stats.totalnew); -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif + JS_UNLOCK_GC(rt); return thing; fail: -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif METER(rt->gcStats.fail++); + JS_UNLOCK_GC(rt); JS_ReportOutOfMemory(cx); return NULL; } @@ -1616,19 +730,11 @@ js_LockGCThing(JSContext *cx, void *thing) #define GC_THING_IS_DEEP(t,o) (GC_TYPE_IS_DEEP(t) || IS_DEEP_STRING(t, o)) -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCLockHashEntry { - JSDHashEntryHdr hdr; - const JSGCThing *thing; - uint32 count; -} JSGCLockHashEntry; - JSBool js_LockGCThingRT(JSRuntime *rt, void *thing) { JSBool ok, deep; - uint8 *flagp; - uintN flags, lock, type; + uint8 *flagp, flags, lock, type; JSGCLockHashEntry *lhe; ok = JS_TRUE; @@ -1731,15 +837,6 @@ out: #include #include "jsprf.h" -typedef struct GCMarkNode GCMarkNode; - -struct GCMarkNode { - void *thing; - const char *name; - GCMarkNode *next; - GCMarkNode *prev; -}; - JS_FRIEND_DATA(FILE *) js_DumpGCHeap; JS_EXPORT_DATA(void *) js_LiveThingToFind; @@ -1747,63 +844,6 @@ JS_EXPORT_DATA(void *) js_LiveThingToFind; #include "dump_xpc.h" #endif -static void -GetObjSlotName(JSScope *scope, JSObject *obj, uint32 slot, char *buf, - size_t bufsize) -{ - jsval nval; - JSScopeProperty *sprop; - JSClass *clasp; - uint32 key; - const char *slotname; - - if (!scope) { - JS_snprintf(buf, bufsize, "**UNKNOWN OBJECT MAP ENTRY**"); - return; - } - - sprop = SCOPE_LAST_PROP(scope); - while (sprop && sprop->slot != slot) - sprop = sprop->parent; - - if (!sprop) { - switch (slot) { - case JSSLOT_PROTO: - JS_snprintf(buf, bufsize, "__proto__"); - break; - case JSSLOT_PARENT: - JS_snprintf(buf, bufsize, "__parent__"); - break; - default: - slotname = NULL; - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (clasp->flags & JSCLASS_IS_GLOBAL) { - key = slot - JSSLOT_START(clasp); -#define JS_PROTO(name,code,init) \ - if ((code) == key) { slotname = js_##name##_str; goto found; } -#include "jsproto.tbl" -#undef JS_PROTO - } - found: - if (slotname) - JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); - else - JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); - break; - } - } else { - nval = ID_TO_VALUE(sprop->id); - if (JSVAL_IS_INT(nval)) { - JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); - } else if (JSVAL_IS_STRING(nval)) { - JS_snprintf(buf, bufsize, "%s", - JS_GetStringBytes(JSVAL_TO_STRING(nval))); - } else { - JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); - } - } -} - static const char * gc_object_class_name(void* thing) { @@ -1855,9 +895,8 @@ gc_object_class_name(void* thing) } static void -gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) +gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp) { - GCMarkNode *prev = (GCMarkNode *)cx->gcCurrentMarkNode; GCMarkNode *next = NULL; char *path = NULL; @@ -1883,7 +922,7 @@ gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) return; fprintf(fp, "%08lx ", (long)thing); - switch (*js_GetGCThingFlags(thing) & GCF_TYPEMASK) { + switch (flags & GCF_TYPEMASK) { case GCX_OBJECT: { JSObject *obj = (JSObject *)thing; @@ -1933,37 +972,6 @@ gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) free(path); } -void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name) -{ - GCMarkNode markNode; - - if (!thing) - return; - - markNode.thing = thing; - markNode.name = name; - markNode.next = NULL; - markNode.prev = (GCMarkNode *)cx->gcCurrentMarkNode; - if (markNode.prev) - markNode.prev->next = &markNode; - cx->gcCurrentMarkNode = &markNode; - - if (thing == js_LiveThingToFind) { - /* - * Dump js_LiveThingToFind each time we reach it during the marking - * phase of GC to print all live references to the thing. - */ - gc_dump_thing(cx, thing, stderr); - } - - js_MarkGCThing(cx, thing); - - if (markNode.prev) - markNode.prev->next = NULL; - cx->gcCurrentMarkNode = markNode.prev; -} - #endif /* !GC_MARK_DEBUG */ static void @@ -1971,11 +979,11 @@ gc_mark_atom_key_thing(void *thing, void *arg) { JSContext *cx = (JSContext *) arg; - GC_MARK(cx, thing, "atom"); + GC_MARK(cx, thing, "atom", NULL); } void -js_MarkAtom(JSContext *cx, JSAtom *atom) +js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg) { jsval key; @@ -1994,45 +1002,103 @@ js_MarkAtom(JSContext *cx, JSAtom *atom) JS_snprintf(name, sizeof name, "<%x>", key); } #endif - GC_MARK(cx, JSVAL_TO_GCTHING(key), name); + GC_MARK(cx, JSVAL_TO_GCTHING(key), name, arg); } if (atom->flags & ATOM_HIDDEN) - js_MarkAtom(cx, atom->entry.value); + js_MarkAtom(cx, atom->entry.value, arg); +} + +/* + * 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 +# 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 NULL; + + flagp = js_GetGCThingFlags(thing); + flags = *flagp; + JS_ASSERT(flags != GCF_FINAL); +#ifdef GC_MARK_DEBUG + if (js_LiveThingToFind == thing) + gc_dump_thing(thing, flags, arg, stderr); +#endif + + if (flags & GCF_MARK) + return 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 -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp); +DeutschSchorrWaite(JSContext *cx, void *thing, uint8 *flagp); -static void -MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, - JSBool shouldCheckRecursion) +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; - JSString *str; #ifdef JS_GCMETER uint32 tailCallNesting; #endif #ifdef GC_MARK_DEBUG JSScope *scope; + JSScopeProperty *sprop; char name[32]; #endif - - /* - * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always - * uses the non-recursive code that otherwise would be called only on - * a low C stack condition. - */ -#ifdef JS_GC_ASSUME_LOW_C_STACK -# define RECURSION_TOO_DEEP() shouldCheckRecursion -#else int stackDummy; -# define RECURSION_TOO_DEEP() (shouldCheckRecursion && \ - !JS_CHECK_STACK_SIZE(cx, stackDummy)) -#endif rt = cx->runtime; METER(tailCallNesting = 0); @@ -2043,82 +1109,135 @@ MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, start: #endif JS_ASSERT(flagp); - JS_ASSERT(*flagp & GCF_MARK); /* the caller must already mark the thing */ 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(cx, thing, js_DumpGCHeap); + gc_dump_thing(thing, *flagp, arg, js_DumpGCHeap); #endif switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; /* If obj->slots is null, obj must be a newborn. */ obj = (JSObject *) thing; vp = obj->slots; if (!vp) - break; + 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; + } /* 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"); + GC_MARK(cx, vp - 1, "slots", arg); /* Set up local variables to loop over unmarked things. */ end = vp + ((obj->map->ops->mark) - ? obj->map->ops->mark(cx, obj, NULL) + ? CALL_GC_THING_MARKER(obj->map->ops->mark, cx, obj, arg) : JS_MIN(obj->map->freeslot, obj->map->nslots)); - thing = NULL; - flagp = NULL; + + 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 (; vp != end; ++vp) { - v = *vp; - if (!JSVAL_IS_GCTHING(v) || v == JSVAL_NULL) - continue; - next_thing = JSVAL_TO_GCTHING(v); - if (next_thing == thing) - continue; - next_flagp = js_GetGCThingFlags(next_thing); - if (*next_flagp & GCF_MARK) - continue; - JS_ASSERT(*next_flagp != GCF_FINAL); - if (thing) { + 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 - GC_MARK(cx, thing, name); -#else - *flagp |= GCF_MARK; - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); -#endif - if (*next_flagp & GCF_MARK) { - /* - * This happens when recursive MarkGCThingChildren marks - * the thing with flags referred by *next_flagp. - */ - thing = NULL; - continue; + 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; + 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 + + 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 - GetObjSlotName(scope, obj, vp - obj->slots, name, sizeof name); + 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; } - if (thing) { - /* - * thing came from the last unmarked GC-thing slot and we - * can optimize tail recursion. - * - * Since we already know that there is enough C stack space, - * we clear shouldCheckRecursion to avoid extra checking in - * RECURSION_TOO_DEEP. - */ - shouldCheckRecursion = JS_FALSE; - goto on_tail_recursion; - } break; #ifdef DEBUG @@ -2130,348 +1249,206 @@ MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, case GCX_MUTABLE_STRING: str = (JSString *)thing; - if (!JSSTRING_IS_DEPENDENT(str)) - break; - thing = JSSTRDEP_BASE(str); - flagp = js_GetGCThingFlags(thing); - if (*flagp & GCF_MARK) - break; + if (JSSTRING_IS_DEPENDENT(str)) { + thing = JSSTRDEP_BASE(str); + flagp = UNMARKED_GC_THING_FLAGS(thing, arg); + if (flagp) { #ifdef GC_MARK_DEBUG - strcpy(name, "base"); -#endif - /* Fallthrough to code to deal with the tail recursion. */ - - on_tail_recursion: -#ifdef GC_MARK_DEBUG - /* - * Do not eliminate C recursion when debugging to allow - * js_MarkNamedGCThing to build a full dump of live GC - * things. - */ - GC_MARK(cx, thing, name); - break; + GC_MARK(cx, thing, "base", arg); + goto out; #else - /* Eliminate tail recursion for the last unmarked child. */ - JS_ASSERT(*flagp != GCF_FINAL); - METER(++tailCallNesting); - *flagp |= GCF_MARK; - goto start; + METER(++tailCallNesting); + goto start; #endif + } + } + break; #if JS_HAS_XML_SUPPORT case GCX_NAMESPACE: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLNamespace(cx, (JSXMLNamespace *)thing); + CALL_GC_THING_MARKER(js_MarkXMLNamespace, cx, (JSXMLNamespace *)thing, + arg); break; case GCX_QNAME: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLQName(cx, (JSXMLQName *)thing); + CALL_GC_THING_MARKER(js_MarkXMLQName, cx, (JSXMLQName *)thing, arg); break; case GCX_XML: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXML(cx, (JSXML *)thing); + CALL_GC_THING_MARKER(js_MarkXML, cx, (JSXML *)thing, arg); break; #endif - add_to_unscanned_bag: - AddThingToUnscannedBag(cx->runtime, thing, flagp); - break; } -#undef RECURSION_TOO_DEEP - +out: METER(rt->gcStats.depth -= 1 + tailCallNesting); METER(rt->gcStats.cdepth--); + return JS_TRUE; } /* - * Avoid using PAGE_THING_GAP inside this macro to optimize the - * thingsPerUnscannedChunk calculation when thingSize is a power of two. + * 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 GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap) \ - JS_BEGIN_MACRO \ - if (0 == ((thingSize) & ((thingSize) - 1))) { \ - pageGap = (thingSize); \ - thingsPerUnscannedChunk = ((GC_PAGE_SIZE / (thingSize)) \ - + JS_BITS_PER_WORD - 1) \ - >> JS_BITS_PER_WORD_LOG2; \ - } else { \ - pageGap = GC_PAGE_SIZE % (thingSize); \ - thingsPerUnscannedChunk = JS_HOWMANY(GC_PAGE_SIZE / (thingSize), \ - JS_BITS_PER_WORD); \ - } \ - JS_END_MACRO +#define JSVAL_EMPTY (2 << JSVAL_TAGBITS) -static void -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp) +/* + * 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) { - JSGCPageInfo *pi; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t chunkIndex; - jsuword bit; + uint32 nslots, limit, index; + jsdouble d; - /* Things from delayed scanning bag are marked as GCF_MARK | GCF_FINAL. */ - JS_ASSERT((*flagp & (GCF_MARK | GCF_FINAL)) == GCF_MARK); - *flagp |= GCF_FINAL; + 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; +} - METER(rt->gcStats.unscanned++); -#ifdef DEBUG - ++rt->gcUnscannedBagSize; - METER(if (rt->gcUnscannedBagSize > rt->gcStats.maxunscanned) - rt->gcStats.maxunscanned = rt->gcUnscannedBagSize); -#endif +static JS_INLINE uint32 +DecodeDSWIndex(uint16 dswIndex, jsval *slots) +{ + uint32 nslots, limit; + jsdouble d; - pi = THING_TO_PAGE(thing); - arena = PAGE_TO_ARENA(pi); - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - chunkIndex = (((jsuword)thing & GC_PAGE_MASK) - pageGap) / - (thingSize * thingsPerUnscannedChunk); - JS_ASSERT(chunkIndex < JS_BITS_PER_WORD); - bit = (jsuword)1 << chunkIndex; - if (pi->unscannedBitmap != 0) { - JS_ASSERT(rt->gcUnscannedArenaStackTop); - if (thingsPerUnscannedChunk != 1) { - if (pi->unscannedBitmap & bit) { - /* Chunk already contains things to scan later. */ - return; - } - } else { - /* - * The chunk must not contain things to scan later if there is - * only one thing per chunk. - */ - JS_ASSERT(!(pi->unscannedBitmap & bit)); - } - pi->unscannedBitmap |= bit; - JS_ASSERT(arena->unscannedPages & ((size_t)1 << PAGE_INDEX(pi))); - } else { - /* - * The thing is the first unscanned thing in the page, set the bit - * corresponding to this page arena->unscannedPages. - */ - pi->unscannedBitmap = bit; - JS_ASSERT(PAGE_INDEX(pi) < JS_BITS_PER_WORD); - bit = (jsuword)1 << PAGE_INDEX(pi); - JS_ASSERT(!(arena->unscannedPages & bit)); - if (arena->unscannedPages != 0) { - arena->unscannedPages |= bit; - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop); - } else { - /* - * The thing is the first unscanned thing in the whole arena, push - * the arena on the stack of unscanned arenas unless the arena - * has already been pushed. We detect that through prevUnscanned - * field which is NULL only for not yet pushed arenas. To ensure - * that prevUnscanned != NULL even when the stack contains one - * element, we make prevUnscanned for the arena at the bottom - * to point to itself. - * - * See comments in ScanDelayedChildren. - */ - arena->unscannedPages = bit; - if (!arena->prevUnscanned) { - if (!rt->gcUnscannedArenaStackTop) { - /* Stack was empty, mark the arena as bottom element. */ - arena->prevUnscanned = arena; - } else { - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - arena->prevUnscanned = rt->gcUnscannedArenaStackTop; - } - rt->gcUnscannedArenaStackTop = arena; - } - } - } - JS_ASSERT(rt->gcUnscannedArenaStackTop); + 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 -ScanDelayedChildren(JSContext *cx) +DeutschSchorrWaite(JSContext *cx, void *thing, uint8 *flagp) { - JSRuntime *rt; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t pageIndex; - JSGCPageInfo *pi; - size_t chunkIndex; - size_t thingOffset, thingLimit; - JSGCThing *thing; - uint8 *flagp; - JSGCArena *prevArena; - - rt = cx->runtime; - arena = rt->gcUnscannedArenaStackTop; - if (!arena) { - JS_ASSERT(rt->gcUnscannedBagSize == 0); - return; - } - - init_size: - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - for (;;) { - /* - * The following assert verifies that the current arena belongs to - * the unscan stack since AddThingToUnscannedBag ensures that even - * for stack's bottom prevUnscanned != NULL but rather points to self. - */ - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - while (arena->unscannedPages != 0) { - pageIndex = JS_FLOOR_LOG2W(arena->unscannedPages); - JS_ASSERT(pageIndex < GC_PAGE_COUNT); - pi = (JSGCPageInfo *)(FIRST_THING_PAGE(arena) + - pageIndex * GC_PAGE_SIZE); - JS_ASSERT(pi->unscannedBitmap); - chunkIndex = JS_FLOOR_LOG2W(pi->unscannedBitmap); - pi->unscannedBitmap &= ~((jsuword)1 << chunkIndex); - if (pi->unscannedBitmap == 0) - arena->unscannedPages &= ~((jsuword)1 << pageIndex); - thingOffset = (pageGap - + chunkIndex * thingsPerUnscannedChunk * thingSize); - JS_ASSERT(thingOffset >= sizeof(JSGCPageInfo)); - thingLimit = thingOffset + thingsPerUnscannedChunk * thingSize; - if (thingsPerUnscannedChunk != 1) { - /* - * thingLimit can go beyond the last allocated thing for the - * last chunk as the real limit can be inside the chunk. - */ - if (arena->list->last == arena && - arena->list->lastLimit < (pageIndex * GC_PAGE_SIZE + - thingLimit)) { - thingLimit = (arena->list->lastLimit - - pageIndex * GC_PAGE_SIZE); - } else if (thingLimit > GC_PAGE_SIZE) { - thingLimit = GC_PAGE_SIZE; - } - JS_ASSERT(thingLimit > thingOffset); - } - JS_ASSERT(arena->list->last != arena || - arena->list->lastLimit >= (pageIndex * GC_PAGE_SIZE + - thingLimit)); - JS_ASSERT(thingLimit <= GC_PAGE_SIZE); - - for (; thingOffset != thingLimit; thingOffset += thingSize) { - /* - * XXX: inline js_GetGCThingFlags() to use already available - * pi. - */ - thing = (void *)((jsuword)pi + thingOffset); - flagp = js_GetGCThingFlags(thing); - if (thingsPerUnscannedChunk != 1) { - /* - * Skip free or already scanned things that share the chunk - * with unscanned ones. - */ - if ((*flagp & (GCF_MARK|GCF_FINAL)) != (GCF_MARK|GCF_FINAL)) - continue; - } - JS_ASSERT((*flagp & (GCF_MARK|GCF_FINAL)) - == (GCF_MARK|GCF_FINAL)); - *flagp &= ~GCF_FINAL; -#ifdef DEBUG - JS_ASSERT(rt->gcUnscannedBagSize != 0); - --rt->gcUnscannedBagSize; - - /* - * Check that GC thing type is consistent with the type of - * things that can be put to the unscanned bag. - */ - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: -# if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - case GCX_QNAME: - case GCX_XML: -# endif - break; - default: - JS_ASSERT(0); - } + jsval top, parent, v, *vp, *end; + JSObject *obj; + JSScope *scope; +#ifdef JS_GCMETER + JSRuntime *rt = cx->runtime; #endif - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); + + 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]; + /* - * We finished scanning of the arena but we can only pop it from - * the stack if the arena is the stack's top. - * - * When MarkGCThingChildren from the above calls - * AddThingToUnscannedBag and the latter pushes new arenas to the - * stack, we have to skip popping of this arena until it becomes - * the top of the stack again. + * If obj is native and owns its own scope, we can minimize the cost + * of searching for the reversed pointer. */ - if (arena == rt->gcUnscannedArenaStackTop) { - prevArena = arena->prevUnscanned; - arena->prevUnscanned = NULL; - if (arena == prevArena) { - /* - * prevUnscanned points to itself and we reached the bottom - * of the stack. - */ - break; - } - rt->gcUnscannedArenaStackTop = arena = prevArena; - } else { - arena = rt->gcUnscannedArenaStackTop; + 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; } - if (arena->list->thingSize != thingSize) - goto init_size; + + *vp++ = parent; + parent = top; + top = JSVAL_CLRTAG(v); } - JS_ASSERT(rt->gcUnscannedArenaStackTop); - JS_ASSERT(!rt->gcUnscannedArenaStackTop->prevUnscanned); - rt->gcUnscannedArenaStackTop = NULL; - JS_ASSERT(rt->gcUnscannedBagSize == 0); } void -js_MarkGCThing(JSContext *cx, void *thing) +js_MarkGCThing(JSContext *cx, void *thing, void *arg) { uint8 *flagp; - if (!thing) + flagp = UNMARKED_GC_THING_FLAGS(thing, arg); + if (!flagp) return; - - flagp = js_GetGCThingFlags(thing); - JS_ASSERT(*flagp != GCF_FINAL); - if (*flagp & GCF_MARK) - return; - *flagp |= GCF_MARK; - - if (!cx->insideGCMarkCallback) { - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); - } else { - /* - * For API compatibility we allow for the callback to assume that - * after it calls js_MarkGCThing for the last time, the callback - * can start to finalize its own objects that are only referenced - * by unmarked GC things. - * - * Since we do not know which call from inside the callback is the - * last, we ensure that the unscanned bag is always empty when we - * return to the callback and all marked things are scanned. - * - * As an optimization we do not check for the stack size here and - * pass JS_FALSE as the last argument to MarkGCThingChildren. - * Otherwise with low C stack the thing would be pushed to the bag - * just to be feed to MarkGCThingChildren from inside - * ScanDelayedChildren. - */ - cx->insideGCMarkCallback = JS_FALSE; - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); - ScanDelayedChildren(cx); - cx->insideGCMarkCallback = JS_TRUE; - } + MARK_GC_THING(cx, thing, flagp, arg); } JS_STATIC_DLL_CALLBACK(JSDHashOperator) @@ -2485,35 +1462,32 @@ 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 - JSBool root_points_to_gcArenaList = JS_FALSE; - jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); uintN i; - JSGCArenaList *arenaList; - JSGCArena *a; - size_t limit; + JSArena *a; + jsuword firstpage; + JSBool root_points_to_gcArenaPool = JS_FALSE; + void *thing = JSVAL_TO_GCTHING(v); for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &cx->runtime->gcArenaList[i]; - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - if (thing - FIRST_THING_PAGE(a) < limit) { - root_points_to_gcArenaList = JS_TRUE; + 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; } - limit = GC_THINGS_SIZE; } } - if (!root_points_to_gcArenaList && rhe->name) { + if (!root_points_to_gcArenaPool && rhe->name) { fprintf(stderr, "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" "invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" "The root's name is \"%s\".\n", rhe->name); } - JS_ASSERT(root_points_to_gcArenaList); + JS_ASSERT(root_points_to_gcArenaPool); #endif - GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root"); + GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root", NULL); } return JS_DHASH_NEXT; } @@ -2525,10 +1499,23 @@ gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) void *thing = (void *)lhe->thing; JSContext *cx = (JSContext *)arg; - GC_MARK(cx, thing, "locked object"); + GC_MARK(cx, thing, "locked object", NULL); return JS_DHASH_NEXT; } +void +js_ForceGC(JSContext *cx, uintN gcflags) +{ + uintN i; + + for (i = 0; i < GCX_NTYPES; i++) + cx->newborn[i] = NULL; + cx->lastAtom = NULL; + cx->runtime->gcPoke = JS_TRUE; + js_GC(cx, gcflags); + JS_ArenaFinish(); +} + #define GC_MARK_JSVALS(cx, len, vec, name) \ JS_BEGIN_MACRO \ jsval _v, *_vp, *_end; \ @@ -2536,129 +1523,28 @@ gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ _v = *_vp; \ if (JSVAL_IS_GCTHING(_v)) \ - GC_MARK(cx, JSVAL_TO_GCTHING(_v), name); \ + GC_MARK(cx, JSVAL_TO_GCTHING(_v), name, NULL); \ } \ JS_END_MACRO void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp) -{ - uintN depth, nslots; - - if (fp->callobj) - GC_MARK(cx, fp->callobj, "call object"); - if (fp->argsobj) - GC_MARK(cx, fp->argsobj, "arguments object"); - if (fp->varobj) - GC_MARK(cx, fp->varobj, "variables object"); - if (fp->script) { - js_MarkScript(cx, fp->script); - if (fp->spbase) { - /* - * Don't mark what has not been pushed yet, or what has been - * popped already. - */ - depth = fp->script->depth; - nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) - < depth * sizeof(jsval)) - ? (uintN)(fp->sp - fp->spbase) - : depth; - GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); - } - } - - /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) || - (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); - if (JSVAL_IS_GCTHING((jsval)fp->thisp)) - GC_MARK(cx, JSVAL_TO_GCTHING((jsval)fp->thisp), "this"); - - /* - * Mark fp->argv, even though in the common case it will be marked via our - * caller's frame, or via a JSStackHeader if fp was pushed by an external - * invocation. - * - * The hard case is when there is not enough contiguous space in the stack - * arena for actual, missing formal, and local root (JSFunctionSpec.extra) - * slots. In this case, fp->argv points to new space in a new arena, and - * marking the caller's operand stack, or an external caller's allocated - * stack tracked by a JSStackHeader, will not mark all the values stored - * and addressable via fp->argv. - * - * So in summary, solely for the hard case of moving argv due to missing - * formals and extra roots, we must mark actuals, missing formals, and any - * local roots arrayed at fp->argv here. - * - * It would be good to avoid redundant marking of the same reference, in - * the case where fp->argv does point into caller-allocated space tracked - * by fp->down->spbase or cx->stackHeaders. This would allow callbacks - * such as the forthcoming rt->gcThingCallback (bug 333078) to compute JS - * reference counts. So this comment deserves a FIXME bug to cite. - */ - if (fp->argv) { - nslots = fp->argc; - if (fp->fun) { - if (fp->fun->nargs > nslots) - nslots = fp->fun->nargs; - if (!FUN_INTERPRETED(fp->fun)) - nslots += fp->fun->u.n.extra; - } - GC_MARK_JSVALS(cx, nslots + 2, fp->argv - 2, "arg"); - } - if (JSVAL_IS_GCTHING(fp->rval)) - GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval"); - if (fp->vars) - GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); - GC_MARK(cx, fp->scopeChain, "scope chain"); - if (fp->sharpArray) - GC_MARK(cx, fp->sharpArray, "sharp array"); - - if (fp->xmlNamespace) - GC_MARK(cx, fp->xmlNamespace, "xmlNamespace"); -} - -static void -MarkWeakRoots(JSContext *cx, JSWeakRoots *wr) -{ - uintN i; - void *thing; - - for (i = 0; i < GCX_NTYPES; i++) - GC_MARK(cx, wr->newborn[i], gc_typenames[i]); - if (wr->lastAtom) - GC_MARK_ATOM(cx, wr->lastAtom); - if (JSVAL_IS_GCTHING(wr->lastInternalResult)) { - thing = JSVAL_TO_GCTHING(wr->lastInternalResult); - if (thing) - GC_MARK(cx, thing, "lastInternalResult"); - } -} - -/* - * When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with - * rt->gcLock already held and when the lock should be kept on return. - */ -void -js_GC(JSContext *cx, JSGCInvocationKind gckind) +js_GC(JSContext *cx, uintN gcflags) { JSRuntime *rt; - JSBool keepAtoms; - uintN i, type; JSContext *iter, *acx; -#if JS_HAS_GENERATORS - JSGenerator **genTodoTail; -#endif JSStackFrame *fp, *chain; + uintN i, depth, nslots, type; JSStackHeader *sh; JSTempValueRooter *tvr; - size_t nbytes, limit, offset; - JSGCArena *a, **ap; - uint8 flags, *flagp, *firstPage; - JSGCThing *thing, *freeList; - JSGCArenaList *arenaList; + size_t nbytes, nflags; + JSArena *a, **ap; + uint8 flags, *flagp, *split; + JSGCThing *thing, *limit, **flp, **oflp; GCFinalizeOp finalizer; - JSBool allClear; + uint32 *bytesptr; + JSBool all_clear; #ifdef JS_THREADSAFE + jsword currentThread; uint32 requestDebit; #endif @@ -2668,45 +1554,32 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); #endif - if (gckind == GC_LAST_DITCH) { - /* The last ditch GC preserves all atoms and weak roots. */ - keepAtoms = JS_TRUE; - } else { - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); - rt->gcPoke = JS_TRUE; - - /* Keep atoms when a suspended compile is running on another context. */ - keepAtoms = (rt->gcKeepAtoms != 0); - } - /* * Don't collect garbage if the runtime isn't up, and cx is not the last * context in the runtime. The last context must force a GC, and nothing * should suppress that final collection or there may be shutdown leaks, * or runtime bloat until the next context is created. */ - if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) + if (rt->state != JSRTS_UP && !(gcflags & GC_LAST_CONTEXT)) return; - restart_after_callback: /* * Let the API user decide to defer a GC if it wants to (unless this * is the last context). Invoke the callback regardless. */ - if (rt->gcCallback && - !rt->gcCallback(cx, JSGC_BEGIN) && - gckind != GC_LAST_CONTEXT) { - return; + if (rt->gcCallback) { + if (!rt->gcCallback(cx, JSGC_BEGIN) && !(gcflags & GC_LAST_CONTEXT)) + return; } /* Lock out other GC allocator and collector invocations. */ - if (gckind != GC_LAST_DITCH) + if (!(gcflags & GC_ALREADY_LOCKED)) JS_LOCK_GC(rt); /* Do nothing if no mutator has executed since the last GC. */ if (!rt->gcPoke) { METER(rt->gcStats.nopoke++); - if (gckind != GC_LAST_DITCH) + if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); return; } @@ -2714,15 +1587,14 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) rt->gcPoke = JS_FALSE; #ifdef JS_THREADSAFE - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - /* Bump gcLevel and return rather than nest on this thread. */ - if (rt->gcThread == cx->thread) { + currentThread = js_CurrentThreadId(); + if (rt->gcThread == currentThread) { JS_ASSERT(rt->gcLevel > 0); rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); - if (gckind != GC_LAST_DITCH) + if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); return; } @@ -2730,29 +1602,25 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) /* * If we're in one or more requests (possibly on more than one context) * running on the current thread, indicate, temporarily, that all these - * requests are inactive. If cx->thread is NULL, then cx is not using + * requests are inactive. NB: if cx->thread is 0, then cx is not using * the request model, and does not contribute to rt->requestCount. */ requestDebit = 0; if (cx->thread) { - JSCList *head, *link; - /* - * Check all contexts on cx->thread->contextList for active requests, - * counting each such context against requestDebit. + * Check all contexts for any with the same thread-id. XXX should we + * keep a sub-list of contexts having the same id? */ - head = &cx->thread->contextList; - for (link = head->next; link != head; link = link->next) { - acx = CX_FROM_THREAD_LINKS(link); - JS_ASSERT(acx->thread == cx->thread); - if (acx->requestDepth) + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { + if (acx->thread == cx->thread && acx->requestDepth) requestDebit++; } } else { /* * We assert, but check anyway, in case someone is misusing the API. * Avoiding the loop over all of rt's contexts is a win in the event - * that the GC runs only on request-less contexts with null threads, + * that the GC runs only on request-less contexts with 0 thread-ids, * in a special thread such as might be used by the UI/DOM/Layout * "mozilla" or "main" thread in Mozilla-the-browser. */ @@ -2779,14 +1647,14 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) JS_AWAIT_GC_DONE(rt); if (requestDebit) rt->requestCount += requestDebit; - if (gckind != GC_LAST_DITCH) + if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); return; } /* No other thread is in GC, so indicate that we're now in GC. */ rt->gcLevel = 1; - rt->gcThread = cx->thread; + rt->gcThread = currentThread; /* Wait for all other requests to finish. */ while (rt->requestCount > 0) @@ -2814,47 +1682,24 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) rt->gcRunning = JS_TRUE; JS_UNLOCK_GC(rt); + /* If a suspended compile is running on another context, keep atoms. */ + if (rt->gcKeepAtoms) + gcflags |= GC_KEEP_ATOMS; + /* Reset malloc counter. */ rt->gcMallocBytes = 0; /* Drop atoms held by the property cache, and clear property weak links. */ js_DisablePropertyCache(cx); js_FlushPropertyCache(cx); -#ifdef DEBUG_scopemeters +#ifdef DEBUG_notme { extern void js_DumpScopeMeters(JSRuntime *rt); js_DumpScopeMeters(rt); } #endif -#ifdef JS_THREADSAFE - /* - * Set all thread local freelists to NULL. We may visit a thread's - * freelist more than once. To avoid redundant clearing we unroll the - * current thread's step. - * - * Also, in case a JSScript wrapped within an object was finalized, we - * null acx->thread->gsnCache.script and finish the cache's hashtable. - * Note that js_DestroyScript, called from script_finalize, will have - * already cleared cx->thread->gsnCache above during finalization, so we - * don't have to here. - */ - memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists); - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (!acx->thread || acx->thread == cx->thread) - continue; - memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists); - GSN_CACHE_CLEAR(&acx->thread->gsnCache); - } -#else - /* The thread-unsafe case just has to clear the runtime's GSN cache. */ - GSN_CACHE_CLEAR(&rt->gsnCache); -#endif - restart: rt->gcNumber++; - JS_ASSERT(!rt->gcUnscannedArenaStackTop); - JS_ASSERT(rt->gcUnscannedBagSize == 0); /* * Mark phase. @@ -2862,16 +1707,11 @@ restart: JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); if (rt->gcLocksHash) JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); - js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx); + js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx); js_MarkWatchPoints(cx); - js_MarkScriptFilenames(rt, keepAtoms); + js_MarkScriptFilenames(rt, gcflags); js_MarkNativeIteratorStates(cx); -#if JS_HAS_GENERATORS - genTodoTail = MarkScheduledGenerators(cx); - JS_ASSERT(!*genTodoTail); -#endif - iter = NULL; while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { /* @@ -2890,7 +1730,47 @@ restart: for (fp = chain; fp; fp = chain = chain->dormantNext) { do { - js_MarkStackFrame(cx, fp); + if (fp->callobj) + GC_MARK(cx, fp->callobj, "call object", NULL); + if (fp->argsobj) + GC_MARK(cx, fp->argsobj, "arguments object", NULL); + if (fp->varobj) + GC_MARK(cx, fp->varobj, "variables object", NULL); + if (fp->script) { + js_MarkScript(cx, fp->script, NULL); + if (fp->spbase) { + /* + * Don't mark what has not been pushed yet, or what + * has been popped already. + */ + depth = fp->script->depth; + nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) + < depth * sizeof(jsval)) + ? (uintN)(fp->sp - fp->spbase) + : depth; + GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); + } + } + GC_MARK(cx, fp->thisp, "this", NULL); + if (fp->argv) { + nslots = fp->argc; + if (fp->fun) { + 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)) + GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval", NULL); + if (fp->vars) + GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); + GC_MARK(cx, fp->scopeChain, "scope chain", NULL); + if (fp->sharpArray) + GC_MARK(cx, fp->sharpArray, "sharp array", NULL); + + if (fp->xmlNamespace) + GC_MARK(cx, fp->xmlNamespace, "xmlNamespace", NULL); } while ((fp = fp->down) != NULL); } @@ -2899,18 +1779,23 @@ restart: acx->fp->dormantNext = NULL; /* Mark other roots-by-definition in acx. */ - GC_MARK(cx, acx->globalObject, "global object"); - MarkWeakRoots(cx, &acx->weakRoots); - if (acx->throwing) { - if (JSVAL_IS_GCTHING(acx->exception)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception"); - } else { - /* Avoid keeping GC-ed junk stored in JSContext.exception. */ - acx->exception = JSVAL_NULL; + GC_MARK(cx, acx->globalObject, "global object", 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); +#endif #if JS_HAS_LVALUE_RETURN if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2"); + GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2", NULL); #endif for (sh = acx->stackHeaders; sh; sh = sh->down) { @@ -2921,25 +1806,15 @@ restart: if (acx->localRootStack) js_MarkLocalRoots(cx, acx->localRootStack); - for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { - switch (tvr->count) { - case JSTVU_SINGLE: + if (tvr->count == -1) { if (JSVAL_IS_GCTHING(tvr->u.value)) { GC_MARK(cx, JSVAL_TO_GCTHING(tvr->u.value), - "tvr->u.value"); + "tvr->u.value", NULL); } - break; - case JSTVU_MARKER: + } else if (tvr->count == -2) { tvr->u.marker(cx, tvr); - break; - case JSTVU_SPROP: - MARK_SCOPE_PROPERTY(cx, tvr->u.sprop); - break; - case JSTVU_WEAK_ROOTS: - MarkWeakRoots(cx, tvr->u.weakRoots); - break; - default: + } else { JS_ASSERT(tvr->count >= 0); GC_MARK_JSVALS(cx, tvr->count, tvr->u.array, "tvr->u.array"); } @@ -2948,71 +1823,38 @@ restart: if (acx->sharpObjectMap.depth > 0) js_GCMarkSharpMap(cx, &acx->sharpObjectMap); } - #ifdef DUMP_CALL_TABLE js_DumpCallTable(cx); #endif - /* - * Mark children of things that caused too deep recursion during above - * marking phase. - */ - ScanDelayedChildren(cx); - -#if JS_HAS_GENERATORS - /* - * Close phase: search and mark part. See comments in - * FindAndMarkObjectsToClose for details. - */ - FindAndMarkObjectsToClose(cx, gckind, genTodoTail); - - /* - * Mark children of things that caused too deep recursion during the - * just-completed marking part of the close phase. - */ - ScanDelayedChildren(cx); -#endif - - JS_ASSERT(!cx->insideGCMarkCallback); - if (rt->gcCallback) { - cx->insideGCMarkCallback = JS_TRUE; + if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_MARK_END); - JS_ASSERT(cx->insideGCMarkCallback); - cx->insideGCMarkCallback = JS_FALSE; - } - JS_ASSERT(rt->gcUnscannedBagSize == 0); - - /* Finalize iterator states before the objects they iterate over. */ - CloseIteratorStates(cx); /* * Sweep phase. * - * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set + * 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); for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - JS_ASSERT(!a->prevUnscanned); - JS_ASSERT(a->unscannedPages == 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) { - JS_ASSERT(((JSGCPageInfo *)(firstPage + offset))-> - unscannedBitmap == 0); - offset += PAGE_THING_GAP(nbytes); + 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); } - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; flags = *flagp; if (flags & GCF_MARK) { *flagp &= ~GCF_MARK; @@ -3021,33 +1863,32 @@ restart: type = flags & GCF_TYPEMASK; finalizer = gc_finalizers[type]; if (finalizer) { - thing = (JSGCThing *)(firstPage + offset); *flagp = (uint8)(flags | GCF_FINAL); if (type >= GCX_EXTERNAL_STRING) - js_PurgeDeflatedStringCache(rt, (JSString *)thing); + js_PurgeDeflatedStringCache((JSString *)thing); finalizer(cx, thing); } /* Set flags to GCF_FINAL, signifying that thing is free. */ *flagp = GCF_FINAL; + + 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; } - limit = GC_THINGS_SIZE; } } - /* - * Sweep the runtime's property tree after finalizing objects, in case any - * had watchpoints referencing tree nodes. Then sweep atoms, which may be - * referenced from dead property ids. - */ - js_SweepScopeProperties(rt); - js_SweepAtomState(&rt->atomState); - /* * Sweep script filenames after sweeping functions in the generic loop - * above. In this way when a scripted function's finalizer destroys the - * script and calls rt->destroyScriptHook, the hook can still access the + * 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); @@ -3057,64 +1898,58 @@ restart: * Free any unused arenas and rebuild the JSGCThing freelist. */ for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - ap = &arenaList->last; + ap = &rt->gcArenaPool[i].first.next; a = *ap; if (!a) continue; - allClear = JS_TRUE; - arenaList->freeList = NULL; - freeList = NULL; - METER(arenaList->stats.nthings = 0); - METER(arenaList->stats.freelen = 0); + all_clear = JS_TRUE; + flp = oflp = &rt->gcFreeList[i]; + *flp = NULL; + METER(rt->gcStats.freelen[i] = 0); nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; + nflags = nbytes / sizeof(JSGCThing); do { - METER(size_t nfree = 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - - if (*flagp != GCF_FINAL) { - allClear = JS_FALSE; - METER(++arenaList->stats.nthings); - } else { - thing = (JSGCThing *)(firstPage + offset); - thing->flagp = flagp; - thing->next = freeList; - freeList = thing; - METER(++nfree); + 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 (allClear) { - /* - * Forget just assembled free list head for the arena - * and destroy the arena itself. - */ - freeList = arenaList->freeList; - DestroyGCArena(rt, arenaList, ap); + + if (all_clear) { + JS_ARENA_DESTROY(&rt->gcArenaPool[i], a, ap); + flp = oflp; + METER(rt->gcStats.afree++); } else { - allClear = JS_TRUE; - arenaList->freeList = freeList; - ap = &a->prev; - METER(arenaList->stats.freelen += nfree); - METER(arenaList->stats.totalfreelen += nfree); - METER(++arenaList->stats.totalarenas); + ap = &a->next; + all_clear = JS_TRUE; + oflp = flp; } - limit = GC_THINGS_SIZE; } while ((a = *ap) != NULL); + + /* Terminate the new freelist. */ + *flp = NULL; } if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END); -#ifdef DEBUG_srcnotesize +#ifdef DEBUG_notme { extern void DumpSrcNoteSizeHist(); DumpSrcNoteSizeHist(); printf("GC HEAP SIZE %lu (%lu)\n", @@ -3123,11 +1958,6 @@ restart: #endif JS_LOCK_GC(rt); - - /* - * We want to restart GC if js_GC was called recursively or if any of the - * finalizers called js_RemoveRoot or js_UnlockGCThingRT. - */ if (rt->gcLevel > 1 || rt->gcPoke) { rt->gcLevel = 1; rt->gcPoke = JS_FALSE; @@ -3143,59 +1973,17 @@ restart: /* If we were invoked during a request, pay back the temporary debit. */ if (requestDebit) rt->requestCount += requestDebit; - rt->gcThread = NULL; + rt->gcThread = 0; JS_NOTIFY_GC_DONE(rt); - - /* - * Unlock unless we have GC_LAST_DITCH which requires locked GC on return. - */ - if (gckind != GC_LAST_DITCH) + if (!(gcflags & GC_ALREADY_LOCKED)) JS_UNLOCK_GC(rt); #endif - /* Execute JSGC_END callback outside the lock. */ if (rt->gcCallback) { - JSWeakRoots savedWeakRoots; - JSTempValueRooter tvr; - - if (gckind == GC_LAST_DITCH) { - /* - * We allow JSGC_END implementation to force a full GC or allocate - * new GC things. Thus we must protect the weak roots from GC or - * overwrites. - */ - savedWeakRoots = cx->weakRoots; - JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr); - JS_KEEP_ATOMS(rt); + if (gcflags & GC_ALREADY_LOCKED) JS_UNLOCK_GC(rt); - } - (void) rt->gcCallback(cx, JSGC_END); - - if (gckind == GC_LAST_DITCH) { + if (gcflags & GC_ALREADY_LOCKED) JS_LOCK_GC(rt); - JS_UNKEEP_ATOMS(rt); - JS_POP_TEMP_ROOT(cx, &tvr); - } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) { - /* - * On shutdown iterate until JSGC_END callback stops creating - * garbage. - */ - goto restart_after_callback; - } } } - -void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes) -{ - uint32 *pbytes, bytes; - -#ifdef JS_THREADSAFE - pbytes = &cx->thread->gcMallocBytes; -#else - pbytes = &cx->runtime->gcMallocBytes; -#endif - bytes = *pbytes; - *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes; -} diff --git a/lib/javascript/jsgc.h b/lib/javascript/jsgc.h index ec623a1a..efc13194 100644 --- a/lib/javascript/jsgc.h +++ b/lib/javascript/jsgc.h @@ -45,7 +45,6 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsdhash.h" -#include "jsutil.h" JS_BEGIN_EXTERN_C @@ -82,12 +81,18 @@ JS_BEGIN_EXTERN_C extern uint8 * js_GetGCThingFlags(void *thing); -/* - * The sole purpose of the function is to preserve public API compatibility - * in JS_GetStringBytes which takes only single JSString* argument. - */ -JSRuntime* -js_GetGCStringRuntime(JSString *str); +/* These are compatible with JSDHashEntryStub. */ +struct JSGCRootHashEntry { + JSDHashEntryHdr hdr; + void *root; + const char *name; +}; + +struct JSGCLockHashEntry { + JSDHashEntryHdr hdr; + const JSGCThing *thing; + uint32 count; +}; #if 1 /* @@ -119,60 +124,6 @@ js_AddRootRT(JSRuntime *rt, void *rp, const char *name); extern JSBool js_RemoveRoot(JSRuntime *rt, void *rp); -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data); -#endif - -extern uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); - -#if JS_HAS_GENERATORS - -/* - * Runtime state to support generators' close hooks. - */ -typedef struct JSGCCloseState { - /* - * Singly linked list of generators that are reachable from GC roots or - * were created after the last GC. - */ - JSGenerator *reachableList; - - /* - * Head of the queue of generators that have already become unreachable but - * whose close hooks are not yet run. - */ - JSGenerator *todoQueue; - -#ifndef JS_THREADSAFE - /* - * Flag indicating that the current thread is excuting a close hook for - * single thread case. - */ - JSBool runningCloseHook; -#endif -} JSGCCloseState; - -extern void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen); - -extern JSBool -js_RunCloseHooks(JSContext *cx); - -#endif - /* * The private JSGCThing struct, which describes a gcFreeList element. */ @@ -202,68 +153,70 @@ extern JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing); extern void -js_MarkAtom(JSContext *cx, JSAtom *atom); +js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg); /* We avoid a large number of unnecessary calls by doing the flag check first */ -#define GC_MARK_ATOM(cx, atom) \ +#define GC_MARK_ATOM(cx, atom, arg) \ JS_BEGIN_MACRO \ if (!((atom)->flags & ATOM_MARK)) \ - js_MarkAtom(cx, atom); \ + js_MarkAtom(cx, atom, arg); \ JS_END_MACRO -/* - * Always use GC_MARK macro and never call js_MarkGCThing directly so - * when GC_MARK_DEBUG is defined the dump of live GC things does not miss - * a thing. - */ extern void -js_MarkGCThing(JSContext *cx, void *thing); +js_MarkGCThing(JSContext *cx, void *thing, void *arg); #ifdef GC_MARK_DEBUG -# define GC_MARK(cx, thing, name) js_MarkNamedGCThing(cx, thing, name) +typedef struct GCMarkNode GCMarkNode; -extern void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name); +struct GCMarkNode { + void *thing; + const char *name; + GCMarkNode *next; + GCMarkNode *prev; +}; + +#define GC_MARK(cx_, thing_, name_, prev_) \ + JS_BEGIN_MACRO \ + GCMarkNode node_; \ + node_.thing = thing_; \ + node_.name = name_; \ + node_.next = NULL; \ + node_.prev = prev_; \ + if (prev_) ((GCMarkNode *)(prev_))->next = &node_; \ + js_MarkGCThing(cx_, thing_, &node_); \ + JS_END_MACRO extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; JS_EXTERN_DATA(void *) js_LiveThingToFind; -#else +#else /* !GC_MARK_DEBUG */ -# define GC_MARK(cx, thing, name) js_MarkGCThing(cx, thing) +#define GC_MARK(cx, thing, name, prev) js_MarkGCThing(cx, thing, NULL) -#endif - -extern void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp); +#endif /* !GC_MARK_DEBUG */ /* - * Kinds of js_GC invocation. + * 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_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 + * cleared early in js_GC, if it is set. + * GC_ALREADY_LOCKED rt->gcLock is already held on entry to js_GC, and kept + * on return to its caller. */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL, - - /* - * Called from js_DestroyContext for last JSContext in a JSRuntime, when - * it is imperative that rt->gcPoke gets cleared early in js_GC. - */ - GC_LAST_CONTEXT, - - /* - * Called from js_NewGCThing as a last-ditch GC attempt. See comments - * before js_GC definition for details. - */ - GC_LAST_DITCH -} JSGCInvocationKind; +#define GC_KEEP_ATOMS 0x1 +#define GC_LAST_CONTEXT 0x2 +#define GC_ALREADY_LOCKED 0x4 extern void -js_GC(JSContext *cx, JSGCInvocationKind gckind); +js_ForceGC(JSContext *cx, uintN gcflags); -/* Call this after succesful malloc of memory for GC-related things. */ extern void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes); +js_GC(JSContext *cx, uintN gcflags); #ifdef DEBUG_notme #define JS_GCMETER 1 @@ -272,10 +225,11 @@ js_UpdateMallocCounter(JSContext *cx, size_t nbytes); #ifdef JS_GCMETER typedef struct JSGCStats { -#ifdef JS_THREADSAFE - uint32 localalloc; /* number of succeeded allocations from local lists */ -#endif uint32 alloc; /* number of allocation attempts */ + 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 */ @@ -287,21 +241,17 @@ typedef struct JSGCStats { 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 unscanned; /* mark C stack overflows or number of times - GC things were put in unscanned bag */ -#ifdef DEBUG - uint32 maxunscanned; /* maximum size of unscanned bag */ -#endif + 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 */ uint32 afree; /* thing arenas freed so far */ uint32 stackseg; /* total extraordinary stack segments scanned */ uint32 segslots; /* total stack segment jsval slots scanned */ - uint32 nclose; /* number of objects with close hooks */ - uint32 maxnclose; /* max number of objects with close hooks */ - uint32 closelater; /* number of close hooks scheduled to run */ - uint32 maxcloselater; /* max number of close hooks scheduled to run */ } JSGCStats; extern JS_FRIEND_API(void) @@ -309,52 +259,6 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp); #endif /* JS_GCMETER */ -typedef struct JSGCArena JSGCArena; -typedef struct JSGCArenaList JSGCArenaList; - -#ifdef JS_GCMETER -typedef struct JSGCArenaStats JSGCArenaStats; - -struct JSGCArenaStats { - uint32 narenas; /* number of arena in list */ - uint32 maxarenas; /* maximun number of allocated arenas */ - uint32 nthings; /* number of allocates JSGCThing */ - uint32 maxthings; /* maximum number number of allocates JSGCThing */ - uint32 totalnew; /* number of succeeded calls to js_NewGCThing */ - uint32 freelen; /* freeList lengths */ - uint32 recycle; /* number of things recycled through freeList */ - uint32 totalarenas; /* total number of arenas with live things that - GC scanned so far */ - uint32 totalfreelen; /* total number of things that GC put to free - list so far */ -}; -#endif - -struct JSGCArenaList { - JSGCArena *last; /* last allocated GC arena */ - uint16 lastLimit; /* end offset of allocated so far things in - the last arena */ - uint16 thingSize; /* size of things to allocate on this list */ - JSGCThing *freeList; /* list of free GC things */ -#ifdef JS_GCMETER - JSGCArenaStats stats; -#endif -}; - -typedef struct JSWeakRoots { - /* Most recently created things by type, members of the GC's root set. */ - JSGCThing *newborn[GCX_NTYPES]; - - /* Atom root for the last-looked-up atom on this context. */ - JSAtom *lastAtom; - - /* Root for the result of the most recent js_InternalInvoke call. */ - jsval lastInternalResult; -} JSWeakRoots; - -JS_STATIC_ASSERT(JSVAL_NULL == 0); -#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) - #ifdef DEBUG_notme #define TOO_MUCH_GC 1 #endif diff --git a/lib/javascript/jshash.c b/lib/javascript/jshash.c index 8e25517d..0f22b5e3 100644 --- a/lib/javascript/jshash.c +++ b/lib/javascript/jshash.c @@ -161,20 +161,20 @@ JS_HashTableDestroy(JSHashTable *ht) } /* - * Multiplicative hash, from Knuth 6.4. - */ -#define BUCKET_HEAD(ht, keyHash) \ - (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) - +** Multiplicative hash, from Knuth 6.4. +*/ JS_PUBLIC_API(JSHashEntry **) JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) { JSHashEntry *he, **hep, **hep0; + JSHashNumber h; #ifdef HASHMETER ht->nlookups++; #endif - hep = hep0 = BUCKET_HEAD(ht, keyHash); + h = keyHash * JS_GOLDEN_RATIO; + h >>= ht->shift; + hep = hep0 = &ht->buckets[h]; while ((he = *hep) != NULL) { if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { /* Move to front of chain if not already there */ @@ -193,72 +193,44 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) return hep; } -static JSBool -Resize(JSHashTable *ht, uint32 newshift) -{ - size_t nb, nentries, i; - JSHashEntry **oldbuckets, *he, *next, **hep; -#ifdef DEBUG - size_t nold = NBUCKETS(ht); -#endif - - JS_ASSERT(newshift < JS_HASH_BITS); - - nb = (size_t)1 << (JS_HASH_BITS - newshift); - - /* Integer overflow protection. */ - if (nb > (size_t)-1 / sizeof(JSHashEntry*)) - return JS_FALSE; - nb *= sizeof(JSHashEntry*); - - oldbuckets = ht->buckets; - ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); - if (!ht->buckets) { - ht->buckets = oldbuckets; - return JS_FALSE; - } - memset(ht->buckets, 0, nb); - - ht->shift = newshift; - nentries = ht->nentries; - - for (i = 0; nentries != 0; i++) { - for (he = oldbuckets[i]; he; he = next) { - JS_ASSERT(nentries != 0); - --nentries; - next = he->next; - hep = BUCKET_HEAD(ht, he->keyHash); - - /* - * Since he comes from the old table, it must be unique and we - * simply add it to the head of bucket chain without chain lookup. - */ - he->next = *hep; - *hep = he; - } - } -#ifdef DEBUG - memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); -#endif - ht->allocOps->freeTable(ht->allocPriv, oldbuckets); - return JS_TRUE; -} - JS_PUBLIC_API(JSHashEntry *) JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, const void *key, void *value) { - uint32 n; - JSHashEntry *he; + uint32 i, n; + JSHashEntry *he, *next, **oldbuckets; + size_t nb; /* Grow the table if it is overloaded */ n = NBUCKETS(ht); if (ht->nentries >= OVERLOADED(n)) { - if (!Resize(ht, ht->shift - 1)) + oldbuckets = ht->buckets; + nb = 2 * n * sizeof(JSHashEntry *); + ht->buckets = (JSHashEntry**) + ht->allocOps->allocTable(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; return NULL; + } + memset(ht->buckets, 0, nb); #ifdef HASHMETER ht->ngrows++; #endif + ht->shift--; + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JS_HashTableRawLookup(ht, he->keyHash, he->key); + JS_ASSERT(*hep == NULL); + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + ht->allocOps->freeTable(ht->allocPriv, oldbuckets); hep = JS_HashTableRawLookup(ht, keyHash, key); } @@ -300,7 +272,9 @@ JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) JS_PUBLIC_API(void) JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) { - uint32 n; + uint32 i, n; + JSHashEntry *next, **oldbuckets; + size_t nb; *hep = he->next; ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); @@ -308,10 +282,33 @@ JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) /* Shrink table if it's underloaded */ n = NBUCKETS(ht); if (--ht->nentries < UNDERLOADED(n)) { - Resize(ht, ht->shift + 1); + oldbuckets = ht->buckets; + nb = n * sizeof(JSHashEntry*) / 2; + ht->buckets = (JSHashEntry**) + ht->allocOps->allocTable(ht->allocPriv, nb); + if (!ht->buckets) { + ht->buckets = oldbuckets; + return; + } + memset(ht->buckets, 0, nb); #ifdef HASHMETER ht->nshrinks++; #endif + ht->shift++; + + for (i = 0; i < n; i++) { + for (he = oldbuckets[i]; he; he = next) { + next = he->next; + hep = JS_HashTableRawLookup(ht, he->keyHash, he->key); + JS_ASSERT(*hep == NULL); + he->next = NULL; + *hep = he; + } + } +#ifdef DEBUG + memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]); +#endif + ht->allocOps->freeTable(ht->allocPriv, oldbuckets); } } @@ -353,22 +350,23 @@ JS_HashTableLookup(JSHashTable *ht, const void *key) JS_PUBLIC_API(int) JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) { - JSHashEntry *he, **hep, **bucket; - uint32 nlimit, n, nbuckets, newlog2; - int rv; + JSHashEntry *he, **hep; + uint32 i, nbuckets; + int rv, n = 0; + JSHashEntry *todo = NULL; - nlimit = ht->nentries; - n = 0; - for (bucket = ht->buckets; n != nlimit; ++bucket) { - hep = bucket; + nbuckets = NBUCKETS(ht); + for (i = 0; i < nbuckets; i++) { + hep = &ht->buckets[i]; while ((he = *hep) != NULL) { - JS_ASSERT(n < nlimit); rv = f(he, n, arg); n++; - if (rv & HT_ENUMERATE_REMOVE) { + if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) { *hep = he->next; - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); - --ht->nentries; + if (rv & HT_ENUMERATE_REMOVE) { + he->next = todo; + todo = he; + } } else { hep = &he->next; } @@ -379,21 +377,11 @@ JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) } out: - /* Shrink table if removal of entries made it underloaded */ - if (ht->nentries != nlimit) { - JS_ASSERT(ht->nentries < nlimit); - nbuckets = NBUCKETS(ht); - if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { - newlog2 = JS_CeilingLog2(ht->nentries); - if (newlog2 < MINBUCKETSLOG2) - newlog2 = MINBUCKETSLOG2; - - /* Check that we really shrink the table. */ - JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); - Resize(ht, JS_HASH_BITS - newlog2); - } + hep = &todo; + while ((he = *hep) != NULL) { + JS_HashTableRawRemove(ht, hep, he); } - return (int)n; + return n; } #ifdef HASHMETER diff --git a/lib/javascript/jshash.h b/lib/javascript/jshash.h index 2a125e19..85e3cf81 100644 --- a/lib/javascript/jshash.h +++ b/lib/javascript/jshash.h @@ -64,6 +64,7 @@ typedef intN (* JS_DLL_CALLBACK JSHashEnumerator)(JSHashEntry *he, intN i, void #define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ #define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ #define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ +#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */ typedef struct JSHashAllocOps { void * (*allocTable)(void *pool, size_t size); diff --git a/lib/javascript/jsinterp.c b/lib/javascript/jsinterp.c index c8c12048..096610cf 100644 --- a/lib/javascript/jsinterp.c +++ b/lib/javascript/jsinterp.c @@ -59,16 +59,18 @@ #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" -#include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" +#if JS_HAS_JIT +#include "jsjit.h" +#endif + #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif @@ -122,6 +124,35 @@ js_EnablePropertyCache(JSContext *cx) cx->runtime->propertyCache.disabled = JS_FALSE; } +/* + * Class for for/in loop property iterator objects. + */ +#define JSSLOT_ITER_STATE JSSLOT_PRIVATE + +static void +prop_iterator_finalize(JSContext *cx, JSObject *obj) +{ + jsval iter_state; + jsval iteratee; + + /* Protect against stillborn iterators. */ + iter_state = obj->slots[JSSLOT_ITER_STATE]; + iteratee = obj->slots[JSSLOT_PARENT]; + if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) { + OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY, + &iter_state, NULL); + } + js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]); +} + +static JSClass prop_iterator_class = { + "PropertyIterator", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iterator_finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + /* * Stack macros and functions. These all use a local variable, jsval *sp, to * point to the next free stack slot. SAVE_SP must be called before any call @@ -139,15 +170,6 @@ js_EnablePropertyCache(JSContext *cx) #endif #define RESTORE_SP(fp) (sp = (fp)->sp) -/* - * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their - * homes in fp, when calling out of the interpreter loop or threaded code. - * RESTORE_SP_AND_PC copies the other way, to update registers after a call - * to a subroutine that interprets a piece of the current script. - */ -#define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc) -#define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc) - /* * Push the generating bytecode's pc onto the parallel pc stack that runs * depth slots below the operands. @@ -180,34 +202,6 @@ js_EnablePropertyCache(JSContext *cx) STORE_OPND(n, v_); \ JS_END_MACRO -#define STORE_INT(cx, n, i) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if (INT_FITS_IN_JSVAL(i)) { \ - v_ = INT_TO_JSVAL(i); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define STORE_UINT(cx, n, u) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if ((u) <= JSVAL_INT_MAX) { \ - v_ = INT_TO_JSVAL(u); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - #define FETCH_NUMBER(cx, n, d) \ JS_BEGIN_MACRO \ jsval v_; \ @@ -222,7 +216,7 @@ js_EnablePropertyCache(JSContext *cx) if (JSVAL_IS_INT(v_)) { \ i = JSVAL_TO_INT(v_); \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = js_ValueToECMAInt32(cx, v_, &i); \ if (!ok) \ goto out; \ @@ -236,7 +230,7 @@ js_EnablePropertyCache(JSContext *cx) if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ ui = (uint32) i_; \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = js_ValueToECMAUint32(cx, v_, &ui); \ if (!ok) \ goto out; \ @@ -254,7 +248,7 @@ js_EnablePropertyCache(JSContext *cx) } else if (JSVAL_IS_DOUBLE(v)) { \ d = *JSVAL_TO_DOUBLE(v); \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = js_ValueToNumber(cx, v, &d); \ if (!ok) \ goto out; \ @@ -269,7 +263,7 @@ js_EnablePropertyCache(JSContext *cx) } else if (JSVAL_IS_BOOLEAN(v)) { \ b = JSVAL_TO_BOOLEAN(v); \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = js_ValueToBoolean(cx, v, &b); \ if (!ok) \ goto out; \ @@ -277,31 +271,12 @@ js_EnablePropertyCache(JSContext *cx) sp--; \ JS_END_MACRO -/* - * Convert a primitive string, number or boolean to a corresponding object. - * v must not be an object, null or undefined when using this macro. - */ -#define PRIMITIVE_TO_OBJECT(cx, v, obj) \ - JS_BEGIN_MACRO \ - SAVE_SP(fp); \ - if (JSVAL_IS_STRING(v)) { \ - obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \ - } else if (JSVAL_IS_INT(v)) { \ - obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \ - } else if (JSVAL_IS_DOUBLE(v)) { \ - obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \ - } else { \ - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \ - obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \ - } \ - JS_END_MACRO - #define VALUE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ if (!JSVAL_IS_PRIMITIVE(v)) { \ obj = JSVAL_TO_OBJECT(v); \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ obj = js_ValueToNonNullObject(cx, v); \ if (!obj) { \ ok = JS_FALSE; \ @@ -317,12 +292,31 @@ js_EnablePropertyCache(JSContext *cx) 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)) { \ + JSString *str_; \ + str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \ + v = STRING_TO_JSVAL(str_); \ + } +#else +#define CHECK_VOID_TOSTRING(cx, v) ((void)0) +#endif + +#if JS_BUG_EAGER_TOSTRING +#define CHECK_EAGER_TOSTRING(hint) (hint = JSTYPE_STRING) +#else +#define CHECK_EAGER_TOSTRING(hint) ((void)0) +#endif + #define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ JS_BEGIN_MACRO \ if (JSVAL_IS_PRIMITIVE(v)) { \ + CHECK_VOID_TOSTRING(cx, v); \ *vp = v; \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ + CHECK_EAGER_TOSTRING(hint); \ ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ if (!ok) \ goto out; \ @@ -463,120 +457,18 @@ js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj, *cursor, *clonedChild, *parent; - JSTempValueRooter tvr; - - obj = fp->blockChain; - if (!obj) { - /* - * Don't force a call object for a lightweight function call, but do - * insist that there is a call object for a heavyweight function call. - */ - JS_ASSERT(!fp->fun || - !(fp->fun->flags & JSFUN_HEAVYWEIGHT) || - fp->callobj); - JS_ASSERT(fp->scopeChain); - return fp->scopeChain; - } - - /* - * We have one or more lexical scopes to reflect into fp->scopeChain, so - * make sure there's a call object at the current head of the scope chain, - * if this frame is a call frame. - */ - if (fp->fun && !fp->callobj) { - JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || - JS_GetPrivate(cx, fp->scopeChain) != fp); - if (!js_GetCallObject(cx, fp, fp->scopeChain)) - return NULL; - } - - /* - * Clone the block chain. To avoid recursive cloning we set the parent of - * the cloned child after we clone the parent. In the following loop when - * clonedChild is null it indicates the first iteration when no special GC - * rooting is necessary. On the second and the following iterations we - * have to protect cloned so far chain against the GC during cloning of - * the cursor object. - */ - cursor = obj; - clonedChild = NULL; - for (;;) { - parent = OBJ_GET_PARENT(cx, cursor); - - /* - * We pass fp->scopeChain and not null even if we override the parent - * slot later as null triggers useless calculations of slot's value in - * js_NewObject that js_CloneBlockObject calls. - */ - cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp); - if (!cursor) { - if (clonedChild) - JS_POP_TEMP_ROOT(cx, &tvr); - return NULL; - } - if (!clonedChild) { - /* - * The first iteration. Check if other follow and root obj if so - * to protect the whole cloned chain against GC. - */ - obj = cursor; - if (!parent) - break; - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - } else { - /* - * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to - * other threads. - */ - clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor); - if (!parent) { - JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj)); - JS_POP_TEMP_ROOT(cx, &tvr); - break; - } - } - clonedChild = cursor; - cursor = parent; - } - fp->flags |= JSFRAME_POP_BLOCKS; - fp->scopeChain = obj; - fp->blockChain = NULL; - return obj; -} - -/* - * Walk the scope chain looking for block scopes whose locals need to be - * copied from stack slots into object slots before fp goes away. - */ -static JSBool -PutBlockObjects(JSContext *cx, JSStackFrame *fp) -{ - JSBool ok; - JSObject *obj; - - ok = JS_TRUE; - for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - if (JS_GetPrivate(cx, obj) != fp) - break; - ok &= js_PutBlockObject(cx, obj); - } - } - return ok; -} - -JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) +JSBool +js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp) { if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { /* Some objects (e.g., With) delegate 'this' to another object. */ thisp = OBJ_THIS_OBJECT(cx, thisp); if (!thisp) - return NULL; + return JS_FALSE; + + /* Default return value for a constructor is the new object. */ + if (fp->flags & JSFRAME_CONSTRUCTING) + fp->rval = OBJECT_TO_JSVAL(thisp); } else { /* * ECMA requires "the global object", but in the presence of multiple @@ -594,8 +486,9 @@ js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) * * The alert should display "true". */ - if (JSVAL_IS_PRIMITIVE(argv[-2]) || - !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) { + JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); + if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || + !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]))) { thisp = cx->globalObject; } else { jsid id; @@ -603,11 +496,11 @@ js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) uintN attrs; /* Walk up the parent chain. */ - thisp = JSVAL_TO_OBJECT(argv[-2]); + 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 NULL; + return JS_FALSE; if (JSVAL_IS_VOID(v)) v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); if (JSVAL_IS_NULL(v)) @@ -616,114 +509,11 @@ js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) } } } - argv[-1] = OBJECT_TO_JSVAL(thisp); - return thisp; + fp->thisp = thisp; + fp->argv[-1] = OBJECT_TO_JSVAL(thisp); + return JS_TRUE; } -#if JS_HAS_NO_SUCH_METHOD - -static JSBool -NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags, - uintN argc) -{ - JSObject *thisp, *argsobj; - jsval *sp, roots[3]; - JSTempValueRooter tvr; - jsid id; - JSBool ok; - jsbytecode *pc; - jsatomid atomIndex; - - /* - * We must call js_ComputeThis here to censor Call objects. A performance - * hit, since we'll call it again in the normal sequence of invoke events, - * but at least it's idempotent. - * - * Normally, we call ComputeThis after all frame members have been set, - * and in particular, after any revision of the callee value at *vp due - * to clasp->convert (see below). This matters because ComputeThis may - * access *vp via fp->argv[-2], to follow the parent chain to a global - * object to use as the 'this' parameter. - * - * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be any - * such defaulting of 'this' to callee (v, *vp) ancestor. - */ - JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0])); - RESTORE_SP(fp); - if (JSVAL_IS_OBJECT(vp[1])) { - thisp = JSVAL_TO_OBJECT(vp[1]); - } else { - PRIMITIVE_TO_OBJECT(cx, vp[1], thisp); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - } - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - - /* From here on, control must flow through label out: to return. */ - memset(roots, 0, sizeof roots); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); - - id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, thisp)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) thisp->map->ops; - thisp = ops->getMethod(cx, thisp, id, &roots[2]); - if (!thisp) { - ok = JS_FALSE; - goto out; - } - vp[1] = OBJECT_TO_JSVAL(thisp); - } else -#endif - { - ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]); - if (!ok) - goto out; - } - if (JSVAL_IS_PRIMITIVE(roots[2])) - goto not_function; - - pc = (jsbytecode *) vp[-(intN)fp->script->depth]; - switch ((JSOp) *pc) { - case JSOP_NAME: - case JSOP_GETPROP: -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: -#endif - atomIndex = GET_ATOM_INDEX(pc); - roots[0] = ATOM_KEY(js_GetAtom(cx, &fp->script->atomMap, atomIndex)); - argsobj = js_NewArrayObject(cx, argc, vp + 2); - if (!argsobj) { - ok = JS_FALSE; - goto out; - } - roots[1] = OBJECT_TO_JSVAL(argsobj); - ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL, - 2, roots, &vp[0]); - break; - - default: - goto not_function; - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - - not_function: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); - ok = JS_FALSE; - goto out; -} - -#endif /* JS_HAS_NO_SUCH_METHOD */ - #ifdef DUMP_CALL_TABLE #include "jsclist.h" @@ -942,13 +732,13 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) key.filename = NULL; key.lineno = 0; name = ""; - if (VALUE_IS_FUNCTION(cx, callee)) { + if (JSVAL_IS_FUNCTION(cx, callee)) { fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); if (fun->atom) name = js_AtomToPrintableString(cx, fun->atom); - if (FUN_INTERPRETED(fun)) { - key.filename = fun->u.i.script->filename; - key.lineno = fun->u.i.script->lineno; + if (fun->interpreted) { + key.filename = fun->u.script->filename; + key.lineno = fun->u.script->lineno; } } keyHash = js_hash_call_key(&key); @@ -1032,7 +822,7 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); break; case JSTYPE_FUNCTION: - if (VALUE_IS_FUNCTION(cx, argval)) { + if (JSVAL_IS_FUNCTION(cx, argval)) { fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); if (fun && fun->atom) { str = ATOM_TO_STRING(fun->atom); @@ -1055,20 +845,6 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) #endif /* DUMP_CALL_TABLE */ -/* - * Conditional assert to detect failure to clear a pending exception that is - * suppressed (or unintentional suppression of a wanted exception). - */ -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver -# define DEBUG_NOT_THROWING 1 -#endif - -#ifdef DEBUG_NOT_THROWING -# define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing) -#else -# define ASSERT_NOT_THROWING(cx) /* nothing */ -#endif - /* * Find a function reference and its 'this' object implicit first parameter * under argc arguments on cx's stack, and call the function. Push missing @@ -1081,7 +857,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) void *mark; JSStackFrame *fp, frame; jsval *sp, *newsp, *limit; - jsval *vp, v, thisv; + jsval *vp, v; JSObject *funobj, *parent, *thisp; JSBool ok; JSClass *clasp; @@ -1102,14 +878,17 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) * Set vp to the callee value's stack slot (it's where rval goes). * Once vp is set, control should flow through label out2: to return. * Set frame.rval early so native class and object ops can throw and - * return false, causing a goto out2 with ok set to false. + * return false, causing a goto out2 with ok set to false. Also set + * frame.flags to flags so that ComputeThis can test bits in it. */ vp = sp - (2 + argc); v = *vp; frame.rval = JSVAL_VOID; + frame.flags = flags; + thisp = JSVAL_TO_OBJECT(vp[1]); /* - * A callee must be an object reference, unless its 'this' parameter + * A callee must be an object reference, unless its |this| parameter * implements the __noSuchMethod__ method, in which case that method will * be called like so: * @@ -1121,18 +900,113 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) */ if (JSVAL_IS_PRIMITIVE(v)) { #if JS_HAS_NO_SUCH_METHOD - if (fp->script && !(flags & JSINVOKE_INTERNAL)) { - ok = NoSuchMethod(cx, fp, vp, flags, argc); - if (ok) - frame.rval = *vp; - goto out2; - } -#endif - goto bad; - } + jsid id; + jsbytecode *pc; + jsatomid atomIndex; + JSAtom *atom; + JSObject *argsobj; + JSArena *a; - /* Load thisv after potentially calling NoSuchMethod, which may set it. */ - thisv = vp[1]; + if (!fp->script || (flags & JSINVOKE_INTERNAL)) + goto bad; + + /* + * We must ComputeThis here to censor Call objects; performance hit, + * but at least it's idempotent. + * + * Normally, we call ComputeThis after all frame members have been + * set, and in particular, after any revision of the callee value at + * *vp due to clasp->convert (see below). This matters because + * ComputeThis may access *vp via fp->argv[-2], to follow the parent + * chain to a global object to use as the |this| parameter. + * + * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be + * any such defaulting of |this| to callee (v, *vp) ancestor. + */ + frame.argv = vp + 2; + ok = js_ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; + thisp = frame.thisp; + + 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)) + goto bad; + + pc = (jsbytecode *) vp[-(intN)fp->script->depth]; + 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); + if (!argsobj) { + ok = JS_FALSE; + goto out2; + } + + sp = vp + 4; + if (argc < 2) { + a = cx->stackPool.current; + if ((jsuword)sp > a->limit) { + /* + * Arguments must be contiguous, and must include argv[-1] + * and argv[-2], so allocate more stack, advance sp, and + * set newsp[1] to thisp (vp[1]). The other argv elements + * will be set below, using negative indexing from sp. + */ + newsp = js_AllocRawStack(cx, 4, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out2; + } + newsp[1] = OBJECT_TO_JSVAL(thisp); + sp = newsp + 4; + } else if ((jsuword)sp > a->avail) { + /* + * Inline, optimized version of JS_ARENA_ALLOCATE to claim + * the small number of words not already allocated as part + * of the caller's operand stack. + */ + JS_ArenaCountAllocation(&cx->stackPool, + (jsuword)sp - a->avail); + a->avail = (jsuword)sp; + } + } + + sp[-4] = v; + JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp)); + sp[-2] = ATOM_KEY(atom); + sp[-1] = OBJECT_TO_JSVAL(argsobj); + fp->sp = sp; + argc = 2; + break; + + default: + goto bad; + } +#else + goto bad; +#endif + } funobj = JSVAL_TO_OBJECT(v); parent = OBJ_GET_PARENT(cx, funobj); @@ -1150,12 +1024,13 @@ 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 ((ops == &js_ObjectOps) ? clasp->call : ops->call) { + if (JS_VERSION_IS_1_2(cx) || + ((ops == &js_ObjectOps) ? clasp->call : ops->call)) { ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); if (!ok) goto out2; - if (VALUE_IS_FUNCTION(cx, v)) { + if (JSVAL_IS_FUNCTION(cx, v)) { /* Make vp refer to funobj to keep it available as argv[-2]. */ *vp = v; funobj = JSVAL_TO_OBJECT(v); @@ -1171,88 +1046,27 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; if (!native) goto bad; - - if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - PRIMITIVE_TO_OBJECT(cx, thisv, thisp); - if (!thisp) - goto out2; - vp[1] = thisv = OBJECT_TO_JSVAL(thisp); - } } else { have_fun: /* Get private data and set derived locals from it. */ fun = (JSFunction *) JS_GetPrivate(cx, funobj); - nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; - if (FUN_INTERPRETED(fun)) { + if (fun->interpreted) { native = NULL; - script = fun->u.i.script; - nvars = fun->u.i.nvars; + script = fun->u.script; } else { - native = fun->u.n.native; + native = fun->u.native; script = NULL; - nvars = 0; - nslots += fun->u.n.extra; } + nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; + nslots += fun->extra; + nvars = fun->nvars; - if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { - /* Handle bound method special case. */ + /* Handle bound method special case. */ + if (fun->flags & JSFUN_BOUND_METHOD) thisp = parent; - } else if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - uintN thispflags = JSFUN_THISP_FLAGS(fun->flags); - - JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT)); - if (JSVAL_IS_STRING(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv)); - } else if (JSVAL_IS_INT(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv)); - } else if (JSVAL_IS_DOUBLE(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(thisv)); - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv)); - } - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - goto init_frame; - } } - if (flags & JSINVOKE_CONSTRUCT) { - /* Default return value for a constructor is the new object. */ - frame.rval = OBJECT_TO_JSVAL(thisp); - } else { - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - } - - init_frame: /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ - frame.thisp = thisp; frame.varobj = NULL; frame.callobj = frame.argsobj = NULL; frame.script = script; @@ -1268,10 +1082,13 @@ have_fun: frame.spbase = NULL; frame.sharpDepth = 0; frame.sharpArray = NULL; - frame.flags = flags; frame.dormantNext = NULL; frame.xmlNamespace = NULL; - frame.blockChain = NULL; + + /* Compute the 'this' parameter and store it in frame as frame.thisp. */ + ok = js_ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; /* From here on, control must flow through label out: to return. */ cx->fp = &frame; @@ -1285,7 +1102,6 @@ have_fun: /* All arguments must be contiguous, so we may have to copy actuals. */ nalloc = nslots; limit = (jsval *) cx->stackPool.current->limit; - JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit); if (sp + nslots > limit) { /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ nalloc += 2 + argc; @@ -1360,10 +1176,6 @@ have_fun: /* Call the function, either a native method or an interpreted script. */ if (native) { -#ifdef DEBUG_NOT_THROWING - JSBool alreadyThrowing = cx->throwing; -#endif - #if JS_HAS_LVALUE_RETURN /* Set by JS_SetCallReturnValue2, used to return reference types. */ cx->rval2set = JS_FALSE; @@ -1374,22 +1186,23 @@ have_fun: frame.scopeChain = fp->scopeChain; ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); JS_RUNTIME_METER(cx->runtime, nativeCalls); -#ifdef DEBUG_NOT_THROWING - if (ok && !alreadyThrowing) - ASSERT_NOT_THROWING(cx); -#endif } else if (script) { #ifdef DUMP_CALL_TABLE LogCall(cx, *vp, argc, frame.argv); #endif /* Use parent scope so js_GetCallObject can find the right "Call". */ frame.scopeChain = parent; - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { + if (fun->flags & JSFUN_HEAVYWEIGHT) { +#if JS_HAS_CALL_OBJECT /* Scope with a call object parented by the callee's parent. */ if (!js_GetCallObject(cx, &frame, parent)) { ok = JS_FALSE; goto out; } +#else + /* Bad old code used the function as a proxy for all calls to it. */ + frame.scopeChain = funobj; +#endif } ok = js_Interpret(cx, script->code, &v); } else { @@ -1404,14 +1217,16 @@ out: if (hook) hook(cx, &frame, JS_FALSE, &ok, hookData); } - +#if JS_HAS_CALL_OBJECT /* If frame has a call object, sync values and clear back-pointer. */ if (frame.callobj) ok &= js_PutCallObject(cx, &frame); - +#endif +#if JS_HAS_ARGS_OBJECT /* If frame has an arguments object, sync values and clear back-pointer. */ if (frame.argsobj) ok &= js_PutArgsObject(cx, &frame); +#endif /* Restore cx->fp now that we're done releasing frame objects. */ cx->fp = fp; @@ -1434,7 +1249,7 @@ out2: return ok; bad: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); + js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT); ok = JS_FALSE; goto out2; } @@ -1472,7 +1287,7 @@ js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, /* * Store *rval in the a scoped local root if a scope is open, else in - * the lastInternalResult pigeon-hole GC root, solely so users of + * 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. @@ -1483,7 +1298,7 @@ js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) ok = JS_FALSE; } else { - cx->weakRoots.lastInternalResult = *rval; + cx->lastInternalResult = *rval; } } } @@ -1501,17 +1316,6 @@ JSBool js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) { - int stackDummy; - - /* - * js_InternalInvoke could result in another try to get or set the same id - * again, see bug 355497. - */ - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OVER_RECURSED); - return JS_FALSE; - } /* * Check general (not object-ops/class-specific) access from the running * script to obj.id only if id has a scripted getter or setter that we're @@ -1529,9 +1333,8 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, */ JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); if (cx->runtime->checkObjectAccess && - VALUE_IS_FUNCTION(cx, fval) && - FUN_INTERPRETED((JSFunction *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) && + JSVAL_IS_FUNCTION(cx, fval) && + ((JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->interpreted && !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) { return JS_FALSE; @@ -1553,11 +1356,10 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, hook = cx->runtime->executeHook; hookData = mark = NULL; oldfp = cx->fp; + frame.callobj = frame.argsobj = NULL; frame.script = script; if (down) { /* Propagate arg/var state for eval and the debugger API. */ - frame.callobj = down->callobj; - frame.argsobj = down->argsobj; frame.varobj = down->varobj; frame.fun = down->fun; frame.thisp = down->thisp; @@ -1568,7 +1370,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, frame.annotation = down->annotation; frame.sharpArray = down->sharpArray; } else { - frame.callobj = frame.argsobj = NULL; obj = chain; if (cx->options & JSOPTION_VAROBJFIX) { while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) @@ -1601,7 +1402,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, frame.flags = flags; frame.dormantNext = NULL; frame.xmlNamespace = NULL; - frame.blockChain = NULL; /* * Here we wrap the call to js_Interpret with code to (conditionally) @@ -1715,7 +1515,7 @@ ImportProperty(JSContext *cx, JSObject *obj, jsid id) ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); if (!ok) goto out; - if (VALUE_IS_FUNCTION(cx, value)) { + if (JSVAL_IS_FUNCTION(cx, value)) { funobj = JSVAL_TO_OBJECT(value); closure = js_CloneFunctionObject(cx, funobj, obj); if (!closure) { @@ -1783,26 +1583,9 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, if (!prop) return JS_TRUE; - /* - * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error. - * An assertion at label bad: will insist that it is null. - */ - if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); -#ifdef DEBUG - prop = NULL; -#endif + /* From here, return true, or goto bad on failure to drop prop. */ + if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) goto bad; - } - - /* - * From here, return true, or else goto bad on failure to null out params. - * If our caller doesn't want prop, drop it (we don't need it any longer). - */ - if (!propp) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - prop = NULL; - } /* If either property is readonly, we have an error. */ report = ((oldAttrs | attrs) & JSPROP_READONLY) @@ -1829,7 +1612,7 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, if (!isFunction) { if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) goto bad; - isFunction = VALUE_IS_FUNCTION(cx, value); + isFunction = JSVAL_IS_FUNCTION(cx, value); } type = (oldAttrs & attrs & JSPROP_GETTER) ? js_getter_str @@ -1853,7 +1636,7 @@ bad: *objp = NULL; *propp = NULL; } - JS_ASSERT(!prop); + OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_FALSE; } @@ -1867,7 +1650,7 @@ js_StrictlyEqual(jsval lval, jsval rval) if (ltag == JSVAL_STRING) { JSString *lstr = JSVAL_TO_STRING(lval), *rstr = JSVAL_TO_STRING(rval); - return js_EqualStrings(lstr, rstr); + return js_CompareStrings(lstr, rstr) == 0; } if (ltag == JSVAL_DOUBLE) { ld = *JSVAL_TO_DOUBLE(lval); @@ -1889,83 +1672,6 @@ js_StrictlyEqual(jsval lval, jsval rval) return lval == rval; } -JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) -{ - JSFunction *fun; - JSObject *obj, *obj2, *proto, *parent; - jsval lval, rval; - JSClass *clasp, *funclasp; - - fun = NULL; - obj2 = NULL; - lval = *vp; - if (!JSVAL_IS_OBJECT(lval) || - (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || - /* XXX clean up to avoid special cases above ObjectOps layer */ - OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || - !obj2->map->ops->construct) - { - fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); - if (!fun) - return JS_FALSE; - } - - clasp = &js_ObjectClass; - if (!obj2) { - proto = parent = NULL; - fun = NULL; - } else { - /* - * Get the constructor prototype object for this function. - * Use the nominal 'this' parameter slot, vp[1], as a local - * root to protect this prototype, in case it has no other - * strong refs. - */ - if (!OBJ_GET_PROPERTY(cx, obj2, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &vp[1])) { - return JS_FALSE; - } - rval = vp[1]; - proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; - parent = OBJ_GET_PARENT(cx, obj2); - - if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { - funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; - if (funclasp) - clasp = funclasp; - } - } - obj = js_NewObject(cx, clasp, proto, parent); - if (!obj) - return JS_FALSE; - - /* Now we have an object with a constructor method; call it. */ - vp[1] = OBJECT_TO_JSVAL(obj); - if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - - /* Check the return value and if it's primitive, force it to be obj. */ - rval = *vp; - if (JSVAL_IS_PRIMITIVE(rval)) { - if (!fun) { - /* native [[Construct]] returning primitive is error */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_NEW_RESULT, - js_ValueToPrintableString(cx, rval)); - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - } - - JS_RUNTIME_METER(cx->runtime, constructs); - return JS_TRUE; -} - static JSBool InternStringElementId(JSContext *cx, jsval idval, jsid *idp) { @@ -1997,7 +1703,7 @@ InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) #define CHECK_ELEMENT_ID(obj, id) \ JS_BEGIN_MACRO \ if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ if (!ok) \ goto out; \ @@ -2018,22 +1724,6 @@ InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) #define MAX_INLINE_CALL_COUNT 1000 -/* - * Threaded interpretation via computed goto appears to be well-supported by - * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., - * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. - * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing. - * Add your compiler support macros here. - */ -#if JS_VERSION >= 160 && ( \ - __GNUC__ >= 3 || \ - (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ - __SUNPRO_C >= 0x570) -# define JS_THREADED_INTERP 1 -#else -# undef JS_THREADED_INTERP -#endif - JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) { @@ -2041,7 +1731,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) JSStackFrame *fp; JSScript *script; uintN inlineCallCount; - JSObject *obj, *obj2, *parent; + JSObject *obj, *obj2, *proto, *parent; JSVersion currentVersion, originalVersion; JSBranchCallback onbranch; JSBool ok, cond; @@ -2051,82 +1741,46 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) void *mark; jsbytecode *endpc, *pc2; JSOp op, op2; + const JSCodeSpec *cs; jsatomid atomIndex; JSAtom *atom; - uintN argc, attrs, flags, slot; + uintN argc, slot, attrs; jsval *vp, lval, rval, ltmp, rtmp; jsid id; - JSObject *withobj, *iterobj; + JSObject *withobj, *origobj, *propobj; + jsval iter_state; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *str2; jsint i, j; jsdouble d, d2; - JSClass *clasp; + JSClass *clasp, *funclasp; JSFunction *fun; JSType type; -#if !defined JS_THREADED_INTERP && defined DEBUG - FILE *tracefp = NULL; +#ifdef DEBUG + FILE *tracefp; #endif #if JS_HAS_EXPORT_IMPORT JSIdArray *ida; #endif +#if JS_HAS_SWITCH_STATEMENT jsint low, high, off, npairs; JSBool match; +#endif #if JS_HAS_GETTER_SETTER JSPropertyOp getter, setter; +#endif +#if JS_HAS_XML_SUPPORT + JSBool foreach = JS_FALSE; #endif int stackDummy; -#ifdef __GNUC__ -# define JS_EXTENSION __extension__ -# define JS_EXTENSION_(s) __extension__ ({ s; }) -#else -# define JS_EXTENSION -# define JS_EXTENSION_(s) s -#endif - -#ifdef JS_THREADED_INTERP - static void *normalJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - JS_EXTENSION &&L_##op, -# include "jsopcode.tbl" -# undef OPDEF - }; - - static void *interruptJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - ((op != JSOP_PUSHOBJ) \ - ? JS_EXTENSION &&interrupt \ - : JS_EXTENSION &&L_JSOP_PUSHOBJ), -# include "jsopcode.tbl" -# undef OPDEF - }; - - register void **jumpTable = normalJumpTable; - -# define DO_OP() JS_EXTENSION_(goto *jumpTable[op]) -# define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) -# define BEGIN_CASE(OP) L_##OP: -# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); -# define END_VARLEN_CASE DO_NEXT_OP(len); -# define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP(); -#else -# define DO_OP() goto do_op -# define DO_NEXT_OP(n) goto advance_pc -# define BEGIN_CASE(OP) case OP: -# define END_CASE(OP) break; -# define END_VARLEN_CASE break; -# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP) -#endif - *result = JSVAL_VOID; rt = cx->runtime; /* Set registerized frame pointer and derived script pointer. */ fp = cx->fp; script = fp->script; - JS_ASSERT(script->length != 0); /* Count of JS function calls that nest in this C js_Interpret frame. */ inlineCallCount = 0; @@ -2145,11 +1799,6 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) if (currentVersion != originalVersion) js_SetVersion(cx, currentVersion); -#ifdef __GNUC__ - flags = 0; /* suppress gcc warnings */ - id = 0; -#endif - /* * Prepare to call a user-supplied branch handler, and abort the script * if it returns false. We reload onbranch after calling out to native @@ -2158,10 +1807,11 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) #define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) LOAD_BRANCH_CALLBACK(cx); + ok = JS_TRUE; #define CHECK_BRANCH(len) \ JS_BEGIN_MACRO \ if (len <= 0 && onbranch) { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ if (!(ok = (*onbranch)(cx, script))) \ goto out; \ } \ @@ -2171,20 +1821,10 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) * Load the debugger's interrupt hook here and after calling out to native * functions (but not to getters, setters, or other native hooks), so we do * not have to reload it each time through the interpreter loop -- we hope - * the compiler can keep it in a register when it is non-null. + * the compiler can keep it in a register. + * XXX if it spills, we still lose */ -#ifdef JS_THREADED_INTERP -# define LOAD_JUMP_TABLE() \ - (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable) -#else -# define LOAD_JUMP_TABLE() /* nothing */ -#endif - -#define LOAD_INTERRUPT_HANDLER(rt) \ - JS_BEGIN_MACRO \ - interruptHandler = (rt)->interruptHandler; \ - LOAD_JUMP_TABLE(); \ - JS_END_MACRO +#define LOAD_INTERRUPT_HANDLER(rt) (interruptHandler = (rt)->interruptHandler) LOAD_INTERRUPT_HANDLER(rt); @@ -2218,69 +1858,13 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) mark = NULL; } - /* - * To support generator_throw and to catch ignored exceptions, fail right - * away if cx->throwing is set. If no exception is pending, null obj in - * case a callable object is being sent into a yield expression, and the - * yield's result is invoked. - */ - ok = !cx->throwing; - if (!ok) { -#ifdef DEBUG_NOT_THROWING - printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n", - (unsigned long) cx->exception); -#endif - goto out; - } - obj = NULL; - -#ifdef JS_THREADED_INTERP - - /* - * This is a loop, but it does not look like a loop. The loop-closing - * jump is distributed throughout interruptJumpTable, and comes back to - * the interrupt label. The dispatch on op is through normalJumpTable. - * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately. - * - * It is important that "op" be initialized before the interrupt label - * because it is possible for "op" to be specially assigned during the - * normally processing of an opcode while looping (in particular, this - * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to - * correctly manage "op" in all other cases. - */ - op = (JSOp) *pc; - if (interruptHandler) { -interrupt: - SAVE_SP_AND_PC(fp); - switch (interruptHandler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - JS_EXTENSION_(goto *normalJumpTable[op]); - -#else /* !JS_THREADED_INTERP */ - - for (;;) { + endpc = script->code + script->length; + while (pc < endpc) { + fp->pc = pc; op = (JSOp) *pc; do_op: - len = js_CodeSpec[op].length; + cs = &js_CodeSpec[op]; + len = cs->length; #ifdef DEBUG tracefp = (FILE *) cx->tracefp; @@ -2291,9 +1875,9 @@ interrupt: js_Disassemble1(cx, script, pc, PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, tracefp); - nuses = js_CodeSpec[op].nuses; + nuses = cs->nuses; if (nuses) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); for (n = -nuses; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { @@ -2305,10 +1889,10 @@ interrupt: fprintf(tracefp, " @ %d\n", sp - fp->spbase); } } -#endif /* DEBUG */ +#endif - if (interruptHandler && op != JSOP_PUSHOBJ) { - SAVE_SP_AND_PC(fp); + if (interruptHandler) { + SAVE_SP(fp); switch (interruptHandler(cx, script, pc, &rval, rt->interruptHandlerData)) { case JSTRAP_ERROR: @@ -2319,145 +1903,123 @@ interrupt: case JSTRAP_RETURN: fp->rval = rval; goto out; +#if JS_HAS_EXCEPTIONS case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; +#endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); } switch (op) { + case JSOP_NOP: + break; -#endif /* !JS_THREADED_INTERP */ + case JSOP_GROUP: + break; - BEGIN_CASE(JSOP_STOP) - goto out; - - EMPTY_CASE(JSOP_NOP) - - BEGIN_CASE(JSOP_GROUP) - obj = NULL; - END_CASE(JSOP_GROUP) - - BEGIN_CASE(JSOP_PUSH) + case JSOP_PUSH: PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_PUSH) + break; - BEGIN_CASE(JSOP_POP) + case JSOP_POP: sp--; - END_CASE(JSOP_POP) + break; - BEGIN_CASE(JSOP_POP2) + case JSOP_POP2: sp -= 2; - END_CASE(JSOP_POP2) + break; - BEGIN_CASE(JSOP_SWAP) - vp = sp - depth; /* swap generating pc's for the decompiler */ - ltmp = vp[-1]; - vp[-1] = vp[-2]; - sp[-2] = ltmp; - rtmp = sp[-1]; + case JSOP_SWAP: + /* + * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs + * for the operands it swaps. + */ + ltmp = sp[-1]; sp[-1] = sp[-2]; - sp[-2] = rtmp; - END_CASE(JSOP_SWAP) + sp[-2] = ltmp; + break; - BEGIN_CASE(JSOP_POPV) + case JSOP_POPV: *result = POP_OPND(); - END_CASE(JSOP_POPV) + break; - BEGIN_CASE(JSOP_ENTERWITH) + case JSOP_ENTERWITH: FETCH_OBJECT(cx, -1, rval, obj); - SAVE_SP_AND_PC(fp); - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) { - ok = JS_FALSE; + SAVE_SP(fp); + withobj = js_NewWithObject(cx, obj, fp->scopeChain, + sp - fp->spbase - 1); + if (!withobj) goto out; - } - withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1); - if (!withobj) { - ok = JS_FALSE; - goto out; - } + rval = INT_TO_JSVAL(sp - fp->spbase); fp->scopeChain = withobj; STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); - END_CASE(JSOP_ENTERWITH) + break; - BEGIN_CASE(JSOP_LEAVEWITH) + case JSOP_LEAVEWITH: rval = POP_OPND(); JS_ASSERT(JSVAL_IS_OBJECT(rval)); withobj = JSVAL_TO_OBJECT(rval); JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); - fp->scopeChain = OBJ_GET_PARENT(cx, withobj); + + 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); - END_CASE(JSOP_LEAVEWITH) + break; - BEGIN_CASE(JSOP_SETRVAL) - ASSERT_NOT_THROWING(cx); + case JSOP_SETRVAL: fp->rval = POP_OPND(); - END_CASE(JSOP_SETRVAL) + break; - BEGIN_CASE(JSOP_RETURN) + case JSOP_RETURN: CHECK_BRANCH(-1); fp->rval = POP_OPND(); /* FALL THROUGH */ - BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ - ASSERT_NOT_THROWING(cx); + case JSOP_RETRVAL: /* fp->rval already set */ if (inlineCallCount) inline_return: { JSInlineFrame *ifp = (JSInlineFrame *) fp; void *hookData = ifp->hookData; - /* - * If fp has blocks on its scope chain, home their locals now, - * before calling any debugger hook, and before freeing stack. - * This matches the order of block putting and hook calling in - * the "out-of-line" return code at the bottom of js_Interpret - * and in js_Invoke. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - if (hookData) { - JSInterpreterHook hook = rt->callHook; + JSInterpreterHook hook = cx->runtime->callHook; if (hook) { - SAVE_SP_AND_PC(fp); hook(cx, fp, JS_FALSE, &ok, hookData); LOAD_INTERRUPT_HANDLER(rt); } } +#if JS_HAS_CALL_OBJECT /* - * If fp has a call object, sync values and clear the back- + * 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) { - SAVE_SP_AND_PC(fp); + if (fp->callobj) ok &= js_PutCallObject(cx, fp); - } - - if (fp->argsobj) { - SAVE_SP_AND_PC(fp); +#endif +#if JS_HAS_ARGS_OBJECT + if (fp->argsobj) ok &= js_PutArgsObject(cx, fp); - } +#endif /* Restore context version only if callee hasn't set version. */ - if (JS_LIKELY(cx->version == currentVersion)) { + if (cx->version == currentVersion) { currentVersion = ifp->callerVersion; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); } /* Store the return value in the caller's operand frame. */ - vp = ifp->rvp; + vp = fp->argv - 2; *vp = fp->rval; /* Restore cx->fp and release the inline frame's space. */ @@ -2469,114 +2031,123 @@ interrupt: RESTORE_SP(fp); /* Restore the calling script's interpreter registers. */ - obj = NULL; script = fp->script; depth = (jsint) script->depth; pc = fp->pc; -#ifndef JS_THREADED_INTERP endpc = script->code + script->length; -#endif /* Store the generating pc for the return value. */ vp[-depth] = (jsval)pc; + /* Set remaining variables for 'goto advance_pc'. */ + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; + len = cs->length; + /* Resume execution in the calling frame. */ inlineCallCount--; - if (JS_LIKELY(ok)) { - JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH); - len = JSOP_CALL_LENGTH; - DO_NEXT_OP(len); - } + if (ok) + goto advance_pc; } goto out; - BEGIN_CASE(JSOP_DEFAULT) +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULT: (void) POP(); /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTO) +#endif + case JSOP_GOTO: len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_IFEQ) + case JSOP_IFEQ: POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); } - END_CASE(JSOP_IFEQ) + break; - BEGIN_CASE(JSOP_IFNE) + case JSOP_IFNE: POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); } - END_CASE(JSOP_IFNE) + break; - BEGIN_CASE(JSOP_OR) + case JSOP_OR: POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); - DO_NEXT_OP(len); } - END_CASE(JSOP_OR) + break; - BEGIN_CASE(JSOP_AND) + case JSOP_AND: POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); - DO_NEXT_OP(len); } - END_CASE(JSOP_AND) + break; - BEGIN_CASE(JSOP_DEFAULTX) + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULTX: (void) POP(); /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTOX) +#endif + case JSOP_GOTOX: len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_IFEQX) + case JSOP_IFEQX: POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); } - END_CASE(JSOP_IFEQX) + break; - BEGIN_CASE(JSOP_IFNEX) + case JSOP_IFNEX: POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); } - END_CASE(JSOP_IFNEX) + break; - BEGIN_CASE(JSOP_ORX) + case JSOP_ORX: POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); - DO_NEXT_OP(len); } - END_CASE(JSOP_ORX) + break; - BEGIN_CASE(JSOP_ANDX) + case JSOP_ANDX: POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); - DO_NEXT_OP(len); } - END_CASE(JSOP_ANDX) + break; + + case JSOP_TOOBJECT: + 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 @@ -2590,15 +2161,16 @@ interrupt: if (JSVAL_IS_INT(idval_)) { \ id = INT_JSVAL_TO_JSID(idval_); \ } else { \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = InternNonIntElementId(cx, idval_, &id); \ if (!ok) \ goto out; \ } \ JS_END_MACRO - BEGIN_CASE(JSOP_IN) - SAVE_SP_AND_PC(fp); +#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); @@ -2620,38 +2192,10 @@ interrupt: STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_IN) + break; +#endif /* JS_HAS_IN_OPERATOR */ - BEGIN_CASE(JSOP_FOREACH) - flags = JSITER_ENUMERATE | JSITER_FOREACH; - goto value_to_iter; - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_FOREACHKEYVAL) - flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE; - goto value_to_iter; -#endif - - BEGIN_CASE(JSOP_FORIN) - /* - * Set JSITER_ENUMERATE to indicate that for-in loop should use - * the enumeration protocol's iterator for compatibility if an - * explicit iterator is not given via the optional __iterator__ - * method. - */ - flags = JSITER_ENUMERATE; - - value_to_iter: - JS_ASSERT(sp > fp->spbase); - SAVE_SP_AND_PC(fp); - ok = js_ValueToIterator(cx, flags, &sp[-1]); - if (!ok) - goto out; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length); - END_CASE(JSOP_FORIN) - - BEGIN_CASE(JSOP_FORPROP) + case JSOP_FORPROP: /* * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop * is not paid for the more common cases. @@ -2662,7 +2206,7 @@ interrupt: i = -2; goto do_forinloop; - BEGIN_CASE(JSOP_FORNAME) + case JSOP_FORNAME: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); @@ -2674,7 +2218,7 @@ interrupt: * then we'd have to move this into the common code and condition * it on op == JSOP_FORNAME. */ - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; @@ -2683,18 +2227,16 @@ interrupt: lval = OBJECT_TO_JSVAL(obj); /* FALL THROUGH */ - BEGIN_CASE(JSOP_FORARG) - BEGIN_CASE(JSOP_FORVAR) - BEGIN_CASE(JSOP_FORLOCAL) + case JSOP_FORARG: + case JSOP_FORVAR: /* * JSOP_FORARG and JSOP_FORVAR don't require any lval computation * here, because they address slots on the stack (in fp->args and - * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which - * addresses fp->spbase. + * fp->vars, respectively). */ /* FALL THROUGH */ - BEGIN_CASE(JSOP_FORELEM) + case JSOP_FORELEM: /* * JSOP_FORELEM simply initializes or updates the iteration state * and leaves the index expression evaluation and assignment to the @@ -2705,21 +2247,237 @@ interrupt: do_forinloop: /* - * Reach under the top of stack to find our property iterator, a - * JSObject that contains the iteration state. + * ECMA-compatible for/in evals the object just once, before loop. + * Bad old bytecodes (since removed) did it on every iteration. */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i])); - iterobj = JSVAL_TO_OBJECT(sp[i]); - - SAVE_SP_AND_PC(fp); - ok = js_CallIteratorNext(cx, iterobj, &rval); - if (!ok) - goto out; - if (rval == JSVAL_HOLE) { + obj = JSVAL_TO_OBJECT(sp[i]); + + /* If the thing to the right of 'in' has no properties, break. */ + if (!obj) { rval = JSVAL_FALSE; goto end_forinloop; } + /* + * Save the thing to the right of 'in' as origobj. Later on, we + * use this variable to suppress enumeration of shadowed prototype + * properties. + */ + origobj = obj; + + /* + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. (An object is used + * rather than a native struct so that the iteration state is + * cleaned up via GC if the for-in loop terminates abruptly.) + */ + vp = &sp[i - 1]; + rval = *vp; + + /* + * 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. + * 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; + + /* + * Root the parent slot so we can get it even in our finalizer + * (otherwise, it would live as long as we do, but it might be + * finalized first). + */ + ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT], + "propobj->parent"); + if (!ok) + goto out; + + /* + * Rewrite the iterator so we know to do the next case. + * Do this before calling the enumerator, which could + * displace cx->newborn and cause GC. + */ + *vp = OBJECT_TO_JSVAL(propobj); + + ok = +#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. + * 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; + } else { + /* This is not the first iteration. Recover iterator state. */ + propobj = JSVAL_TO_OBJECT(rval); + JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class); + obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]); + iter_state = propobj->slots[JSSLOT_ITER_STATE]; + } + + enum_next_property: + { + 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? */ + if (iter_state == JSVAL_NULL) { + /* Enumerate the properties on obj's prototype chain. */ + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) { + /* End of property list -- terminate loop. */ + rval = JSVAL_FALSE; +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif + goto end_forinloop; + } + + 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. + * We do this before checking 'ok' to ensure that propobj is + * in a valid state even if OBJ_ENUMERATE returned JS_FALSE. + * NB: This code knows that the first slots are pre-allocated. + */ + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + if (!ok) + goto out; + + /* + * Update the iterator JSObject's parent link to refer to the + * current object. This is used in the iterator JSObject's + * finalizer. + */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + goto enum_next_property; + } + + /* Skip properties not owned by obj when looking from origobj. */ + ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop); + if (!ok) + goto out; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* + * 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; + } + +#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); + } else { + rval = INT_JSID_TO_JSVAL(fid); + } + } + switch (op) { case JSOP_FORARG: slot = GET_ARGNO(pc); @@ -2729,30 +2487,20 @@ interrupt: case JSOP_FORVAR: slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); + JS_ASSERT(slot < fp->fun->nvars); fp->vars[slot] = rval; break; - case JSOP_FORLOCAL: - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = rval; - break; - case JSOP_FORELEM: /* FORELEM is not a SET operation, it's more like BINDNAME. */ PUSH_OPND(rval); break; default: - JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME); - /* Convert lval to a non-null object containing id. */ VALUE_TO_OBJECT(cx, lval, obj); - if (op == JSOP_FORPROP) - STORE_OPND(-1, OBJECT_TO_JSVAL(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; @@ -2769,28 +2517,22 @@ interrupt: end_forinloop: sp += i + 1; PUSH_OPND(rval); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); + break; + } - BEGIN_CASE(JSOP_DUP) + case JSOP_DUP: JS_ASSERT(sp > fp->spbase); - vp = sp - 1; /* address top of stack */ - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = *vp; - PUSH(rval); - END_CASE(JSOP_DUP) + rval = sp[-1]; + PUSH_OPND(rval); + break; - BEGIN_CASE(JSOP_DUP2) - JS_ASSERT(sp - 2 >= fp->spbase); - vp = sp - 1; /* address top of stack */ - lval = vp[-1]; - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = vp[2] = *vp; - PUSH(lval); - PUSH(rval); - END_CASE(JSOP_DUP2) + case JSOP_DUP2: + JS_ASSERT(sp - 1 > fp->spbase); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + PUSH_OPND(lval); + PUSH_OPND(rval); + break; #define PROPERTY_OP(n, call) \ JS_BEGIN_MACRO \ @@ -2798,7 +2540,7 @@ interrupt: FETCH_OBJECT(cx, n, lval, obj); \ \ /* Get or set the property, set ok false if error, true if success. */\ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ call; \ if (!ok) \ goto out; \ @@ -2816,48 +2558,21 @@ interrupt: CHECK_ELEMENT_ID(obj, id); \ \ /* Get or set the element, set ok false if error, true if success. */ \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ call; \ if (!ok) \ goto out; \ JS_END_MACRO -#define NATIVE_GET(cx,obj,pobj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_GETTER(sprop)) { \ - /* Fast path for Object instance properties. */ \ - JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ - !SPROP_HAS_STUB_SETTER(sprop)); \ - *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ - ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ - : JSVAL_VOID; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeGet(cx, obj, pobj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define NATIVE_SET(cx,obj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_SETTER(sprop) && \ - (sprop)->slot != SPROP_INVALID_SLOT) { \ - /* Fast path for Object instance properties. */ \ - LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeSet(cx, obj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - /* - * CACHED_GET and CACHED_SET use cx, obj, id, and rval from their callers' - * environments. + * 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. 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; \ @@ -2865,8 +2580,17 @@ interrupt: JS_LOCK_OBJ(cx, obj); \ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ if (sprop) { \ - NATIVE_GET(cx, obj, obj, sprop, &rval); \ - JS_UNLOCK_OBJ(cx, obj); \ + JSScope *scope_ = OBJ_SCOPE(obj); \ + slot = (uintN)sprop->slot; \ + *(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, vp); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ + LOCKED_OBJ_SET_SLOT(obj, slot, *(vp)); \ + JS_UNLOCK_SCOPE(cx, scope_); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ ok = call; \ @@ -2886,7 +2610,13 @@ interrupt: if (sprop && \ !(sprop->attrs & JSPROP_READONLY) && \ (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ - NATIVE_SET(cx, obj, sprop, &rval); \ + JS_UNLOCK_SCOPE(cx, scope_); \ + ok = SPROP_SET(cx, sprop, obj, obj, &rval); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) { \ + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval); \ + GC_POKE(cx, JSVAL_NULL); /* XXX second arg ignored */ \ + } \ JS_UNLOCK_SCOPE(cx, scope_); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ @@ -2897,19 +2627,18 @@ interrupt: JS_END_MACRO #define BEGIN_LITOPX_CASE(OP,PCOFF) \ - BEGIN_CASE(OP) \ - pc2 = pc; \ + case OP: \ atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ do_##OP: \ atom = js_GetAtom(cx, &script->atomMap, atomIndex); -#define END_LITOPX_CASE(OP) \ - END_CASE(OP) +#define END_LITOPX_CASE \ + break; \ BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) obj = fp->varobj; rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | @@ -2918,75 +2647,60 @@ interrupt: if (!ok) goto out; STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_SETCONST) - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_ENUMCONSTELEM) - FETCH_ELEMENT_ID(-1, id); - FETCH_OBJECT(cx, -2, lval, obj); - CHECK_ELEMENT_ID(obj, id); - rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - if (!ok) - goto out; - sp -= 3; - END_CASE(JSOP_ENUMCONSTELEM) -#endif + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_BINDNAME) + END_LITOPX_CASE - BEGIN_CASE(JSOP_SETNAME) + case JSOP_SETNAME: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); obj = JSVAL_TO_OBJECT(lval); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; sp--; STORE_OPND(-1, rval); obj = NULL; - END_CASE(JSOP_SETNAME) + break; #define INTEGER_OP(OP, EXTRA_CODE) \ JS_BEGIN_MACRO \ FETCH_INT(cx, -1, j); \ FETCH_INT(cx, -2, i); \ + if (!ok) \ + goto out; \ EXTRA_CODE \ - i = i OP j; \ + d = i OP j; \ sp--; \ - STORE_INT(cx, -1, i); \ + STORE_NUMBER(cx, -1, d); \ JS_END_MACRO #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) - BEGIN_CASE(JSOP_BITOR) + case JSOP_BITOR: BITWISE_OP(|); - END_CASE(JSOP_BITOR) + break; - BEGIN_CASE(JSOP_BITXOR) + case JSOP_BITXOR: BITWISE_OP(^); - END_CASE(JSOP_BITXOR) + break; - BEGIN_CASE(JSOP_BITAND) + case JSOP_BITAND: BITWISE_OP(&); - END_CASE(JSOP_BITAND) + break; #define RELATIONAL_OP(OP) \ JS_BEGIN_MACRO \ @@ -3039,21 +2753,21 @@ interrupt: ops = (JSXMLObjectOps *) obj2->map->ops; \ if (obj2 == JSVAL_TO_OBJECT(rval)) \ rval = lval; \ - SAVE_SP_AND_PC(fp); \ + SAVE_SP(fp); \ ok = ops->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ cond = cond OP JS_TRUE; \ } else -#define EXTENDED_EQUALITY_OP(OP) \ +#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_AND_PC(fp); \ + SAVE_SP(fp); \ ok = xclasp->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ @@ -3061,7 +2775,7 @@ interrupt: } else #else #define XML_EQUALITY_OP(OP) /* nothing */ -#define EXTENDED_EQUALITY_OP(OP) /* nothing */ +#define XML_NAME_EQUALITY_OP(OP) /* nothing */ #endif #define EQUALITY_OP(OP, IFNAN) \ @@ -3075,13 +2789,13 @@ interrupt: if (ltmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ + cond = js_CompareStrings(str, str2) OP 0; \ } else if (ltmp == JSVAL_DOUBLE) { \ d = *JSVAL_TO_DOUBLE(lval); \ d2 = *JSVAL_TO_DOUBLE(rval); \ cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } else { \ - EXTENDED_EQUALITY_OP(OP) \ + XML_NAME_EQUALITY_OP(OP) \ /* Handle all undefined (=>NaN) and int combinations. */ \ cond = lval OP rval; \ } \ @@ -3103,7 +2817,7 @@ interrupt: if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ + cond = js_CompareStrings(str, str2) OP 0; \ } else { \ VALUE_TO_NUMBER(cx, lval, d); \ VALUE_TO_NUMBER(cx, rval, d2); \ @@ -3115,14 +2829,15 @@ interrupt: STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO - BEGIN_CASE(JSOP_EQ) + case JSOP_EQ: EQUALITY_OP(==, JS_FALSE); - END_CASE(JSOP_EQ) + break; - BEGIN_CASE(JSOP_NE) + case JSOP_NE: EQUALITY_OP(!=, JS_TRUE); - END_CASE(JSOP_NE) + break; +#if !JS_BUG_FALLIBLE_EQOPS #define NEW_EQUALITY_OP(OP) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ @@ -3132,84 +2847,85 @@ interrupt: STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO - BEGIN_CASE(JSOP_NEW_EQ) + case JSOP_NEW_EQ: NEW_EQUALITY_OP(==); - END_CASE(JSOP_NEW_EQ) + break; - BEGIN_CASE(JSOP_NEW_NE) + case JSOP_NEW_NE: NEW_EQUALITY_OP(!=); - END_CASE(JSOP_NEW_NE) + break; - BEGIN_CASE(JSOP_CASE) - pc2 = (jsbytecode *) sp[-2-depth]; +#if JS_HAS_SWITCH_STATEMENT + case JSOP_CASE: NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); + } else { + PUSH(lval); } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASE) + break; - BEGIN_CASE(JSOP_CASEX) - pc2 = (jsbytecode *) sp[-2-depth]; + case JSOP_CASEX: NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - DO_NEXT_OP(len); + } else { + PUSH(lval); } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASEX) + break; +#endif - BEGIN_CASE(JSOP_LT) +#endif /* !JS_BUG_FALLIBLE_EQOPS */ + + case JSOP_LT: RELATIONAL_OP(<); - END_CASE(JSOP_LT) + break; - BEGIN_CASE(JSOP_LE) + case JSOP_LE: RELATIONAL_OP(<=); - END_CASE(JSOP_LE) + break; - BEGIN_CASE(JSOP_GT) + case JSOP_GT: RELATIONAL_OP(>); - END_CASE(JSOP_GT) + break; - BEGIN_CASE(JSOP_GE) + case JSOP_GE: RELATIONAL_OP(>=); - END_CASE(JSOP_GE) + break; #undef EQUALITY_OP #undef RELATIONAL_OP - BEGIN_CASE(JSOP_LSH) + case JSOP_LSH: SIGNED_SHIFT_OP(<<); - END_CASE(JSOP_LSH) + break; - BEGIN_CASE(JSOP_RSH) + case JSOP_RSH: SIGNED_SHIFT_OP(>>); - END_CASE(JSOP_RSH) + break; - BEGIN_CASE(JSOP_URSH) + case JSOP_URSH: { uint32 u; FETCH_INT(cx, -1, j); FETCH_UINT(cx, -2, u); - u >>= j & 31; + j &= 31; + d = u >> j; sp--; - STORE_UINT(cx, -1, u); + STORE_NUMBER(cx, -1, d); + break; } - END_CASE(JSOP_URSH) #undef INTEGER_OP #undef BITWISE_OP #undef SIGNED_SHIFT_OP - BEGIN_CASE(JSOP_ADD) + case JSOP_ADD: rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); #if JS_HAS_XML_SUPPORT @@ -3219,13 +2935,14 @@ interrupt: JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj2->map->ops; - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = ops->concatenate(cx, obj2, rval, &rval); if (!ok) goto out; sp--; STORE_OPND(-1, rval); - } else + break; + } #endif { VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); @@ -3233,20 +2950,16 @@ interrupt: VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); rval = sp[-1]; if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); if (cond) { str = JSVAL_TO_STRING(lval); ok = (str2 = js_ValueToString(cx, rval)) != NULL; - if (!ok) - goto out; - sp[-1] = STRING_TO_JSVAL(str2); } else { str2 = JSVAL_TO_STRING(rval); ok = (str = js_ValueToString(cx, lval)) != NULL; - if (!ok) - goto out; - sp[-2] = STRING_TO_JSVAL(str); } + if (!ok) + goto out; str = js_ConcatStrings(cx, str, str2); if (!str) { ok = JS_FALSE; @@ -3262,7 +2975,7 @@ interrupt: STORE_NUMBER(cx, -1, d); } } - END_CASE(JSOP_ADD) + break; #define BINARY_OP(OP) \ JS_BEGIN_MACRO \ @@ -3273,20 +2986,20 @@ interrupt: STORE_NUMBER(cx, -1, d); \ JS_END_MACRO - BEGIN_CASE(JSOP_SUB) + case JSOP_SUB: BINARY_OP(-); - END_CASE(JSOP_SUB) + break; - BEGIN_CASE(JSOP_MUL) + case JSOP_MUL: BINARY_OP(*); - END_CASE(JSOP_MUL) + break; - BEGIN_CASE(JSOP_DIV) + case JSOP_DIV: FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { -#ifdef XP_WIN +#if defined(XP_WIN) /* XXX MSVC miscompiles such that (NaN == 0) */ if (JSDOUBLE_IS_NaN(d2)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); @@ -3303,111 +3016,149 @@ interrupt: d /= d2; STORE_NUMBER(cx, -1, d); } - END_CASE(JSOP_DIV) + break; - BEGIN_CASE(JSOP_MOD) + case JSOP_MOD: FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); } else { -#ifdef XP_WIN +#if defined(XP_WIN) /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) #endif d = fmod(d, d2); STORE_NUMBER(cx, -1, d); } - END_CASE(JSOP_MOD) + break; - BEGIN_CASE(JSOP_NOT) + case JSOP_NOT: POP_BOOLEAN(cx, rval, cond); PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); - END_CASE(JSOP_NOT) + break; - BEGIN_CASE(JSOP_BITNOT) + case JSOP_BITNOT: FETCH_INT(cx, -1, i); - i = ~i; - STORE_INT(cx, -1, i); - END_CASE(JSOP_BITNOT) + d = (jsdouble) ~i; + STORE_NUMBER(cx, -1, d); + break; - BEGIN_CASE(JSOP_NEG) - /* - * Optimize the case of an int-tagged operand by noting that - * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0 - * when -i is the negative zero which is jsdouble. - */ - rval = FETCH_OPND(-1); - if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) { - i = -i; - JS_ASSERT(INT_FITS_IN_JSVAL(i)); - rval = INT_TO_JSVAL(i); - } else { - if (JSVAL_IS_DOUBLE(rval)) { - d = *JSVAL_TO_DOUBLE(rval); - } else { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - } + case JSOP_NEG: + FETCH_NUMBER(cx, -1, d); #ifdef HPUX - /* - * Negation of a zero doesn't produce a negative - * zero on HPUX. Perform the operation by bit - * twiddling. - */ - JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; + /* + * Negation of a zero doesn't produce a negative + * zero on HPUX. Perform the operation by bit + * twiddling. + */ + JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; #else - d = -d; + d = -d; #endif - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - } - STORE_OPND(-1, rval); - END_CASE(JSOP_NEG) + STORE_NUMBER(cx, -1, d); + break; - BEGIN_CASE(JSOP_POS) - rval = FETCH_OPND(-1); - if (!JSVAL_IS_NUMBER(rval)) { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - sp[-1] = rval; - } - sp[-1-depth] = (jsval)pc; - END_CASE(JSOP_POS) + case JSOP_POS: + FETCH_NUMBER(cx, -1, d); + STORE_NUMBER(cx, -1, d); + break; - BEGIN_CASE(JSOP_NEW) + case JSOP_NEW: /* Get immediate argc and find the constructor function. */ argc = GET_ARGC(pc); +#if JS_HAS_INITIALIZERS do_new: - SAVE_SP_AND_PC(fp); +#endif + SAVE_SP(fp); vp = sp - (2 + argc); JS_ASSERT(vp >= fp->spbase); - ok = js_InvokeConstructor(cx, vp, argc); - if (!ok) + fun = NULL; + obj2 = NULL; + lval = *vp; + if (!JSVAL_IS_OBJECT(lval) || + (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || + /* XXX clean up to avoid special cases above ObjectOps layer */ + OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || + !obj2->map->ops->construct) + { + fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); + if (!fun) { + ok = JS_FALSE; + goto out; + } + } + + clasp = &js_ObjectClass; + if (!obj2) { + proto = parent = NULL; + fun = NULL; + } else { + /* + * Get the constructor prototype object for this function. + * Use the nominal |this| parameter slot, vp[1], as a local + * root to protect this prototype, in case it has no other + * strong refs. + */ + ok = OBJ_GET_PROPERTY(cx, obj2, + 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); + + if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { + funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; + if (funclasp) + clasp = funclasp; + } + } + obj = js_NewObject(cx, clasp, proto, parent); + if (!obj) { + ok = JS_FALSE; goto out; + } + + /* Now we have an object with a constructor method; call it. */ + vp[1] = OBJECT_TO_JSVAL(obj); + ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT); RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); LOAD_INTERRUPT_HANDLER(rt); - obj = JSVAL_TO_OBJECT(*vp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } - BEGIN_CASE(JSOP_DELNAME) + /* Check the return value and update obj from it. */ + rval = *vp; + if (JSVAL_IS_PRIMITIVE(rval)) { + if (fun || !JS_VERSION_IS_ECMA(cx)) { + *vp = OBJECT_TO_JSVAL(obj); + break; + } + /* native [[Construct]] returning primitive is error */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + JS_RUNTIME_METER(rt, constructs); + break; + + case JSOP_DELNAME: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; @@ -3421,43 +3172,42 @@ interrupt: goto out; } PUSH_OPND(rval); - END_CASE(JSOP_DELNAME) + break; - BEGIN_CASE(JSOP_DELPROP) + case JSOP_DELPROP: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); STORE_OPND(-1, rval); - END_CASE(JSOP_DELPROP) + break; - BEGIN_CASE(JSOP_DELELEM) + case JSOP_DELELEM: ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); - END_CASE(JSOP_DELELEM) + break; - BEGIN_CASE(JSOP_TYPEOFEXPR) - BEGIN_CASE(JSOP_TYPEOF) + case JSOP_TYPEOF: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); type = JS_TypeOfValue(cx, rval); atom = rt->atomState.typeAtoms[type]; STORE_OPND(-1, ATOM_KEY(atom)); - END_CASE(JSOP_TYPEOF) + break; - BEGIN_CASE(JSOP_VOID) + case JSOP_VOID: (void) POP_OPND(); PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_VOID) + break; - BEGIN_CASE(JSOP_INCNAME) - BEGIN_CASE(JSOP_DECNAME) - BEGIN_CASE(JSOP_NAMEINC) - BEGIN_CASE(JSOP_NAMEDEC) + case JSOP_INCNAME: + case JSOP_DECNAME: + case JSOP_NAMEINC: + case JSOP_NAMEDEC: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; @@ -3469,42 +3219,36 @@ interrupt: i = 0; goto do_incop; - BEGIN_CASE(JSOP_INCPROP) - BEGIN_CASE(JSOP_DECPROP) - BEGIN_CASE(JSOP_PROPINC) - BEGIN_CASE(JSOP_PROPDEC) + case JSOP_INCPROP: + case JSOP_DECPROP: + case JSOP_PROPINC: + case JSOP_PROPDEC: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); lval = FETCH_OPND(-1); i = -1; goto do_incop; - BEGIN_CASE(JSOP_INCELEM) - BEGIN_CASE(JSOP_DECELEM) - BEGIN_CASE(JSOP_ELEMINC) - BEGIN_CASE(JSOP_ELEMDEC) + case JSOP_INCELEM: + case JSOP_DECELEM: + case JSOP_ELEMINC: + case JSOP_ELEMDEC: FETCH_ELEMENT_ID(-1, id); lval = FETCH_OPND(-2); i = -2; do_incop: - { - const JSCodeSpec *cs; - 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_AND_PC(fp); + SAVE_SP(fp); CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; - /* Preload for use in the if/else immediately below. */ - cs = &js_CodeSpec[op]; - /* The expression result goes in rtmp, the updated value in rval. */ if (JSVAL_IS_INT(rval) && rval != INT_TO_JSVAL(JSVAL_INT_MIN) && @@ -3534,8 +3278,8 @@ interrupt: ok = js_NewNumberValue(cx, d, &rtmp); \ if (!ok) \ goto out; \ + *vp = rtmp; \ } \ - *vp = rtmp; \ (cs->format & JOF_INC) ? d++ : d--; \ ok = js_NewNumberValue(cx, d, &rval); \ } else { \ @@ -3572,130 +3316,103 @@ interrupt: goto out; sp += i; PUSH_OPND(rtmp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - } + break; -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \ +/* + * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because + * it must break from the switch case that calls it, not from the do...while(0) + * loop created by the JS_BEGIN/END_MACRO brackets. + */ +#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \ slot = SLOT; \ JS_ASSERT(slot < fp->fun->COUNT); \ vp = fp->BASE + slot; \ rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_incop + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + *vp = rval; \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_incop; - BEGIN_CASE(JSOP_INCARG) + case JSOP_INCARG: FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); - BEGIN_CASE(JSOP_DECARG) + case JSOP_DECARG: FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); - BEGIN_CASE(JSOP_ARGINC) + case JSOP_ARGINC: FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); - BEGIN_CASE(JSOP_ARGDEC) + case JSOP_ARGDEC: FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); - BEGIN_CASE(JSOP_INCVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX); - BEGIN_CASE(JSOP_DECVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN); - BEGIN_CASE(JSOP_VARINC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX); - BEGIN_CASE(JSOP_VARDEC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN); - - end_nonint_fast_incop: - len = JSOP_INCARG_LENGTH; /* all fast incops are same length */ - DO_NEXT_OP(len); + case JSOP_INCVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX); + case JSOP_DECVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN); + case JSOP_VARINC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX); + case JSOP_VARDEC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN); #undef FAST_INCREMENT_OP do_nonint_fast_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - NONINT_INCREMENT_OP_MIDDLE(); *vp = rval; PUSH_OPND(rtmp); - len = cs->length; - DO_NEXT_OP(len); - } + break; -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \ +#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \ slot = GET_VARNO(pc); \ JS_ASSERT(slot < fp->nvars); \ lval = fp->vars[slot]; \ if (JSVAL_IS_NULL(lval)) { \ op = SLOWOP; \ - DO_OP(); \ + goto do_op; \ } \ slot = JSVAL_TO_INT(lval); \ obj = fp->varobj; \ rval = OBJ_GET_SLOT(cx, obj, slot); \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_global_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - OBJ_SET_SLOT(cx, obj, slot, rval); \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_global_incop + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + OBJ_SET_SLOT(cx, obj, slot, rval); \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_global_incop; - BEGIN_CASE(JSOP_INCGVAR) + case JSOP_INCGVAR: FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); - BEGIN_CASE(JSOP_DECGVAR) + case JSOP_DECGVAR: FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); - BEGIN_CASE(JSOP_GVARINC) + case JSOP_GVARINC: FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); - BEGIN_CASE(JSOP_GVARDEC) + case JSOP_GVARDEC: FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); - end_nonint_fast_global_incop: - len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ - JS_ASSERT(len == js_CodeSpec[op].length); - DO_NEXT_OP(len); - #undef FAST_GLOBAL_INCREMENT_OP do_nonint_fast_global_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - vp = sp++; SAVE_SP(fp); NONINT_INCREMENT_OP_MIDDLE(); OBJ_SET_SLOT(cx, obj, slot, rval); STORE_OPND(-1, rtmp); - len = cs->length; - DO_NEXT_OP(len); - } + break; - BEGIN_CASE(JSOP_GETPROP) - BEGIN_CASE(JSOP_GETXPROP) + case JSOP_GETPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - lval = FETCH_OPND(-1); - if (JSVAL_IS_STRING(lval) && - atom == cx->runtime->atomState.lengthAtom) { - rval = INT_TO_JSVAL(JSSTRING_LENGTH(JSVAL_TO_STRING(lval))); - obj = NULL; - } else { - id = ATOM_TO_JSID(atom); - VALUE_TO_OBJECT(cx, lval, obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - SAVE_SP_AND_PC(fp); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - } + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); STORE_OPND(-1, rval); - END_CASE(JSOP_GETPROP) + break; - BEGIN_CASE(JSOP_SETPROP) + case JSOP_SETPROP: /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ rval = FETCH_OPND(-1); @@ -3706,35 +3423,34 @@ interrupt: sp--; STORE_OPND(-1, rval); obj = NULL; - END_CASE(JSOP_SETPROP) + break; - BEGIN_CASE(JSOP_GETELEM) - BEGIN_CASE(JSOP_GETXELEM) + case JSOP_GETELEM: ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); - END_CASE(JSOP_GETELEM) + break; - BEGIN_CASE(JSOP_SETELEM) + case JSOP_SETELEM: rval = FETCH_OPND(-1); ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp -= 2; STORE_OPND(-1, rval); obj = NULL; - END_CASE(JSOP_SETELEM) + break; - BEGIN_CASE(JSOP_ENUMELEM) + case JSOP_ENUMELEM: /* Funky: the value to set is under the [obj, id] pair. */ FETCH_ELEMENT_ID(-1, id); FETCH_OBJECT(cx, -2, lval, obj); CHECK_ELEMENT_ID(obj, id); rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp -= 3; - END_CASE(JSOP_ENUMELEM) + break; /* * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the @@ -3744,34 +3460,33 @@ interrupt: * e.g., arguments[i](), the 'this' parameter would and must bind to the * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. */ -#define LAZY_ARGS_THISP ((JSObject *) JSVAL_VOID) +#define LAZY_ARGS_THISP ((JSObject *) 1) - BEGIN_CASE(JSOP_PUSHOBJ) + case JSOP_PUSHOBJ: if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_PUSHOBJ) + break; - BEGIN_CASE(JSOP_CALL) - BEGIN_CASE(JSOP_EVAL) + case JSOP_CALL: + case JSOP_EVAL: argc = GET_ARGC(pc); vp = sp - (argc + 2); lval = *vp; - SAVE_SP_AND_PC(fp); - if (VALUE_IS_FUNCTION(cx, lval) && + SAVE_SP(fp); + + if (JSVAL_IS_FUNCTION(cx, lval) && (obj = JSVAL_TO_OBJECT(lval), fun = (JSFunction *) JS_GetPrivate(cx, obj), - FUN_INTERPRETED(fun))) + fun->interpreted && + !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) && + argc >= (uintN)(fun->nargs + fun->extra))) /* inline_call: */ { - uintN nframeslots, nvars, nslots, missing; - JSArena *a; - jsuword avail, nbytes; - JSBool overflow; + uintN nframeslots, nvars; void *newmark; - jsval *rvp; JSInlineFrame *newifp; JSInterpreterHook hook; @@ -3783,69 +3498,32 @@ interrupt: goto out; } - /* Compute the total number of stack slots needed for fun. */ - nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); - nvars = fun->u.i.nvars; - script = fun->u.i.script; - depth = (jsint) script->depth; - nslots = nframeslots + nvars + 2 * depth; - - /* Allocate missing expected args adjacent to actual args. */ - missing = (fun->nargs > argc) ? fun->nargs - argc : 0; - a = cx->stackPool.current; - avail = a->avail; - newmark = (void *) avail; - if (missing) { - newsp = sp + missing; - overflow = (jsuword) newsp > a->limit; - if (overflow) - nslots += 2 + argc + missing; - else if ((jsuword) newsp > avail) - avail = a->avail = (jsuword) newsp; - } -#ifdef __GNUC__ - else overflow = JS_FALSE; /* suppress bogus gcc warnings */ +#if JS_HAS_JIT + /* ZZZbe should do this only if interpreted often enough. */ + ok = jsjit_Compile(cx, fun); + if (!ok) + goto out; #endif - /* Allocate the inline frame with its vars and operand slots. */ - newsp = (jsval *) avail; - nbytes = nslots * sizeof(jsval); - avail += nbytes; - if (avail <= a->limit) { - a->avail = avail; - } else { - JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, - nbytes); - if (!newsp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_STACK_OVERFLOW, - (fp && fp->fun) - ? JS_GetFunctionName(fp->fun) - : "script"); - goto bad_inline_call; - } - } + /* Compute the number of stack slots needed for fun. */ + nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1) + / sizeof(jsval); + nvars = fun->nvars; + script = fun->u.script; + depth = (jsint) script->depth; - /* Move args if missing overflow arena a, push missing args. */ - rvp = vp; - if (missing) { - if (overflow) { - memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); - vp = newsp; - sp = vp + 2 + argc; - newsp = sp + missing; - } - do { - PUSH(JSVAL_VOID); - } while (--missing != 0); + /* Allocate the frame and space for vars and operands. */ + newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth, + &newmark); + if (!newsp) { + ok = JS_FALSE; + goto bad_inline_call; } - - /* Claim space for the stack frame and initialize it. */ newifp = (JSInlineFrame *) newsp; newsp += nframeslots; - newifp->frame.callobj = NULL; - newifp->frame.argsobj = NULL; - newifp->frame.varobj = NULL; + + /* Initialize the stack frame. */ + memset(newifp, 0, sizeof(JSInlineFrame)); newifp->frame.script = script; newifp->frame.fun = fun; newifp->frame.argc = argc; @@ -3854,32 +3532,15 @@ interrupt: newifp->frame.nvars = nvars; newifp->frame.vars = newsp; newifp->frame.down = fp; - newifp->frame.annotation = NULL; - newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); - newifp->frame.sharpDepth = 0; - newifp->frame.sharpArray = NULL; - newifp->frame.flags = 0; - newifp->frame.dormantNext = NULL; - newifp->frame.xmlNamespace = NULL; - newifp->frame.blockChain = NULL; - newifp->rvp = rvp; + newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj); newifp->mark = newmark; /* Compute the 'this' parameter now that argv is set. */ - if (!JSVAL_IS_OBJECT(vp[1])) { - PRIMITIVE_TO_OBJECT(cx, vp[1], obj2); - if (!obj2) - goto bad_inline_call; - vp[1] = OBJECT_TO_JSVAL(obj2); - } - newifp->frame.thisp = - js_ComputeThis(cx, - JSFUN_BOUND_METHOD_TEST(fun->flags) - ? parent - : JSVAL_TO_OBJECT(vp[1]), - newifp->frame.argv); - if (!newifp->frame.thisp) + ok = js_ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); + if (!ok) { + js_FreeRawStack(cx, newmark); goto bad_inline_call; + } #ifdef DUMP_CALL_TABLE LogCall(cx, *vp, argc, vp + 2); #endif @@ -3893,25 +3554,16 @@ interrupt: SAVE_SP(&newifp->frame); /* Call the debugger hook if present. */ - hook = rt->callHook; + hook = cx->runtime->callHook; if (hook) { - newifp->frame.pc = NULL; newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, - rt->callHookData); + cx->runtime->callHookData); LOAD_INTERRUPT_HANDLER(rt); - } else { - newifp->hookData = NULL; - } - - /* Scope with a call object parented by the callee's parent. */ - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && - !js_GetCallObject(cx, &newifp->frame, parent)) { - goto bad_inline_call; } /* Switch to new version if currentVersion wasn't overridden. */ newifp->callerVersion = cx->version; - if (JS_LIKELY(cx->version == currentVersion)) { + if (cx->version == currentVersion) { currentVersion = script->version; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); @@ -3920,24 +3572,14 @@ interrupt: /* Push the frame and set interpreter registers. */ cx->fp = fp = &newifp->frame; pc = script->code; -#ifndef JS_THREADED_INTERP endpc = pc + script->length; -#endif - obj = NULL; inlineCallCount++; JS_RUNTIME_METER(rt, inlineCalls); - - /* Load first opcode and dispatch it (safe since JSOP_STOP). */ - op = *pc; - DO_OP(); + continue; bad_inline_call: - RESTORE_SP(fp); - JS_ASSERT(fp->pc == pc); script = fp->script; depth = (jsint) script->depth; - js_FreeRawStack(cx, newmark); - ok = JS_FALSE; goto out; } @@ -3951,34 +3593,33 @@ interrupt: #if JS_HAS_LVALUE_RETURN if (cx->rval2set) { /* - * Use the stack depth we didn't claim in our budget, but that - * we know is there on account of [fun, this] already having - * been pushed, at a minimum (if no args). Those two slots - * have been popped and [rval] has been pushed, which leaves - * one more slot for rval2 before we might overflow. + * Sneaky: use the stack depth we didn't claim in our budget, + * but that we know is there on account of [fun, this] already + * having been pushed, at a minimum (if no args). Those two + * slots have been popped and [rval] has been pushed, which + * leaves one more slot for rval2 before we might overflow. * * NB: rval2 must be the property identifier, and rval the * object from which to get the property. The pair form an * ECMA "reference type", which can be used on the right- or - * left-hand side of assignment ops. Note well: only native - * methods can return reference types. See JSOP_SETCALL just - * below for the left-hand-side case. + * left-hand side of assignment ops. Only native methods can + * return reference types. See JSOP_SETCALL just below for + * the left-hand-side case. */ PUSH_OPND(cx->rval2); + cx->rval2set = JS_FALSE; ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); - sp--; STORE_OPND(-1, rval); - cx->rval2set = JS_FALSE; } -#endif /* JS_HAS_LVALUE_RETURN */ +#endif obj = NULL; - END_CASE(JSOP_CALL) + break; #if JS_HAS_LVALUE_RETURN - BEGIN_CASE(JSOP_SETCALL) + case JSOP_SETCALL: argc = GET_ARGC(pc); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_Invoke(cx, argc, 0); RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); @@ -3994,26 +3635,24 @@ interrupt: PUSH_OPND(cx->rval2); cx->rval2set = JS_FALSE; obj = NULL; - END_CASE(JSOP_SETCALL) + break; #endif - BEGIN_CASE(JSOP_NAME) + case JSOP_NAME: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ - len = JSOP_NAME_LENGTH; - endpc = script->code + script->length; for (pc2 = pc + len; pc2 < endpc; pc2++) { op2 = (JSOp)*pc2; if (op2 == JSOP_TYPEOF) { PUSH_OPND(JSVAL_VOID); - DO_NEXT_OP(len); + goto advance_pc; } if (op2 != JSOP_GROUP) break; @@ -4027,38 +3666,53 @@ interrupt: ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; - } else { - sprop = (JSScopeProperty *)prop; - NATIVE_GET(cx, obj, obj2, sprop, &rval); - OBJ_DROP_PROPERTY(cx, obj2, prop); + PUSH_OPND(rval); + break; } - PUSH_OPND(rval); - END_CASE(JSOP_NAME) - BEGIN_CASE(JSOP_UINT16) + /* Get and push the obj[id] property's value. */ + sprop = (JSScopeProperty *)prop; + slot = (uintN)sprop->slot; + rval = (slot != SPROP_INVALID_SLOT) + ? LOCKED_OBJ_GET_SLOT(obj2, slot) + : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj2); + ok = SPROP_GET(cx, sprop, obj, obj2, &rval); + JS_LOCK_OBJ(cx, obj2); + if (!ok) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + goto out; + } + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) + LOCKED_OBJ_SET_SLOT(obj2, slot, rval); + OBJ_DROP_PROPERTY(cx, obj2, prop); + PUSH_OPND(rval); + break; + + case JSOP_UINT16: i = (jsint) GET_ATOM_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); obj = NULL; - END_CASE(JSOP_UINT16) + break; - BEGIN_CASE(JSOP_UINT24) + case JSOP_UINT24: i = (jsint) GET_LITERAL_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); - END_CASE(JSOP_UINT24) + break; - BEGIN_CASE(JSOP_LITERAL) + case JSOP_LITERAL: atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; - END_CASE(JSOP_LITERAL) + break; - BEGIN_CASE(JSOP_FINDNAME) + case JSOP_FINDNAME: atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; @@ -4066,28 +3720,11 @@ interrupt: } PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(ATOM_KEY(atom)); - END_CASE(JSOP_FINDNAME) + break; - BEGIN_CASE(JSOP_LITOPX) - /* - * Load atomIndex, which is used by code at each do_JSOP_* label. - * - * Also set pc2 to point at the bytecode extended by this prefix - * to have a leading 24 bit atomIndex, instead of the unextended - * 16-bit atomIndex that normally comes after op. This enables - * JOF_INDEXCONST format ops (which have multiple immediates) to - * collect their other immediate via GET_VARNO(pc2) or similar. - * - * Finally, load op and, if threading, adjust pc so that it will - * be advanced properly at the end of op's case by DO_NEXT_OP. - */ + case JSOP_LITOPX: atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - pc += JSOP_LITOPX_LENGTH - (1 + ATOM_INDEX_LEN); -#ifndef JS_THREADED_INTERP - len = js_CodeSpec[op].length; -#endif + op = pc[1 + LITERAL_INDEX_LEN]; switch (op) { case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; case JSOP_BINDNAME: goto do_JSOP_BINDNAME; @@ -4103,6 +3740,7 @@ interrupt: 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; @@ -4119,14 +3757,14 @@ interrupt: case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; case JSOP_XMLPI: goto do_JSOP_XMLPI; #endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; default: JS_ASSERT(0); } /* NOTREACHED */ + break; - BEGIN_CASE(JSOP_NUMBER) - BEGIN_CASE(JSOP_STRING) - BEGIN_CASE(JSOP_OBJECT) + case JSOP_NUMBER: + case JSOP_STRING: + case JSOP_OBJECT: atomIndex = GET_ATOM_INDEX(pc); do_JSOP_NUMBER: @@ -4135,7 +3773,7 @@ interrupt: atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; - END_CASE(JSOP_NUMBER) + break; BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) { @@ -4211,7 +3849,7 @@ interrupt: * "RegExp" class in the global object, which could entail a * JSNewResolveOp call. */ - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); /* * If obj's parent is not obj2, we must clone obj so that it @@ -4231,7 +3869,7 @@ interrupt: * and you get what you deserve). * * This same coupling between instance parent and constructor - * parent turns up everywhere (see jsobj.c's FindClassObject, + * parent turns up everywhere (see jsobj.c's FindConstructor, * js_ConstructObject, and js_NewObject). It's fundamental to * the design of the language when you consider multiple global * objects and separate compilation and execution, even though @@ -4258,24 +3896,24 @@ interrupt: PUSH_OPND(rval); obj = NULL; } - END_LITOPX_CASE(JSOP_REGEXP) + END_LITOPX_CASE - BEGIN_CASE(JSOP_ZERO) + case JSOP_ZERO: PUSH_OPND(JSVAL_ZERO); obj = NULL; - END_CASE(JSOP_ZERO) + break; - BEGIN_CASE(JSOP_ONE) + case JSOP_ONE: PUSH_OPND(JSVAL_ONE); obj = NULL; - END_CASE(JSOP_ONE) + break; - BEGIN_CASE(JSOP_NULL) + case JSOP_NULL: PUSH_OPND(JSVAL_NULL); obj = NULL; - END_CASE(JSOP_NULL) + break; - BEGIN_CASE(JSOP_THIS) + case JSOP_THIS: obj = fp->thisp; clasp = OBJ_GET_CLASS(cx, obj); if (clasp->flags & JSCLASS_IS_EXTENDED) { @@ -4293,31 +3931,38 @@ interrupt: PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_CASE(JSOP_THIS) + break; - BEGIN_CASE(JSOP_FALSE) + case JSOP_FALSE: PUSH_OPND(JSVAL_FALSE); obj = NULL; - END_CASE(JSOP_FALSE) + break; - BEGIN_CASE(JSOP_TRUE) + case JSOP_TRUE: PUSH_OPND(JSVAL_TRUE); obj = NULL; - END_CASE(JSOP_TRUE) + break; - BEGIN_CASE(JSOP_TABLESWITCH) +#if JS_HAS_SWITCH_STATEMENT + case JSOP_TABLESWITCH: pc2 = pc; len = GET_JUMP_OFFSET(pc2); /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to + * ECMAv2 forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); @@ -4331,9 +3976,9 @@ interrupt: if (off) len = off; } - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_LOOKUPSWITCH) + case JSOP_LOOKUPSWITCH: lval = POP_OPND(); pc2 = pc; len = GET_JUMP_OFFSET(pc2); @@ -4341,7 +3986,7 @@ interrupt: if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); + goto advance_pc; } pc2 += JUMP_OFFSET_LEN; @@ -4356,7 +4001,7 @@ interrupt: if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMP_OFFSET(pc2); \ - DO_NEXT_OP(len); \ + goto advance_pc; \ } \ pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ npairs--; \ @@ -4366,7 +4011,7 @@ interrupt: SEARCH_PAIRS( match = (JSVAL_IS_STRING(rval) && ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); + !js_CompareStrings(str2, str))); ) } else if (JSVAL_IS_DOUBLE(lval)) { d = *JSVAL_TO_DOUBLE(lval); @@ -4380,21 +4025,27 @@ interrupt: ) } #undef SEARCH_PAIRS - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_TABLESWITCHX) + case JSOP_TABLESWITCHX: pc2 = pc; len = GET_JUMPX_OFFSET(pc2); /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to + * ECMAv2 forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } pc2 += JUMPX_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); @@ -4408,9 +4059,9 @@ interrupt: if (off) len = off; } - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_LOOKUPSWITCHX) + case JSOP_LOOKUPSWITCHX: lval = POP_OPND(); pc2 = pc; len = GET_JUMPX_OFFSET(pc2); @@ -4418,7 +4069,7 @@ interrupt: if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); + goto advance_pc; } pc2 += JUMPX_OFFSET_LEN; @@ -4433,7 +4084,7 @@ interrupt: if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMPX_OFFSET(pc2); \ - DO_NEXT_OP(len); \ + goto advance_pc; \ } \ pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ npairs--; \ @@ -4443,7 +4094,7 @@ interrupt: SEARCH_EXTENDED_PAIRS( match = (JSVAL_IS_STRING(rval) && ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); + !js_CompareStrings(str2, str))); ) } else if (JSVAL_IS_DOUBLE(lval)) { d = *JSVAL_TO_DOUBLE(lval); @@ -4457,14 +4108,17 @@ interrupt: ) } #undef SEARCH_EXTENDED_PAIRS - END_VARLEN_CASE + break; - EMPTY_CASE(JSOP_CONDSWITCH) + case JSOP_CONDSWITCH: + break; + +#endif /* JS_HAS_SWITCH_STATEMENT */ #if JS_HAS_EXPORT_IMPORT - BEGIN_CASE(JSOP_EXPORTALL) + case JSOP_EXPORTALL: + SAVE_SP(fp); obj = fp->varobj; - SAVE_SP_AND_PC(fp); ida = JS_Enumerate(cx, obj); if (!ida) { ok = JS_FALSE; @@ -4487,12 +4141,12 @@ interrupt: } JS_DestroyIdArray(cx, ida); } - END_CASE(JSOP_EXPORTALL) + break; BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) id = ATOM_TO_JSID(atom); obj = fp->varobj; - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; @@ -4509,30 +4163,29 @@ interrupt: } if (!ok) goto out; - END_LITOPX_CASE(JSOP_EXPORTNAME) + END_LITOPX_CASE - BEGIN_CASE(JSOP_IMPORTALL) + case JSOP_IMPORTALL: id = (jsid) JSVAL_VOID; PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; - END_CASE(JSOP_IMPORTALL) + break; - BEGIN_CASE(JSOP_IMPORTPROP) + case JSOP_IMPORTPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; - END_CASE(JSOP_IMPORTPROP) + break; - BEGIN_CASE(JSOP_IMPORTELEM) + case JSOP_IMPORTELEM: ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); sp -= 2; - END_CASE(JSOP_IMPORTELEM) + break; #endif /* JS_HAS_EXPORT_IMPORT */ - BEGIN_CASE(JSOP_TRAP) - SAVE_SP_AND_PC(fp); + case JSOP_TRAP: switch (JS_HandleTrap(cx, script, pc, &rval)) { case JSTRAP_ERROR: ok = JS_FALSE; @@ -4542,32 +4195,33 @@ interrupt: op = (JSOp) JSVAL_TO_INT(rval); JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); LOAD_INTERRUPT_HANDLER(rt); - DO_OP(); + goto do_op; case JSTRAP_RETURN: fp->rval = rval; goto out; +#if JS_HAS_EXCEPTIONS case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; +#endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); - END_CASE(JSOP_TRAP) + break; - BEGIN_CASE(JSOP_ARGUMENTS) - SAVE_SP_AND_PC(fp); + case JSOP_ARGUMENTS: + SAVE_SP(fp); ok = js_GetArgsValue(cx, fp, &rval); if (!ok) goto out; PUSH_OPND(rval); - obj = NULL; - END_CASE(JSOP_ARGUMENTS) + break; - BEGIN_CASE(JSOP_ARGSUB) + case JSOP_ARGSUB: id = INT_TO_JSID(GET_ARGNO(pc)); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; @@ -4583,66 +4237,66 @@ interrupt: obj = LAZY_ARGS_THISP; } PUSH_OPND(rval); - END_CASE(JSOP_ARGSUB) + break; #undef LAZY_ARGS_THISP - BEGIN_CASE(JSOP_ARGCNT) + case JSOP_ARGCNT: id = ATOM_TO_JSID(rt->atomState.lengthAtom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; PUSH_OPND(rval); - END_CASE(JSOP_ARGCNT) + break; - BEGIN_CASE(JSOP_GETARG) + case JSOP_GETARG: slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); PUSH_OPND(fp->argv[slot]); obj = NULL; - END_CASE(JSOP_GETARG) + break; - BEGIN_CASE(JSOP_SETARG) + case JSOP_SETARG: slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); vp = &fp->argv[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; - END_CASE(JSOP_SETARG) + break; - BEGIN_CASE(JSOP_GETVAR) + case JSOP_GETVAR: slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); + JS_ASSERT(slot < fp->fun->nvars); PUSH_OPND(fp->vars[slot]); obj = NULL; - END_CASE(JSOP_GETVAR) + break; - BEGIN_CASE(JSOP_SETVAR) + case JSOP_SETVAR: slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); + JS_ASSERT(slot < fp->fun->nvars); vp = &fp->vars[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; - END_CASE(JSOP_SETVAR) + break; - BEGIN_CASE(JSOP_GETGVAR) + case JSOP_GETGVAR: slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); lval = fp->vars[slot]; if (JSVAL_IS_NULL(lval)) { op = JSOP_NAME; - DO_OP(); + goto do_op; } slot = JSVAL_TO_INT(lval); obj = fp->varobj; rval = OBJ_GET_SLOT(cx, obj, slot); PUSH_OPND(rval); - END_CASE(JSOP_GETGVAR) + break; - BEGIN_CASE(JSOP_SETGVAR) + case JSOP_SETGVAR: slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); rval = FETCH_OPND(-1); @@ -4656,7 +4310,7 @@ interrupt: */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; @@ -4667,10 +4321,10 @@ interrupt: OBJ_SET_SLOT(cx, obj, slot, rval); } obj = NULL; - END_CASE(JSOP_SETGVAR) + break; - BEGIN_CASE(JSOP_DEFCONST) - BEGIN_CASE(JSOP_DEFVAR) + case JSOP_DEFCONST: + case JSOP_DEFVAR: atomIndex = GET_ATOM_INDEX(pc); do_JSOP_DEFCONST: @@ -4685,7 +4339,7 @@ interrupt: /* Lookup id in order to check for redeclaration problems. */ id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); if (!ok) goto out; @@ -4725,9 +4379,14 @@ interrupt: } OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_DEFVAR) + break; BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) + { + 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 = ATOM_TO_JSID(fun->atom); @@ -4762,7 +4421,6 @@ interrupt: * promote compile-cost sharing and amortizing, and because Script * is not and will not be standardized. */ - JS_ASSERT(!fp->blockChain); obj2 = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneFunctionObject(cx, obj, obj2); @@ -4794,7 +4452,7 @@ interrupt: * and setters do not need a slot, their value is stored elsewhere * in the property itself, not in obj->slots. */ - flags = JSFUN_GSFLAG2ATTR(fun->flags); + flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); if (flags) { attrs |= flags | JSPROP_SHARED; rval = JSVAL_VOID; @@ -4807,15 +4465,15 @@ interrupt: * as well as multiple HTML script tags. */ parent = fp->varobj; - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); if (ok) { ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, - (flags & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (flags & JSFUN_GETTER) + ? (JSPropertyOp) obj : NULL, - (flags & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (flags & JSFUN_SETTER) + ? (JSPropertyOp) obj : NULL, attrs, &prop); @@ -4839,8 +4497,10 @@ interrupt: } #endif OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_DEFFUN) + } + END_LITOPX_CASE +#if JS_HAS_LEXICAL_CLOSURE BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) /* * Define a local function (i.e., one nested at the top level of @@ -4849,49 +4509,14 @@ interrupt: * This is an optimization over JSOP_DEFFUN that avoids requiring * a call object for the outer function's activation. */ - slot = GET_VARNO(pc2); + slot = GET_VARNO(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); - JS_ASSERT(!fp->blockChain); - if (!(fp->flags & JSFRAME_POP_BLOCKS)) { - /* - * If the compiler-created function object (obj) is scoped by a - * let-induced body block, temporarily update fp->blockChain so - * that js_GetScopeChain will clone the block into the runtime - * scope needed to parent the function object's clone. - */ - parent = OBJ_GET_PARENT(cx, obj); - if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass) - fp->blockChain = parent; - parent = js_GetScopeChain(cx, fp); - } else { - /* - * We have already emulated JSOP_ENTERBLOCK for the enclosing - * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so - * we just load fp->scopeChain into parent. - * - * In typical execution scenarios, the prolog bytecodes that - * include this JSOP_DEFLOCALFUN run, then come main bytecodes - * including JSOP_ENTERBLOCK for the outermost (body) block. - * JSOP_ENTERBLOCK will detect that it need not do anything if - * the body block was entered above due to a local function. - * Finally the matching JSOP_LEAVEBLOCK runs. - * - * If the matching JSOP_LEAVEBLOCK for the body block does not - * run for some reason, the body block will be properly "put" - * (via js_PutBlockObject) by the PutBlockObjects call at the - * bottom of js_Interpret. - */ - parent = fp->scopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj)); - JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent)) - == &js_CallClass); - } - - /* If re-parenting, store a clone of the function object. */ + parent = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != parent) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; @@ -4899,20 +4524,16 @@ interrupt: } } fp->vars[slot] = OBJECT_TO_JSVAL(obj); - END_LITOPX_CASE(JSOP_DEFLOCALFUN) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) /* Push the specified function object literal. */ obj = ATOM_TO_OBJECT(atom); /* If re-parenting, push a clone of the function object. */ - SAVE_SP_AND_PC(fp); - parent = js_GetScopeChain(cx, fp); - if (!parent) { - ok = JS_FALSE; - goto out; - } + parent = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != parent) { + SAVE_SP(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; @@ -4921,12 +4542,12 @@ interrupt: } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE(JSOP_ANONFUNOBJ) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ rval = ATOM_KEY(atom); - JS_ASSERT(VALUE_IS_FUNCTION(cx, rval)); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); /* * 1. Create a new object as if by the expression new Object(). @@ -4936,12 +4557,8 @@ interrupt: * current scope chain, and then making the new object the parent * of the Function object clone. */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } + SAVE_SP(fp); + obj2 = fp->scopeChain; parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); if (!parent) { ok = JS_FALSE; @@ -4983,17 +4600,17 @@ interrupt: * value is Result(3), and attributes are { DontDelete, ReadOnly }. */ fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); if (attrs) { attrs |= JSPROP_SHARED; rval = JSVAL_VOID; } ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj : NULL, attrs | JSPROP_ENUMERATE | JSPROP_PERMANENT | @@ -5003,7 +4620,7 @@ interrupt: /* Restore fp->scopeChain now that obj is defined in parent. */ fp->scopeChain = obj2; if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; goto out; } @@ -5013,7 +4630,7 @@ interrupt: */ PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE(JSOP_NAMEDFUNOBJ) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) /* @@ -5024,7 +4641,7 @@ interrupt: * Get immediate operand atom, which is a function object literal. * From it, get the function to close. */ - JS_ASSERT(VALUE_IS_FUNCTION(cx, ATOM_KEY(atom))); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom))); obj = ATOM_TO_OBJECT(atom); /* @@ -5034,12 +4651,8 @@ interrupt: * have seen the right parent already and created a sufficiently * well-scoped function object. */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } + SAVE_SP(fp); + obj2 = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { @@ -5062,18 +4675,18 @@ interrupt: * a JSPropertyOp and passed accordingly). */ fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); if (attrs) { attrs |= JSPROP_SHARED; rval = JSVAL_VOID; } parent = fp->varobj; ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj : NULL, attrs | JSPROP_ENUMERATE | JSPROP_PERMANENT, @@ -5082,7 +4695,7 @@ interrupt: /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ fp->scopeChain = obj2; if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; goto out; } @@ -5098,12 +4711,16 @@ interrupt: } #endif OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_CLOSURE) + END_LITOPX_CASE +#endif /* JS_HAS_LEXICAL_CLOSURE */ #if JS_HAS_GETTER_SETTER - BEGIN_CASE(JSOP_GETTER) - BEGIN_CASE(JSOP_SETTER) + case JSOP_GETTER: + case JSOP_SETTER: + JS_ASSERT(len == 1); op2 = (JSOp) *++pc; + cs = &js_CodeSpec[op2]; + len = cs->length; switch (op2) { case JSOP_SETNAME: case JSOP_SETPROP: @@ -5121,6 +4738,7 @@ interrupt: 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); @@ -5139,6 +4757,7 @@ interrupt: JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); break; +#endif /* JS_HAS_INITIALIZERS */ default: JS_ASSERT(0); @@ -5147,7 +4766,7 @@ interrupt: /* Ensure that id has a type suitable for use with obj. */ CHECK_ELEMENT_ID(obj, id); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, @@ -5167,12 +4786,12 @@ interrupt: goto out; if (op == JSOP_GETTER) { - getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); + getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); setter = NULL; attrs = JSPROP_GETTER; } else { getter = NULL; - setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); + setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); attrs = JSPROP_SETTER; } attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; @@ -5189,18 +4808,18 @@ interrupt: obj = NULL; sp += i; - if (js_CodeSpec[op2].ndefs) + if (cs->ndefs) STORE_OPND(-1, rval); - len = js_CodeSpec[op2].length; - DO_NEXT_OP(len); + break; #endif /* JS_HAS_GETTER_SETTER */ - BEGIN_CASE(JSOP_NEWINIT) +#if JS_HAS_INITIALIZERS + case JSOP_NEWINIT: argc = 0; fp->sharpDepth++; goto do_new; - BEGIN_CASE(JSOP_ENDINIT) + case JSOP_ENDINIT: if (--fp->sharpDepth == 0) fp->sharpArray = NULL; @@ -5208,10 +4827,10 @@ interrupt: JS_ASSERT(sp - fp->spbase >= 1); lval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); - END_CASE(JSOP_ENDINIT) + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); + break; - BEGIN_CASE(JSOP_INITPROP) + case JSOP_INITPROP: /* Pop the property's value into rval. */ JS_ASSERT(sp - fp->spbase >= 2); rval = FETCH_OPND(-1); @@ -5222,7 +4841,7 @@ interrupt: i = -1; goto do_init; - BEGIN_CASE(JSOP_INITELEM) + case JSOP_INITELEM: /* Pop the element's value into rval. */ JS_ASSERT(sp - fp->spbase >= 3); rval = FETCH_OPND(-1); @@ -5241,17 +4860,16 @@ interrupt: CHECK_ELEMENT_ID(obj, id); /* Set the property named by obj[id] to rval. */ - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp += i; - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); + break; #if JS_HAS_SHARP_VARS - BEGIN_CASE(JSOP_DEFSHARP) - SAVE_SP_AND_PC(fp); + case JSOP_DEFSHARP: + SAVE_SP(fp); obj = fp->sharpArray; if (!obj) { obj = js_NewArrayObject(cx, 0, NULL); @@ -5275,16 +4893,16 @@ interrupt: ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; - END_CASE(JSOP_DEFSHARP) + break; - BEGIN_CASE(JSOP_USESHARP) + case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); id = INT_TO_JSID(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; } else { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; @@ -5292,61 +4910,38 @@ interrupt: if (!JSVAL_IS_OBJECT(rval)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - - SAVE_SP_AND_PC(fp); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SHARP_USE, numBuf); ok = JS_FALSE; goto out; } PUSH_OPND(rval); - END_CASE(JSOP_USESHARP) + break; #endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ +#if JS_HAS_EXCEPTIONS /* No-ops for ease of decompilation and jit'ing. */ - EMPTY_CASE(JSOP_TRY) - EMPTY_CASE(JSOP_FINALLY) + case JSOP_TRY: + case JSOP_FINALLY: + break; /* Reset the stack to the given depth. */ - BEGIN_CASE(JSOP_SETSP) + case JSOP_SETSP: i = (jsint) GET_ATOM_INDEX(pc); JS_ASSERT(i >= 0); - - for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - if (OBJ_BLOCK_DEPTH(cx, obj) + (jsint)OBJ_BLOCK_COUNT(cx, obj) <= i) { - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) < i || OBJ_BLOCK_COUNT(cx, obj) == 0); - break; - } - } - fp->blockChain = obj; - - JS_ASSERT(ok); - for (obj = fp->scopeChain; - (clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass || - clasp == &js_BlockClass; - obj = OBJ_GET_PARENT(cx, obj)) { - if (JS_GetPrivate(cx, obj) != fp || - OBJ_BLOCK_DEPTH(cx, obj) < i) { - break; - } - if (clasp == &js_BlockClass) - ok &= js_PutBlockObject(cx, obj); - else - JS_SetPrivate(cx, obj, NULL); - } - - fp->scopeChain = obj; - - /* Set sp after js_PutBlockObject to avoid potential GC hazards. */ sp = fp->spbase + i; - /* Don't fail until after we've updated all stacks. */ - if (!ok) - goto out; - END_CASE(JSOP_SETSP) + 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; - BEGIN_CASE(JSOP_GOSUB) + case JSOP_GOSUB: JS_ASSERT(cx->exception != JSVAL_HOLE); if (!cx->throwing) { lval = JSVAL_HOLE; @@ -5355,26 +4950,21 @@ interrupt: cx->throwing = JS_FALSE; } PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; + i = PTRDIFF(pc, script->main, jsbytecode) + len; len = GET_JUMP_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_GOSUBX) + case JSOP_GOSUBX: JS_ASSERT(cx->exception != JSVAL_HOLE); - if (!cx->throwing) { - lval = JSVAL_HOLE; - } else { - lval = cx->exception; - cx->throwing = JS_FALSE; - } + lval = cx->throwing ? cx->exception : JSVAL_HOLE; PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; + i = PTRDIFF(pc, script->main, jsbytecode) + len; len = GET_JUMPX_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_RETSUB) + case JSOP_RETSUB: rval = POP(); JS_ASSERT(JSVAL_IS_INT(rval)); lval = POP(); @@ -5390,46 +4980,71 @@ interrupt: ok = JS_FALSE; goto out; } - len = JSVAL_TO_INT(rval); - pc = script->main; - END_VARLEN_CASE + i = JSVAL_TO_INT(rval); + pc = script->main + i; + len = 0; + break; - BEGIN_CASE(JSOP_EXCEPTION) - JS_ASSERT(cx->throwing); + case JSOP_EXCEPTION: PUSH(cx->exception); cx->throwing = JS_FALSE; - END_CASE(JSOP_EXCEPTION) + break; - BEGIN_CASE(JSOP_THROWING) + case JSOP_THROWING: JS_ASSERT(!cx->throwing); cx->throwing = JS_TRUE; - cx->exception = POP_OPND(); - END_CASE(JSOP_THROWING) + break; - BEGIN_CASE(JSOP_THROW) - JS_ASSERT(!cx->throwing); + case JSOP_THROW: cx->throwing = JS_TRUE; cx->exception = POP_OPND(); ok = JS_FALSE; /* let the code at out try to catch the exception. */ goto out; - BEGIN_CASE(JSOP_SETLOCALPOP) - /* - * The stack must have a block with at least one local slot below - * the exception object. - */ + BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) + /* Load the value into rval, while keeping it live on stack. */ JS_ASSERT(sp - fp->spbase >= 2); - slot = GET_UINT16(pc); - JS_ASSERT(slot + 1 < (uintN)depth); - fp->spbase[slot] = POP_OPND(); - END_CASE(JSOP_SETLOCALPOP) + rval = FETCH_OPND(-1); - BEGIN_CASE(JSOP_INSTANCEOF) - SAVE_SP_AND_PC(fp); + /* Get the immediate catch variable name into id. */ + id = ATOM_TO_JSID(atom); + + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(-2); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + + 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; + 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) || !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { + SAVE_SP(fp); str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -5441,19 +5056,21 @@ interrupt: } lval = FETCH_OPND(-2); cond = JS_FALSE; + SAVE_SP(fp); ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); if (!ok) goto out; sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); - END_CASE(JSOP_INSTANCEOF) + break; +#endif /* JS_HAS_INSTANCEOF */ #if JS_HAS_DEBUGGER_KEYWORD - BEGIN_CASE(JSOP_DEBUGGER) + case JSOP_DEBUGGER: { JSTrapHandler handler = rt->debuggerHandler; if (handler) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); switch (handler(cx, script, pc, &rval, rt->debuggerHandlerData)) { case JSTRAP_ERROR: @@ -5464,56 +5081,58 @@ interrupt: case JSTRAP_RETURN: fp->rval = rval; goto out; +#if JS_HAS_EXCEPTIONS case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; +#endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); } + break; } - END_CASE(JSOP_DEBUGGER) #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT - BEGIN_CASE(JSOP_DEFXMLNS) + case JSOP_DEFXMLNS: rval = POP(); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_SetDefaultXMLNamespace(cx, rval); if (!ok) goto out; - END_CASE(JSOP_DEFXMLNS) + break; - BEGIN_CASE(JSOP_ANYNAME) - SAVE_SP_AND_PC(fp); + case JSOP_ANYNAME: + SAVE_SP(fp); ok = js_GetAnyName(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); - END_CASE(JSOP_ANYNAME) + break; BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) PUSH_OPND(ATOM_KEY(atom)); - END_LITOPX_CASE(JSOP_QNAMEPART) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) rval = ATOM_KEY(atom); lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + 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(JSOP_QNAMECONST) + END_LITOPX_CASE - BEGIN_CASE(JSOP_QNAME) + case JSOP_QNAME: rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_ConstructXMLQNameObject(cx, lval, rval); if (!obj) { ok = JS_FALSE; @@ -5521,36 +5140,36 @@ interrupt: } sp--; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_QNAME) + break; - BEGIN_CASE(JSOP_TOATTRNAME) + case JSOP_TOATTRNAME: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_ToAttributeName(cx, &rval); if (!ok) goto out; STORE_OPND(-1, rval); - END_CASE(JSOP_TOATTRNAME) + break; - BEGIN_CASE(JSOP_TOATTRVAL) + case JSOP_TOATTRVAL: rval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_STRING(rval)); - SAVE_SP_AND_PC(fp); + 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)); - END_CASE(JSOP_TOATTRVAL) + break; - BEGIN_CASE(JSOP_ADDATTRNAME) - BEGIN_CASE(JSOP_ADDATTRVAL) + 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_AND_PC(fp); + SAVE_SP(fp); str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); if (!str) { ok = JS_FALSE; @@ -5558,34 +5177,34 @@ interrupt: } sp--; STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_ADDATTRNAME) + break; - BEGIN_CASE(JSOP_BINDXMLNAME) + case JSOP_BINDXMLNAME: lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); PUSH_OPND(rval); - END_CASE(JSOP_BINDXMLNAME) + break; - BEGIN_CASE(JSOP_SETXMLNAME) + case JSOP_SETXMLNAME: obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); lval = FETCH_OPND(-2); rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_SetXMLProperty(cx, obj, lval, &rval); if (!ok) goto out; sp -= 2; STORE_OPND(-1, rval); obj = NULL; - END_CASE(JSOP_SETXMLNAME) + break; - BEGIN_CASE(JSOP_XMLNAME) + case JSOP_XMLNAME: lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; @@ -5593,13 +5212,13 @@ interrupt: if (!ok) goto out; STORE_OPND(-1, rval); - END_CASE(JSOP_XMLNAME) + break; - BEGIN_CASE(JSOP_DESCENDANTS) - BEGIN_CASE(JSOP_DELDESC) + case JSOP_DESCENDANTS: + case JSOP_DELDESC: FETCH_OBJECT(cx, -2, lval, obj); rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); ok = js_GetXMLDescendants(cx, obj, rval, &rval); if (!ok) goto out; @@ -5614,62 +5233,63 @@ interrupt: sp--; STORE_OPND(-1, rval); - END_CASE(JSOP_DESCENDANTS) + break; - BEGIN_CASE(JSOP_FILTER) + case JSOP_FILTER: FETCH_OBJECT(cx, -1, lval, obj); len = GET_JUMP_OFFSET(pc); - SAVE_SP_AND_PC(fp); - ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval); + 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); - END_VARLEN_CASE + break; - BEGIN_CASE(JSOP_ENDFILTER) + case JSOP_ENDFILTER: *result = POP_OPND(); goto out; - EMPTY_CASE(JSOP_STARTXML) - EMPTY_CASE(JSOP_STARTXMLEXPR) + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + break; - BEGIN_CASE(JSOP_TOXML) + case JSOP_TOXML: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_ValueToXMLObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXML) + break; - BEGIN_CASE(JSOP_TOXMLLIST) + case JSOP_TOXMLLIST: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_ValueToXMLListObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXMLLIST) + break; - BEGIN_CASE(JSOP_XMLTAGEXPR) + case JSOP_XMLTAGEXPR: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); str = js_ValueToString(cx, rval); if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLTAGEXPR) + break; - BEGIN_CASE(JSOP_XMLELTEXPR) + case JSOP_XMLELTEXPR: rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); if (VALUE_IS_XML(cx, rval)) { str = js_ValueToXMLString(cx, rval); } else { @@ -5682,10 +5302,10 @@ interrupt: goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLELTEXPR) + break; BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); if (!obj) { ok = JS_FALSE; @@ -5693,7 +5313,7 @@ interrupt: } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE(JSOP_XMLOBJECT) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) str = ATOM_TO_STRING(atom); @@ -5703,7 +5323,7 @@ interrupt: goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCDATA) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) str = ATOM_TO_STRING(atom); @@ -5713,13 +5333,13 @@ interrupt: goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCOMMENT) + 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_AND_PC(fp); + SAVE_SP(fp); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2); @@ -5728,66 +5348,36 @@ interrupt: goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLPI) + END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) /* Get an immediate atom naming the property. */ id = ATOM_TO_JSID(atom); - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - if (!JSVAL_IS_PRIMITIVE(lval)) { - STORE_OPND(-1, lval); - obj = JSVAL_TO_OBJECT(lval); + 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; + /* 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)); - } - } else { - if (JSVAL_IS_STRING(lval)) { - i = JSProto_String; - } else if (JSVAL_IS_NUMBER(lval)) { - i = JSProto_Number; - } else if (JSVAL_IS_BOOLEAN(lval)) { - i = JSProto_Boolean; - } else { - JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - lval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_PROPERTIES, - JS_GetStringBytes(str)); - } + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, &rval); + if (!obj) ok = JS_FALSE; - goto out; - } - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj); - if (!ok) - goto out; - JS_ASSERT(obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + } else { CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - obj = (JSObject *) lval; /* keep tagged as non-object */ } if (!ok) goto out; STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_GETMETHOD) + 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_AND_PC(fp); + SAVE_SP(fp); /* Special-case XML object method lookup, per ECMA-357. */ if (OBJECT_IS_XML(cx, obj)) { @@ -5803,247 +5393,21 @@ interrupt: --sp; STORE_OPND(-1, rval); obj = NULL; - END_LITOPX_CASE(JSOP_SETMETHOD) + END_LITOPX_CASE - BEGIN_CASE(JSOP_GETFUNNS) - SAVE_SP_AND_PC(fp); + case JSOP_GETFUNNS: ok = js_GetFunctionNamespace(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); - END_CASE(JSOP_GETFUNNS) + break; + + case JSOP_FOREACH: + foreach = JS_TRUE; + break; #endif /* JS_HAS_XML_SUPPORT */ - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK, 0) - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - vp = sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(vp <= fp->spbase + depth); - while (sp < vp) { - STORE_OPND(0, JSVAL_VOID); - sp++; - } - - /* - * If this frame had to reflect the compile-time block chain into - * the runtime scope chain, we can't optimize block scopes out of - * runtime any longer, because an outer block that parents obj has - * been cloned onto the scope chain. To avoid re-cloning such a - * parent and accumulating redundant clones via js_GetScopeChain, - * we must clone each block eagerly on entry, and push it on the - * scope chain, until this frame pops. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - JS_ASSERT(!fp->blockChain); - - /* - * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for - * the body block in order to correctly scope the local cloned - * function object it creates. - */ - parent = fp->scopeChain; - if (OBJ_GET_PROTO(cx, parent) == obj) { - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - } else { - obj = js_CloneBlockObject(cx, obj, parent, fp); - if (!obj) { - ok = JS_FALSE; - goto out; - } - fp->scopeChain = obj; - } - } else { - JS_ASSERT(!fp->blockChain || - OBJ_GET_PARENT(cx, obj) == fp->blockChain); - fp->blockChain = obj; - } - END_LITOPX_CASE(JSOP_ENTERBLOCK) - - BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) - BEGIN_CASE(JSOP_LEAVEBLOCK) - { - JSObject **chainp; - - /* Grab the result of the expression. */ - if (op == JSOP_LEAVEBLOCKEXPR) - rval = FETCH_OPND(-1); - - chainp = &fp->blockChain; - obj = *chainp; - if (!obj) { - chainp = &fp->scopeChain; - obj = *chainp; - - /* - * This block was cloned, so clear its private data and sync - * its locals to their property slots. - */ - SAVE_SP_AND_PC(fp); - ok = js_PutBlockObject(cx, obj); - if (!ok) - goto out; - } - - sp -= GET_UINT16(pc); - JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth); - - /* Store the result into the topmost stack slot. */ - if (op == JSOP_LEAVEBLOCKEXPR) - STORE_OPND(-1, rval); - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR - ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1 - : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - - *chainp = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(chainp != &fp->blockChain || - !*chainp || - OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass); - } - END_CASE(JSOP_LEAVEBLOCK) - - BEGIN_CASE(JSOP_GETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - PUSH_OPND(fp->spbase[slot]); - obj = NULL; - END_CASE(JSOP_GETLOCAL) - - BEGIN_CASE(JSOP_SETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETLOCAL) - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \ - slot = GET_UINT16(pc); \ - JS_ASSERT(slot < (uintN)depth); \ - vp = fp->spbase + slot; \ - rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE) - - BEGIN_CASE(JSOP_INCLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, +=, MAX); - END_CASE(JSOP_INCLOCAL) - - BEGIN_CASE(JSOP_DECLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, -=, MIN); - END_CASE(JSOP_DECLOCAL) - - BEGIN_CASE(JSOP_LOCALINC) - FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX); - END_CASE(JSOP_LOCALINC) - - BEGIN_CASE(JSOP_LOCALDEC) - FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN); - END_CASE(JSOP_LOCALDEC) - -#undef FAST_LOCAL_INCREMENT_OP - - EMPTY_CASE(JSOP_STARTITER) - - BEGIN_CASE(JSOP_ENDITER) - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - iterobj = JSVAL_TO_OBJECT(sp[-1]); - - /* - * js_CloseNativeIterator checks whether the iterator is not - * native, and also detects the case of a native iterator that - * has already escaped, even though a for-in loop caused it to - * be created. See jsiter.c. - */ - SAVE_SP_AND_PC(fp); - js_CloseNativeIterator(cx, iterobj); - *--sp = JSVAL_NULL; - END_CASE(JSOP_ENDITER) - -#if JS_HAS_GENERATORS - BEGIN_CASE(JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - SAVE_SP_AND_PC(fp); - obj = js_NewGenerator(cx, fp); - if (!obj) { - ok = JS_FALSE; - } else { - JS_ASSERT(!fp->callobj && !fp->argsobj); - fp->rval = OBJECT_TO_JSVAL(obj); - } - goto out; - - BEGIN_CASE(JSOP_YIELD) - ASSERT_NOT_THROWING(cx); - if (fp->flags & JSFRAME_FILTERING) { - /* FIXME: bug 309894 -- fix to eliminate this error. */ - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_YIELD_FROM_FILTER); - ok = JS_FALSE; - goto out; - } - if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - fp->argv[-2], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_YIELD, - JSSTRING_CHARS(str)); - } - ok = JS_FALSE; - goto out; - } - fp->rval = FETCH_OPND(-1); - fp->flags |= JSFRAME_YIELDING; - pc += JSOP_YIELD_LENGTH; - SAVE_SP_AND_PC(fp); - goto out; - - BEGIN_CASE(JSOP_ARRAYPUSH) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - lval = fp->spbase[slot]; - obj = JSVAL_TO_OBJECT(lval); - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); - rval = FETCH_OPND(-1); - - /* We know that the array is created with only a 'length' slot. */ - i = obj->map->freeslot - (JSSLOT_FREE(&js_ArrayClass) + 1); - id = INT_TO_JSID(i); - - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - --sp; - END_CASE(JSOP_ARRAYPUSH) -#endif /* JS_HAS_GENERATORS */ - -#if !JS_HAS_GENERATORS - L_JSOP_GENERATOR: - L_JSOP_YIELD: - L_JSOP_ARRAYPUSH: -#endif - -#if !JS_HAS_DESTRUCTURING - L_JSOP_FOREACHKEYVAL: - L_JSOP_ENUMCONSTELEM: -#endif - -#ifdef JS_THREADED_INTERP - L_JSOP_BACKPATCH: - L_JSOP_BACKPATCH_POP: -#else - default: -#endif - { + default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", op); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, @@ -6051,10 +5415,7 @@ interrupt: ok = JS_FALSE; goto out; } - -#ifndef JS_THREADED_INTERP - - } /* switch (op) */ + } advance_pc: pc += len; @@ -6064,9 +5425,9 @@ interrupt: intN ndefs, n; jsval *siter; - ndefs = js_CodeSpec[op].ndefs; + ndefs = cs->ndefs; if (ndefs) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) --ndefs; for (n = -ndefs; n < 0; n++) { @@ -6087,17 +5448,17 @@ interrupt: } fputc('\n', tracefp); } -#endif /* DEBUG */ +#endif } -#endif /* !JS_THREADED_INTERP */ - out: + +#if JS_HAS_EXCEPTIONS if (!ok) { /* - * Has an exception been raised? Also insist that we are not in an - * XML filtering predicate expression, to avoid catching exceptions - * within the filtering predicate, such as this example taken from - * tests/e4x/Regress/regress-301596.js: + * 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); @@ -6115,15 +5476,17 @@ out: * 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 */ - if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) { + if (cx->throwing && JS_LIKELY(mark != NULL)) { /* * Call debugger throw hook if set (XXX thread safety?). */ JSTrapHandler handler = rt->throwHook; if (handler) { - SAVE_SP_AND_PC(fp); + SAVE_SP(fp); switch (handler(cx, script, pc, &rval, rt->throwHookData)) { case JSTRAP_ERROR: cx->throwing = JS_FALSE; @@ -6144,41 +5507,32 @@ out: /* * Look for a try block in script that can catch this exception. */ -#if JS_HAS_GENERATORS - if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) { - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; - } else { - pc = js_FindFinallyHandler(script, pc); - if (!pc) { - cx->throwing = JS_FALSE; - ok = JS_TRUE; - fp->rval = JSVAL_VOID; - goto no_catch; - } - } -#else SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; + 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 - - /* Don't clear cx->throwing to save cx->exception from GC. */ - len = 0; - ok = JS_TRUE; - DO_NEXT_OP(len); + goto advance_pc; + } } no_catch:; } +#endif /* * Check whether control fell off the end of a lightweight function, or an * exception thrown under such a function was not caught by it. If so, go * to the inline code under JSOP_RETURN. */ - if (inlineCallCount) + 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. @@ -6186,12 +5540,6 @@ no_catch:; * Restore the previous frame's execution state. */ if (JS_LIKELY(mark != NULL)) { - /* If fp has blocks on its scope chain, home their locals now. */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - fp->sp = fp->spbase; fp->spbase = NULL; js_FreeRawStack(cx, mark); diff --git a/lib/javascript/jsinterp.h b/lib/javascript/jsinterp.h index ab60b3af..edf70f86 100644 --- a/lib/javascript/jsinterp.h +++ b/lib/javascript/jsinterp.h @@ -48,14 +48,7 @@ JS_BEGIN_EXTERN_C /* - * JS stack frame, may be allocated on the C stack by native callers. Always - * allocated on cx->stackPool for calls from the interpreter to an interpreted - * function. - * - * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you - * add new members, update both files. But first, try to remove members. The - * sharp* and xml* members should be moved onto the stack as local variables - * with well-known slots, if possible. + * JS stack frame, allocated on the C stack. */ struct JSStackFrame { JSObject *callobj; /* lazily created Call object */ @@ -80,12 +73,10 @@ struct JSStackFrame { uint32 flags; /* frame flags -- see below */ JSStackFrame *dormantNext; /* next dormant frame chain */ JSObject *xmlNamespace; /* null or default xml namespace in E4X */ - JSObject *blockChain; /* active compile-time block scopes */ }; typedef struct JSInlineFrame { JSStackFrame frame; /* base struct */ - jsval *rvp; /* ptr to caller's return value slot */ void *mark; /* mark before inline frame */ void *hookData; /* debugger call hook data */ JSVersion callerVersion; /* dynamic version of calling script */ @@ -105,11 +96,6 @@ typedef struct JSInlineFrame { #define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name references based on scope chain */ #define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */ -#define JSFRAME_YIELDING 0x200 /* js_Interpret dispatched JSOP_YIELD */ -#define JSFRAME_FILTERING 0x400 /* XML filtering predicate expression */ -#define JSFRAME_ITERATOR 0x800 /* trying to get an iterator for for-in */ -#define JSFRAME_POP_BLOCKS 0x1000 /* scope chain contains blocks to pop */ -#define JSFRAME_GENERATOR 0x2000 /* frame belongs to generator-iterator */ #define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ #define JSFRAME_OVERRIDE_BITS 8 @@ -271,24 +257,18 @@ extern void js_DumpCallTable(JSContext *cx); #endif /* - * Refresh and return fp->scopeChain. It may be stale if block scopes are - * active but not yet reflected by objects in the scope chain. If a block - * scope contains a with, eval, XML filtering predicate, or similar such - * dynamically scoped construct, then compile-time block scope at fp->blocks - * must reflect at runtime. - */ -extern JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp); - -/* - * Compute the 'this' parameter for a call with nominal 'this' given by thisp - * and arguments including argv[-1] (nominal 'this') and argv[-2] (callee). + * 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', - * per ECMA-262, so js_ComputeThis censors them. + * 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 JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv); +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 @@ -299,27 +279,11 @@ extern JS_FRIEND_API(JSBool) js_Invoke(JSContext *cx, uintN argc, uintN flags); /* - * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that - * we can share bits stored in JSStackFrame.flags and passed to: - * - * js_Invoke - * js_InternalInvoke - * js_ValueToFunction - * js_ValueToFunctionObject - * js_ValueToCallableObject - * js_ReportIsNotFunction - * - * See jsfun.h for the latter four and flag renaming macros. + * Consolidated js_Invoke flags simply rename the low JSFRAME_* flags. */ #define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING #define JSINVOKE_INTERNAL JSFRAME_INTERNAL #define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER -#define JSINVOKE_ITERATOR JSFRAME_ITERATOR - -/* - * Mask to isolate construct and iterator flags for use with jsfun.h functions. - */ -#define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR) /* * "Internal" calls may come from C or C++ code using a JSContext on which no @@ -350,9 +314,6 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, extern JSBool js_StrictlyEqual(jsval lval, jsval rval); -extern JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc); - extern JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result); diff --git a/lib/javascript/jsiter.c b/lib/javascript/jsiter.c deleted file mode 100644 index 0a4de542..00000000 --- a/lib/javascript/jsiter.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript iterators. - */ -#include "jsstddef.h" -#include /* for memcpy */ -#include "jstypes.h" -#include "jsutil.h" -#include "jsarena.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -extern const char js_throw_str[]; /* from jsscan.h */ - -#define JSSLOT_ITER_STATE (JSSLOT_PRIVATE) -#define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS -#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS. -#endif - -/* - * Shared code to close iterator's state either through an explicit call or - * when GC detects that the iterator is no longer reachable. - */ -void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj) -{ - jsval *slots; - jsval state, parent; - JSObject *iterable; - - JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)); - slots = iterobj->slots; - - /* Avoid double work if js_CloseNativeIterator was called on obj. */ - state = slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - return; - - /* Protect against failure to fully initialize obj. */ - parent = slots[JSSLOT_PARENT]; - if (!JSVAL_IS_PRIMITIVE(parent)) { - iterable = JSVAL_TO_OBJECT(parent); -#if JS_HAS_XML_SUPPORT - if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) && - OBJECT_IS_XML(cx, iterable)) { - ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, - NULL, NULL); - } else -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); - } - slots[JSSLOT_ITER_STATE] = JSVAL_NULL; -} - -JSClass js_IteratorClass = { - "Iterator", - JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ - JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) -{ - jsval state; - JSBool ok; - - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - /* Initialize iterobj in case of enumerate hook failure. */ - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); - if (!js_RegisterCloseableIterator(cx, iterobj)) - return JS_FALSE; - if (!obj) - return JS_TRUE; - - ok = -#if JS_HAS_XML_SUPPORT - ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) - ? ((JSXMLObjectOps *) obj->map->ops)-> - enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) - : -#endif - OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); - if (!ok) - return JS_FALSE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (flags & JSITER_ENUMERATE) { - /* - * The enumerating iterator needs the original object to suppress - * enumeration of deleted or shadowed prototype properties. Since the - * enumerator never escapes to scripts, we use the prototype slot to - * store the original object. - */ - JS_ASSERT(obj != iterobj); - iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); - } - return JS_TRUE; -} - -static JSBool -Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool keyonly; - uintN flags; - JSObject *obj; - - keyonly = JS_FALSE; - if (!js_ValueToBoolean(cx, argv[1], &keyonly)) - return JS_FALSE; - flags = keyonly ? 0 : JSITER_FOREACH; - - if (cx->fp->flags & JSFRAME_CONSTRUCTING) { - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(argv[0])) { - obj = JSVAL_TO_OBJECT(argv[0]); - } else { - obj = js_ValueToNonNullObject(cx, argv[0]); - if (!obj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - } - return InitNativeIterator(cx, iterobj, obj, flags); - } - - *rval = argv[0]; - return js_ValueToIterator(cx, flags, rval); -} - -static JSBool -NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) -{ - jsval vec[2]; - JSTempValueRooter tvr; - JSObject *aobj; - - vec[0] = ID_TO_VALUE(key); - vec[1] = val; - - JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); - aobj = js_NewArrayObject(cx, 2, vec); - *rval = OBJECT_TO_JSVAL(aobj); - JS_POP_TEMP_ROOT(cx, &tvr); - - return aobj != NULL; -} - -static JSBool -IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *iterable; - jsval state; - uintN flags; - JSBool foreach, ok; - jsid id; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); - - iterable = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(iterable); - state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); - if (JSVAL_IS_NULL(state)) - goto stop; - - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); - JS_ASSERT(!(flags & JSITER_ENUMERATE)); - foreach = (flags & JSITER_FOREACH) != 0; - ok = -#if JS_HAS_XML_SUPPORT - (foreach && OBJECT_IS_XML(cx, iterable)) - ? ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, - &id, rval) - : -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); - if (!ok) - return JS_FALSE; - - OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state); - if (JSVAL_IS_NULL(state)) - goto stop; - - if (foreach) { -#if JS_HAS_XML_SUPPORT - if (!OBJECT_IS_XML(cx, iterable) && - !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { - return JS_FALSE; - } -#endif - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } else { - *rval = ID_TO_VALUE(id); - } - return JS_TRUE; - - stop: - JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -static JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(!JS_IsExceptionPending(cx)); - if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) - JS_SetPendingException(cx, v); - return JS_FALSE; -} - -static JSBool -iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) - return JS_FALSE; - - if (!IteratorNextImpl(cx, obj, rval)) - return JS_FALSE; - - if (*rval == JSVAL_HOLE) { - *rval = JSVAL_NULL; - js_ThrowStopIteration(cx, obj); - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec iterator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, iterator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -uintN -js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) -{ - if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) - return 0; - return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); -} - -void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) -{ - uintN flags; - - /* - * If this iterator is not an instance of the native default iterator - * class, leave it to be GC'ed. - */ - if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)) - return; - - /* - * If this iterator was not created by js_ValueToIterator called from the - * for-in loop code in js_Interpret, leave it to be GC'ed. - */ - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (!(flags & JSITER_ENUMERATE)) - return; - - js_CloseIteratorState(cx, iterobj); -} - -/* - * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. - * Otherwise construct the defualt iterator. - */ -JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) -{ - JSObject *obj; - JSTempValueRooter tvr; - const JSAtom *atom; - JSBool ok; - JSObject *iterobj; - jsval arg; - JSString *str; - - JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | - JSITER_FOREACH | - JSITER_KEYVALUE))); - - /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ - JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); - - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(*vp)) { - obj = JSVAL_TO_OBJECT(*vp); - } else { - /* - * Enumerating over null and undefined gives an empty enumerator. - * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of - * the first production in 12.6.4 and step 4 of the second production, - * but it's "web JS" compatible. - */ - if ((flags & JSITER_ENUMERATE)) { - if (!js_ValueToObject(cx, *vp, &obj)) - return JS_FALSE; - if (!obj) - goto default_iter; - } else { - obj = js_ValueToNonNullObject(cx, *vp); - if (!obj) - return JS_FALSE; - } - } - - JS_ASSERT(obj); - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - atom = cx->runtime->atomState.iteratorAtom; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } - - if (JSVAL_IS_VOID(*vp)) { - default_iter: - /* - * Fail over to the default enumerating native iterator. - * - * Create iterobj with a NULL parent to ensure that we use the correct - * scope chain to lookup the iterator's constructor. Since we use the - * parent slot to keep track of the iterable, we must fix it up after. - */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); - if (!iterobj) - goto bad; - - /* Store iterobj in *vp to protect it from GC (callers must root vp). */ - *vp = OBJECT_TO_JSVAL(iterobj); - - if (!InitNativeIterator(cx, iterobj, obj, flags)) - goto bad; - } else { - arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); - if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) - goto bad; - if (JSVAL_IS_PRIMITIVE(*vp)) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR_RETURN, - JSSTRING_CHARS(str), - JSSTRING_CHARS(ATOM_TO_STRING(atom))); - } - goto bad; - } - } - - ok = JS_TRUE; - out: - if (obj) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - bad: - ok = JS_FALSE; - goto out; -} - -static JSBool -CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) -{ - JSObject *obj, *origobj; - jsval state; - JSBool foreach; - jsid id; - JSObject *obj2; - JSBool cond; - JSClass *clasp; - JSExtendedClass *xclasp; - JSProperty *prop; - JSString *str; - - JS_ASSERT(flags & JSITER_ENUMERATE); - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); - origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); - state = iterobj->slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - goto stop; - - foreach = (flags & JSITER_FOREACH) != 0; -#if JS_HAS_XML_SUPPORT - /* - * Treat an XML object specially only when it starts the prototype chain. - * Otherwise we need to do the usual deleted and shadowed property checks. - */ - if (obj == origobj && OBJECT_IS_XML(cx, obj)) { - if (foreach) { - JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; - - if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, - &id, rval)) { - return JS_FALSE; - } - } else { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_FALSE; - } - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) - goto stop; - } else -#endif - { - restart: - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_TRUE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) { -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - /* - * We just finished enumerating an XML obj that is present on - * the prototype chain of a non-XML origobj. Stop further - * prototype chain searches because XML objects don't - * enumerate prototypes. - */ - JS_ASSERT(origobj != obj); - JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); - } else -#endif - { - obj = OBJ_GET_PROTO(cx, obj); - if (obj) { - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) - return JS_FALSE; - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (!JSVAL_IS_NULL(state)) - goto restart; - } - } - goto stop; - } - - /* Skip properties not in obj when looking from origobj. */ - if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) - goto restart; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* - * If the id was found in a prototype object or an unrelated object - * (specifically, not in an inner object for obj), skip it. This step - * means that all OBJ_LOOKUP_PROPERTY implementations must return an - * object further along on the prototype chain, or else possibly an - * object returned by the JSExtendedClass.outerObject optional hook. - */ - if (obj != obj2) { - cond = JS_FALSE; - clasp = OBJ_GET_CLASS(cx, obj2); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - cond = xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj; - } - if (!cond) - goto restart; - } - - if (foreach) { - /* Get property querying the original object. */ - if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) - return JS_FALSE; - } - } - - if (foreach) { - if (flags & JSITER_KEYVALUE) { - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } - } else { - /* Make rval a string for uniformity and compatibility. */ - if (JSID_IS_ATOM(id)) { - *rval = ATOM_KEY(JSID_TO_ATOM(id)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - } - return JS_TRUE; - - stop: - JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) -{ - uintN flags; - - /* Fast path for native iterators */ - if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (flags & JSITER_ENUMERATE) - return CallEnumeratorNext(cx, iterobj, flags, rval); - - /* - * Call next directly as all the methods of the native iterator are - * read-only and permanent. - */ - if (!IteratorNextImpl(cx, iterobj, rval)) - return JS_FALSE; - } else { - jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); - - if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) - return JS_FALSE; - if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { - /* Check for StopIteration. */ - if (!cx->throwing || - JSVAL_IS_PRIMITIVE(cx->exception) || - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) - != &js_StopIterationClass) { - return JS_FALSE; - } - - /* Inline JS_ClearPendingException(cx). */ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; - *rval = JSVAL_HOLE; - return JS_TRUE; - } - } - - return JS_TRUE; -} - -static JSBool -stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - *bp = !JSVAL_IS_PRIMITIVE(v) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; - return JS_TRUE; -} - -JSClass js_StopIterationClass = { - js_StopIteration_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, stopiter_hasInstance, - NULL, NULL -}; - -#if JS_HAS_GENERATORS - -static void -generator_finalize(JSContext *cx, JSObject *obj) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * gen can be open on shutdown when close hooks are ignored or when - * the embedding cancels scheduled close hooks. - */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED || - gen->state == JSGEN_OPEN); - JS_free(cx, gen); - } -} - -static uint32 -generator_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * We must mark argv[-2], as js_MarkStackFrame will not. Note that - * js_MarkStackFrame will mark thisp (argv[-1]) and actual arguments, - * plus any missing formals and local GC roots. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(gen->frame.argv[-2])); - GC_MARK(cx, JSVAL_TO_GCTHING(gen->frame.argv[-2]), "generator"); - js_MarkStackFrame(cx, &gen->frame); - } - return 0; -} - -JSClass js_GeneratorClass = { - js_Generator_str, - JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, generator_mark, NULL -}; - -/* - * Called from the JSOP_GENERATOR case in the interpreter, with fp referring - * to the frame by which the generator function was activated. Create a new - * JSGenerator object, which contains its own JSStackFrame that we populate - * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return - * from the activation in fp, so we can steal away fp->callobj and fp->argsobj - * if they are non-null. - */ -JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj; - uintN argc, nargs, nvars, depth, nslots; - JSGenerator *gen; - jsval *newsp; - - /* After the following return, failing control flow must goto bad. */ - obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); - if (!obj) - return NULL; - - /* Load and compute stack slot counts. */ - argc = fp->argc; - nargs = JS_MAX(argc, fp->fun->nargs); - nvars = fp->nvars; - depth = fp->script->depth; - nslots = 2 + nargs + nvars + 2 * depth; - - /* Allocate obj's private data struct. */ - gen = (JSGenerator *) - JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); - if (!gen) - goto bad; - - gen->obj = obj; - - /* Steal away objects reflecting fp and point them at gen->frame. */ - gen->frame.callobj = fp->callobj; - if (fp->callobj) { - JS_SetPrivate(cx, fp->callobj, &gen->frame); - fp->callobj = NULL; - } - gen->frame.argsobj = fp->argsobj; - if (fp->argsobj) { - JS_SetPrivate(cx, fp->argsobj, &gen->frame); - fp->argsobj = NULL; - } - - /* These two references can be shared with fp until it goes away. */ - gen->frame.varobj = fp->varobj; - gen->frame.thisp = fp->thisp; - - /* Copy call-invariant script and function references. */ - gen->frame.script = fp->script; - gen->frame.fun = fp->fun; - - /* Use newsp to carve space out of gen->stack. */ - newsp = gen->stack; - gen->arena.next = NULL; - gen->arena.base = (jsuword) newsp; - gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots); - -#define COPY_STACK_ARRAY(vec,cnt,num) \ - JS_BEGIN_MACRO \ - gen->frame.cnt = cnt; \ - gen->frame.vec = newsp; \ - newsp += (num); \ - memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ - JS_END_MACRO - - /* Copy argv, rval, and vars. */ - *newsp++ = fp->argv[-2]; - *newsp++ = fp->argv[-1]; - COPY_STACK_ARRAY(argv, argc, nargs); - gen->frame.rval = fp->rval; - COPY_STACK_ARRAY(vars, nvars, nvars); - -#undef COPY_STACK_ARRAY - - /* Initialize or copy virtual machine state. */ - gen->frame.down = NULL; - gen->frame.annotation = NULL; - gen->frame.scopeChain = fp->scopeChain; - gen->frame.pc = fp->pc; - - /* Allocate generating pc and operand stack space. */ - gen->frame.spbase = gen->frame.sp = newsp + depth; - - /* Copy remaining state (XXX sharp* and xml* should be local vars). */ - gen->frame.sharpDepth = 0; - gen->frame.sharpArray = NULL; - gen->frame.flags = fp->flags | JSFRAME_GENERATOR; - gen->frame.dormantNext = NULL; - gen->frame.xmlNamespace = NULL; - gen->frame.blockChain = NULL; - - /* Note that gen is newborn. */ - gen->state = JSGEN_NEWBORN; - - if (!JS_SetPrivate(cx, obj, gen)) { - JS_free(cx, gen); - goto bad; - } - - /* - * Register with GC to ensure that suspended finally blocks will be - * executed. - */ - js_RegisterGenerator(cx, gen); - return obj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -typedef enum JSGeneratorOp { - JSGENOP_NEXT, - JSGENOP_SEND, - JSGENOP_THROW, - JSGENOP_CLOSE -} JSGeneratorOp; - -/* - * Start newborn or restart yielding generator and perform the requested - * operation inside its frame. - */ -static JSBool -SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, - JSGenerator *gen, jsval arg, jsval *rval) -{ - JSStackFrame *fp; - jsval junk; - JSArena *arena; - JSBool ok; - - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - if (gen->state == JSGEN_OPEN) { - /* - * Store the argument to send as the result of the yield - * expression. - */ - gen->frame.sp[-1] = arg; - } - gen->state = JSGEN_RUNNING; - break; - - case JSGENOP_THROW: - JS_SetPendingException(cx, arg); - gen->state = JSGEN_RUNNING; - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - JS_SetPendingException(cx, JSVAL_ARETURN); - gen->state = JSGEN_CLOSING; - break; - } - - /* Extend the current stack pool with gen->arena. */ - arena = cx->stackPool.current; - JS_ASSERT(!arena->next); - JS_ASSERT(!gen->arena.next); - JS_ASSERT(cx->stackPool.current != &gen->arena); - cx->stackPool.current = arena->next = &gen->arena; - - /* Push gen->frame around the interpreter activation. */ - fp = cx->fp; - cx->fp = &gen->frame; - gen->frame.down = fp; - ok = js_Interpret(cx, gen->frame.pc, &junk); - cx->fp = fp; - gen->frame.down = NULL; - - /* Retract the stack pool and sanitize gen->arena. */ - JS_ASSERT(!gen->arena.next); - JS_ASSERT(arena->next == &gen->arena); - JS_ASSERT(cx->stackPool.current == &gen->arena); - cx->stackPool.current = arena; - arena->next = NULL; - - if (gen->frame.flags & JSFRAME_YIELDING) { - /* Yield cannot fail, throw or be called on closing. */ - JS_ASSERT(ok); - JS_ASSERT(!cx->throwing); - JS_ASSERT(gen->state == JSGEN_RUNNING); - JS_ASSERT(op != JSGENOP_CLOSE); - gen->frame.flags &= ~JSFRAME_YIELDING; - gen->state = JSGEN_OPEN; - *rval = gen->frame.rval; - return JS_TRUE; - } - - gen->state = JSGEN_CLOSED; - - if (ok) { - /* Returned, explicitly or by falling off the end. */ - if (op == JSGENOP_CLOSE) - return JS_TRUE; - return js_ThrowStopIteration(cx, obj); - } - - /* - * An error, silent termination by branch callback or an exception. - * Propagate the condition to the caller. - */ - return JS_FALSE; -} - -/* - * Execute gen's close hook after the GC detects that the object has become - * unreachable. - */ -JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen) -{ - /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ - return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL); -} - -/* - * Common subroutine of generator_(next|send|throw|close) methods. - */ -static JSBool -generator_op(JSContext *cx, JSGeneratorOp op, - JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSGenerator *gen; - JSString *str; - jsval arg; - - if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) - return JS_FALSE; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen == NULL) { - /* This happens when obj is the generator prototype. See bug 352885. */ - goto closed_generator; - } - - switch (gen->state) { - case JSGEN_NEWBORN: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_THROW: - break; - - case JSGENOP_SEND: - if (!JSVAL_IS_VOID(argv[0])) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - argv[0], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_SEND, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - } - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - gen->state = JSGEN_CLOSED; - return JS_TRUE; - } - break; - - case JSGEN_OPEN: - break; - - case JSGEN_RUNNING: - case JSGEN_CLOSING: - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], - JS_GetFunctionId(gen->frame.fun)); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_NESTING_GENERATOR, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - - default: - JS_ASSERT(gen->state == JSGEN_CLOSED); - - closed_generator: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - return js_ThrowStopIteration(cx, obj); - case JSGENOP_THROW: - JS_SetPendingException(cx, argv[0]); - return JS_FALSE; - default: - JS_ASSERT(op == JSGENOP_CLOSE); - return JS_TRUE; - } - } - - arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) - ? argv[0] - : JSVAL_VOID; - if (!SendToGenerator(cx, op, obj, gen, arg, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval); -} - -static JSBool -generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval); -} - -static JSBool -generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval); -} - -static JSBool -generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval); -} - -static JSFunctionSpec generator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -#endif /* JS_HAS_GENERATORS */ - -JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *stop; - - /* Idempotency required: we initialize several things, possibly lazily. */ - if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) - return NULL; - if (stop) - return stop; - - proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, - NULL, iterator_methods, NULL, NULL); - if (!proto) - return NULL; - proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - -#if JS_HAS_GENERATORS - /* Initialize the generator internals if configured. */ - if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, - NULL, generator_methods, NULL, NULL)) { - return NULL; - } -#endif - - return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, - NULL, NULL, NULL, NULL); -} diff --git a/lib/javascript/jsiter.h b/lib/javascript/jsiter.h deleted file mode 100644 index 1a99b6b0..00000000 --- a/lib/javascript/jsiter.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsiter_h___ -#define jsiter_h___ -/* - * JavaScript iterators. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ -#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ -#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ - -extern void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); - -extern void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj); - -/* - * Convert the value stored in *vp to its iteration object. The flags should - * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating - * for-in semantics are required, and when the caller can guarantee that the - * iterator will never be exposed to scripts. - */ -extern JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); - -/* - * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to - * JSVAL_HOLE. Otherwise set it to the result of the next call. - */ -extern JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); - -#if JS_HAS_GENERATORS - -/* - * Generator state codes. - */ -typedef enum JSGeneratorState { - JSGEN_NEWBORN, /* not yet started */ - JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ - JSGEN_RUNNING, /* currently executing via .next(), etc., call */ - JSGEN_CLOSING, /* close method is doing asynchronous return */ - JSGEN_CLOSED /* closed, cannot be started or closed again */ -} JSGeneratorState; - -struct JSGenerator { - JSGenerator *next; - JSObject *obj; - JSGeneratorState state; - JSStackFrame frame; - JSArena arena; - jsval stack[1]; -}; - -#define FRAME_TO_GENERATOR(fp) \ - ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame))) - -extern JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen); - -#endif - -extern JSClass js_GeneratorClass; -extern JSClass js_IteratorClass; -extern JSClass js_StopIterationClass; - -extern JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj); - -#endif /* jsiter_h___ */ diff --git a/lib/javascript/jskeyword.tbl b/lib/javascript/jskeyword.tbl deleted file mode 100644 index 49b9c6c8..00000000 --- a/lib/javascript/jskeyword.tbl +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(case, TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(continue, TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(default, TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(delete, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(do, TOK_DO, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(else, TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(export, TOK_EXPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) -JS_KEYWORD(for, TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(function, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(if, TOK_IF, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(in, TOK_IN, JSOP_IN, JSVERSION_DEFAULT) -JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) -JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) -JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) -JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) -JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) -JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) -JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) -JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) -#if JS_HAS_CONST -JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) -#else -JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(catch, TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(finally, TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) - -JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) - -#if JS_HAS_RESERVED_JAVA_KEYWORDS -JS_KEYWORD(abstract, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(boolean, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(byte, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(char, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(double, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(final, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(float, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(goto, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(implements, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(import, TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(int, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(interface, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(long, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(native, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(package, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(private, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(protected, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(public, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(short, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(static, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(synchronized,TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throws, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(transient, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(volatile, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_DEBUGGER_KEYWORD -JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT) -#elif JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_GENERATORS -JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_1_7) -#endif - -#if JS_HAS_BLOCK_SCOPE -JS_KEYWORD(let, TOK_LET, JSOP_NOP, JSVERSION_1_7) -#endif diff --git a/lib/javascript/jskwgen.c b/lib/javascript/jskwgen.c deleted file mode 100644 index 5ae39bd9..00000000 --- a/lib/javascript/jskwgen.c +++ /dev/null @@ -1,460 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is String Switch Generator for JavaScript Keywords, - * released 2005-12-09. - * - * The Initial Developer of the Original Code is - * Igor Bukanov. - * Portions created by the Initial Developer are Copyright (C) 2005-2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstddef.h" -#include -#include -#include -#include -#include -#include - -#include "jsconfig.h" - -const char * const keyword_list[] = { -#define JS_KEYWORD(keyword, type, op, version) #keyword, -#include "jskeyword.tbl" -#undef JS_KEYWORD -}; - -struct gen_opt { - FILE *output; /* output file for generated source */ - unsigned use_if_threshold; /* max number of choices to generate - "if" selector instead of "switch" */ - unsigned char_tail_test_threshold; /* max number of unprocessed columns - to use inlined char compare - for remaining chars and not generic - string compare code */ - unsigned indent_level; /* current source identation level */ -}; - -static unsigned column_to_compare; - -static int -length_comparator(const void *a, const void *b) -{ - const char *str1 = keyword_list[*(unsigned *)a]; - const char *str2 = keyword_list[*(unsigned *)b]; - return (int)strlen(str1) - (int)strlen(str2); -} - -static int -column_comparator(const void *a, const void *b) -{ - const char *str1 = keyword_list[*(unsigned *)a]; - const char *str2 = keyword_list[*(unsigned *)b]; - return (int)str1[column_to_compare] - (int)str2[column_to_compare]; -} - -static unsigned -count_different_lengths(unsigned indexes[], unsigned nelem) -{ - unsigned nlength, current_length, i, l; - - current_length = 0; - nlength = 0; - for (i = 0; i != nelem; ++i) { - l = (unsigned)strlen(keyword_list[indexes[i]]); - assert(l != 0); - if (current_length != l) { - ++nlength; - current_length = l; - } - } - return nlength; -} - -static void -find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column, - unsigned *span_result, unsigned *count_result) -{ - unsigned i, count; - unsigned char c, prev, minc, maxc; - - assert(nelem != 0); - minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column]; - count = 1; - for (i = 1; i != nelem; ++i) { - c = (unsigned char)keyword_list[indexes[i]][column]; - if (prev != c) { - prev = c; - ++count; - if (minc > c) { - minc = c; - } else if (maxc < c) { - maxc = c; - } - } - } - - *span_result = maxc - minc + 1; - *count_result = count; -} - -static unsigned -find_optimal_switch_column(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned columns[], unsigned unprocessed_columns, - int *use_if_result) -{ - unsigned i; - unsigned span, min_span, min_span_index; - unsigned nchar, min_nchar, min_nchar_index; - - assert(unprocessed_columns != 0); - i = 0; - min_nchar = min_span = (unsigned)-1; - min_nchar_index = min_span_index = 0; - do { - column_to_compare = columns[i]; - qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); - find_char_span_and_count(indexes, nelem, column_to_compare, - &span, &nchar); - assert(span != 0); - if (span == 1) { - assert(nchar == 1); - *use_if_result = 1; - return 1; - } - assert(nchar != 1); - if (min_span > span) { - min_span = span; - min_span_index = i; - } - if (min_nchar > nchar) { - min_nchar = nchar; - min_nchar_index = i; - } - } while (++i != unprocessed_columns); - - if (min_nchar <= opt->use_if_threshold) { - *use_if_result = 1; - i = min_nchar_index; - } else { - *use_if_result = 0; - i = min_span_index; - } - - /* - * Restore order corresponding to i if it was destroyed by - * subsequent sort. - */ - if (i != unprocessed_columns - 1) { - column_to_compare = columns[i]; - qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); - } - - return i; -} - - -static void -p(struct gen_opt *opt, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vfprintf(opt->output, format, ap); - va_end(ap); -} - -/* Size for '\xxx' where xxx is octal escape */ -#define MIN_QUOTED_CHAR_BUFFER 7 - -static char * -qchar(char c, char *quoted_buffer) -{ - char *s; - - s = quoted_buffer; - *s++ = '\''; - switch (c) { - case '\n': c = 'n'; goto one_char_escape; - case '\r': c = 'r'; goto one_char_escape; - case '\t': c = 't'; goto one_char_escape; - case '\f': c = 't'; goto one_char_escape; - case '\0': c = '0'; goto one_char_escape; - case '\'': goto one_char_escape; - one_char_escape: - *s++ = '\\'; - break; - default: - if (!isprint(c)) { - *s++ = '\\'; - *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6))); - *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3))); - c = (char)('0' + (0x7 & ((unsigned char)c))); - } - } - *s++ = c; - *s++ = '\''; - *s = '\0'; - assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER); - return quoted_buffer; -} - -static void -nl(struct gen_opt *opt) -{ - putc('\n', opt->output); -} - -static void -indent(struct gen_opt *opt) -{ - unsigned n = opt->indent_level; - while (n != 0) { - --n; - fputs(" ", opt->output); - } -} - -static void -line(struct gen_opt *opt, const char *format, ...) -{ - va_list ap; - - indent(opt); - va_start(ap, format); - vfprintf(opt->output, format, ap); - va_end(ap); - nl(opt); -} - -static void -generate_letter_switch_r(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned columns[], unsigned unprocessed_columns) -{ - char qbuf[MIN_QUOTED_CHAR_BUFFER]; - - assert(nelem != 0); - if (nelem == 1) { - unsigned kw_index = indexes[0]; - const char *keyword = keyword_list[kw_index]; - - if (unprocessed_columns == 0) { - line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); - } else if (unprocessed_columns > opt->char_tail_test_threshold) { - line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword); - } else { - unsigned i, column; - - indent(opt); p(opt, "if ("); - for (i = 0; i != unprocessed_columns; ++i) { - column = columns[i]; - qchar(keyword[column], qbuf); - p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ", - column, qbuf); - } - p(opt, ") {"); nl(opt); - ++opt->indent_level; - line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); - --opt->indent_level; - line(opt, "}"); - line(opt, "JSKW_NO_MATCH()"); - } - } else { - unsigned optimal_column_index, optimal_column; - unsigned i; - int use_if; - char current; - - assert(unprocessed_columns != 0); - optimal_column_index = find_optimal_switch_column(opt, indexes, nelem, - columns, - unprocessed_columns, - &use_if); - optimal_column = columns[optimal_column_index]; - columns[optimal_column_index] = columns[unprocessed_columns - 1]; - - if (!use_if) - line(opt, "switch (JSKW_AT(%u)) {", optimal_column); - - current = keyword_list[indexes[0]][optimal_column]; - for (i = 0; i != nelem;) { - unsigned same_char_begin = i; - char next = current; - - for (++i; i != nelem; ++i) { - next = keyword_list[indexes[i]][optimal_column]; - if (next != current) - break; - } - qchar(current, qbuf); - if (use_if) { - line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf); - } else { - line(opt, " case %s:", qbuf); - } - ++opt->indent_level; - generate_letter_switch_r(opt, indexes + same_char_begin, - i - same_char_begin, - columns, unprocessed_columns - 1); - --opt->indent_level; - if (use_if) { - line(opt, "}"); - } - current = next; - } - - if (!use_if) { - line(opt, "}"); - } - - columns[optimal_column_index] = optimal_column; - - line(opt, "JSKW_NO_MATCH()"); - } -} - -static void -generate_letter_switch(struct gen_opt *opt, - unsigned indexes[], unsigned nelem, - unsigned current_length) -{ - unsigned *columns; - unsigned i; - - columns = malloc(sizeof(columns[0]) * current_length); - if (!columns) { - perror("malloc"); - exit(EXIT_FAILURE); - } - for (i = 0; i != current_length; ++i) { - columns[i] = i; - } - generate_letter_switch_r(opt, indexes, nelem, columns, current_length); - free(columns); -} - - -static void -generate_switch(struct gen_opt *opt) -{ - unsigned *indexes; - unsigned nlength; - unsigned i, current; - int use_if; - unsigned nelem; - - nelem = sizeof(keyword_list)/sizeof(keyword_list[0]); - - line(opt, "/*"); - line(opt, " * Generating switch for the list of %u entries:", nelem); - for (i = 0; i != nelem; ++i) { - line(opt, " * %s", keyword_list[i]); - } - line(opt, " */"); - - indexes = malloc(sizeof(indexes[0]) * nelem); - if (!indexes) { - perror("malloc"); - exit(EXIT_FAILURE); - } - for (i = 0; i != nelem; ++i) - indexes[i] = i; - qsort(indexes, nelem, sizeof(indexes[i]), length_comparator); - nlength = count_different_lengths(indexes, nelem); - - use_if = (nlength <= opt->use_if_threshold); - - if (!use_if) - line(opt, "switch (JSKW_LENGTH()) {"); - - current = (unsigned)strlen(keyword_list[indexes[0]]); - for (i = 0; i != nelem;) { - unsigned same_length_begin = i; - unsigned next = current; - - for (++i; i != nelem; ++i) { - next = (unsigned)strlen(keyword_list[indexes[i]]); - if (next != current) - break; - } - if (use_if) { - line(opt, "if (JSKW_LENGTH() == %u) {", current); - } else { - line(opt, " case %u:", current); - } - ++opt->indent_level; - generate_letter_switch(opt, indexes + same_length_begin, - i - same_length_begin, - current); - --opt->indent_level; - if (use_if) { - line(opt, "}"); - } - current = next; - } - if (!use_if) - line(opt, "}"); - line(opt, "JSKW_NO_MATCH()"); - free(indexes); -} - -int main(int argc, char **argv) -{ - struct gen_opt opt; - - if (argc < 2) { - opt.output = stdout; - } else { - opt.output = fopen(argv[1], "w"); - if (!opt.output) { - perror("fopen"); - exit(EXIT_FAILURE); - } - } - opt.indent_level = 1; - opt.use_if_threshold = 3; - opt.char_tail_test_threshold = 4; - - generate_switch(&opt); - - if (opt.output != stdout) { - if (fclose(opt.output)) { - perror("fclose"); - exit(EXIT_FAILURE); - } - } - - return EXIT_SUCCESS; -} diff --git a/lib/javascript/jslibmath.h b/lib/javascript/jslibmath.h index 3f75f30b..544d8f80 100644 --- a/lib/javascript/jslibmath.h +++ b/lib/javascript/jslibmath.h @@ -50,15 +50,36 @@ #include "jsconfig.h" /* - * Define on which platforms to use fdlibm. Not used by default under - * assumption that native math library works unless proved guilty. - * Plus there can be problems with endian-ness and such in fdlibm itself. - * - * fdlibm compatibility notes: - * - fdlibm broken on OSF1/alpha + * Define which platforms on which to use fdlibm. Not used + * by default since there can be problems with endian-ness and such. */ -#ifndef JS_USE_FDLIBM_MATH +#if defined(_WIN32) && !defined(__MWERKS__) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(SUNOS4) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(IRIX) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(SOLARIS) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(HPUX) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(linux) +#define JS_USE_FDLIBM_MATH 1 + +#elif defined(OSF1) +/* Want to use some fdlibm functions but fdlibm broken on OSF1/alpha. */ +#define JS_USE_FDLIBM_MATH 0 + +#elif defined(AIX) +#define JS_USE_FDLIBM_MATH 1 + +#else #define JS_USE_FDLIBM_MATH 0 #endif @@ -73,24 +94,7 @@ #define fd_atan atan #define fd_atan2 atan2 #define fd_ceil ceil - -/* The right copysign function is not always named the same thing. */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define fd_copysign __builtin_copysign -#elif defined WINCE -#define fd_copysign _copysign -#elif defined _WIN32 -#if _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -#define fd_copysign js_copysign -extern double js_copysign(double, double); -#else -#define fd_copysign _copysign -#endif -#else #define fd_copysign copysign -#endif - #define fd_cos cos #define fd_exp exp #define fd_fabs fabs @@ -196,6 +200,26 @@ extern double fd_asin __P((double)); extern double fd_atan __P((double)); extern double fd_copysign __P((double, double)); +#elif defined(linux) + +#define fd_atan atan +#define fd_atan2 atan2 +#define fd_ceil ceil +#define fd_cos cos +#define fd_fabs fabs +#define fd_floor floor +#define fd_fmod fmod +#define fd_sin sin +#define fd_sqrt sqrt +#define fd_tan tan +#define fd_copysign copysign + +extern double fd_asin __P((double)); +extern double fd_acos __P((double)); +extern double fd_exp __P((double)); +extern double fd_log __P((double)); +extern double fd_pow __P((double, double)); + #elif defined(OSF1) #define fd_acos acos diff --git a/lib/javascript/jslock.c b/lib/javascript/jslock.c index 48550099..968e98b8 100644 --- a/lib/javascript/jslock.c +++ b/lib/javascript/jslock.c @@ -45,6 +45,7 @@ #include "jsstddef.h" #include #include "jspubtd.h" +#include "prthread.h" #include "jsutil.h" /* Added by JSIFY */ #include "jstypes.h" #include "jsbit.h" @@ -118,44 +119,6 @@ js_CompareAndSwap(jsword *w, jsword ov, jsword nv) return (int)res; } -#elif (defined(__USLC__) || defined(_SCO_DS)) && defined(i386) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ - -asm int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ -%ureg w, nv; - movl ov,%eax - lock - cmpxchgl nv,(w) - sete %al - andl $1,%eax -%ureg w; mem ov, nv; - movl ov,%eax - movl nv,%ecx - lock - cmpxchgl %ecx,(w) - sete %al - andl $1,%eax -%ureg nv; - movl ov,%eax - movl w,%edx - lock - cmpxchgl nv,(%edx) - sete %al - andl $1,%eax -%mem w, ov, nv; - movl ov,%eax - movl nv,%ecx - movl w,%edx - lock - cmpxchgl %ecx,(%edx) - sete %al - andl $1,%eax -} -#pragma asm full_optimization js_CompareAndSwap - #elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC) static JS_INLINE int @@ -200,6 +163,12 @@ js_CompareAndSwap(jsword *w, jsword ov, jsword nv) #endif /* !NSPR_LOCK */ +jsword +js_CurrentThreadId() +{ + return CurrentThreadId(); +} + void js_InitLock(JSThinLock *tl) { @@ -347,7 +316,7 @@ ShareScope(JSRuntime *rt, JSScope *scope) * scope->ownercx's transition to null against tests of that member * in ClaimScope. */ - scope->lock.owner = CX_THINLOCK_ID(scope->ownercx); + scope->lock.owner = scope->ownercx->thread; #ifdef NSPR_LOCK JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat); #endif @@ -606,8 +575,8 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) #ifndef NSPR_LOCK tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + me = cx->thread; + JS_ASSERT(me == CurrentThreadId()); if (js_CompareAndSwap(&tl->owner, 0, me)) { /* * Got the lock with one compare-and-swap. Even so, someone else may @@ -697,8 +666,8 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) #ifndef NSPR_LOCK tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + me = cx->thread; + JS_ASSERT(me == CurrentThreadId()); if (js_CompareAndSwap(&tl->owner, 0, me)) { if (scope == OBJ_SCOPE(obj)) { obj->slots[slot] = v; @@ -902,6 +871,13 @@ js_CleanupLocks() #endif /* !NSPR_LOCK */ } +void +js_InitContextForLocking(JSContext *cx) +{ + cx->thread = CurrentThreadId(); + JS_ASSERT(Thin_GetWait(cx->thread) == 0); +} + #ifndef NSPR_LOCK /* @@ -1022,7 +998,7 @@ js_Dequeue(JSThinLock *tl) JS_INLINE void js_Lock(JSThinLock *tl, jsword me) { - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + JS_ASSERT(me == CurrentThreadId()); if (js_CompareAndSwap(&tl->owner, 0, me)) return; if (Thin_RemoveWait(ReadWord(tl->owner)) != me) @@ -1036,22 +1012,14 @@ js_Lock(JSThinLock *tl, jsword me) JS_INLINE void js_Unlock(JSThinLock *tl, jsword me) { - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - - /* - * Only me can hold the lock, no need to use compare and swap atomic - * operation for this common case. - */ - if (tl->owner == me) { - tl->owner = 0; + JS_ASSERT(me == CurrentThreadId()); + if (js_CompareAndSwap(&tl->owner, me, 0)) return; - } - JS_ASSERT(Thin_GetWait(tl->owner)); if (Thin_RemoveWait(ReadWord(tl->owner)) == me) js_Dequeue(tl); #ifdef DEBUG else - JS_ASSERT(0); /* unbalanced unlock */ + JS_ASSERT(0); #endif } @@ -1062,7 +1030,7 @@ js_LockRuntime(JSRuntime *rt) { PR_Lock(rt->rtLock); #ifdef DEBUG - rt->rtLockOwner = js_CurrentThreadId(); + rt->rtLockOwner = CurrentThreadId(); #endif } @@ -1078,9 +1046,9 @@ js_UnlockRuntime(JSRuntime *rt) void js_LockScope(JSContext *cx, JSScope *scope) { - jsword me = CX_THINLOCK_ID(cx); + jsword me = cx->thread; - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); + JS_ASSERT(me == CurrentThreadId()); JS_ASSERT(scope->ownercx != cx); if (CX_THREAD_IS_RUNNING_GC(cx)) return; @@ -1103,7 +1071,7 @@ js_LockScope(JSContext *cx, JSScope *scope) void js_UnlockScope(JSContext *cx, JSScope *scope) { - jsword me = CX_THINLOCK_ID(cx); + jsword me = cx->thread; /* We hope compilers use me instead of reloading cx->thread in the macro. */ if (CX_THREAD_IS_RUNNING_GC(cx)) @@ -1215,7 +1183,7 @@ js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) LOGIT(oldscope, '0'); oldscope->u.count = 0; tl = &oldscope->lock; - me = CX_THINLOCK_ID(cx); + me = cx->thread; JS_UNLOCK0(tl, me); } @@ -1225,15 +1193,6 @@ js_LockObj(JSContext *cx, JSObject *obj) JSScope *scope; JS_ASSERT(OBJ_IS_NATIVE(obj)); - - /* - * We must test whether the GC is calling and return without mutating any - * state, especially cx->lockedSealedScope. Note asymmetry with respect to - * js_UnlockObj, which is a thin-layer on top of js_UnlockScope. - */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - for (;;) { scope = OBJ_SCOPE(obj); if (SCOPE_IS_SEALED(scope) && scope->object == obj && @@ -1265,7 +1224,7 @@ js_UnlockObj(JSContext *cx, JSObject *obj) JSBool js_IsRuntimeLocked(JSRuntime *rt) { - return js_CurrentThreadId() == rt->rtLockOwner; + return CurrentThreadId() == rt->rtLockOwner; } JSBool @@ -1292,11 +1251,10 @@ js_IsScopeLocked(JSContext *cx, JSScope *scope) * a thin or fat lock to cope with shared (concurrent) ownership. */ if (scope->ownercx) { - JS_ASSERT(scope->ownercx == cx || scope->ownercx->thread == cx->thread); + JS_ASSERT(scope->ownercx == cx); return JS_TRUE; } - return js_CurrentThreadId() == - ((JSThread *)Thin_RemoveWait(ReadWord(scope->lock.owner)))->id; + return CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner)); } #endif /* DEBUG */ diff --git a/lib/javascript/jslock.h b/lib/javascript/jslock.h index f9ed03db..0ab71927 100644 --- a/lib/javascript/jslock.h +++ b/lib/javascript/jslock.h @@ -45,7 +45,6 @@ #include "pratom.h" #include "prlock.h" #include "prcvar.h" -#include "prthread.h" #include "jsprvtd.h" /* for JSScope, etc. */ #include "jspubtd.h" /* for JSRuntime, etc. */ @@ -69,9 +68,6 @@ typedef struct JSThinLock { JSFatLock *fat; } JSThinLock; -#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread) -#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) - typedef PRLock JSLock; typedef struct JSFatLockTable { @@ -87,7 +83,8 @@ typedef struct JSFatLockTable { #define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p)) #define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) -#define js_CurrentThreadId() (jsword)PR_GetCurrentThread() +#define CurrentThreadId() (jsword)PR_GetCurrentThread() +#define JS_CurrentThreadId() js_CurrentThreadId() #define JS_NEW_LOCK() PR_NewLock() #define JS_DESTROY_LOCK(l) PR_DestroyLock(l) #define JS_ACQUIRE_LOCK(l) PR_Lock(l) @@ -135,6 +132,7 @@ typedef struct JSFatLockTable { #define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ js_TransferScopeLock(cx, scope, newscope) +extern jsword js_CurrentThreadId(); extern void js_LockRuntime(JSRuntime *rt); extern void js_UnlockRuntime(JSRuntime *rt); extern void js_LockObj(JSContext *cx, JSObject *obj); @@ -143,6 +141,7 @@ extern void js_LockScope(JSContext *cx, JSScope *scope); extern void js_UnlockScope(JSContext *cx, JSScope *scope); extern int js_SetupLocks(int,int); extern void js_CleanupLocks(); +extern void js_InitContextForLocking(JSContext *); extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); extern JS_FRIEND_API(jsval) js_GetSlotThreadSafe(JSContext *, JSObject *, uint32); @@ -182,13 +181,9 @@ extern JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope); JS_LOCK_RUNTIME_VOID(_rt, e); \ JS_END_MACRO -/* FIXME: bug 353962 hackaround */ -#define JS_USE_ONLY_NSPR_LOCKS 1 - #if defined(JS_USE_ONLY_NSPR_LOCKS) || \ !( (defined(_WIN32) && defined(_M_IX86)) || \ (defined(__GNUC__) && defined(__i386__)) || \ - ((defined(__USLC__) || defined(_SCO_DS)) && defined(i386)) || \ (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) || \ defined(AIX) ) @@ -260,7 +255,7 @@ extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); JS_NO_TIMEOUT) #define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) -#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX)) -#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX)) +#define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread) +#define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread) #endif /* jslock_h___ */ diff --git a/lib/javascript/jslog2.c b/lib/javascript/jslog2.c index 876e5285..9bfca4dd 100644 --- a/lib/javascript/jslog2.c +++ b/lib/javascript/jslog2.c @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -38,16 +38,26 @@ #include "jsstddef.h" #include "jsbit.h" -#include "jsutil.h" /* ** Compute the log of the least power of 2 greater than or equal to n */ JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) { - JSIntn log2; + JSIntn log2 = 0; - JS_CEILING_LOG2(log2, n); + if (n & (n-1)) + log2++; + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; return log2; } @@ -57,38 +67,17 @@ JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) */ JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n) { - JSIntn log2; + JSIntn log2 = 0; - JS_FLOOR_LOG2(log2, n); + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; return log2; } - -/* - * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. - */ -#if !defined(JS_HAS_GCC_BUILTIN_CLZ) && JS_BYTES_PER_WORD == 8 - -JSUword -js_FloorLog2wImpl(JSUword n) -{ - JSUword log2, m; - - JS_ASSERT(n != 0); - - log2 = 0; - m = n >> 32; - if (m != 0) { n = m; log2 = 32; } - m = n >> 16; - if (m != 0) { n = m; log2 |= 16; } - m = n >> 8; - if (m != 0) { n = m; log2 |= 8; } - m = n >> 4; - if (m != 0) { n = m; log2 |= 4; } - m = n >> 2; - if (m != 0) { n = m; log2 |= 2; } - log2 |= (n >> 1); - - return log2; -} - -#endif diff --git a/lib/javascript/jsmath.c b/lib/javascript/jsmath.c index 20629163..19005eaf 100644 --- a/lib/javascript/jsmath.c +++ b/lib/javascript/jsmath.c @@ -92,9 +92,9 @@ static JSConstDoubleSpec math_constants[] = { {0,0,0,{0,0,0}} }; -JSClass js_MathClass = { - js_Math_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Math), +static JSClass math_class = { + "Math", + 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS @@ -153,21 +153,6 @@ math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; -#if !JS_USE_FDLIBM_MATH && defined(_MSC_VER) - /* - * MSVC's atan2 does not yield the result demanded by ECMA when both x - * and y are infinite. - * - The result is a multiple of pi/4. - * - The sign of x determines the sign of the result. - * - The sign of y determines the multiplicator, 1 or 3. - */ - if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) { - z = fd_copysign(M_PI / 4, x); - if (y < 0) - z *= 3; - return js_NewDoubleValue(cx, z, rval); - } -#endif z = fd_atan2(x, y); return js_NewNumberValue(cx, z, rval); } @@ -256,7 +241,7 @@ math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } - if (x == 0 && x == z && fd_copysign(1.0, z) == -1) + if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1)) z = x; else z = (x > z) ? x : z; @@ -281,7 +266,7 @@ math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } - if (x == 0 && x == z && fd_copysign(1.0,x) == -1) + if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1)) z = x; else z = (x < z) ? x : z; @@ -307,11 +292,6 @@ math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } - /* pow(x, +-0) is always 1, even for x = NaN. */ - if (y == 0) { - *rval = JSVAL_ONE; - return JS_TRUE; - } #endif z = fd_pow(x, y); return js_NewNumberValue(cx, z, rval); @@ -404,21 +384,6 @@ math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return js_NewNumberValue(cx, z, rval); } -#if defined _WIN32 && !defined WINCE && _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -double -js_copysign(double x, double y) -{ - jsdpun xu, yu; - - xu.d = x; - yu.d = y; - xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT; - xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT; - return xu.d; -} -#endif - static JSBool math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -468,7 +433,7 @@ static JSBool math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - *rval = ATOM_KEY(CLASS_ATOM(cx, Math)); + *rval = ATOM_KEY(cx->runtime->atomState.MathAtom); return JS_TRUE; } #endif @@ -503,7 +468,7 @@ js_InitMathClass(JSContext *cx, JSObject *obj) { JSObject *Math; - Math = JS_DefineObject(cx, obj, js_Math_str, &js_MathClass, NULL, 0); + Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0); if (!Math) return NULL; if (!JS_DefineFunctions(cx, Math, math_static_methods)) diff --git a/lib/javascript/jsmath.h b/lib/javascript/jsmath.h index 1f60630b..b67dba1d 100644 --- a/lib/javascript/jsmath.h +++ b/lib/javascript/jsmath.h @@ -47,8 +47,6 @@ JS_BEGIN_EXTERN_C -extern JSClass js_MathClass; - extern JSObject * js_InitMathClass(JSContext *cx, JSObject *obj); diff --git a/lib/javascript/jsnum.c b/lib/javascript/jsnum.c index 987619db..8a559630 100644 --- a/lib/javascript/jsnum.c +++ b/lib/javascript/jsnum.c @@ -154,16 +154,16 @@ const char js_parseFloat_str[] = "parseFloat"; const char js_parseInt_str[] = "parseInt"; static JSFunctionSpec number_functions[] = { - {js_isNaN_str, num_isNaN, 1,0,0}, - {js_isFinite_str, num_isFinite, 1,0,0}, - {js_parseFloat_str, num_parseFloat, 1,0,0}, - {js_parseInt_str, num_parseInt, 2,0,0}, + {"isNaN", num_isNaN, 1,0,0}, + {"isFinite", num_isFinite, 1,0,0}, + {"parseFloat", num_parseFloat, 1,0,0}, + {"parseInt", num_parseInt, 2,0,0}, {0,0,0,0,0} }; JSClass js_NumberClass = { - js_Number_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), + "Number", + JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS @@ -201,14 +201,10 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[64]; JSString *str; - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - 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)); - } + 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)); d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); if (!numStr) { @@ -260,14 +256,10 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsint base; JSString *str; - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - 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)); - } + 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)); d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); base = 10; if (argc != 0) { @@ -281,9 +273,9 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; } } - if (base == 10) { + if (base == 10) str = js_NumberToString(cx, d); - } else { + else { char *dStr = JS_dtobasestr(base, d); if (!dStr) { JS_ReportOutOfMemory(cx); @@ -317,7 +309,7 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, return JS_FALSE; JS_ASSERT(JSVAL_IS_STRING(*rval)); numStr = JSVAL_TO_STRING(*rval); - num = js_GetStringBytes(cx->runtime, numStr); + num = js_GetStringBytes(numStr); /* Find bit before the decimal. */ dec = strchr(num, '.'); @@ -379,9 +371,6 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, *tmpDest++ = '\0'; } - if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - str = JS_NewString(cx, buf, size); if (!str) { JS_free(cx, buf); @@ -396,10 +385,6 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, static JSBool num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (JSVAL_IS_NUMBER((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); @@ -407,6 +392,7 @@ num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } +#if JS_HAS_NUMBER_FORMATS #define MAX_PRECISION 100 static JSBool @@ -418,14 +404,10 @@ 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 (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - 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)); - } + 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)); d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); if (JSVAL_IS_VOID(argv[0])) { @@ -477,17 +459,21 @@ num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rv /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0); } +#endif /* JS_HAS_NUMBER_FORMATS */ + static JSFunctionSpec number_methods[] = { #if JS_HAS_TOSOURCE - {js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER,0}, + {js_toSource_str, num_toSource, 0,0,0}, +#endif + {js_toString_str, num_toString, 0,0,0}, + {js_toLocaleString_str, num_toLocaleString, 0,0,0}, + {js_valueOf_str, num_valueOf, 0,0,0}, +#if JS_HAS_NUMBER_FORMATS + {"toFixed", num_toFixed, 1,0,0}, + {"toExponential", num_toExponential, 1,0,0}, + {"toPrecision", num_toPrecision, 1,0,0}, #endif - {js_toString_str, num_toString, 0,JSFUN_THISP_NUMBER,0}, - {js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER,0}, - {js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER,0}, - {"toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER,0}, - {"toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER,0}, - {"toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER,0}, {0,0,0,0,0} }; @@ -517,6 +503,7 @@ static JSConstDoubleSpec number_constants[] = { static jsdouble NaN; + #if (defined XP_WIN || defined XP_OS2) && \ !defined WINCE && \ !defined __MWERKS__ && \ @@ -693,7 +680,7 @@ js_NumberToObject(JSContext *cx, jsdouble d) if (!obj) return NULL; if (!js_NewNumberValue(cx, d, &v)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); @@ -707,9 +694,9 @@ js_NumberToString(JSContext *cx, jsdouble d) char buf[DTOSTR_STANDARD_BUFFER_SIZE]; char *numStr; - if (JSDOUBLE_IS_INT(d, i)) { + if (JSDOUBLE_IS_INT(d, i)) numStr = IntToString(i, buf, sizeof buf); - } else { + else { numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); if (!numStr) { JS_ReportOutOfMemory(cx); @@ -760,8 +747,19 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) } else if (JSVAL_IS_BOOLEAN(v)) { *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; } else { +#if JS_BUG_FALLIBLE_TONUM + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); +badstr: + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN, + JS_GetStringBytes(str)); + + } + return JS_FALSE; +#else badstr: *dp = *cx->runtime->jsNaN; +#endif } return JS_TRUE; } diff --git a/lib/javascript/jsobj.c b/lib/javascript/jsobj.c index b552acaf..03a59809 100644 --- a/lib/javascript/jsobj.c +++ b/lib/javascript/jsobj.c @@ -46,7 +46,6 @@ #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ -#include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsdhash.h" @@ -63,7 +62,6 @@ #include "jslock.h" #include "jsnum.h" #include "jsobj.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" @@ -71,18 +69,10 @@ #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - #ifdef JS_THREADSAFE #define NATIVE_DROP_PROPERTY js_DropProperty @@ -109,7 +99,7 @@ JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { JSClass js_ObjectClass = { js_Object_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), + 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS @@ -178,10 +168,7 @@ obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) pobj = JSVAL_TO_OBJECT(*vp); if (pobj) { clasp = OBJ_GET_CLASS(cx, pobj); - if (clasp == &js_CallClass || clasp == &js_BlockClass) { - /* Censor activations and lexical scopes per ECMA-262. */ - *vp = JSVAL_NULL; - } else if (clasp->flags & JSCLASS_IS_EXTENDED) { + if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; if (xclasp->outerObject) { pobj = xclasp->outerObject(cx, pobj); @@ -205,17 +192,6 @@ obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_OBJECT(*vp)) return JS_TRUE; pobj = JSVAL_TO_OBJECT(*vp); - - if (pobj) { - /* - * Innerize pobj here to avoid sticking unwanted properties on the - * outer object. This ensures that any with statements only grant - * access to the inner object. - */ - OBJ_TO_INNER_OBJECT(cx, pobj); - if (!pobj) - return JS_FALSE; - } slot = (uint32) JSVAL_TO_INT(id); if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) return JS_FALSE; @@ -455,15 +431,15 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) 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; + ++map->depth; ida = JS_Enumerate(cx, obj); - --map->depth; + --map->depth; if (!ida) return NULL; @@ -663,7 +639,7 @@ js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) 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"); + GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry", NULL); return JS_DHASH_NEXT; } @@ -698,7 +674,7 @@ js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map) #define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */ -#if JS_HAS_TOSOURCE +#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE JSBool js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -718,7 +694,6 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uintN attrs; #endif jsval *val; - JSString *gsopold[2]; JSString *gsop[2]; JSAtom *atom; JSString *idstr, *valstr, *str; @@ -729,8 +704,11 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; } - /* If outermost, we need parentheses to be an expression, not a block. */ - outermost = (cx->sharpObjectMap.depth == 0); + /* + * obj_toString for 1.2 calls toSource, and doesn't want the extra parens + * on the outside. + */ + outermost = !JS_VERSION_IS_1_2(cx) && cx->sharpObjectMap.depth == 0; he = js_EnterSharpObject(cx, obj, &ida, &chars); if (!he) return JS_FALSE; @@ -833,37 +811,14 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, val = argv + 2; for (i = 0, length = ida->length; i < length; i++) { - JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; - /* Get strings for id and value and GC-root them via argv. */ id = ida->vector[i]; #if JS_HAS_GETTER_SETTER + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto error; -#endif - - /* - * Convert id to a jsval and then to a string. Decide early whether we - * prefer get/set or old getter/setter syntax. - */ - atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; - idstr = js_ValueToString(cx, ID_TO_VALUE(id)); - if (!idstr) { - ok = JS_FALSE; - OBJ_DROP_PROPERTY(cx, obj2, prop); - goto error; - } - *rval = STRING_TO_JSVAL(idstr); /* local root */ - idIsLexicalIdentifier = js_IsIdentifier(idstr); - needOldStyleGetterSetter = - !idIsLexicalIdentifier || - js_CheckKeyword(JSSTRING_CHARS(idstr), - JSSTRING_LENGTH(idstr)) != TOK_EOF; - -#if JS_HAS_GETTER_SETTER - valcnt = 0; if (prop) { ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); @@ -875,24 +830,29 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { if (attrs & JSPROP_GETTER) { val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; - gsopold[valcnt] = +#ifdef OLD_GETTER_SETTER + gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getterAtom); +#else gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom); +#endif valcnt++; } if (attrs & JSPROP_SETTER) { val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; - gsopold[valcnt] = +#ifdef OLD_GETTER_SETTER + gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setterAtom); +#else gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom); +#endif valcnt++; } } else { valcnt = 1; gsop[0] = NULL; - gsopold[0] = NULL; ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); } OBJ_DROP_PROPERTY(cx, obj2, prop); @@ -900,15 +860,8 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, #else /* !JS_HAS_GETTER_SETTER */ - /* - * We simplify the source code at the price of minor dead code bloat in - * the ECMA version (for testing only, see jsconfig.h). The null - * default values in gsop[j] suffice to disable non-ECMA getter and - * setter code. - */ valcnt = 1; gsop[0] = NULL; - gsopold[0] = NULL; ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); #endif /* !JS_HAS_GETTER_SETTER */ @@ -916,12 +869,23 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!ok) goto error; + /* Convert id to a jsval and then to a string. */ + 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; + } + *rval = STRING_TO_JSVAL(idstr); /* local root */ + /* - * If id is a string that's not an identifier, then it needs to be - * quoted. Also, negative integer ids must be quoted. + * If id is a string that's a reserved identifier, or else id is not + * an identifier at all, then it needs to be quoted. Also, negative + * integer ids must be quoted. */ if (atom - ? !idIsLexicalIdentifier + ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr)) : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { idstr = js_QuoteString(cx, idstr, (jschar)'\''); if (!idstr) { @@ -944,33 +908,16 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, vchars = JSSTRING_CHARS(valstr); vlength = JSSTRING_LENGTH(valstr); - if (vchars[0] == '#') - needOldStyleGetterSetter = JS_TRUE; - - if (needOldStyleGetterSetter) - gsop[j] = gsopold[j]; - #ifndef OLD_GETTER_SETTER /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ - if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && - !needOldStyleGetterSetter) { - const jschar *start = vchars; - if (vchars[0] == '(') - vchars++; - vchars = js_strchr_limit(vchars, '(', vchars + vlength); - if (vchars) { - vlength -= vchars - start + 1; - } else { - gsop[j] = NULL; - vchars = start; - } + if (gsop[j]) { + int n = strlen(js_function_str) + 2; + vchars += n; + vlength -= n + 1; } -#else - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; #endif /* If val[j] is a non-sharp object, consider sharpening it. */ @@ -987,14 +934,10 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (IS_SHARP(he)) { vchars = vsharp; vlength = js_strlen(vchars); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; } else { if (vsharp) { vsharplength = js_strlen(vsharp); MAKE_SHARP(he); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; } js_LeaveSharpObject(cx, NULL); } @@ -1017,7 +960,6 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); SAFE_ADD(vsharplength); SAFE_ADD(vlength); - /* Account for the trailing null. */ SAFE_ADD((outermost ? 2 : 1) + 1); #undef SAFE_ADD @@ -1040,31 +982,28 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } comma = ", "; - if (needOldStyleGetterSetter) { - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - if (gsop[j]) { - chars[nchars++] = ' '; - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - } - chars[nchars++] = ':'; - } else { /* New style "decompilation" */ - if (gsop[j]) { - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - chars[nchars++] = ' '; - } - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - /* Extraneous space after id here will be extracted later */ - chars[nchars++] = gsop[j] ? ' ' : ':'; +#ifdef OLD_GETTER_SETTER + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + if (gsop[j]) { + chars[nchars++] = ' '; + gsoplength = JSSTRING_LENGTH(gsop[j]); + js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); + nchars += gsoplength; } - + chars[nchars++] = ':'; +#else + if (gsop[j]) { + gsoplength = JSSTRING_LENGTH(gsop[j]); + js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); + nchars += gsoplength; + chars[nchars++] = ' '; + } + js_strncpy(&chars[nchars], idstrchars, idstrlength); + nchars += idstrlength; + if (!gsop[j]) + chars[nchars++] = ':'; +#endif if (vsharplength) { js_strncpy(&chars[nchars], vsharp, vsharplength); nchars += vsharplength; @@ -1114,7 +1053,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, chars = NULL; goto error; } -#endif /* JS_HAS_TOSOURCE */ +#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */ JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -1125,6 +1064,11 @@ js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, const char *clazz, *prefix; JSString *str; +#if JS_HAS_INITIALIZERS + if (JS_VERSION_IS_1_2(cx)) + return js_obj_toSource(cx, obj, argc, argv, rval); +#endif + clazz = OBJ_GET_CLASS(cx, obj)->name; nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); @@ -1177,22 +1121,18 @@ obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller) + JSPrincipals *principals, const char *caller) { JSRuntime *rt; JSPrincipals *scopePrincipals; - const char *callerstr; rt = cx->runtime; if (rt->findObjectPrincipals) { scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); if (!principals || !scopePrincipals || !principals->subsume(principals, scopePrincipals)) { - callerstr = js_AtomToPrintableString(cx, caller); - if (!callerstr) - return JS_FALSE; JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, callerstr); + JSMSG_BAD_INDIRECT_CALL, caller); return JS_FALSE; } } @@ -1257,10 +1197,10 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || caller->pc); - indirectCall = (caller && *caller->pc != JSOP_EVAL); + indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL); - if (indirectCall && + if (JS_VERSION_IS_ECMA(cx) && + indirectCall && !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, @@ -1299,17 +1239,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #if JS_HAS_EVAL_THIS_SCOPE /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ if (indirectCall) { - callerScopeChain = js_GetScopeChain(cx, caller); - if (!callerScopeChain) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; + callerScopeChain = caller->scopeChain; if (obj != callerScopeChain) { if (!js_CheckPrincipalsAccess(cx, obj, caller->script->principals, - cx->runtime->atomState.evalAtom)) - { + js_eval_str)) { return JS_FALSE; } @@ -1334,14 +1268,14 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* From here on, control must exit through label out with ok set. */ #endif +#if JS_BUG_EVAL_THIS_SCOPE + /* An old version used the object in which eval was found for scope. */ + scopeobj = obj; +#else /* Compile using caller's current scope object. */ - if (caller) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } - } + if (caller) + scopeobj = caller->scopeChain; +#endif } /* Ensure we compile this eval with the right object in the scope chain. */ @@ -1351,14 +1285,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = JSVAL_TO_STRING(argv[0]); if (caller) { + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } } else { file = NULL; line = 0; @@ -1386,6 +1315,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto out; } +#if !JS_BUG_EVAL_THIS_SCOPE #if JS_HAS_SCRIPT_OBJECT if (argc < 2) #endif @@ -1394,13 +1324,13 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (caller) scopeobj = caller->scopeChain; } +#endif /* * Belt-and-braces: check that the lesser of eval's principals and the * caller's principals has access to scopeobj. */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, - cx->runtime->atomState.evalAtom); + ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, js_eval_str); if (ok) ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); @@ -1506,6 +1436,7 @@ obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #endif /* JS_HAS_OBJ_WATCHPOINT */ +#if JS_HAS_NEW_OBJ_METHODS /* * Prototype and property query methods, to complement the 'in' and * 'instanceof' operators. @@ -1635,6 +1566,7 @@ obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); return ok; } +#endif /* JS_HAS_NEW_OBJ_METHODS */ #if JS_HAS_GETTER_SETTER static JSBool @@ -1754,9 +1686,11 @@ obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, const char js_watch_str[] = "watch"; const char js_unwatch_str[] = "unwatch"; #endif +#if JS_HAS_NEW_OBJ_METHODS const char js_hasOwnProperty_str[] = "hasOwnProperty"; const char js_isPrototypeOf_str[] = "isPrototypeOf"; const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; +#endif #if JS_HAS_GETTER_SETTER const char js_defineGetter_str[] = "__defineGetter__"; const char js_defineSetter_str[] = "__defineSetter__"; @@ -1776,9 +1710,11 @@ static JSFunctionSpec object_methods[] = { {js_watch_str, obj_watch, 2,0,0}, {js_unwatch_str, obj_unwatch, 1,0,0}, #endif +#if JS_HAS_NEW_OBJ_METHODS {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, +#endif #if JS_HAS_GETTER_SETTER {js_defineGetter_str, obj_defineGetter, 2,0,0}, {js_defineSetter_str, obj_defineSetter, 2,0,0}, @@ -1932,7 +1868,7 @@ with_getObjectOps(JSContext *cx, JSClass *clasp) JSClass js_WithClass = { "With", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, + 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, @@ -1952,286 +1888,67 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) return obj; } -JSObject * -js_NewBlockObject(JSContext *cx) -{ - JSObject *obj; - - /* - * Null obj's proto slot so that Object.prototype.* does not pollute block - * scopes. Make sure obj has its own scope too, since clearing proto does - * not affect OBJ_SCOPE(obj). - */ - obj = js_NewObject(cx, &js_BlockClass, NULL, NULL); - if (!obj || !js_GetMutableScope(cx, obj)) - return NULL; - OBJ_SET_PROTO(cx, obj, NULL); - return obj; -} - -JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp) -{ - JSObject *clone; - - clone = js_NewObject(cx, &js_BlockClass, proto, parent); - if (!clone) - return NULL; - clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp); - clone->slots[JSSLOT_BLOCK_DEPTH] = - OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH); - return clone; -} - -/* - * XXXblock this reverses a path in the property tree -- try to share - * the prototype's scope harder! - */ -JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - uintN depth, slot; - JSScopeProperty *sprop; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - JS_ASSERT(fp); - depth = OBJ_BLOCK_DEPTH(cx, obj); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->getter != js_BlockClass.getProperty) - continue; - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - slot = depth + (uintN)sprop->shortid; - JS_ASSERT(slot < fp->script->depth); - if (!js_DefineNativeProperty(cx, obj, sprop->id, - fp->spbase[slot], NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, sprop->shortid, - NULL)) { - JS_SetPrivate(cx, obj, NULL); - return JS_FALSE; - } - } - - return JS_SetPrivate(cx, obj, NULL); -} - +#if JS_HAS_OBJ_PROTO_PROP static JSBool -block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - JSStackFrame *fp; - jsint slot; + JSObject *parent, *proto; + jsval v; - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - *vp = fp->spbase[slot]; - return JS_TRUE; -} - -static JSBool -block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - fp->spbase[slot] = *vp; - return JS_TRUE; -} - -#if JS_HAS_XDR - -#define NO_PARENT_INDEX (jsatomid)-1 - -jsatomid -FindObjectAtomIndex(JSAtomMap *map, JSObject *obj) -{ - size_t i; - JSAtom *atom; - - for (i = 0; i < map->length; i++) { - atom = map->vector[i]; - if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj)) - return i; - } - - return NO_PARENT_INDEX; -} - -static JSBool -block_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - jsatomid parentId; - JSAtomMap *atomMap; - JSObject *obj, *parent; - uint16 depth, count, i; - uint32 tmp; - JSTempValueRooter tvr; - JSScopeProperty *sprop; - jsid propid; - JSAtom *atom; - int16 shortid; - JSBool ok; - - cx = xdr->cx; -#ifdef __GNUC__ - obj = NULL; /* quell GCC overwarning */ -#endif - - atomMap = &xdr->script->atomMap; - if (xdr->mode == JSXDR_ENCODE) { - obj = *objp; - parent = OBJ_GET_PARENT(cx, obj); - parentId = FindObjectAtomIndex(atomMap, parent); - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - tmp = (uint32)(depth << 16) | count; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else count = 0; -#endif - - /* First, XDR the parent atomid. */ - if (!JS_XDRUint32(xdr, &parentId)) + if (!JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT, + js_GetErrorMessage, NULL, + JSMSG_DEPRECATED_USAGE, + js_WithClass.name)) { return JS_FALSE; + } - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewBlockObject(cx); + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewWithObject(cx, NULL, NULL, -1); if (!obj) return JS_FALSE; - *objp = obj; - - /* - * If there's a parent id, then get the parent out of our script's - * atomMap. We know that we XDR block object in outer-to-inner order, - * which means that getting the parent now will work. - */ - if (parentId == NO_PARENT_INDEX) { - parent = NULL; - } else { - atom = js_GetAtom(cx, atomMap, parentId); - JS_ASSERT(ATOM_IS_OBJECT(atom)); - parent = ATOM_TO_OBJECT(atom); - } - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); + *rval = OBJECT_TO_JSVAL(obj); } - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); - - if (!JS_XDRUint32(xdr, &tmp)) { - JS_POP_TEMP_ROOT(cx, &tvr); - return JS_FALSE; - } - - if (xdr->mode == JSXDR_DECODE) { - depth = (uint16)(tmp >> 16); - count = (uint16)tmp; - obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth); - } - - /* - * XDR the block object's properties. We know that there are 'count' - * properties to XDR, stored as id/shortid pairs. We do not XDR any - * non-native properties, only those that the compiler created. - */ - sprop = NULL; - ok = JS_TRUE; - for (i = 0; i < count; i++) { - if (xdr->mode == JSXDR_ENCODE) { - /* Find a property to XDR. */ - do { - /* If sprop is NULL, this is the first property. */ - sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; - } while (!(sprop->flags & SPROP_HAS_SHORTID)); - - JS_ASSERT(sprop->getter == js_BlockClass.getProperty); - propid = sprop->id; - JS_ASSERT(JSID_IS_ATOM(propid)); - atom = JSID_TO_ATOM(propid); - shortid = sprop->shortid; - JS_ASSERT(shortid >= 0); - } - - /* XDR the real id, then the shortid. */ - if (!js_XDRStringAtom(xdr, &atom) || - !JS_XDRUint16(xdr, (uint16 *)&shortid)) { - ok = JS_FALSE; - break; - } - - if (xdr->mode == JSXDR_DECODE) { - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, shortid, NULL)) { - ok = JS_FALSE; - break; - } + parent = cx->fp->scopeChain; + if (argc > 0) { + if (!js_ValueToObject(cx, argv[0], &proto)) + return JS_FALSE; + v = OBJECT_TO_JSVAL(proto); + if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v)) + return JS_FALSE; + if (argc > 1) { + if (!js_ValueToObject(cx, argv[1], &parent)) + return JS_FALSE; } } - - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; + v = OBJECT_TO_JSVAL(parent); + return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v); } - -#else -# define block_xdrObject NULL #endif -JSClass js_BlockClass = { - "Block", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), - JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL -}; - -JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, - NULL, NULL, NULL); - if (!proto) - return NULL; - - OBJ_SET_PROTO(cx, proto, NULL); - return proto; -} - JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj) { JSObject *proto; jsval eval; +#if JS_HAS_SHARP_VARS + JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1); +#endif + proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, object_props, object_methods, NULL, NULL); if (!proto) return NULL; +#if JS_HAS_OBJ_PROTO_PROP + if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0, + NULL, NULL, NULL, NULL)) { + return NULL; + } +#endif + /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ if (!OBJ_GET_PROPERTY(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.evalAtom), @@ -2292,6 +2009,10 @@ js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) return map; } +static JSBool +GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, + JSObject **protop); + static jsval * AllocSlots(JSContext *cx, jsval *slots, uint32 nslots) { @@ -2356,30 +2077,9 @@ FreeSlots(JSContext *cx, jsval *slots) JS_free(cx, slots - 1); } -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) -{ - JSProtoKey key; - JSAtom *atom; - - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null) { - *idp = INT_TO_JSID(key); - } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { - *idp = INT_TO_JSID(JSProto_Object); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - jsid id; JSObject *obj; JSObjectOps *ops; JSObjectMap *map; @@ -2390,15 +2090,10 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) /* Bootstrap the ur-object, and make it the default prototype object. */ if (!proto) { - if (!js_GetClassId(cx, clasp, &id)) + if (!GetClassPrototype(cx, parent, clasp->name, &proto)) return NULL; - if (!js_GetClassPrototype(cx, parent, id, &proto)) + if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto)) return NULL; - if (!proto && - !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), - &proto)) { - return NULL; - } } /* Always call the class's getObjectOps hook if it has one. */ @@ -2408,7 +2103,7 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) /* * Allocate a zeroed object from the GC heap. Do this *after* any other - * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps, + * 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)); @@ -2416,12 +2111,12 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) return NULL; /* - * Root obj to prevent it from being collected out from under this call. - * to js_NewObject. 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 further below. + * 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_TEMP_ROOT_OBJECT(cx, obj, &tvr); + 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 @@ -2490,7 +2185,7 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) out: JS_POP_TEMP_ROOT(cx, &tvr); - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; + cx->newborn[GCX_OBJECT] = (JSGCThing *) obj; return obj; bad: @@ -2498,96 +2193,18 @@ bad: goto out; } -JS_STATIC_DLL_CALLBACK(JSObject *) -js_InitNullClass(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(0); - return NULL; -} - -#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); -#include "jsproto.tbl" -#undef JS_PROTO - -static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) init, -#include "jsproto.tbl" -#undef JS_PROTO -}; - JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) { - JSBool ok; - JSObject *tmp, *cobj; - JSResolvingKey rkey; - JSResolvingEntry *rentry; - uint32 generation; - JSObjectOp init; - jsval v; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - *objp = NULL; - return JS_TRUE; - } - - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (!ok) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - *objp = JSVAL_TO_OBJECT(v); - return JS_TRUE; - } - - rkey.obj = obj; - rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) - return JS_FALSE; - if (!rentry) { - /* Already caching key in obj -- suppress recursion. */ - *objp = NULL; - return JS_TRUE; - } - generation = cx->resolvingTable->generation; - - cobj = NULL; - init = lazy_prototype_init[key]; - if (init) { - if (!init(cx, obj)) { - ok = JS_FALSE; - } else { - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (ok && !JSVAL_IS_PRIMITIVE(v)) - cobj = JSVAL_TO_OBJECT(v); - } - } - - js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); - *objp = cobj; - return ok; -} - -JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) -{ - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) - return JS_TRUE; - - return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); -} - -JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) -{ - JSObject *obj, *cobj, *pobj; - JSProtoKey key; + JSAtom *atom; + JSObject *obj, *pobj; JSProperty *prop; JSScopeProperty *sprop; + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { /* Find the topmost object in the scope chain. */ do { @@ -2602,25 +2219,9 @@ js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) } } - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - if (JSID_IS_INT(id)) { - key = JSID_TO_INT(id); - JS_ASSERT(key != JSProto_Null); - if (!js_GetClassObject(cx, obj, key, &cobj)) - return JS_FALSE; - if (cobj) { - *vp = OBJECT_TO_JSVAL(cobj); - return JS_TRUE; - } - id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - } - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, - &pobj, &prop)) { + if (!js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), + JSRESOLVE_CLASSNAME, &pobj, &prop)) { return JS_FALSE; } if (!prop) { @@ -2640,19 +2241,16 @@ JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv) { - jsid id; jsval cval, rval; JSTempValueRooter argtvr, tvr; JSObject *obj, *ctor; JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); - if (!js_GetClassId(cx, clasp, &id) || - !js_FindClassObject(cx, parent, id, &cval)) { + 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); @@ -2696,11 +2294,10 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, obj = JSVAL_TO_OBJECT(rval); /* - * If the instance's class differs from what was requested, throw a type - * error. If the given class has both the JSCLASS_HAS_PRIVATE and the - * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its - * private data set at this point, then the constructor was replaced and - * we should throw a type error. + * 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 | @@ -2717,7 +2314,7 @@ out: return obj; bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; obj = NULL; goto out; } @@ -2822,6 +2419,16 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) } } +#if JS_BUG_EMPTY_INDEX_ZERO +#define CHECK_FOR_EMPTY_INDEX(id) \ + JS_BEGIN_MACRO \ + if (JSSTRING_LENGTH(str_) == 0) \ + id = JSVAL_ZERO; \ + JS_END_MACRO +#else +#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */ +#endif + /* JSVAL_INT_MAX as a string */ #define JSVAL_INT_MAX_STRING "1073741823" @@ -2837,6 +2444,8 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) 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); \ } \ } \ JS_END_MACRO @@ -2888,7 +2497,8 @@ HidePropertyName(JSContext *cx, jsid *idp) /* * 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 uses the atom's entry.value member for this linkage. + * 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); @@ -2913,8 +2523,7 @@ js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { return HidePropertyName(cx, &id) && - js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, - objp, propp); + js_LookupProperty(cx, obj, id, objp, propp); } JSScopeProperty * @@ -3001,6 +2610,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, { JSClass *clasp; JSScope *scope; + JSProperty *prop; JSScopeProperty *sprop; /* @@ -3017,7 +2627,6 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, */ if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { JSObject *pobj; - JSProperty *prop; /* * If JS_THREADSAFE and id is found, js_LookupProperty returns with @@ -3105,7 +2714,7 @@ bad: /* * Given pc pointing after a property accessing bytecode, return true if the - * access is "object-detecting" in the sense used by web scripts, e.g., when + * access is a "object-detecting" in the sense used by web pages, e.g., when * checking whether document.all is defined. */ static JSBool @@ -3138,7 +2747,7 @@ Detecting(JSContext *cx, jsbytecode *pc) /* * Special case #2: handle (document.all == undefined). Don't worry * about someone redefining undefined, which was added by Edition 3, - * so is read/write for backward compatibility. + * so was read/write for backward compatibility. */ if (op == JSOP_NAME) { atom = GET_ATOM(cx, script, pc); @@ -3221,7 +2830,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, return JS_FALSE; } if (!entry) { - /* Already resolving id in obj -- suppress recursion. */ + /* Already resolving id in obj -- dampen recursion. */ JS_UNLOCK_OBJ(cx, obj); goto out; } @@ -3432,107 +3041,14 @@ js_FindIdentifierBase(JSContext *cx, jsid id) return obj; } -JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(pobj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); - scope = OBJ_SCOPE(pobj); - JS_ASSERT(scope->object == pobj); - - slot = sprop->slot; - *vp = (slot != SPROP_INVALID_SLOT) - ? LOCKED_OBJ_GET_SLOT(pobj, slot) - : JSVAL_VOID; - if (SPROP_HAS_STUB_GETTER(sprop)) - return JS_TRUE; - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_GET(cx, sprop, obj, pobj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == pobj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); - } - - return JS_TRUE; -} - -JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - jsval pval; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->object == obj); - - slot = sprop->slot; - if (slot != SPROP_INVALID_SLOT) { - pval = LOCKED_OBJ_GET_SLOT(obj, slot); - - /* If sprop has a stub setter, keep scope locked and just store *vp. */ - if (SPROP_HAS_STUB_SETTER(sprop)) - goto set_slot; - } else { - /* - * Allow API consumers to create shared properties with stub setters. - * Such properties lack value storage, so setting them is like writing - * to /dev/null. - */ - if (SPROP_HAS_STUB_SETTER(sprop)) - return JS_TRUE; - pval = JSVAL_VOID; - } - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_SET(cx, sprop, obj, obj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == obj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - set_slot: - GC_POKE(cx, pval); - LOCKED_OBJ_SET_SLOT(obj, slot, *vp); - } - - return JS_TRUE; -} - JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *obj2; JSProperty *prop; + JSScope *scope; JSScopeProperty *sprop; + uint32 slot; /* * Handle old bug that took empty string as zero index. Also convert @@ -3543,9 +3059,17 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (!js_LookupProperty(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { - jsbytecode *pc; + jsval default_val; - *vp = JSVAL_VOID; +#if JS_BUG_NULL_INDEX_PROPS + /* Indexed properties defaulted to null in old versions. */ + default_val = (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) + ? JSVAL_NULL + : JSVAL_VOID; +#else + default_val = JSVAL_VOID; +#endif + *vp = default_val; if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) return JS_FALSE; @@ -3554,41 +3078,27 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) * Give a strict warning if foo.bar is evaluated by a script for an * object foo with no property named 'bar'. */ - if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) { - JSOp op; - uintN flags; + if (JS_HAS_STRICT_OPTION(cx) && + *vp == default_val && + cx->fp && cx->fp->pc && + (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM)) + { + jsbytecode *pc; JSString *str; - op = *pc; - if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) { - flags = JSREPORT_ERROR; - } else { - if (!JS_HAS_STRICT_OPTION(cx) || - (op != JSOP_GETPROP && op != JSOP_GETELEM)) { - return JS_TRUE; - } - - /* - * XXX do not warn about missing __iterator__ as the function - * may be called from JS_GetMethodById. See bug 355145. - */ - if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) - return JS_TRUE; - - /* Kludge to allow (typeof foo == "undefined") tests. */ - JS_ASSERT(cx->fp->script); - pc += js_CodeSpec[op].length; - if (Detecting(cx, pc)) - return JS_TRUE; - - flags = JSREPORT_WARNING | JSREPORT_STRICT; - } + /* Kludge to allow (typeof foo == "undefined") tests. */ + JS_ASSERT(cx->fp->script); + pc = cx->fp->pc; + pc += js_CodeSpec[*pc].length; + if (Detecting(cx, pc)) + return JS_TRUE; /* Ok, bad undefined property reference: whine about it. */ str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (!str || - !JS_ReportErrorFlagsAndNumber(cx, flags, + !JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING|JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_UNDEFINED_PROP, JS_GetStringBytes(str))) { @@ -3603,12 +3113,33 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) return OBJ_GET_PROPERTY(cx, obj2, id, vp); } + /* Unlock obj2 before calling getter, relock after to avoid deadlock. */ + scope = OBJ_SCOPE(obj2); sprop = (JSScopeProperty *) prop; - if (!js_NativeGet(cx, obj, obj2, sprop, vp)) - return JS_FALSE; + slot = sprop->slot; + if (slot != SPROP_INVALID_SLOT) { + JS_ASSERT(slot < obj2->map->freeslot); + *vp = LOCKED_OBJ_GET_SLOT(obj2, slot); - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); - JS_UNLOCK_OBJ(cx, obj2); + /* If sprop has a stub getter, we're done. */ + if (!sprop->getter) + goto out; + } else { + *vp = JSVAL_VOID; + } + + JS_UNLOCK_SCOPE(cx, scope); + if (!SPROP_GET(cx, sprop, obj, obj2, vp)) + return JS_FALSE; + JS_LOCK_SCOPE(cx, scope); + + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + LOCKED_OBJ_SET_SLOT(obj2, slot, *vp); + PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); + } + +out: + JS_UNLOCK_SCOPE(cx, scope); return JS_TRUE; } @@ -3623,6 +3154,8 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) intN shortid; JSClass *clasp; JSPropertyOp getter, setter; + jsval pval; + uint32 slot; /* * Handle old bug that took empty string as zero index. Also convert @@ -3666,25 +3199,8 @@ 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); - - /* - * Here, we'll either return true or goto read_only_error, which - * reports a strict warning or throws an error. So we redefine - * the |flags| local variable to be JSREPORT_* flags to pass to - * JS_ReportErrorFlagsAndNumberUC at label read_only_error. We - * must likewise re-task flags further below for the other 'goto - * read_only_error;' case. - */ - flags = JSREPORT_ERROR; - if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) { - if (!JS_HAS_STRICT_OPTION(cx)) { - /* Just return true per ECMA if not in strict mode. */ - return JS_TRUE; - } - - /* Strict mode: report a read-only strict warning. */ - flags = JSREPORT_STRICT | JSREPORT_WARNING; - } + if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) + return JS_TRUE; goto read_only_error; } @@ -3698,13 +3214,8 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JS_UNLOCK_SCOPE(cx, scope); /* Don't clone a shared prototype property. */ - if (attrs & JSPROP_SHARED) { - if (SPROP_HAS_STUB_SETTER(sprop) && - !(sprop->attrs & JSPROP_GETTER)) { - return JS_TRUE; - } + if (attrs & JSPROP_SHARED) return SPROP_SET(cx, sprop, obj, pobj, vp); - } /* Restore attrs to the ECMA default for new properties. */ attrs = JSPROP_ENUMERATE; @@ -3736,10 +3247,8 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) } if (!sprop) { - if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { - flags = JSREPORT_ERROR; + if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) goto read_only_error; - } /* Find or make a property descriptor with the right heritage. */ JS_LOCK_OBJ(cx, obj); @@ -3774,8 +3283,37 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); } - if (!js_NativeSet(cx, obj, sprop, vp)) + /* Get the current property value from its slot. */ + slot = sprop->slot; + if (slot != SPROP_INVALID_SLOT) { + JS_ASSERT(slot < obj->map->freeslot); + pval = LOCKED_OBJ_GET_SLOT(obj, slot); + + /* If sprop has a stub setter, keep scope locked and just store *vp. */ + if (!sprop->setter) + goto set_slot; + } + + /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */ + JS_UNLOCK_SCOPE(cx, scope); + + /* Let the setter modify vp before copying from it to obj->slots[slot]. */ + if (!SPROP_SET(cx, sprop, obj, obj, vp)) return JS_FALSE; + + /* Relock obj's scope until we are done with sprop. */ + JS_LOCK_SCOPE(cx, scope); + + /* + * Check whether sprop is still around (was not deleted), and whether it + * has a slot (it may never have had one, or we may have lost a race with + * someone who cleared scope). + */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + set_slot: + GC_POKE(cx, pval); + LOCKED_OBJ_SET_SLOT(obj, slot, *vp); + } JS_UNLOCK_SCOPE(cx, scope); return JS_TRUE; @@ -3784,11 +3322,12 @@ js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); - if (!str) - return JS_FALSE; - return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage, - NULL, JSMSG_READ_ONLY, - JS_GetStringChars(str)); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_READ_ONLY, + JS_GetStringBytes(str)); + } + return JS_FALSE; } } @@ -3850,6 +3389,8 @@ js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, JSBool js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { +#if JS_HAS_PROP_DELETE + JSObject *proto; JSProperty *prop; JSScopeProperty *sprop; @@ -3857,7 +3398,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) JSScope *scope; JSBool ok; - *rval = JSVAL_TRUE; + *rval = JS_VERSION_IS_ECMA(cx) ? JSVAL_TRUE : JSVAL_VOID; /* * Handle old bug that took empty string as zero index. Also convert @@ -3925,15 +3466,24 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) ok = js_RemoveScopeProperty(cx, scope, id); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; + +#else /* !JS_HAS_PROP_DELETE */ + + jsval null = JSVAL_NULL; + + *rval = JSVAL_VOID; + return js_SetProperty(cx, obj, id, &null); + +#endif /* !JS_HAS_PROP_DELETE */ } JSBool js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) { - jsval v, save; + jsval v; JSString *str; - v = save = OBJECT_TO_JSVAL(obj); + v = OBJECT_TO_JSVAL(obj); switch (hint) { case JSTYPE_STRING: /* @@ -3948,6 +3498,25 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) if (!JSVAL_IS_PRIMITIVE(v)) { if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) return JS_FALSE; + + /* + * JS1.2 never failed (except for malloc failure) to convert an + * object to a string. ECMA requires an error if both toString + * and valueOf fail to produce a primitive value. + */ + if (!JSVAL_IS_PRIMITIVE(v) && JS_VERSION_IS_1_2(cx)) { + char *bytes = JS_smprintf("[object %s]", + OBJ_GET_CLASS(cx, obj)->name); + if (!bytes) + return JS_FALSE; + str = JS_NewString(cx, bytes, strlen(bytes)); + if (!str) { + free(bytes); + return JS_FALSE; + } + v = STRING_TO_JSVAL(str); + goto out; + } } break; @@ -3960,10 +3529,12 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) { goto out; } + /* Don't convert to string (source object literal) for JS1.2. */ + if (JS_VERSION_IS_1_2(cx) && hint == JSTYPE_BOOLEAN) + goto out; if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, - NULL, &v)) { + NULL, &v)) return JS_FALSE; - } } break; } @@ -3977,14 +3548,14 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) str = NULL; } *vp = OBJECT_TO_JSVAL(obj); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str); + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, JS_GetStringBytes(str), (hint == JSTYPE_VOID) ? "primitive type" - : js_type_strs[hint]); + : js_type_str[hint]); } return JS_FALSE; } @@ -4184,10 +3755,15 @@ js_MarkNativeIteratorStates(JSContext *cx) end = cursor + state->ida->length; for (; cursor != end; ++cursor) { id = *cursor; - MARK_ID(cx, id); + 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, @@ -4343,7 +3919,7 @@ js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) &fval)) { return JS_FALSE; } - if (VALUE_IS_FUNCTION(cx, fval)) { + if (JSVAL_IS_FUNCTION(cx, fval)) { if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) return JS_FALSE; args = js_GetArgsObject(cx, cx->fp); @@ -4360,7 +3936,7 @@ js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return ok; } #endif - ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR); + ReportIsNotFunction(cx, &argv[-2], 0); return JS_FALSE; } return clasp->call(cx, obj, argc, argv, rval); @@ -4386,7 +3962,7 @@ js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, &cval)) { return JS_FALSE; } - if (VALUE_IS_FUNCTION(cx, cval)) { + if (JSVAL_IS_FUNCTION(cx, cval)) { if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) return JS_FALSE; args = js_GetArgsObject(cx, cx->fp); @@ -4427,7 +4003,7 @@ js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) &fval)) { return JS_FALSE; } - if (VALUE_IS_FUNCTION(cx, fval)) { + if (JSVAL_IS_FUNCTION(cx, fval)) { return js_InternalCall(cx, obj, fval, 1, &v, &rval) && js_ValueToBoolean(cx, rval, bp); } @@ -4462,15 +4038,21 @@ js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) } JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop) +js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop) +{ + return GetClassPrototype(cx, NULL, name, protop); +} + +static JSBool +GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, + JSObject **protop) { jsval v; JSObject *ctor; - if (!js_FindClassObject(cx, scope, id, &v)) + if (!js_FindConstructor(cx, scope, name, &v)) return JS_FALSE; - if (VALUE_IS_FUNCTION(cx, v)) { + if (JSVAL_IS_FUNCTION(cx, v)) { ctor = JSVAL_TO_OBJECT(v); if (!OBJ_GET_PROPERTY(cx, ctor, ATOM_TO_JSID(cx->runtime->atomState @@ -4488,7 +4070,7 @@ js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, * instance that delegates to this object, or just query the * prototype for its class. */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); } } *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; @@ -4617,11 +4199,16 @@ js_ValueToNonNullObject(JSContext *cx, jsval v) JSBool js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) { +#if JS_HAS_VALUEOF_HINT jsval argv[1]; argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, rval); +#else + return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL, + rval); +#endif } JSBool @@ -4661,76 +4248,60 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, } if (!ok) JS_ClearPendingException(cx); + ok = JSVAL_IS_PRIMITIVE(fval) || + js_InternalCall(cx, obj, fval, argc, argv, rval); JS_SetErrorReporter(cx, older); - - return JSVAL_IS_PRIMITIVE(fval) || - js_InternalCall(cx, obj, fval, argc, argv, rval); + return ok; } #if JS_HAS_XDR +#include "jsxdrapi.h" + JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; - JSAtom *atom; JSClass *clasp; + const char *className; uint32 classId, classDef; - JSProtoKey protoKey; - jsid classKey; + JSBool ok; JSObject *proto; cx = xdr->cx; - atom = NULL; if (xdr->mode == JSXDR_ENCODE) { clasp = OBJ_GET_CLASS(cx, *objp); - classId = JS_XDRFindClassIdByName(xdr, clasp->name); + className = clasp->name; + classId = JS_XDRFindClassIdByName(xdr, className); classDef = !classId; - if (classDef) { - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; - protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); - if (protoKey != JSProto_Null) { - classDef |= (protoKey << 1); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - } - } + if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId)) + return JS_FALSE; } else { - clasp = NULL; /* quell GCC overwarning */ classDef = 0; + className = NULL; + clasp = NULL; /* quell GCC overwarning */ } - /* - * XDR a flag word, which could be 0 for a class use, in which case no - * name follows, only the id in xdr's class registry; 1 for a class def, - * in which case the flag word is followed by the class name transferred - * from or to atom; or a value greater than 1, an odd number that when - * divided by two yields the JSProtoKey for class. In the last case, as - * in the 0 classDef case, no name is transferred via atom. - */ + /* XDR a flag word followed (if true) by the class name. */ if (!JS_XDRUint32(xdr, &classDef)) return JS_FALSE; - if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom)) + if (classDef && !JS_XDRCString(xdr, (char **) &className)) return JS_FALSE; - if (!JS_XDRUint32(xdr, &classId)) - return JS_FALSE; + /* From here on, return through out: to free className if it was set. */ + ok = JS_XDRUint32(xdr, &classId); + if (!ok) + goto out; - if (xdr->mode == JSXDR_DECODE) { + if (xdr->mode != JSXDR_ENCODE) { if (classDef) { - /* NB: we know that JSProto_Null is 0 here, for backward compat. */ - protoKey = classDef >> 1; - classKey = (protoKey != JSProto_Null) - ? INT_TO_JSID(protoKey) - : ATOM_TO_JSID(atom); - if (!js_GetClassPrototype(cx, NULL, classKey, &proto)) - return JS_FALSE; + ok = GetClassPrototype(cx, NULL, className, &proto); + if (!ok) + goto out; clasp = OBJ_GET_CLASS(cx, proto); - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; + ok = JS_XDRRegisterClass(xdr, clasp, &classId); + if (!ok) + goto out; } else { clasp = JS_XDRFindClassById(xdr, classId); if (!clasp) { @@ -4738,7 +4309,8 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_FIND_CLASS, numBuf); - return JS_FALSE; + ok = JS_FALSE; + goto out; } } } @@ -4746,9 +4318,14 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp) if (!clasp->xdrObject) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_XDR_CLASS, clasp->name); - return JS_FALSE; + ok = JS_FALSE; + } else { + ok = clasp->xdrObject(xdr, objp); } - return clasp->xdrObject(xdr, objp); +out: + if (xdr->mode != JSXDR_ENCODE && className) + JS_free(cx, (void *)className); + return ok; } #endif /* JS_HAS_XDR */ @@ -4775,10 +4352,6 @@ MeterEntryCount(uintN count) js_entry_count_hist[JS_MIN(count, 10)]++; } -#define DEBUG_scopemeters -#endif /* DEBUG_brendan */ - -#ifdef DEBUG_scopemeters void js_DumpScopeMeters(JSRuntime *rt) { @@ -4819,7 +4392,8 @@ js_DumpScopeMeters(JSRuntime *rt) memset(js_entry_count_hist, 0, sizeof js_entry_count_hist); fflush(logfp); } -#endif + +#endif /* DEBUG_brendan */ uint32 js_Mark(JSContext *cx, JSObject *obj, void *arg) @@ -4841,13 +4415,50 @@ js_Mark(JSContext *cx, JSObject *obj, void *arg) for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; - MARK_SCOPE_PROPERTY(cx, sprop); + MARK_SCOPE_PROPERTY(sprop); + 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 = JSID_TO_ATOM(sprop->id); + const char *id = (atom && ATOM_IS_STRING(atom)) + ? JS_GetStringBytes(ATOM_TO_STRING(atom)) + : "unknown"; +#endif + + if (sprop->attrs & JSPROP_GETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_getter_str); +#endif + GC_MARK(cx, + JSVAL_TO_GCTHING((jsval) sprop->getter), + buf, + arg); + } + if (sprop->attrs & JSPROP_SETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_setter_str); +#endif + GC_MARK(cx, + JSVAL_TO_GCTHING((jsval) sprop->setter), + buf, + arg); + } + } +#endif /* JS_HAS_GETTER_SETTER */ } /* No one runs while the GC is running, so we can use LOCKED_... here. */ clasp = LOCKED_OBJ_GET_CLASS(obj); if (clasp->mark) - (void) clasp->mark(cx, obj, NULL); + (void) clasp->mark(cx, obj, arg); if (scope->object != obj) { /* diff --git a/lib/javascript/jsobj.h b/lib/javascript/jsobj.h index eb3aedb7..a2400e98 100644 --- a/lib/javascript/jsobj.h +++ b/lib/javascript/jsobj.h @@ -236,17 +236,11 @@ extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; extern JSClass js_ObjectClass; extern JSClass js_WithClass; -extern JSClass js_BlockClass; + +extern JSObject * +js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); /* - * Block scope object macros. The slots reserved by js_BlockClass are: - * - * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null - * JSSLOT_BLOCK_DEPTH int depth of block slots in frame - * - * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. - * OBJ_BLOCK_COUNT depends on this arrangement. - * * 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 @@ -256,40 +250,11 @@ extern JSClass js_BlockClass; */ #define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) -#define OBJ_BLOCK_COUNT(cx,obj) \ - ((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 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)) -/* - * To make sure this slot is well-defined, always call js_NewWithObject to - * create a With object, don't call js_NewObject directly. When creating a - * With object that does not correspond to a stack slot, pass -1 for depth. - * - * When popping the stack across this object's "with" statement, client code - * must call JS_SetPrivate(cx, withobj, NULL). - */ -extern JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); - -/* - * Create a new block scope object not linked to any proto or parent object. - * Blocks are created by the compiler to reify let blocks and comprehensions. - * Only when dynamic scope is captured do they need to be cloned and spliced - * into an active scope chain. - */ -extern JSObject * -js_NewBlockObject(JSContext *cx); - -extern JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp); - -extern JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj); - struct JSSharpObjectMap { jsrefcount depth; jsatomid sharpgen; @@ -331,9 +296,6 @@ extern JSBool js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, uintN argc, jsval *argv, jsval *rval); -extern JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj); - extern JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj); @@ -365,24 +327,11 @@ js_HoldObjectMap(JSContext *cx, JSObjectMap *map); extern JSObjectMap * js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); - extern JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); -/* - * Fast access to immutable standard objects (constructors and prototypes). - */ extern JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - -extern JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj); - -extern JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp); +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp); extern JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, @@ -461,15 +410,11 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, /* * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. - * JSRESOLVE_HIDDEN flags hidden function param/local name lookups, just for - * internal use by fun_resolve and similar built-ins. */ extern JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); -#define JSRESOLVE_HIDDEN 0x8000 - extern JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, JSProperty **propp); @@ -480,19 +425,6 @@ js_FindIdentifierBase(JSContext *cx, jsid id); extern JSObject * js_FindVariableScope(JSContext *cx, JSFunction **funp); -/* - * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop - * (pobj's scope for Get, obj's for Set) locked, and on successful return, that - * scope is again locked. But on failure, both functions return false with the - * scope containing sprop unlocked. - */ -extern JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp); - -extern JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); - extern JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); @@ -550,8 +482,7 @@ extern JSBool js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); extern JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop); +js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop); extern JSBool js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, @@ -590,7 +521,7 @@ js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller extern JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller); + JSPrincipals *principals, const char *caller); JS_END_EXTERN_C #endif /* jsobj_h___ */ diff --git a/lib/javascript/jsopcode.c b/lib/javascript/jsopcode.c index 3dec776c..80c7eb4d 100644 --- a/lib/javascript/jsopcode.c +++ b/lib/javascript/jsopcode.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: + * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -66,16 +66,26 @@ #include "jsobj.h" #include "jsopcode.h" #include "jsregexp.h" -#include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" -#if JS_HAS_DESTRUCTURING -# include "jsnum.h" -#endif +const char js_const_str[] = "const"; +const char js_var_str[] = "var"; +const char js_function_str[] = "function"; +const char js_in_str[] = "in"; +const char js_instanceof_str[] = "instanceof"; +const char js_new_str[] = "new"; +const char js_delete_str[] = "delete"; +const char js_typeof_str[] = "typeof"; +const char js_void_str[] = "void"; +const char js_null_str[] = "null"; +const char js_this_str[] = "this"; +const char js_false_str[] = "false"; +const char js_true_str[] = "true"; +const char js_default_str[] = "default"; -static const char js_incop_strs[][3] = {"++", "--"}; +const char *js_incop_str[] = {"++", "--"}; /* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ #ifdef __WINDOWS_386__ @@ -133,41 +143,6 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) return JS_TRUE; } -const char * -ToDisassemblySource(JSContext *cx, jsval v) -{ - JSObject *obj; - JSScopeProperty *sprop; - char *source; - const char *bytes; - JSString *str; - - if (!JSVAL_IS_PRIMITIVE(v)) { - obj = JSVAL_TO_OBJECT(v); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - source = JS_sprintf_append(NULL, "depth %d {", - OBJ_BLOCK_DEPTH(cx, obj)); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); - if (!bytes) - return NULL; - source = JS_sprintf_append(source, "%s: %d%s", - bytes, sprop->shortid, - sprop->parent ? ", " : ""); - } - source = JS_sprintf_append(source, "}"); - if (!source) - return NULL; - str = JS_NewString(cx, source, strlen(source)); - if (!str) - return NULL; - return JS_GetStringBytes(str); - } - } - return js_ValueToPrintableSource(cx, v); -} - JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, FILE *fp) @@ -177,7 +152,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, ptrdiff_t len, off, jmplen; uint32 type; JSAtom *atom; - const char *bytes; + JSString *str; op = (JSOp)*pc; if (op >= JSOP_LIMIT) { @@ -213,17 +188,17 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, case JOF_CONST: atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) return 0; - fprintf(fp, " %s", bytes); + fprintf(fp, " %s", JS_GetStringBytes(str)); break; case JOF_UINT16: - case JOF_LOCAL: - fprintf(fp, " %u", GET_UINT16(pc)); + fprintf(fp, " %u", GET_ARGC(pc)); break; +#if JS_HAS_SWITCH_STATEMENT case JOF_TABLESWITCH: case JOF_TABLESWITCHX: { @@ -269,15 +244,16 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, off = GetJumpOffset(pc, pc2); pc2 += jmplen; - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) return 0; - fprintf(fp, "\n\t%s: %d", bytes, off); + fprintf(fp, "\n\t%s: %d", JS_GetStringBytes(str), off); npairs--; } len = 1 + pc2 - pc; break; } +#endif /* JS_HAS_SWITCH_STATEMENT */ case JOF_QARG: fprintf(fp, " %u", GET_ARGNO(pc)); @@ -287,35 +263,37 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, fprintf(fp, " %u", GET_VARNO(pc)); break; +#if JS_HAS_LEXICAL_CLOSURE case JOF_INDEXCONST: fprintf(fp, " %u", GET_VARNO(pc)); pc += VARNO_LEN; atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) return 0; - fprintf(fp, " %s", bytes); + 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_UINT24(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) + atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) return 0; - fprintf(fp, " %s", bytes); + fprintf(fp, " %s", JS_GetStringBytes(str)); break; } JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL); - fprintf(fp, " %u", GET_UINT24(pc)); + fprintf(fp, " %u", GET_LITERAL_INDEX(pc)); break; case JOF_LITOPX: atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) return 0; /* @@ -325,9 +303,11 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, pc += 1 + LITERAL_INDEX_LEN; op = *pc; cs = &js_CodeSpec[op]; - fprintf(fp, " %s op %s", bytes, cs->name); + 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, @@ -375,19 +355,15 @@ typedef struct Sprinter { static JSBool SprintAlloc(Sprinter *sp, size_t nb) { - char *base; - - base = sp->base; - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); + if (!sp->base) { + JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb); } else { - JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); + JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb); } - if (!base) { + if (!sp->base) { JS_ReportOutOfMemory(sp->context); return JS_FALSE; } - sp->base = base; sp->size += nb; return JS_TRUE; } @@ -412,12 +388,6 @@ SprintPut(Sprinter *sp, const char *s, size_t len) return offset; } -static ptrdiff_t -SprintCString(Sprinter *sp, const char *s) -{ - return SprintPut(sp, s, strlen(s)); -} - static ptrdiff_t Sprint(Sprinter *sp, const char *format, ...) { @@ -432,7 +402,7 @@ Sprint(Sprinter *sp, const char *format, ...) JS_ReportOutOfMemory(sp->context); return -1; } - offset = SprintCString(sp, bp); + offset = SprintPut(sp, bp, strlen(bp)); free(bp); return offset; } @@ -450,22 +420,18 @@ const jschar js_EscapeMap[] = { 0 }; -#define DONT_ESCAPE 0x10000 - static char * -QuoteString(Sprinter *sp, JSString *str, uint32 quote) +QuoteString(Sprinter *sp, JSString *str, jschar quote) { - JSBool dontEscape, ok; - jschar qc, c; ptrdiff_t off, len, nb; const jschar *s, *t, *u, *z; char *bp; + jschar c; + JSBool ok; /* Sample off first for later return value pointer computation. */ - dontEscape = (quote & DONT_ESCAPE) != 0; - qc = (jschar) quote; off = sp->offset; - if (qc && Sprint(sp, "%c", (char)qc) < 0) + if (quote && Sprint(sp, "%c", (char)quote) < 0) return NULL; /* Loop control variables: z points at end of string sentinel. */ @@ -474,7 +440,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) for (t = s; t < z; s = ++t) { /* Move t forward from s past un-quote-worthy characters. */ c = *t; - while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) { + while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) { c = *++t; if (t == z) break; @@ -498,9 +464,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) /* Use js_EscapeMap, \u, or \x only if necessary. */ if ((u = js_strchr(js_EscapeMap, c)) != NULL) { - ok = dontEscape - ? Sprint(sp, "%c", (char)c) >= 0 - : Sprint(sp, "\\%c", (char)u[1]) >= 0; + ok = Sprint(sp, "\\%c", (char)u[1]) >= 0; } else { #ifdef JS_C_STRINGS_ARE_UTF8 /* If this is a surrogate pair, make sure to print the pair. */ @@ -512,7 +476,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) if (t == z) { char numbuf[10]; JS_snprintf(numbuf, sizeof numbuf, "0x%x", c); - JS_ReportErrorFlagsAndNumber(sp->context, JSREPORT_ERROR, + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_BAD_SURROGATE_CHAR, numbuf); @@ -534,7 +498,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) } /* Sprint the closing quote and return the quoted string. */ - if (qc && Sprint(sp, "%c", (char)qc) < 0) + if (quote && Sprint(sp, "%c", (char)quote) < 0) return NULL; /* @@ -564,14 +528,6 @@ js_QuoteString(JSContext *cx, JSString *str, jschar quote) /************************************************************************/ -#if JS_HAS_BLOCK_SCOPE -typedef enum JSBraceState { - ALWAYS_BRACE, - MAYBE_BRACE, - DONT_BRACE -} JSBraceState; -#endif - struct JSPrinter { Sprinter sprinter; /* base class state */ JSArenaPool pool; /* string allocation pool */ @@ -579,12 +535,7 @@ struct JSPrinter { JSPackedBool pretty; /* pretty-print: indent, use newlines */ JSPackedBool grouped; /* in parenthesized expression context */ JSScript *script; /* script being printed */ - jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */ JSScope *scope; /* script function scope */ -#if JS_HAS_BLOCK_SCOPE - JSBraceState braceState; /* remove braces around let declaration */ - ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */ -#endif }; /* @@ -609,12 +560,7 @@ js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) jp->pretty = pretty; jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; jp->script = NULL; - jp->dvgfence = NULL; jp->scope = NULL; -#if JS_HAS_BLOCK_SCOPE - jp->braceState = ALWAYS_BRACE; - jp->spaceOffset = -1; -#endif return jp; } @@ -642,34 +588,6 @@ js_GetPrinterOutput(JSPrinter *jp) return str; } -#if !JS_HAS_BLOCK_SCOPE -# define SET_MAYBE_BRACE(jp) jp -# define CLEAR_MAYBE_BRACE(jp) jp -#else -# define SET_MAYBE_BRACE(jp) ((jp)->braceState = MAYBE_BRACE, (jp)) -# define CLEAR_MAYBE_BRACE(jp) ((jp)->braceState = ALWAYS_BRACE, (jp)) - -static void -SetDontBrace(JSPrinter *jp) -{ - ptrdiff_t offset; - const char *bp; - - /* When not pretty-printing, newline after brace is chopped. */ - JS_ASSERT(jp->spaceOffset < 0); - offset = jp->sprinter.offset - (jp->pretty ? 3 : 2); - - /* The shortest case is "if (x) {". */ - JS_ASSERT(offset >= 6); - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - JS_ASSERT(!jp->pretty || bp[offset+2] == '\n'); - jp->spaceOffset = offset; - jp->braceState = DONT_BRACE; - } -} -#endif - int js_printf(JSPrinter *jp, const char *format, ...) { @@ -684,49 +602,9 @@ js_printf(JSPrinter *jp, const char *format, ...) /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */ if (*format == '\t') { - format++; - -#if JS_HAS_BLOCK_SCOPE - if (*format == '}' && jp->braceState != ALWAYS_BRACE) { - JSBraceState braceState; - - braceState = jp->braceState; - jp->braceState = ALWAYS_BRACE; - if (braceState == DONT_BRACE) { - ptrdiff_t offset, delta, from; - - JS_ASSERT(format[1] == '\n' || format[1] == ' '); - offset = jp->spaceOffset; - JS_ASSERT(offset >= 6); - - /* Replace " {\n" at the end of jp->sprinter with "\n". */ - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - delta = 2; - if (jp->pretty) { - /* If pretty, we don't have to worry about 'else'. */ - JS_ASSERT(bp[offset+2] == '\n'); - } else if (bp[offset-1] != ')') { - /* Must keep ' ' to avoid 'dolet' or 'elselet'. */ - ++offset; - delta = 1; - } - - from = offset + delta; - memmove(bp + offset, bp + from, jp->sprinter.offset - from); - jp->sprinter.offset -= delta; - jp->spaceOffset = -1; - - format += 2; - if (*format == '\0') - return 0; - } - } - } -#endif - if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) return -1; + format++; } /* Suppress newlines (must be once per format, at the end) if not pretty. */ @@ -762,7 +640,7 @@ js_printf(JSPrinter *jp, const char *format, ...) JSBool js_puts(JSPrinter *jp, const char *s) { - return SprintCString(&jp->sprinter, s) >= 0; + return SprintPut(&jp->sprinter, s, strlen(s)) >= 0; } /************************************************************************/ @@ -772,79 +650,28 @@ typedef struct SprintStack { ptrdiff_t *offsets; /* stack of postfix string offsets */ jsbytecode *opcodes; /* parallel stack of JS opcodes */ uintN top; /* top of stack index */ - uintN inArrayInit; /* array initialiser/comprehension level */ JSPrinter *printer; /* permanent output goes here */ } SprintStack; -/* - * Get a stacked offset from ss->sprinter.base, or if the stacked value |off| - * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try - * to decompile the code that generated the missing value. This is used when - * reporting errors, where the model stack will lack |pcdepth| non-negative - * offsets (see js_DecompileValueGenerator and js_DecompileCode). - * - * If the stacked offset is -1, return 0 to index the NUL padding at the start - * of ss->sprinter.base. If this happens, it means there is a decompiler bug - * to fix, but it won't violate memory safety. - */ -static ptrdiff_t -GetOff(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - JSString *str; - - off = ss->offsets[i]; - if (off < 0) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder - JS_ASSERT(off < -1); -#endif - if (++off == 0) { - if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) - memset(ss->sprinter.base, 0, ss->sprinter.offset); - return 0; - } - - str = js_DecompileValueGenerator(ss->sprinter.context, off, - JSVAL_NULL, NULL); - if (!str) - return 0; - off = SprintCString(&ss->sprinter, JS_GetStringBytes(str)); - if (off < 0) - off = 0; - ss->offsets[i] = off; - } - return off; -} - -static const char * -GetStr(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - - /* - * Must call GetOff before using ss->sprinter.base, since it may be null - * until bootstrapped by GetOff. - */ - off = GetOff(ss, i); - return OFF2STR(&ss->sprinter, off); -} - /* Gap between stacked strings to allow for insertion of parens and commas. */ #define PAREN_SLOP (2 + 1) /* * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, - * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in - * bytecode, so they don't preempt valid opcodes. + * JSOP_SETPROP, and JSOP_SETELEM, respectively. See the first assertion in + * PushOff. */ -#define JSOP_GETPROP2 256 -#define JSOP_GETELEM2 257 +#define JSOP_GETPROP2 254 +#define JSOP_GETELEM2 255 static JSBool PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) { uintN top; +#if JSOP_LIMIT > JSOP_GETPROP2 +#error JSOP_LIMIT must be <= JSOP_GETPROP2 +#endif if (!SprintAlloc(&ss->sprinter, PAREN_SLOP)) return JS_FALSE; @@ -862,7 +689,6 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) : (op == JSOP_GETELEM2) ? JSOP_GETELEM : (jsbytecode) op; ss->top = ++top; - memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); ss->sprinter.offset += PAREN_SLOP; return JS_TRUE; } @@ -881,27 +707,20 @@ PopOff(SprintStack *ss, JSOp op) return 0; ss->top = --top; - off = GetOff(ss, top); topcs = &js_CodeSpec[ss->opcodes[top]]; cs = &js_CodeSpec[op]; if (topcs->prec != 0 && topcs->prec < cs->prec) { - ss->sprinter.offset = ss->offsets[top] = off - 2; - off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); + ss->offsets[top] -= 2; + ss->sprinter.offset = ss->offsets[top]; + off = Sprint(&ss->sprinter, "(%s)", + OFF2STR(&ss->sprinter, ss->sprinter.offset + 2)); } else { - ss->sprinter.offset = off; + off = ss->sprinter.offset = ss->offsets[top]; } return off; } -static const char * -PopStr(SprintStack *ss, JSOp op) -{ - ptrdiff_t off; - - off = PopOff(ss, op); - return OFF2STR(&ss->sprinter, off); -} - +#if JS_HAS_SWITCH_STATEMENT typedef struct TableEntry { jsval key; ptrdiff_t offset; @@ -909,21 +728,18 @@ typedef struct TableEntry { jsint order; /* source order for stable tableswitch sort */ } TableEntry; -static JSBool -CompareOffsets(void *arg, const void *v1, const void *v2, int *result) +static int +CompareOffsets(const void *v1, const void *v2, void *arg) { - ptrdiff_t offset_diff; const TableEntry *te1 = (const TableEntry *) v1, *te2 = (const TableEntry *) v2; - offset_diff = te1->offset - te2->offset; - *result = (offset_diff == 0 ? te1->order - te2->order - : offset_diff < 0 ? -1 - : 1); - return JS_TRUE; + if (te1->offset == te2->offset) + return (int) (te1->order - te2->order); + return (int) (te1->offset - te2->offset); } -static jsbytecode * +static JSBool Decompile(SprintStack *ss, jsbytecode *pc, intN nb); static JSBool @@ -933,20 +749,17 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, { JSContext *cx; JSPrinter *jp; - ptrdiff_t off, off2, diff, caseExprOff; char *lval, *rval; uintN i; + ptrdiff_t diff, off, off2, caseExprOff; jsval key; JSString *str; cx = ss->sprinter.context; jp = ss->printer; - /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ - off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); - lval = OFF2STR(&ss->sprinter, off); - - js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval); + lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP)); + js_printf(jp, "\tswitch (%s) {\n", lval); if (tableLength) { diff = table[0].offset - defaultOffset; @@ -959,7 +772,9 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, jp->indent -= 4; } - caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0; + caseExprOff = isCondSwitch + ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length + : 0; for (i = 0; i < tableLength; i++) { off = table[i].offset; @@ -982,9 +797,6 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_FALSE; } caseExprOff = nextCaseExprOff; - - /* Balance the stack as if this JSOP_CASE matched. */ - --ss->top; } else { /* * key comes from an atom, not the decompiler, so we need to @@ -1026,10 +838,6 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, if (!Decompile(ss, pc + off, off2 - off)) return JS_FALSE; jp->indent -= 4; - - /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */ - if (isCondSwitch) - ++ss->top; } } @@ -1039,12 +847,9 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, jp->indent -= 2; } js_printf(jp, "\t}\n"); - - /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */ - if (isCondSwitch) - --ss->top; return JS_TRUE; } +#endif static JSAtom * GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) @@ -1074,450 +879,27 @@ GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) return NULL; } -/* - * NB: Indexed by SRC_DECL_* defines from jsemit.h. - */ -static const char * const var_prefix[] = {"var ", "const ", "let "}; - static const char * VarPrefix(jssrcnote *sn) { - if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) { - ptrdiff_t type = js_GetSrcNoteOffset(sn, 0); - if ((uintN)type <= SRC_DECL_LET) - return var_prefix[type]; + if (sn) { + if (SN_TYPE(sn) == SRC_VAR) + return "var "; + if (SN_TYPE(sn) == SRC_CONST) + return "const "; } return ""; } -#define LOCAL_ASSERT_RV(expr, rv) \ - JS_BEGIN_MACRO \ - JS_ASSERT(expr); \ - if (!(expr)) return (rv); \ - JS_END_MACRO -const char * -GetLocal(SprintStack *ss, jsint i) -{ - ptrdiff_t off; - JSContext *cx; - JSScript *script; - jsatomid j, n; - JSAtom *atom; - JSObject *obj; - jsint depth, count; - JSScopeProperty *sprop; - const char *rval; - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") - - off = ss->offsets[i]; - if (off >= 0) - return OFF2STR(&ss->sprinter, off); - - /* - * We must be called from js_DecompileValueGenerator (via Decompile) when - * dereferencing a local that's undefined or null. Search script->atomMap - * for the block containing this local by its stack index, i. - */ - cx = ss->sprinter.context; - script = ss->printer->script; - for (j = 0, n = script->atomMap.length; j < n; j++) { - atom = script->atomMap.vector[j]; - if (ATOM_IS_OBJECT(atom)) { - obj = ATOM_TO_OBJECT(atom); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - if ((jsuint)(i - depth) < (jsuint)count) - break; - } - } - } - - LOCAL_ASSERT(j < n); - i -= depth; - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->shortid == i) - break; - } - - LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - return rval; - -#undef LOCAL_ASSERT -} - -#if JS_HAS_DESTRUCTURING - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) -#define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op = *pc])->length) - -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); - -static jsbytecode * -DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - JSBool *hole) -{ - JSContext *cx; - JSPrinter *jp; - JSOp op; - const JSCodeSpec *cs; - uintN oplen, i; - const char *lval, *xval; - ptrdiff_t todo; - JSAtom *atom; - - *hole = JS_FALSE; - cx = ss->sprinter.context; - jp = ss->printer; - LOAD_OP_DATA(pc); - - switch (op) { - case JSOP_POP: - *hole = JS_TRUE; - todo = SprintPut(&ss->sprinter, ", ", 2); - break; - - case JSOP_DUP: - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - lval = PopStr(ss, JSOP_NOP); - todo = SprintCString(&ss->sprinter, lval); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(*pc == JSOP_POP); - break; - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - case JSOP_SETLOCAL: - LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP); - /* FALL THROUGH */ - - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - atom = NULL; - lval = NULL; - if (op == JSOP_SETARG) - atom = GetSlotAtom(jp, js_GetArgument, i); - else if (op == JSOP_SETVAR) - atom = GetSlotAtom(jp, js_GetLocalVariable, i); - else if (op == JSOP_SETGVAR) - atom = GET_ATOM(cx, jp->script, pc); - else - lval = GetLocal(ss, i); - if (atom) - lval = js_AtomToPrintableString(cx, atom); - LOCAL_ASSERT(lval); - todo = SprintCString(&ss->sprinter, lval); - if (op != JSOP_SETLOCALPOP) { - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(op == JSOP_POP); - } - break; - - default: - /* - * We may need to auto-parenthesize the left-most value decompiled - * here, so add back PAREN_SLOP temporarily. Then decompile until the - * opcode that would reduce the stack depth to (ss->top-1), which we - * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for - * the nb parameter. - */ - todo = ss->sprinter.offset; - ss->sprinter.offset = todo + PAREN_SLOP; - pc = Decompile(ss, pc, -ss->top); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); - xval = PopStr(ss, JSOP_NOP); - lval = PopStr(ss, JSOP_GETPROP); - ss->sprinter.offset = todo; - if (*lval == '\0') { - /* lval is from JSOP_BINDNAME, so just print xval. */ - todo = SprintCString(&ss->sprinter, xval); - } else if (*xval == '\0') { - /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ - todo = SprintCString(&ss->sprinter, lval); - } else { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[ss->opcodes[ss->top+1]].format - & JOF_XMLNAME) - ? "%s.%s" - : "%s[%s]", - lval, xval); - } - break; - } - - if (todo < 0) - return NULL; - - LOCAL_ASSERT(pc < endpc); - pc += oplen; - return pc; -} - -/* - * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring - * left-hand side object or array initialiser, including nested destructuring - * initialisers. On successful return, the decompilation will be pushed on ss - * and the return value will point to the POP or GROUP bytecode following the - * destructuring expression. - * - * At any point, if pc is equal to endpc and would otherwise advance, we stop - * immediately and return endpc. - */ -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) -{ - ptrdiff_t head, todo; - JSContext *cx; - JSPrinter *jp; - JSOp op, saveop; - const JSCodeSpec *cs; - uintN oplen; - jsint i, lasti; - jsdouble d; - const char *lval; - jsbytecode *pc2; - jsatomid atomIndex; - JSAtom *atom; - jssrcnote *sn; - JSString *str; - JSBool hole; - - LOCAL_ASSERT(*pc == JSOP_DUP); - pc += JSOP_DUP_LENGTH; - - /* - * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP - * chars so the destructuring decompilation accumulates contiguously in - * ss->sprinter starting with "[". - */ - head = SprintPut(&ss->sprinter, "[", 1); - if (head < 0 || !PushOff(ss, head, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - LOCAL_ASSERT(head == ss->sprinter.offset - 1); - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); - - cx = ss->sprinter.context; - jp = ss->printer; - lasti = -1; - - while (pc < endpc) { - LOAD_OP_DATA(pc); - saveop = op; - - switch (op) { - case JSOP_POP: - pc += oplen; - goto out; - - /* Handle the optimized number-pushing opcodes. */ - case JSOP_ZERO: d = i = 0; goto do_getelem; - case JSOP_ONE: d = i = 1; goto do_getelem; - case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem; - case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem; - - /* Handle the extended literal form of JSOP_NUMBER. */ - case JSOP_LITOPX: - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - LOCAL_ASSERT(op == JSOP_NUMBER); - goto do_getatom; - - case JSOP_NUMBER: - atomIndex = GET_ATOM_INDEX(pc); - - do_getatom: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - d = *ATOM_TO_DOUBLE(atom); - LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d)); - i = (jsint)d; - - do_getelem: - sn = js_GetSrcNote(jp->script, pc); - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_GETELEM); - - /* Distinguish object from array by opcode or source note. */ - if (saveop == JSOP_LITERAL || - (sn && SN_TYPE(sn) == SRC_INITPROP)) { - *OFF2STR(&ss->sprinter, head) = '{'; - if (Sprint(&ss->sprinter, "%g: ", d) < 0) - return NULL; - } else { - /* Sanity check for the gnarly control flow above. */ - LOCAL_ASSERT(i == d); - - /* Fill in any holes (holes at the end don't matter). */ - while (++lasti < i) { - if (SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - } - break; - - case JSOP_LITERAL: - atomIndex = GET_LITERAL_INDEX(pc); - goto do_getatom; - - case JSOP_GETPROP: - *OFF2STR(&ss->sprinter, head) = '{'; - atom = GET_ATOM(cx, jp->script, pc); - str = ATOM_TO_STRING(atom); - if (!QuoteString(&ss->sprinter, str, - js_IsIdentifier(str) ? 0 : (jschar)'\'')) { - return NULL; - } - if (SprintPut(&ss->sprinter, ": ", 2) < 0) - return NULL; - break; - - default: - LOCAL_ASSERT(0); - } - - pc += oplen; - if (pc == endpc) - return pc; - - /* - * Decompile the left-hand side expression whose bytecode starts at pc - * and continues for a bounded number of bytecodes or stack operations - * (and which in any event stops before endpc). - */ - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc || *pc != JSOP_DUP) - break; - - /* - * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another - * destructuring initialiser abuts this one, and we should stop. This - * happens with source of the form '[a] = [b] = c'. - */ - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_DESTRUCT) - break; - - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - - pc += JSOP_DUP_LENGTH; - } - -out: - lval = OFF2STR(&ss->sprinter, head); - todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1); - if (todo < 0) - return NULL; - return pc; -} - -static jsbytecode * -DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - jssrcnote *sn, ptrdiff_t *todop) -{ - JSOp op; - const JSCodeSpec *cs; - uintN oplen, start, end, i; - ptrdiff_t todo; - JSBool hole; - const char *rval; - - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); - - todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - - for (;;) { - pc += oplen; - if (pc == endpc) - return pc; - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op != JSOP_PUSH && op != JSOP_GETLOCAL) - break; - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - - LOCAL_ASSERT(op == JSOP_SETSP); - if (SprintPut(&ss->sprinter, "] = [", 5) < 0) - return NULL; - - start = GET_UINT16(pc); - end = ss->top - 1; - for (i = start; i < end; i++) { - rval = GetStr(ss, i); - if (Sprint(&ss->sprinter, "%s%s", - (i == start) ? "" : ", ", - (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) { - return NULL; - } - } - - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - ss->sprinter.offset = ss->offsets[i]; - ss->top = start; - *todop = todo; - return pc; -} - -#undef LOCAL_ASSERT -#undef LOAD_OP_DATA - -#endif /* JS_HAS_DESTRUCTURING */ - -/* - * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise - * the decompiler starts at pc and continues until it reaches an opcode for - * which decompiling would result in the stack depth equaling -(nb + 1). - */ -static jsbytecode * +static JSBool Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { JSContext *cx; JSPrinter *jp, *jp2; - jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done; + jsbytecode *endpc, *done, *forelem_tail, *forelem_done; ptrdiff_t tail, todo, len, oplen, cond, next; JSOp op, lastop, saveop; - const JSCodeSpec *cs; + const JSCodeSpec *cs, *topcs; jssrcnote *sn, *sn2; const char *lval, *rval, *xval, *fmt; jsint i, argc; @@ -1535,34 +917,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #endif jsval val; int stackDummy; - - static const char exception_cookie[] = "/*EXCEPTION*/"; - static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; - static const char forelem_cookie[] = "/*FORELEM*/"; - static const char with_cookie[] = "/*WITH*/"; - static const char dot_format[] = "%s.%s"; - static const char index_format[] = "%s[%s]"; - static const char predot_format[] = "%s%s.%s"; - static const char postdot_format[] = "%s.%s%s"; - static const char preindex_format[] = "%s%s[%s]"; - static const char postindex_format[] = "%s[%s]%s"; - static const char ss_format[] = "%s%s"; + static const char catch_cookie[] = "/*CATCH*/"; + static const char with_cookie[] = "/*WITH*/"; /* * Local macros */ -#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return NULL -#define POP_STR() PopStr(ss, op) -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, 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 * common ATOM_TO_STRING(atom) here and near the call sites. */ -#define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom)) -#define ATOM_IS_KEYWORD(atom) \ - (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \ - JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF) +#define ATOM_IS_IDENTIFIER(atom) \ + (!ATOM_KEYWORD(atom) && js_IsIdentifier(ATOM_TO_STRING(atom))) /* * Given an atom already fetched from jp->script's atom map, quote/escape its @@ -1581,7 +951,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } \ rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ if (!rval) \ - return NULL; \ + return JS_FALSE; \ JS_END_MACRO /* @@ -1597,162 +967,64 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) cx = ss->sprinter.context; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return NULL; + return JS_FALSE; } jp = ss->printer; - startpc = pc; - endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb; + endpc = pc + nb; forelem_tail = forelem_done = NULL; tail = -1; todo = -2; /* NB: different from Sprint() error return. */ - saveop = JSOP_NOP; + op = JSOP_NOP; sn = NULL; rval = NULL; #if JS_HAS_XML_SUPPORT foreach = inXML = quoteAttr = JS_FALSE; #endif - while (nb < 0 || pc < endpc) { - /* - * Move saveop to lastop so prefixed bytecodes can take special action - * while sharing maximal code. Set op and saveop to the new bytecode, - * use op in POP_STR to trigger automatic parenthesization, but push - * saveop at the bottom of the loop if this op pushes. Thus op may be - * set to nop or otherwise mutated to suppress auto-parens. - */ - lastop = saveop; + while (pc < endpc) { + lastop = op; op = saveop = (JSOp) *pc; + if (op >= JSOP_LIMIT) { + switch (op) { + case JSOP_GETPROP2: + saveop = JSOP_GETPROP; + break; + case JSOP_GETELEM2: + saveop = JSOP_GETELEM; + break; + default:; + } + } cs = &js_CodeSpec[saveop]; len = oplen = cs->length; - if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs) - return pc; - - if (pc + oplen == jp->dvgfence) { - JSStackFrame *fp; - uint32 format, mode, type; - - /* - * Rewrite non-get ops to their "get" format if the error is in - * the bytecode at pc, so we don't decompile more than the error - * expression. - */ - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - format = cs->format; - if (((fp && pc == fp->pc) || - (pc == startpc && cs->nuses != 0)) && - format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) { - mode = (format & JOF_MODEMASK); - if (mode == JOF_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 to - * JSOP_NAME. - */ - type = format & JOF_TYPEMASK; - op = (type == JOF_QARG) - ? JSOP_GETARG - : (type == JOF_QVAR) - ? JSOP_GETVAR - : (type == JOF_LOCAL) - ? JSOP_GETLOCAL - : JSOP_NAME; - - i = cs->nuses - js_CodeSpec[op].nuses; - while (--i >= 0) - PopOff(ss, JSOP_NOP); - } else { - /* - * We must replace the faulting pc's bytecode with a - * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM}, - * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to - * throw away the assignment op's right-hand operand and - * decompile it as if it were a GET of its left-hand - * operand. - */ - if (mode == JOF_PROP) { - op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; - } else if (mode == JOF_ELEM) { - op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; - } else { - /* - * Zero mode means precisely that op is uncategorized - * for our purposes, so we must write per-op special - * case code here. - */ - switch (op) { - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - op = JSOP_GETELEM; - break; -#if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: - op = JSOP_CALL; - break; -#endif - default: - LOCAL_ASSERT(0); - } - } - } - } - - saveop = op; - if (op >= JSOP_LIMIT) { - switch (op) { - case JSOP_GETPROP2: - saveop = JSOP_GETPROP; - break; - case JSOP_GETELEM2: - saveop = JSOP_GETELEM; - break; - default:; - } - } - LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen); - - jp->dvgfence = NULL; - } - if (cs->token) { switch (cs->nuses) { case 2: - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - /* - * Avoid over-parenthesizing y in x op= y based on its - * expansion: x = x op y (replace y by z = w to see the - * problem). - */ - op = pc[oplen]; - LOCAL_ASSERT(op != saveop); - } rval = POP_STR(); lval = POP_STR(); - if (op != saveop) { + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { /* Print only the right operand of the assignment-op. */ - todo = SprintCString(&ss->sprinter, rval); - op = saveop; + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); } else if (!inXML) { todo = Sprint(&ss->sprinter, "%s %s %s", lval, cs->token, rval); } else { /* In XML, just concatenate the two operands. */ - LOCAL_ASSERT(op == JSOP_ADD); - todo = Sprint(&ss->sprinter, ss_format, lval, rval); + JS_ASSERT(op == JSOP_ADD); + todo = Sprint(&ss->sprinter, "%s%s", lval, rval); } break; case 1: rval = POP_STR(); - todo = Sprint(&ss->sprinter, ss_format, cs->token, rval); + todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval); break; case 0: - todo = SprintCString(&ss->sprinter, cs->token); + todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token)); break; default: @@ -1779,10 +1051,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) sn = js_GetSrcNote(jp->script, pc); todo = -2; switch (sn ? SN_TYPE(sn) : SRC_NULL) { +#if JS_HAS_DO_WHILE_LOOP case SRC_WHILE: - js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n"); + js_printf(jp, "\tdo {\n"); jp->indent += 4; break; +#endif /* JS_HAS_DO_WHILE_LOOP */ case SRC_FOR: rval = ""; @@ -1816,7 +1090,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } /* Do the loop body. */ - js_printf(SET_MAYBE_BRACE(jp), ") {\n"); + js_printf(jp, ") {\n"); jp->indent += 4; oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0; DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); @@ -1833,9 +1107,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) jp->indent -= 4; rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval); + js_printf(jp, "\t%s:\n", rval); jp->indent += 4; break; @@ -1844,9 +1118,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval); + js_printf(jp, "\t%s: {\n", rval); jp->indent += 4; break; @@ -1855,17 +1129,66 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) js_printf(jp, "\t}\n"); break; + case SRC_CATCH: + jp->indent -= 4; + sn = js_GetSrcNote(jp->script, pc); + pc += oplen; + js_printf(jp, "\t} catch ("); + + LOCAL_ASSERT(*pc == JSOP_NAME); + pc += js_CodeSpec[JSOP_NAME].length; + LOCAL_ASSERT(*pc == JSOP_PUSHOBJ); + pc += js_CodeSpec[JSOP_PUSHOBJ].length; + LOCAL_ASSERT(*pc == JSOP_NEWINIT); + pc += js_CodeSpec[JSOP_NEWINIT].length; + LOCAL_ASSERT(*pc == JSOP_EXCEPTION); + pc += js_CodeSpec[JSOP_EXCEPTION].length; + 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); + LOCAL_ASSERT(*pc == JSOP_ENTERWITH); + pc += js_CodeSpec[JSOP_ENTERWITH].length; + + len = js_GetSrcNoteOffset(sn, 0); + if (len) { + js_printf(jp, " if "); + DECOMPILE_CODE(pc, len); + js_printf(jp, "%s", POP_STR()); + pc += len; + LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + pc += js_CodeSpec[*pc].length; + } + + js_printf(jp, ") {\n"); + jp->indent += 4; + todo = Sprint(&ss->sprinter, catch_cookie); + len = 0; + break; + case SRC_FUNCDEF: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); + JS_ASSERT(ATOM_IS_OBJECT(atom)); do_function: obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), jp->indent, jp->pretty); if (!jp2) - return NULL; + return JS_FALSE; jp2->scope = jp->scope; js_puts(jp2, "\n"); ok = js_DecompileFunction(jp2, fun); @@ -1879,90 +1202,49 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } js_DestroyPrinter(jp2); if (!ok) - return NULL; + return JS_FALSE; break; - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, len - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - default:; } + case JSOP_RETRVAL: break; case JSOP_GROUP: - cs = &js_CodeSpec[lastop]; - if ((cs->prec != 0 && - cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) || - pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ || - pc[JSOP_GROUP_LENGTH] == JSOP_DUP) { - /* - * Force parens if this JSOP_GROUP forced re-association - * against precedence, or if this is a call or constructor - * expression, or if it is destructured (JSOP_DUP). - * - * This is necessary to handle the operator new grammar, - * by which new x(y).z means (new x(y))).z. For example - * new (x(y).z) must decompile with the constructor - * parenthesized, but normal precedence has JSOP_GETPROP - * (for the final .z) higher than JSOP_NEW. In general, - * if the call or constructor expression is parenthesized, - * we preserve parens. - */ - op = JSOP_NAME; - rval = POP_STR(); - todo = SprintCString(&ss->sprinter, rval); - } else { - /* - * Don't explicitly parenthesize -- just fix the top - * opcode so that the auto-parens magic in PopOff can do - * its thing. - */ - LOCAL_ASSERT(ss->top != 0); - ss->opcodes[ss->top-1] = saveop = lastop; - todo = -2; - } - break; + /* Use last real op so PopOff adds parens if needed. */ + todo = PopOff(ss, lastop); - case JSOP_STARTITER: - todo = -2; + /* Now add user-supplied parens only if PopOff did not. */ + cs = &js_CodeSpec[lastop]; + topcs = &js_CodeSpec[ss->opcodes[ss->top]]; + if (topcs->prec >= cs->prec) { + todo = Sprint(&ss->sprinter, "(%s)", + OFF2STR(&ss->sprinter, todo)); + } break; case JSOP_PUSH: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - /* FALL THROUGH */ - case JSOP_PUSHOBJ: case JSOP_BINDNAME: do_JSOP_BINDNAME: todo = Sprint(&ss->sprinter, ""); break; +#if JS_HAS_EXCEPTIONS case JSOP_TRY: - js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n"); + js_printf(jp, "\ttry {\n"); jp->indent += 4; todo = -2; break; + { + static const char exception_cookie[] = "/*EXCEPTION*/"; + static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; + case JSOP_FINALLY: jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n"); + js_printf(jp, "\t} finally {\n"); jp->indent += 4; /* @@ -1972,7 +1254,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ todo = Sprint(&ss->sprinter, exception_cookie); if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; + return JS_FALSE; todo = Sprint(&ss->sprinter, retsub_pc_cookie); break; @@ -1983,6 +1265,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); todo = -2; break; + } case JSOP_SWAP: /* @@ -2003,161 +1286,41 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_SETSP: - { - uintN newtop, oldtop, i; - /* * The compiler models operand stack depth and fixes the stack * pointer on entry to a catch clause based on its depth model. * The decompiler must match the code generator's model, which * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops. */ - newtop = (uintN) GET_UINT16(pc); - oldtop = ss->top; - LOCAL_ASSERT(newtop <= oldtop); - todo = -2; - -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - todo = Sprint(&ss->sprinter, "%s[] = [", - VarPrefix(sn)); - if (todo < 0) - return NULL; - for (i = newtop; i < oldtop; i++) { - rval = OFF2STR(&ss->sprinter, ss->offsets[i]); - if (Sprint(&ss->sprinter, ss_format, - (i == newtop) ? "" : ", ", - (i == oldtop - 1 && *rval == '\0') - ? ", " : rval) < 0) { - return NULL; - } - } - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - - /* - * Kill newtop before the end_groupassignment: label by - * retracting/popping early. Control will either jump to - * do_forloop: or do_letheadbody: or else break from our - * case JSOP_SETSP: after the switch (*pc2) below. - */ - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } - - end_groupassignment: - /* - * Thread directly to the next opcode if we can, to handle - * the special cases of a group assignment in the first or - * last part of a for(;;) loop head, or in a let block or - * expression head. - * - * NB: todo at this point indexes space in ss->sprinter - * that is liable to be overwritten. The code below knows - * exactly how long rval lives, or else copies it down via - * SprintCString. - */ - rval = OFF2STR(&ss->sprinter, todo); - todo = -2; - pc2 = pc + oplen; - switch (*pc2) { - case JSOP_NOP: - /* First part of for(;;) or let block/expr head. */ - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - if (SN_TYPE(sn) == SRC_FOR) { - pc = pc2; - goto do_forloop; - } - if (SN_TYPE(sn) == SRC_DECL) { - if (ss->top == jp->script->depth) { - /* - * This must be an empty destructuring - * in the head of a let whose body block - * is also empty. - */ - pc = pc2 + 1; - len = js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); - js_printf(jp, "\tlet (%s) {\n", rval); - js_printf(jp, "\t}\n"); - goto end_setsp; - } - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - op = JSOP_POP; - pc = pc2 + 1; - goto do_letheadbody; - } - } - break; - - case JSOP_GOTO: - case JSOP_GOTOX: - /* Third part of for(;;) loop head. */ - cond = GetJumpOffset(pc2, pc2); - sn = js_GetSrcNote(jp->script, pc2 + cond - 1); - if (sn && SN_TYPE(sn) == SRC_FOR) { - todo = SprintCString(&ss->sprinter, rval); - saveop = JSOP_NOP; - } - break; - } - - /* - * If control flow reaches this point with todo still -2, - * just print rval as an expression statement. - */ - if (todo == -2) - js_printf(jp, "\t%s;\n", rval); - end_setsp: - break; - } -#endif - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } + LOCAL_ASSERT(ss->top >= (uintN) GET_ATOM_INDEX(pc)); + ss->top = (uintN) GET_ATOM_INDEX(pc); break; - } case JSOP_EXCEPTION: - /* The catch decompiler handles this op itself. */ - LOCAL_ASSERT(JS_FALSE); + /* + * The only other JSOP_EXCEPTION case occurs as part of a code + * sequence that follows a SRC_CATCH-annotated JSOP_NOP. + */ + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN); + todo = -2; break; +#endif /* JS_HAS_EXCEPTIONS */ case JSOP_POP: - /* - * By default, do not automatically parenthesize when popping - * a stacked expression decompilation. We auto-parenthesize - * only when JSOP_POP is annotated with SRC_PCDELTA, meaning - * comma operator. - */ - op = JSOP_POPV; - /* FALL THROUGH */ - case JSOP_POPV: sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_FOR: - /* Force parens around 'in' expression at 'for' front. */ - if (ss->opcodes[ss->top-1] == JSOP_IN) - op = JSOP_LSH; rval = POP_STR(); todo = -2; goto do_forloop; case SRC_PCDELTA: - /* Comma operator: use JSOP_POP for correct precedence. */ - op = JSOP_POP; - /* Pop and save to avoid blowing stack depth budget. */ lval = JS_strdup(cx, POP_STR()); if (!lval) - return NULL; + return JS_FALSE; /* * The offset tells distance to the end of the right-hand @@ -2169,7 +1332,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (!Decompile(ss, done, pc - done)) { JS_free(cx, (char *)lval); - return NULL; + return JS_FALSE; } /* Pop Decompile result and print comma expression. */ @@ -2183,91 +1346,25 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; - case SRC_DECL: - /* This pop is at the end of the let block/expr head. */ - pc += JSOP_POP_LENGTH; -#if JS_HAS_DESTRUCTURING - do_letheadbody: -#endif - len = js_GetSrcNoteOffset(sn, 0); - if (pc[len] == JSOP_LEAVEBLOCK) { - js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n", - POP_STR()); - jp->indent += 4; - DECOMPILE_CODE(pc, len); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - } else { - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); - - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - - if (!Decompile(ss, pc, len)) { - JS_free(cx, (char *)lval); - return NULL; - } - rval = POP_STR(); - todo = Sprint(&ss->sprinter, - (*rval == '{') - ? "let (%s) (%s)" - : "let (%s) %s", - lval, rval); - JS_free(cx, (char *)lval); - } - break; - default: - /* Turn off parens around a yield statement. */ - if (ss->opcodes[ss->top-1] == JSOP_YIELD) - op = JSOP_NOP; - rval = POP_STR(); - if (*rval != '\0') { -#if JS_HAS_BLOCK_SCOPE - /* - * If a let declaration is the only child of a control - * structure that does not require braces, it must not - * be braced. If it were braced explicitly, it would - * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK. - */ - if (jp->braceState == MAYBE_BRACE && - pc + JSOP_POP_LENGTH == endpc && - !strncmp(rval, var_prefix[SRC_DECL_LET], 4) && - rval[4] != '(') { - SetDontBrace(jp); - } -#endif - js_printf(jp, - (*rval == '{' || - (strncmp(rval, js_function_str, 8) == 0 && - rval[8] == ' ')) - ? "\t(%s);\n" - : "\t%s;\n", - rval); - } + if (*rval != '\0') + js_printf(jp, "\t%s;\n", rval); todo = -2; break; } break; case JSOP_POP2: - case JSOP_ENDITER: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; (void) PopOff(ss, op); - if (op == JSOP_POP2) - (void) PopOff(ss, op); + (void) PopOff(ss, op); + todo = -2; break; case JSOP_ENTERWITH: - LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); + JS_ASSERT(!js_GetSrcNote(jp->script, pc)); rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval); + js_printf(jp, "\twith (%s) {\n", rval); jp->indent += 4; todo = Sprint(&ss->sprinter, with_cookie); break; @@ -2278,276 +1375,28 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; rval = POP_STR(); + if (sn && SN_TYPE(sn) == SRC_CATCH) { + LOCAL_ASSERT(strcmp(rval, catch_cookie) == 0); + LOCAL_ASSERT((uintN) js_GetSrcNoteOffset(sn, 0) == ss->top); + break; + } LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); jp->indent -= 4; js_printf(jp, "\t}\n"); break; - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK) - { - JSAtom **atomv, *smallv[5]; - JSScopeProperty *sprop; - - obj = ATOM_TO_OBJECT(atom); - argc = OBJ_BLOCK_COUNT(cx, obj); - if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) { - atomv = smallv; - } else { - atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *)); - if (!atomv) - return NULL; - } - - /* From here on, control must flow through enterblock_out. */ - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - LOCAL_ASSERT(sprop->shortid < argc); - atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id); - } - ok = JS_TRUE; - for (i = 0; i < argc; i++) { - atom = atomv[i]; - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval || - !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { - ok = JS_FALSE; - goto enterblock_out; - } - } - - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { -#if JS_HAS_BLOCK_SCOPE - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen) != NULL; - if (!ok) - goto enterblock_out; - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; -#endif - - case SRC_CATCH: - jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch ("); - - pc2 = pc; - pc += oplen; - LOCAL_ASSERT(*pc == JSOP_EXCEPTION); - pc += JSOP_EXCEPTION_LENGTH; - if (*pc == JSOP_DUP) { - sn2 = js_GetSrcNote(jp->script, pc); - if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) { - /* - * This is a hidden dup to save the exception for - * later. It must exist only when the catch has - * an exception guard. - */ - LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); - pc += JSOP_DUP_LENGTH; - } - } -#if JS_HAS_DESTRUCTURING - if (*pc == JSOP_DUP) { - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - goto enterblock_out; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(ss, JSOP_NOP); - js_puts(jp, lval); - } else { -#endif - LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); - i = GET_UINT16(pc); - pc += JSOP_SETLOCALPOP_LENGTH; - atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; - str = ATOM_TO_STRING(atom); - if (!QuoteString(&jp->sprinter, str, 0)) { - ok = JS_FALSE; - goto enterblock_out; - } -#if JS_HAS_DESTRUCTURING - } -#endif - - len = js_GetSrcNoteOffset(sn, 0); - if (len) { - len -= PTRDIFF(pc, pc2, jsbytecode); - LOCAL_ASSERT(len > 0); - js_printf(jp, " if "); - ok = Decompile(ss, pc, len) != NULL; - if (!ok) - goto enterblock_out; - js_printf(jp, "%s", POP_STR()); - pc += len; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - pc += js_CodeSpec[*pc].length; - } - - js_printf(jp, ") {\n"); - jp->indent += 4; - len = 0; - break; - } - - todo = -2; - - enterblock_out: - if (atomv != smallv) - JS_free(cx, atomv); - if (!ok) - return NULL; - } - END_LITOPX_CASE - - case JSOP_LEAVEBLOCK: - case JSOP_LEAVEBLOCKEXPR: - { - uintN top, depth; - - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (op == JSOP_LEAVEBLOCKEXPR) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE); - rval = POP_STR(); - } else if (sn) { - LOCAL_ASSERT(op == JSOP_LEAVEBLOCK); - if (SN_TYPE(sn) == SRC_HIDDEN) - break; - LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH); - LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top); - } - top = ss->top; - depth = GET_UINT16(pc); - LOCAL_ASSERT(top >= depth); - top -= depth; - ss->top = top; - ss->sprinter.offset = GetOff(ss, top); - if (op == JSOP_LEAVEBLOCKEXPR) - todo = SprintCString(&ss->sprinter, rval); - break; - } - - case JSOP_GETLOCAL: - i = GET_UINT16(pc); - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT((uintN)i < ss->top); - rval = GetLocal(ss, i); - -#if JS_HAS_DESTRUCTURING - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - - todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval); - break; - - case JSOP_SETLOCAL: - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - rval = POP_STR(); - goto do_setlval; - - case JSOP_INCLOCAL: - case JSOP_DECLOCAL: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_inclval; - - case JSOP_LOCALINC: - case JSOP_LOCALDEC: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_lvalinc; - - case JSOP_FORLOCAL: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - atom = NULL; - goto do_forlvalinloop; - - case JSOP_RETRVAL: - todo = -2; - break; - case JSOP_SETRVAL: case JSOP_RETURN: + lval = js_CodeSpec[JSOP_RETURN].name; rval = POP_STR(); if (*rval != '\0') - js_printf(jp, "\t%s %s;\n", js_return_str, rval); + js_printf(jp, "\t%s %s;\n", lval, rval); else - js_printf(jp, "\t%s;\n", js_return_str); + js_printf(jp, "\t%s;\n", lval); todo = -2; break; -#if JS_HAS_GENERATORS - case JSOP_YIELD: - op = JSOP_SETNAME; /* turn off most parens */ - rval = POP_STR(); - todo = (*rval != '\0') - ? Sprint(&ss->sprinter, - (strncmp(rval, js_yield_str, 5) == 0 && - (rval[5] == ' ' || rval[5] == '\0')) - ? "%s (%s)" - : "%s %s", - js_yield_str, rval) - : SprintCString(&ss->sprinter, js_yield_str); - break; - - case JSOP_ARRAYPUSH: - { - uintN pos, blockpos, startpos; - ptrdiff_t start; - - rval = POP_STR(); - pos = ss->top; - while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK && - op != JSOP_NEWINIT) { - LOCAL_ASSERT(pos != 0); - } - blockpos = pos; - while (ss->opcodes[pos] == JSOP_ENTERBLOCK) { - if (pos == 0) - break; - --pos; - } - LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); - startpos = pos; - start = ss->offsets[pos]; - LOCAL_ASSERT(ss->sprinter.base[start] == '[' || - ss->sprinter.base[start] == '#'); - pos = blockpos; - while (ss->opcodes[++pos] == JSOP_STARTITER) - LOCAL_ASSERT(pos < ss->top); - LOCAL_ASSERT(pos < ss->top); - xval = OFF2STR(&ss->sprinter, ss->offsets[pos]); - lval = OFF2STR(&ss->sprinter, start); - RETRACT(&ss->sprinter, lval); - todo = Sprint(&ss->sprinter, "%s%s%.*s", - lval, rval, rval - xval, xval); - if (todo < 0) - return NULL; - ss->offsets[startpos] = todo; - todo = -2; - break; - } -#endif - +#if JS_HAS_EXCEPTIONS case JSOP_THROWING: todo = -2; break; @@ -2560,6 +1409,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = POP_STR(); js_printf(jp, "\t%s %s;\n", cs->name, rval); break; +#endif /* JS_HAS_EXCEPTIONS */ case JSOP_GOTO: case JSOP_GOTOX: @@ -2570,7 +1420,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); js_printf(jp, "\tcontinue %s;\n", rval); break; @@ -2582,7 +1432,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); js_printf(jp, "\tbreak %s;\n", rval); break; @@ -2597,73 +1447,37 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_IFEQ: case JSOP_IFEQX: - { - JSBool elseif = JS_FALSE; - - if_again: len = GetJumpOffset(pc, pc); sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_IF: case SRC_IF_ELSE: - op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); - if (ss->inArrayInit) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); - if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) - return NULL; - } else { - js_printf(SET_MAYBE_BRACE(jp), - elseif ? " if (%s) {\n" : "\tif (%s) {\n", - rval); - jp->indent += 4; - } - + js_printf(jp, "\tif (%s) {\n", rval); + jp->indent += 4; if (SN_TYPE(sn) == SRC_IF) { DECOMPILE_CODE(pc + oplen, len - oplen); } else { - LOCAL_ASSERT(!ss->inArrayInit); - tail = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, tail - oplen); + len = js_GetSrcNoteOffset(sn, 0); + DECOMPILE_CODE(pc + oplen, len - oplen); jp->indent -= 4; - pc += tail; + pc += len; LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); oplen = js_CodeSpec[*pc].length; len = GetJumpOffset(pc, pc); - js_printf(jp, "\t} else"); - - /* - * If the second offset for sn is non-zero, it tells - * the distance from the goto around the else, to the - * ifeq for the if inside the else that forms an "if - * else if" chain. Thus cond spans the condition of - * the second if, so we simply decompile it and start - * over at label if_again. - */ - cond = js_GetSrcNoteOffset(sn, 1); - if (cond != 0) { - DECOMPILE_CODE(pc + oplen, cond - oplen); - pc += cond; - elseif = JS_TRUE; - goto if_again; - } - - js_printf(SET_MAYBE_BRACE(jp), " {\n"); + js_printf(jp, "\t} else {\n"); jp->indent += 4; DECOMPILE_CODE(pc + oplen, len - oplen); } - - if (!ss->inArrayInit) { - jp->indent -= 4; - js_printf(jp, "\t}\n"); - } + jp->indent -= 4; + js_printf(jp, "\t}\n"); todo = -2; break; case SRC_WHILE: rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval); + js_printf(jp, "\twhile (%s) {\n", rval); jp->indent += 4; tail = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, tail - oplen); @@ -2675,13 +1489,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case SRC_COND: xval = JS_strdup(cx, POP_STR()); if (!xval) - return NULL; + return JS_FALSE; len = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, len - oplen); lval = JS_strdup(cx, POP_STR()); if (!lval) { JS_free(cx, (void *)xval); - return NULL; + return JS_FALSE; } pc += len; LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); @@ -2699,14 +1513,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; } break; - } case JSOP_IFNE: case JSOP_IFNEX: +#if JS_HAS_DO_WHILE_LOOP /* Currently, this must be a do-while loop's upward branch. */ jp->indent -= 4; js_printf(jp, "\t} while (%s);\n", POP_STR()); todo = -2; +#else + JS_ASSERT(0); +#endif /* JS_HAS_DO_WHILE_LOOP */ break; case JSOP_OR: @@ -2717,7 +1534,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* Top of stack is the first clause in a disjunction (||). */ lval = JS_strdup(cx, POP_STR()); if (!lval) - return NULL; + return JS_FALSE; done = pc + GetJumpOffset(pc, pc); pc += len; len = PTRDIFF(done, pc, jsbytecode); @@ -2761,10 +1578,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) atom = GET_ATOM(cx, jp->script, pc); do_fornameinloop: - lval = ""; - do_forlvalinloop: sn = js_GetSrcNote(jp->script, pc); xval = NULL; + lval = ""; goto do_forinloop; case JSOP_FORPROP: @@ -2774,7 +1590,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), (jschar)'\''); if (!xval) - return NULL; + return JS_FALSE; atom = NULL; } lval = POP_STR(); @@ -2788,72 +1604,34 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) sn2 = js_GetSrcNote(jp->script, pc); tail = js_GetSrcNoteOffset(sn2, 0); - do_forinhead: - if (!atom && xval) { - /* - * If xval is not a dummy empty string, we have to strdup - * it to save it from being clobbered by the first Sprint - * below. Standard dumb decompiler operating procedure! - */ - if (*xval == '\0') { - xval = NULL; - } else { - xval = JS_strdup(cx, xval); - if (!xval) - return NULL; - } - } - + do_forinbody: #if JS_HAS_XML_SUPPORT if (foreach) { foreach = JS_FALSE; - todo = Sprint(&ss->sprinter, "for %s (%s%s", - js_each_str, VarPrefix(sn), lval); + js_printf(jp, "\tfor %s (%s%s", + js_each_str, VarPrefix(sn), lval); } else #endif - { - todo = Sprint(&ss->sprinter, "for (%s%s", - VarPrefix(sn), lval); - } + js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval); if (atom) { - if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0) - return NULL; xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!xval) - return NULL; - } else if (xval) { - LOCAL_ASSERT(*xval != '\0'); - ok = (Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? ".%s" - : "[%s]", - xval) - >= 0); - JS_free(cx, (char *)xval); - if (!ok) - return NULL; - } - if (todo < 0) - return NULL; - - lval = OFF2STR(&ss->sprinter, todo); - rval = GetStr(ss, ss->top-1); - RETRACT(&ss->sprinter, rval); - if (ss->inArrayInit) { - todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval); - if (todo < 0) - return NULL; - ss->offsets[ss->top-1] = todo; - ss->sprinter.offset += PAREN_SLOP; - DECOMPILE_CODE(pc + oplen, tail - oplen); - } else { - js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n", - lval, rval); - jp->indent += 4; - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); + return JS_FALSE; + RETRACT(&ss->sprinter, xval); + js_printf(jp, *lval ? ".%s" : "%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); + jp->indent += 4; + DECOMPILE_CODE(pc + oplen, tail - oplen); + jp->indent -= 4; + js_printf(jp, "\t}\n"); todo = -2; break; @@ -2864,12 +1642,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* * Arrange for the JSOP_ENUMELEM case to set tail for use by - * do_forinhead: code that uses on it to find the loop-closing + * do_forinbody: code that uses on it to find the loop-closing * jump (whatever its format, normal or extended), in order to * bound the recursively decompiled loop body. */ sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(!forelem_tail); + JS_ASSERT(!forelem_tail); forelem_tail = pc + js_GetSrcNoteOffset(sn, 0); /* @@ -2882,17 +1660,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * Since a for..in loop can't nest in the head of another for * loop, we can use forelem_{tail,done} singletons to remember * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto) - * to label do_forinhead. + * to label do_forinbody. */ - LOCAL_ASSERT(!forelem_done); + JS_ASSERT(!forelem_done); forelem_done = pc + GetJumpOffset(pc, pc); - - /* Our net stack balance after forelem;ifeq is +1. */ - todo = SprintCString(&ss->sprinter, forelem_cookie); break; case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: /* * The stack has the object under the (top) index expression. * The "rval" property id is underneath those two on the stack. @@ -2902,18 +1676,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ atom = NULL; xval = POP_STR(); - op = JSOP_GETELEM; /* lval must have high precedence */ lval = POP_STR(); - op = saveop; - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); - LOCAL_ASSERT(forelem_tail > pc); + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); + JS_ASSERT(forelem_tail > pc); tail = forelem_tail - pc; forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); + JS_ASSERT(forelem_done > pc); len = forelem_done - pc; forelem_done = NULL; - goto do_forinhead; + goto do_forinbody; #if JS_HAS_GETTER_SETTER case JSOP_GETTER: @@ -2923,54 +1694,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #endif case JSOP_DUP2: - rval = GetStr(ss, ss->top-2); - todo = SprintCString(&ss->sprinter, rval); + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]); + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2])) - return NULL; + return JS_FALSE; /* FALL THROUGH */ case JSOP_DUP: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - len = 0; - lval = POP_STR(); - op = saveop = JSOP_ENUMELEM; - rval = POP_STR(); - - if (strcmp(rval, forelem_cookie) == 0) { - LOCAL_ASSERT(forelem_tail > pc); - tail = forelem_tail - pc; - forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); - len = forelem_done - pc; - forelem_done = NULL; - xval = NULL; - atom = NULL; - - /* - * Null sn if this is a 'for (var [k, v] = i in o)' - * loop, because 'var [k, v = i;' has already been - * hoisted. - */ - if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR) - sn = NULL; - goto do_forinhead; - } - - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); - break; - } -#endif - - rval = GetStr(ss, ss->top-1); - saveop = ss->opcodes[ss->top-1]; - todo = SprintCString(&ss->sprinter, rval); + rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); + op = ss->opcodes[ss->top-1]; + todo = SprintPut(&ss->sprinter, rval, strlen(rval)); break; case JSOP_SETARG: @@ -2994,7 +1727,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) do_setname: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) - return NULL; + return JS_FALSE; rval = POP_STR(); if (op == JSOP_SETNAME) (void) PopOff(ss, op); @@ -3003,26 +1736,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) sn = js_GetSrcNote(jp->script, pc - 1); if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { todo = Sprint(&ss->sprinter, "%s %s= %s", - lval, - (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token, - rval); + lval, js_CodeSpec[lastop].token, rval); } else { sn = js_GetSrcNote(jp->script, pc); todo = Sprint(&ss->sprinter, "%s%s = %s", VarPrefix(sn), lval, rval); } - if (op == JSOP_SETLOCALPOP) { - if (!PushOff(ss, todo, saveop)) - return NULL; - rval = POP_STR(); - LOCAL_ASSERT(*rval != '\0'); - js_printf(jp, "\t%s;\n", rval); - todo = -2; - } break; case JSOP_NEW: @@ -3031,12 +1750,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #if JS_HAS_LVALUE_RETURN case JSOP_SETCALL: #endif - op = JSOP_SETNAME; /* turn off most parens */ + saveop = op; + op = JSOP_NOP; /* turn off parens */ argc = GET_ARGC(pc); argv = (char **) JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv); if (!argv) - return NULL; + return JS_FALSE; ok = JS_TRUE; for (i = argc; i > 0; i--) { @@ -3051,19 +1771,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); - op = saveop; + /* Get the callee's decompiled image in argv[0]. */ argv[0] = JS_strdup(cx, POP_STR()); if (!argv[i]) ok = JS_FALSE; lval = "(", rval = ")"; - if (op == JSOP_NEW) { - if (argc == 0) - lval = rval = ""; + if (saveop == JSOP_NEW) { todo = Sprint(&ss->sprinter, "%s %s%s", js_new_str, argv[0], lval); } else { - todo = Sprint(&ss->sprinter, ss_format, + todo = Sprint(&ss->sprinter, "%s%s", argv[0], lval); } if (todo < 0) @@ -3071,7 +1789,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) for (i = 1; i <= argc; i++) { if (!argv[i] || - Sprint(&ss->sprinter, ss_format, + Sprint(&ss->sprinter, "%s%s", argv[i], (i < argc) ? ", " : "") < 0) { ok = JS_FALSE; break; @@ -3086,11 +1804,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } JS_free(cx, argv); if (!ok) - return NULL; + return JS_FALSE; + op = saveop; #if JS_HAS_LVALUE_RETURN if (op == JSOP_SETCALL) { if (!PushOff(ss, todo, op)) - return NULL; + return JS_FALSE; todo = Sprint(&ss->sprinter, ""); } #endif @@ -3100,9 +1819,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) atom = GET_ATOM(cx, jp->script, pc); lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, lval); - do_delete_lval: todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); break; @@ -3113,12 +1831,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_DELELEM: - op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); - op = saveop; lval = POP_STR(); - if (*xval == '\0') - goto do_delete_lval; todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? "%s %s.%s" @@ -3135,7 +1849,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; #endif - case JSOP_TYPEOFEXPR: case JSOP_TYPEOF: case JSOP_VOID: rval = POP_STR(); @@ -3162,45 +1875,35 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) do_incatom: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, lval); - do_inclval: - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); + todo = Sprint(&ss->sprinter, "%s%s", + js_incop_str[!(cs->format & JOF_INC)], lval); break; case JSOP_INCPROP: case JSOP_DECPROP: - GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; + GET_ATOM_QUOTE_AND_FMT("%s%s[%s]", "%s%s.%s", rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, - js_incop_strs[!(cs->format & JOF_INC)], + js_incop_str[!(cs->format & JOF_INC)], lval, rval); break; case JSOP_INCELEM: case JSOP_DECELEM: - op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); - op = JSOP_GETELEM; lval = POP_STR(); if (*xval != '\0') { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? predot_format - : preindex_format, - js_incop_strs[!(cs->format & JOF_INC)], + ? "%s%s.%s" + : "%s%s[%s]", + js_incop_str[!(cs->format & JOF_INC)], lval, xval); } else { - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); + todo = Sprint(&ss->sprinter, "%s%s", + js_incop_str[!(cs->format & JOF_INC)], lval); } break; @@ -3224,44 +1927,35 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) do_atominc: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_lvalinc: - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); + return JS_FALSE; + todo = STR2OFF(&ss->sprinter, lval); + SprintPut(&ss->sprinter, + js_incop_str[!(cs->format & JOF_INC)], + 2); break; case JSOP_PROPINC: case JSOP_PROPDEC: - GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; + GET_ATOM_QUOTE_AND_FMT("%s[%s]%s", "%s.%s%s", rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, lval, rval, - js_incop_strs[!(cs->format & JOF_INC)]); + js_incop_str[!(cs->format & JOF_INC)]); break; case JSOP_ELEMINC: case JSOP_ELEMDEC: - op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); - op = JSOP_GETELEM; lval = POP_STR(); if (*xval != '\0') { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? postdot_format - : postindex_format, + ? "%s.%s%s" + : "%s[%s]%s", lval, xval, - js_incop_strs[!(cs->format & JOF_INC)]); + js_incop_str[!(cs->format & JOF_INC)]); } else { - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); + todo = Sprint(&ss->sprinter, "%s%s", + lval, js_incop_str[!(cs->format & JOF_INC)]); } break; @@ -3271,11 +1965,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* FALL THROUGH */ case JSOP_GETPROP: - case JSOP_GETXPROP: atom = GET_ATOM(cx, jp->script, pc); do_getprop: - GET_QUOTE_AND_FMT(index_format, dot_format, rval); + GET_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval); do_getprop_lval: lval = POP_STR(); @@ -3308,22 +2001,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) do_setprop_rval: rval = POP_STR(); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc - 1); todo = Sprint(&ss->sprinter, fmt, lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token + ? js_CodeSpec[lastop].token : "", rval); break; @@ -3334,44 +2016,37 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* FALL THROUGH */ case JSOP_GETELEM: - case JSOP_GETXELEM: - op = JSOP_NOP; /* turn off parens */ + op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); - op = saveop; + op = JSOP_GETELEM; lval = POP_STR(); if (*xval == '\0') { todo = Sprint(&ss->sprinter, "%s", lval); } else { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? dot_format - : index_format, + ? "%s.%s" + : "%s[%s]", lval, xval); } break; case JSOP_SETELEM: + op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); - op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); - cs = &js_CodeSpec[ss->opcodes[ss->top]]; - op = JSOP_GETELEM; /* lval must have high precedence */ + op = JSOP_SETELEM; lval = POP_STR(); - op = saveop; if (*xval == '\0') goto do_setlval; sn = js_GetSrcNote(jp->script, pc - 1); todo = Sprint(&ss->sprinter, - (cs->format & JOF_XMLNAME) + (js_CodeSpec[lastop].format & JOF_XMLNAME) ? "%s.%s %s= %s" : "%s[%s] %s= %s", lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token + ? js_CodeSpec[lastop].token : "", rval); break; @@ -3383,21 +2058,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_ARGCNT: - todo = Sprint(&ss->sprinter, dot_format, + todo = Sprint(&ss->sprinter, "%s.%s", js_arguments_str, js_length_str); break; case JSOP_GETARG: - i = GET_ARGNO(pc); - atom = GetSlotAtom(jp, js_GetArgument, i); -#if JS_HAS_DESTRUCTURING - if (!atom) { - todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); - break; - } -#else + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); -#endif goto do_name; case JSOP_GETVAR: @@ -3409,15 +2076,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_GETGVAR: atom = GET_ATOM(cx, jp->script, pc); do_name: - lval = ""; - do_qname: sn = js_GetSrcNote(jp->script, pc); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); - todo = Sprint(&ss->sprinter, "%s%s%s", - VarPrefix(sn), lval, rval); + todo = Sprint(&ss->sprinter, "%s%s", VarPrefix(sn), rval); break; case JSOP_UINT16: @@ -3425,7 +2089,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) goto do_sprint_int; case JSOP_UINT24: - i = (jsint) GET_UINT24(pc); + i = (jsint) GET_LITERAL_INDEX(pc); do_sprint_int: todo = Sprint(&ss->sprinter, "%u", (unsigned) i); break; @@ -3438,17 +2102,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) atomIndex = GET_LITERAL_INDEX(pc); todo = Sprint(&ss->sprinter, ""); if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; + return JS_FALSE; atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); goto do_name; case JSOP_LITOPX: atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = saveop = *pc2; - pc += len - (1 + ATOM_INDEX_LEN); - cs = &js_CodeSpec[op]; - len = cs->length; + op = pc[1 + LITERAL_INDEX_LEN]; switch (op) { case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; case JSOP_BINDNAME: goto do_JSOP_BINDNAME; @@ -3476,8 +2136,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; case JSOP_XMLPI: goto do_JSOP_XMLPI; #endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; - default: LOCAL_ASSERT(0); + default: JS_ASSERT(0); } /* NOTREACHED */ break; @@ -3493,7 +2152,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) 0, *JSVAL_TO_DOUBLE(val)); if (!numStr) { JS_ReportOutOfMemory(cx); - return NULL; + return JS_FALSE; } todo = Sprint(&ss->sprinter, numStr); } @@ -3501,9 +2160,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) BEGIN_LITOPX_CASE(JSOP_STRING) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - inXML ? DONT_ESCAPE : '"'); + inXML ? 0 : (jschar)'"'); if (!rval) - return NULL; + return JS_FALSE; todo = STR2OFF(&ss->sprinter, rval); END_LITOPX_CASE @@ -3521,14 +2180,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (op == JSOP_OBJECT || op == JSOP_REGEXP) { if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, &val)) { - return NULL; + return JS_FALSE; } } else { if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), - JS_IN_GROUP_CONTEXT | - JS_DONT_PRETTY_PRINT, + (pc + len < endpc && + pc[len] == JSOP_GROUP) + ? JS_IN_GROUP_CONTEXT | + JS_DONT_PRETTY_PRINT + : JS_DONT_PRETTY_PRINT, 0, NULL, &val)) { - return NULL; + return JS_FALSE; } } str = JSVAL_TO_STRING(val); @@ -3536,15 +2198,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JSSTRING_LENGTH(str)); break; +#if JS_HAS_SWITCH_STATEMENT case JSOP_TABLESWITCH: case JSOP_TABLESWITCHX: { + jsbytecode *pc2; ptrdiff_t jmplen, off, off2; jsint j, n, low, high; TableEntry *table, pivot; sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; @@ -3564,14 +2228,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) table = (TableEntry *) JS_malloc(cx, (size_t)n * sizeof *table); if (!table) - return NULL; + return JS_FALSE; for (i = j = 0; i < n; i++) { table[j].label = NULL; off2 = GetJumpOffset(pc, pc2); if (off2) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); + JS_ASSERT(SN_TYPE(sn) == SRC_LABEL); table[j].label = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) @@ -3592,7 +2256,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JS_FALSE); JS_free(cx, table); if (!ok) - return NULL; + return ok; todo = -2; break; } @@ -3600,12 +2264,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_LOOKUPSWITCH: case JSOP_LOOKUPSWITCHX: { + jsbytecode *pc2; ptrdiff_t jmplen, off, off2; jsatomid npairs, k; TableEntry *table; sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; @@ -3618,11 +2283,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) table = (TableEntry *) JS_malloc(cx, (size_t)npairs * sizeof *table); if (!table) - return NULL; + return JS_FALSE; for (k = 0; k < npairs; k++) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); + JS_ASSERT(SN_TYPE(sn) == SRC_LABEL); table[k].label = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); @@ -3641,19 +2306,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JS_FALSE); JS_free(cx, table); if (!ok) - return NULL; + return ok; todo = -2; break; } case JSOP_CONDSWITCH: { + jsbytecode *pc2; ptrdiff_t off, off2, caseOff; jsint ncases; TableEntry *table; sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); off = js_GetSrcNoteOffset(sn, 1); @@ -3665,14 +2331,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) off2 = off; for (ncases = 0; off2 != 0; ncases++) { pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); + JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || + *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { /* End of cases, but count default as a case. */ off2 = 0; } else { sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); off2 = js_GetSrcNoteOffset(sn, 0); } } @@ -3685,19 +2351,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) table = (TableEntry *) JS_malloc(cx, (size_t)ncases * sizeof *table); if (!table) - return NULL; + return JS_FALSE; pc2 = pc; off2 = off; for (i = 0; i < ncases; i++) { pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); + JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || + *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); caseOff = pc2 - pc; table[i].key = INT_TO_JSVAL((jsint) caseOff); table[i].offset = caseOff + GetJumpOffset(pc2, pc2); if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); + JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); off2 = js_GetSrcNoteOffset(sn, 0); } } @@ -3715,7 +2381,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) JS_TRUE); JS_free(cx, table); if (!ok) - return NULL; + return ok; todo = -2; break; } @@ -3725,51 +2391,62 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { lval = POP_STR(); if (!lval) - return NULL; + return JS_FALSE; js_printf(jp, "\tcase %s:\n", lval); todo = -2; break; } +#endif /* JS_HAS_SWITCH_STATEMENT */ + +#if !JS_BUG_FALLIBLE_EQOPS case JSOP_NEW_EQ: case JSOP_NEW_NE: rval = POP_STR(); lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %c== %s", - lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval); + todo = Sprint(&ss->sprinter, "%s %c%s %s", + lval, + (op == JSOP_NEW_EQ) ? '=' : '!', +#if JS_HAS_TRIPLE_EQOPS + JS_VERSION_IS_ECMA(cx) ? "==" : +#endif + "=", + rval); break; +#endif +#if JS_HAS_LEXICAL_CLOSURE BEGIN_LITOPX_CASE(JSOP_CLOSURE) - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); + JS_ASSERT(ATOM_IS_OBJECT(atom)); todo = -2; goto do_function; END_LITOPX_CASE +#endif #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTALL: - js_printf(jp, "\texport *;\n"); + js_printf(jp, "\texport *\n"); todo = -2; break; BEGIN_LITOPX_CASE(JSOP_EXPORTNAME) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); - js_printf(jp, "\texport %s;\n", rval); + js_printf(jp, "\texport %s\n", rval); todo = -2; END_LITOPX_CASE case JSOP_IMPORTALL: lval = POP_STR(); - js_printf(jp, "\timport %s.*;\n", lval); + js_printf(jp, "\timport %s.*\n", lval); todo = -2; break; case JSOP_IMPORTPROP: do_importprop: - GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n", - "\timport %s.%s;\n", + GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n", rval); lval = POP_STR(); js_printf(jp, fmt, lval, rval); @@ -3782,7 +2459,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (js_CodeSpec[lastop].format & JOF_XMLNAME) goto do_importprop; lval = POP_STR(); - js_printf(jp, "\timport %s[%s];\n", lval, xval); + js_printf(jp, "\timport %s[%s]\n", lval, xval); todo = -2; break; #endif /* JS_HAS_EXPORT_IMPORT */ @@ -3790,8 +2467,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_TRAP: op = JS_GetTrapOpcode(cx, jp->script, pc); if (op == JSOP_LIMIT) - return NULL; - saveop = op; + return JS_FALSE; *pc = op; cs = &js_CodeSpec[op]; len = cs->length; @@ -3800,15 +2476,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; +#if JS_HAS_INITIALIZERS case JSOP_NEWINIT: - { - JSBool isArray; - LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); lval = POP_STR(); - isArray = (*lval == 'A'); - todo = ss->sprinter.offset; #if JS_HAS_SHARP_VARS op = (JSOp)pc[len]; if (op == JSOP_DEFSHARP) { @@ -3816,35 +2488,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) cs = &js_CodeSpec[op]; len = cs->length; i = (jsint) GET_ATOM_INDEX(pc); - if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0) - return NULL; - } + todo = Sprint(&ss->sprinter, "#%u=%c", + (unsigned) i, + (*lval == 'O') ? '{' : '['); + } else #endif /* JS_HAS_SHARP_VARS */ - if (isArray) { - ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) - return NULL; - } else { - if (SprintCString(&ss->sprinter, "{") < 0) - return NULL; + { + todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "["); } break; - } case JSOP_ENDINIT: - op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); sn = js_GetSrcNote(jp->script, pc); - - /* Skip any #n= prefix to find the opening bracket. */ - for (xval = rval; *xval != '[' && *xval != '{'; xval++) - continue; - if (*xval == '[') - --ss->inArrayInit; todo = Sprint(&ss->sprinter, "%s%s%c", rval, (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", - (*xval == '[') ? ']' : '}'); + (*rval == '{') ? '}' : ']'); break; case JSOP_INITPROP: @@ -3853,7 +2513,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) (jschar) (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); if (!xval) - return NULL; + return JS_FALSE; rval = POP_STR(); lval = POP_STR(); do_initprop: @@ -3870,32 +2530,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval); #else if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { - if (!atom || !ATOM_IS_STRING(atom) || - !ATOM_IS_IDENTIFIER(atom) || - ATOM_IS_KEYWORD(atom) || - ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ || - strncmp(rval, js_function_str, 8) != 0) && - ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { - todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval, - (lval[1] != '\0') ? ", " : "", xval, - (lastop == JSOP_GETTER || - lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval); - } else { - rval += 8 + 1; - LOCAL_ASSERT(rval[strlen(rval)-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); - } + 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, + strlen(rval) - 1, + rval); } else { todo = Sprint(&ss->sprinter, "%s%s%s:%s", lval, @@ -3911,10 +2554,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = POP_STR(); lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_INITPROP) { - atom = NULL; + if (sn && SN_TYPE(sn) == SRC_LABEL) goto do_initprop; - } todo = Sprint(&ss->sprinter, "%s%s%s", lval, (lval[1] != '\0' || *xval != '0') ? ", " : "", @@ -3933,6 +2574,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); break; #endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ #if JS_HAS_DEBUGGER_KEYWORD case JSOP_DEBUGGER: @@ -3956,28 +2598,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_ANYNAME: - if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { - len += JSOP_TOATTRNAME_LENGTH; - todo = SprintPut(&ss->sprinter, "@*", 2); - } else { - todo = SprintPut(&ss->sprinter, "*", 1); - } + todo = SprintPut(&ss->sprinter, "*", 1); break; BEGIN_LITOPX_CASE(JSOP_QNAMEPART) - if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) { - saveop = JSOP_TOATTRNAME; - len += JSOP_TOATTRNAME_LENGTH; - lval = "@"; - goto do_qname; - } goto do_name; END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_QNAMECONST) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) - return NULL; + return JS_FALSE; RETRACT(&ss->sprinter, rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); @@ -3990,9 +2621,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_TOATTRNAME: - op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); - todo = Sprint(&ss->sprinter, "@[%s]", rval); + todo = Sprint(&ss->sprinter, "@%s", rval); break; case JSOP_TOATTRVAL: @@ -4037,7 +2667,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_TOXMLLIST: - op = JSOP_NOP; /* turn off parens */ todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); inXML = JS_FALSE; break; @@ -4070,6 +2699,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; BEGIN_LITOPX_CASE(JSOP_XMLOBJECT) + atom = GET_ATOM(cx, jp->script, pc); todo = Sprint(&ss->sprinter, "", ATOM_TO_OBJECT(atom)); END_LITOPX_CASE @@ -4077,29 +2707,28 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) BEGIN_LITOPX_CASE(JSOP_XMLCDATA) todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0)) - return NULL; + 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 NULL; + return JS_FALSE; todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && - (*rval == '\0' || - (SprintPut(&ss->sprinter, " ", 1) >= 0 && - SprintCString(&ss->sprinter, rval))); + SprintPut(&ss->sprinter, " ", 1) >= 0 && + SprintPut(&ss->sprinter, rval, strlen(rval)); JS_free(cx, (char *)rval); if (!ok) - return NULL; + return JS_FALSE; SprintPut(&ss->sprinter, "?>", 2); END_LITOPX_CASE @@ -4120,10 +2749,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (todo < 0) { /* -2 means "don't push", -1 means reported error. */ if (todo == -1) - return NULL; + return JS_FALSE; } else { - if (!PushOff(ss, todo, saveop)) - return NULL; + if (!PushOff(ss, todo, op)) + return JS_FALSE; } pc += len; } @@ -4139,102 +2768,50 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #undef GET_QUOTE_AND_FMT #undef GET_ATOM_QUOTE_AND_FMT - return pc; -} - -static JSBool -InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) -{ - size_t offsetsz, opcodesz; - void *space; - - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); - - /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - offsetsz = depth * sizeof(ptrdiff_t); - opcodesz = depth * sizeof(jsbytecode); - JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); - if (!space) - return JS_FALSE; - ss->offsets = (ptrdiff_t *) space; - ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); - - ss->top = ss->inArrayInit = 0; - ss->printer = jp; return JS_TRUE; } + JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth) +js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len) { - uintN depth, i; SprintStack ss; JSContext *cx; - void *mark; + void *mark, *space; + size_t offsetsz, opcodesz; JSBool ok; JSScript *oldscript; char *last; - depth = script->depth; - JS_ASSERT(pcdepth <= depth); - /* Initialize a sprinter for use with the offset stack. */ + ss.printer = jp; cx = jp->sprinter.context; mark = JS_ARENA_MARK(&cx->tempPool); - ok = InitSprintStack(cx, &ss, jp, depth); - if (!ok) + INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP); + + /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ + offsetsz = script->depth * sizeof(ptrdiff_t); + opcodesz = script->depth * sizeof(jsbytecode); + JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); + if (!space) { + ok = JS_FALSE; goto out; - - /* - * If we are called from js_DecompileValueGenerator with a portion of - * script's bytecode that starts with a non-zero model stack depth given - * by pcdepth, attempt to initialize the missing string offsets in ss to - * |spindex| negative indexes from fp->sp for the activation fp in which - * the error arose. - * - * See js_DecompileValueGenerator for how its |spindex| parameter is used, - * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are - * potentially stored below. - */ - ss.top = pcdepth; - if (pcdepth != 0) { - JSStackFrame *fp; - ptrdiff_t top; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - top = fp ? fp->sp - fp->spbase : 0; - for (i = 0; i < pcdepth; i++) { - ss.offsets[i] = -1; - ss.opcodes[i] = JSOP_NOP; - } - if (fp && fp->pc == pc && (uintN)top == pcdepth) { - for (i = 0; i < pcdepth; i++) { - ptrdiff_t off; - jsbytecode *genpc; - - off = (intN)i - (intN)depth; - genpc = (jsbytecode *) fp->spbase[off]; - if (JS_UPTRDIFF(genpc, script->code) < script->length) { - ss.offsets[i] += (ptrdiff_t)i - top; - ss.opcodes[i] = *genpc; - } - } - } } + ss.offsets = (ptrdiff_t *) space; + ss.opcodes = (jsbytecode *) ((char *)space + offsetsz); + ss.top = 0; /* Call recursive subroutine to do the hard work. */ oldscript = jp->script; jp->script = script; - ok = Decompile(&ss, pc, len) != NULL; + ok = Decompile(&ss, pc, len); jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ if (ss.top) { do { - last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); - } while (ss.top > pcdepth); + last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP)); + } while (ss.top); js_printf(jp, "%s", last); } @@ -4247,7 +2824,7 @@ out: JSBool js_DecompileScript(JSPrinter *jp, JSScript *script) { - return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); + return js_DecompileCode(jp, script, script->code, (uintN)script->length); } static const char native_code_str[] = "\t[native code]\n"; @@ -4259,15 +2836,15 @@ js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) JSScope *scope, *save; JSBool ok; - if (!FUN_INTERPRETED(fun)) { + if (!fun->interpreted) { js_printf(jp, native_code_str); return JS_TRUE; } - script = fun->u.i.script; + script = fun->u.script; scope = fun->object ? OBJ_SCOPE(fun->object) : NULL; save = jp->scope; jp->scope = scope; - ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); + ok = js_DecompileCode(jp, script, script->code, (uintN)script->length); jp->scope = save; return ok; } @@ -4281,8 +2858,6 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) JSAtom **params; JSScope *scope, *oldscope; JSScopeProperty *sprop; - jsbytecode *pc, *endpc; - ptrdiff_t len; JSBool ok; /* @@ -4296,9 +2871,9 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) js_puts(jp, "("); } - if (JSFUN_GETTER_TEST(fun->flags)) + if (fun->flags & JSFUN_GETTER) js_printf(jp, "%s ", js_getter_str); - else if (JSFUN_SETTER_TEST(fun->flags)) + else if (fun->flags & JSFUN_SETTER) js_printf(jp, "%s ", js_setter_str); js_printf(jp, "%s ", js_function_str); @@ -4306,12 +2881,8 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) return JS_FALSE; js_puts(jp, "("); - if (FUN_INTERPRETED(fun) && fun->object) { + if (fun->interpreted && fun->object) { size_t paramsize; -#ifdef JS_HAS_DESTRUCTURING - SprintStack ss; - JSScript *oldscript; -#endif /* * Print the parameters. @@ -4332,7 +2903,6 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) 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) { @@ -4343,89 +2913,26 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) JS_ASSERT(JSID_IS_ATOM(sprop->id)); params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); } - - pc = fun->u.i.script->main; - endpc = pc + fun->u.i.script->length; - ok = JS_TRUE; - -#ifdef JS_HAS_DESTRUCTURING - /* Skip JSOP_GENERATOR in case of destructuring parameters. */ - if (*pc == JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - - ss.printer = NULL; - oldscript = jp->script; - jp->script = fun->u.i.script; - oldscope = jp->scope; - jp->scope = scope; -#endif - for (i = 0; i < nargs; i++) { if (i > 0) js_puts(jp, ", "); - -#if JS_HAS_DESTRUCTURING -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - - if (!params[i]) { - ptrdiff_t todo; - const char *lval; - - LOCAL_ASSERT(*pc == JSOP_GETARG); - pc += JSOP_GETARG_LENGTH; - LOCAL_ASSERT(*pc == JSOP_DUP); - if (!ss.printer) { - ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth); - if (!ok) - break; - } - pc = DecompileDestructuring(&ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - break; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(&ss, JSOP_NOP); - todo = SprintCString(&jp->sprinter, lval); - if (todo < 0) { - ok = JS_FALSE; - break; - } - continue; - } - -#undef LOCAL_ASSERT -#endif - - if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) { - ok = JS_FALSE; - break; - } + if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) + return JS_FALSE; } - -#ifdef JS_HAS_DESTRUCTURING - jp->script = oldscript; - jp->scope = oldscope; -#endif JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!ok) - return JS_FALSE; #ifdef __GNUC__ } else { scope = NULL; - pc = NULL; #endif } js_printf(jp, ") {\n"); indent = jp->indent; jp->indent += 4; - if (FUN_INTERPRETED(fun) && fun->object) { + if (fun->interpreted && fun->object) { oldscope = jp->scope; jp->scope = scope; - len = fun->u.i.script->code + fun->u.i.script->length - pc; - ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); + ok = js_DecompileScript(jp, fun->u.script); jp->scope = oldscope; if (!ok) { jp->indent = indent; @@ -4444,21 +2951,20 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) return JS_TRUE; } -#undef LOCAL_ASSERT_RV - JSString * js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSString *fallback) { JSStackFrame *fp, *down; - jsbytecode *pc, *begin, *end; - jsval *sp, *spbase, *base, *limit; - intN depth, pcdepth; + jsbytecode *pc, *begin, *end, *tmp; + jsval *sp, *base, *limit; JSScript *script; JSOp op; const JSCodeSpec *cs; + uint32 format, mode, type; + intN depth; jssrcnote *sn; - ptrdiff_t len, oplen; + uintN len, off; JSPrinter *jp; JSString *name; @@ -4469,17 +2975,6 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, /* Try to find sp's generating pc depth slots under it on the stack. */ pc = fp->pc; - sp = fp->sp; - spbase = fp->spbase; - if ((uintN)(sp - spbase) > fp->script->depth) { - /* - * Preparing to make an internal invocation, using an argv stack - * segment pushed just above fp's operand stack space. Such an argv - * stack has no generating pc "basement", so we must fall back. - */ - goto do_fallback; - } - if (spindex == JSDVG_SEARCH_STACK) { if (!pc) { /* @@ -4487,12 +2982,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, * in which a decompilable bytecode string that generated the * value as an actual argument might exist. */ - JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun))); + JS_ASSERT(!fp->script && !(fp->fun && fp->fun->interpreted)); down = fp->down; if (!down) goto do_fallback; script = down->script; - spbase = down->spbase; base = fp->argv; limit = base + fp->argc; } else { @@ -4503,7 +2997,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, * initialized a (default-all-zeroes) frame. */ script = fp->script; - spbase = base = fp->spbase; + base = fp->spbase; limit = fp->sp; } @@ -4525,19 +3019,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, * * In the script or scripted function case, the same reasoning * applies to fp rather than to fp->down. - * - * We search from limit to base to find the most recently calculated - * value matching v under assumption that it is it that caused - * exception, see bug 328664. */ - for (sp = limit;;) { - if (sp <= base) - goto do_fallback; - --sp; + for (sp = base; sp < limit; sp++) { if (*sp == v) { depth = (intN)script->depth; - sp -= depth; - pc = (jsbytecode *) *sp; + pc = (jsbytecode *) sp[-depth]; break; } } @@ -4571,7 +3057,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, /* * Again, be paranoid, this time about possibly loading an invalid pc - * from fp->sp[-(1+depth)]. + * from sp[-(1+depth)]. */ if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) { pc = fp->pc; @@ -4582,213 +3068,100 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, if (op == JSOP_TRAP) op = JS_GetTrapOpcode(cx, script, pc); - /* None of these stack-writing ops generates novel values. */ - JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && - op != JSOP_DUP && op != JSOP_DUP2 && - op != JSOP_SWAP); + /* XXX handle null as a special case, to avoid calling null "object" */ + if (op == JSOP_NULL) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); - /* - * |this| could convert to a very long object initialiser, so cite it by - * its keyword name instead. - */ - if (op == JSOP_THIS) - return JS_NewStringCopyZ(cx, js_this_str); - - /* - * JSOP_BINDNAME is special: it generates a value, the base object of a - * reference. But if it is the generating op for a diagnostic produced by - * js_DecompileValueGenerator, the name being bound is irrelevant. Just - * fall back to the base object. - */ - if (op == JSOP_BINDNAME) - goto do_fallback; - - /* NAME ops are self-contained, others require left or right context. */ cs = &js_CodeSpec[op]; - begin = pc; - end = pc + cs->length; - if ((cs->format & JOF_MODEMASK) != JOF_NAME) { - JSSrcNoteType noteType; + format = cs->format; + mode = (format & JOF_MODEMASK); + /* NAME ops are self-contained, but others require left context. */ + if (mode == JOF_NAME) { + begin = pc; + } else { sn = js_GetSrcNote(script, pc); - if (!sn) - goto do_fallback; - noteType = SN_TYPE(sn); - if (noteType == SRC_PCBASE) { - begin -= js_GetSrcNoteOffset(sn, 0); - } else if (noteType == SRC_PCDELTA) { - end = begin + js_GetSrcNoteOffset(sn, 0); - begin += cs->length; - } else { + 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; len = PTRDIFF(end, begin, jsbytecode); - if (len <= 0) - goto do_fallback; - - /* - * Walk forward from script->main and compute starting stack depth. - * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. - * FIXME: Optimize to use last empty-stack sequence point. - */ - pcdepth = 0; - for (pc = script->main; pc < begin; pc += oplen) { - jsbytecode *pc2; - uint32 type; - intN nuses, ndefs; - - /* Let pc2 be non-null only for JSOP_LITOPX. */ - pc2 = NULL; - op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - cs = &js_CodeSpec[op]; - oplen = cs->length; - - if (op == JSOP_SETSP) { - pcdepth = GET_UINT16(pc); - continue; - } - - /* - * A (C ? T : E) expression requires skipping either T (if begin is in - * E) or both T and E (if begin is after the whole expression) before - * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that - * tests condition C. We know that the stack depth can't change from - * what it was with C on top of stack. - */ - sn = js_GetSrcNote(script, pc); - if (sn && SN_TYPE(sn) == SRC_COND) { - ptrdiff_t jmpoff, jmplen; - - jmpoff = js_GetSrcNoteOffset(sn, 0); - if (pc + jmpoff < begin) { - pc += jmpoff; - op = *pc; - JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); - cs = &js_CodeSpec[op]; - oplen = cs->length; - jmplen = GetJumpOffset(pc, pc); - if (pc + jmplen < begin) { - oplen = (uintN) jmplen; - continue; - } + if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT | JOF_FOR)) { + tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode)); + if (!tmp) + return NULL; + memcpy(tmp, begin, len * sizeof(jsbytecode)); + if (mode == JOF_NAME) { + /* + * 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 + * JSOP_GET* code. For JSOP_SET{PROP,ELEM}, we must use the "2nd" + * form of JSOP_GET{PROP,ELEM}, to throw away the assignment op's + * right-hand operand and decompile it as if it were a GET of its + * left-hand operand. + */ + off = len - cs->length; + JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode)); + if (mode == JOF_PROP) { + tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; + } else if (mode == JOF_ELEM) { + tmp[off] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; + } else { /* - * Ok, begin lies in E. Manually pop C off the model stack, - * since we have moved beyond the IFEQ now. + * A zero mode means precisely that op is uncategorized for our + * purposes, so we must write per-op special case code here. */ - --pcdepth; + switch (op) { + case JSOP_ENUMELEM: + tmp[off] = JSOP_GETELEM; + break; +#if JS_HAS_LVALUE_RETURN + case JSOP_SETCALL: + tmp[off] = JSOP_CALL; + break; +#endif + default: + JS_ASSERT(0); + } } } - - type = cs->format & JOF_TYPEMASK; - switch (type) { - case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: - { - jsint jmplen, i, low, high; - - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - for (i = low; i <= high; i++) - pc2 += jmplen; - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: - { - jsint jmplen; - jsbytecode *pc2; - jsatomid npairs; - - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - while (npairs) { - pc2 += ATOM_INDEX_LEN; - pc2 += jmplen; - npairs--; - } - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LITOPX: - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - cs = &js_CodeSpec[op]; - JS_ASSERT(cs->length > ATOM_INDEX_LEN); - oplen += cs->length - (1 + ATOM_INDEX_LEN); - break; - - default:; - } - - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - continue; - - nuses = cs->nuses; - if (nuses < 0) { - /* Call opcode pushes [callee, this, argv...]. */ - nuses = 2 + GET_ARGC(pc); - } else if (op == JSOP_RETSUB) { - /* Pop [exception or hole, retsub pc-index]. */ - JS_ASSERT(nuses == 0); - nuses = 2; - } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) { - JS_ASSERT(nuses == 0); - nuses = GET_UINT16(pc); - } - pcdepth -= nuses; - JS_ASSERT(pcdepth >= 0); - - ndefs = cs->ndefs; - if (op == JSOP_FINALLY) { - /* Push [exception or hole, retsub pc-index]. */ - JS_ASSERT(ndefs == 0); - ndefs = 2; - } else if (op == JSOP_ENTERBLOCK) { - jsatomid atomIndex; - JSAtom *atom; - JSObject *obj; - - JS_ASSERT(ndefs == 0); - atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth); - ndefs = OBJ_BLOCK_COUNT(cx, obj); - } - pcdepth += ndefs; + begin = tmp; + } else { + /* No need to revise script bytecode. */ + tmp = NULL; } name = NULL; jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); if (jp) { - if (fp->fun && fp->fun->object) { - JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); - jp->scope = OBJ_SCOPE(fp->fun->object); + if (fp->fun && fp->fun->object) { + JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); + jp->scope = OBJ_SCOPE(fp->fun->object); } - jp->dvgfence = end; - if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) + if (js_DecompileCode(jp, script, begin, len)) name = js_GetPrinterOutput(jp); js_DestroyPrinter(jp); } + if (tmp) + JS_free(cx, tmp); return name; do_fallback: - return fallback ? fallback : js_ValueToSource(cx, v); + return fallback ? fallback : js_ValueToString(cx, v); } diff --git a/lib/javascript/jsopcode.h b/lib/javascript/jsopcode.h index 3f7e1de9..2a488f6e 100644 --- a/lib/javascript/jsopcode.h +++ b/lib/javascript/jsopcode.h @@ -45,7 +45,6 @@ #include #include "jsprvtd.h" #include "jspubtd.h" -#include "jsutil.h" JS_BEGIN_EXTERN_C @@ -60,14 +59,6 @@ typedef enum JSOp { JSOP_LIMIT } JSOp; -typedef enum JSOpLength { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - op##_LENGTH = length, -#include "jsopcode.tbl" -#undef OPDEF - JSOP_LIMIT_LENGTH -} JSOpLength; - /* * JS bytecode formats. */ @@ -79,7 +70,7 @@ typedef enum JSOpLength { #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_INDEXCONST 8 /* uint16 slot index + constant pool index */ +#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 */ @@ -87,7 +78,6 @@ typedef enum JSOpLength { #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_LOCAL 14 /* block-local operand stack variable */ #define JOF_TYPEMASK 0x000f /* mask for above immediate types */ #define JOF_NAME 0x0010 /* name operation */ #define JOF_PROP 0x0020 /* obj.prop operation */ @@ -174,55 +164,35 @@ typedef enum JSOpLength { #define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ GET_ATOM_INDEX(pc)) -/* A full atom index for JSOP_UINT24 uses 24 bits of immediate operand. */ -#define UINT24_HI(i) ((jsbytecode)((i) >> 16)) -#define UINT24_MID(i) ((jsbytecode)((i) >> 8)) -#define UINT24_LO(i) ((jsbytecode)(i)) -#define GET_UINT24(pc) ((jsatomid)(((pc)[1] << 16) | \ +/* 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_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ - (pc)[2] = UINT24_MID(i), \ - (pc)[3] = UINT24_LO(i)) - -/* Same format for JSOP_LITERAL, etc., but future-proof with different names. */ -#define LITERAL_INDEX_LEN 3 -#define LITERAL_INDEX_HI(i) UINT24_HI(i) -#define LITERAL_INDEX_MID(i) UINT24_MID(i) -#define LITERAL_INDEX_LO(i) UINT24_LO(i) -#define GET_LITERAL_INDEX(pc) GET_UINT24(pc) -#define SET_LITERAL_INDEX(pc,i) SET_UINT24(pc,i) +#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) -JS_STATIC_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= - ATOM_INDEX_LIMIT_LOG2 + 1); - -/* Common uint16 immediate format helpers. */ -#define UINT16_HI(i) ((jsbytecode)((i) >> 8)) -#define UINT16_LO(i) ((jsbytecode)(i)) -#define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) -#define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) -#define UINT16_LIMIT ((uintN)1 << 16) - /* Actual argument count operand format helpers. */ -#define ARGC_HI(argc) UINT16_HI(argc) -#define ARGC_LO(argc) UINT16_LO(argc) -#define GET_ARGC(pc) GET_UINT16(pc) -#define ARGC_LIMIT UINT16_LIMIT +#define ARGC_HI(argc) ((jsbytecode)((argc) >> 8)) +#define ARGC_LO(argc) ((jsbytecode)(argc)) +#define GET_ARGC(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) +#define ARGC_LIMIT ((uint32)1 << 16) /* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */ -#define GET_ARGNO(pc) GET_UINT16(pc) -#define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) -#define ARGNO_LEN 2 -#define ARGNO_LIMIT UINT16_LIMIT - -#define GET_VARNO(pc) GET_UINT16(pc) -#define SET_VARNO(pc,varno) SET_UINT16(pc,varno) -#define VARNO_LEN 2 -#define VARNO_LIMIT UINT16_LIMIT +#define GET_ARGNO(pc) GET_ARGC(pc) +#define SET_ARGNO(pc,argno) SET_JUMP_OFFSET(pc,argno) +#define ARGNO_LEN JUMP_OFFSET_LEN +#define GET_VARNO(pc) GET_ARGC(pc) +#define SET_VARNO(pc,varno) SET_JUMP_OFFSET(pc,varno) +#define VARNO_LEN JUMP_OFFSET_LEN struct JSCodeSpec { const char *name; /* JS bytecode name */ @@ -234,6 +204,20 @@ struct JSCodeSpec { uint32 format; /* immediate operand format */ }; +extern const char js_const_str[]; +extern const char js_var_str[]; +extern const char js_function_str[]; +extern const char js_in_str[]; +extern const char js_instanceof_str[]; +extern const char js_new_str[]; +extern const char js_delete_str[]; +extern const char js_typeof_str[]; +extern const char js_void_str[]; +extern const char js_null_str[]; +extern const char js_this_str[]; +extern const char js_false_str[]; +extern const char js_true_str[]; +extern const char js_default_str[]; extern const JSCodeSpec js_CodeSpec[]; extern uintN js_NumCodeSpecs; extern const jschar js_EscapeMap[]; @@ -284,8 +268,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, * Decompilers, for script, function, and expression pretty-printing. */ extern JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth); +js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len); extern JSBool js_DecompileScript(JSPrinter *jp, JSScript *script); diff --git a/lib/javascript/jsopcode.tbl b/lib/javascript/jsopcode.tbl index 4a4ca898..6e649413 100644 --- a/lib/javascript/jsopcode.tbl +++ b/lib/javascript/jsopcode.tbl @@ -1,5 +1,4 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=0 ft=C: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -63,33 +62,6 @@ * prec Operator precedence, zero if not an operator * format Bytecode plus immediate operand encoding format * - * Precedence Operators Opcodes - * 1 let (x = y) z, w JSOP_LEAVEBLOCKEXPR - * 2 , JSOP_POP with SRC_PCDELTA note - * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_ASSIGNING) - * 4 ?: JSOP_IFEQ, JSOP_IFEQX - * 5 || JSOP_OR, JSOP_ORX - * 6 && JSOP_AND, JSOP_ANDX - * 7 | JSOP_BITOR - * 8 ^ JSOP_BITXOR - * 9 & JSOP_BITAND - * 10 ==, !=, etc. JSOP_EQ, JSOP_NE, etc. - * 11 <, in, etc. JSOP_LT, JSOP_IN, etc. - * 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH - * 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc. - * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD - * 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc. - * 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc. - * 17 delete, new JSOP_DEL*, JSOP_NEW - * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. - * 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc. - * - * The push-numeric-constant operators, JSOP_ZERO, JSOP_NUMBER, etc., have - * lower precedence than the member operators emitted for the . operator, to - * cause the decompiler to parenthesize the . left operand, e.g. (0).foo. - * Otherwise the . could be taken as a decimal point. We use the same level - * 16 for function expressions too, to force parenthesization. - * * This file is best viewed with 128 columns: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 */ @@ -99,88 +71,88 @@ /* Longstanding JavaScript bytecodes. */ OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) +OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_DETECTING) OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP) /* Get the arguments object for the current, lightweight function activation. */ -OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) +OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 12, JOF_BYTE) /* ECMA-compliant for-in loop with argument or local variable loop control. */ -OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 19, JOF_QVAR|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 0, JOF_QARG|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 0, JOF_QVAR|JOF_NAME|JOF_FOR) /* More longstanding bytecodes. */ OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) -OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16) -OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL) -OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL) -OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) -OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC) -OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC) -OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC) -OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC) -OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) -OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) -OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) -OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) -OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) -OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 10, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_NEG, 34, "neg", "-", 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 10, JOF_UINT16) +OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEL) +OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEL) +OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEL) +OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) +OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_INC) +OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_INC) +OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC) +OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEC) +OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEC) +OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) +OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) +OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) +OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) +OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 11, JOF_CONST|JOF_PROP) +OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 1, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 11, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) +OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_PUSHOBJ, 57, "pushobj", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16) -OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 16, JOF_CONST) -OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING) +OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 11, JOF_UINT16) +OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME) +OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 12, JOF_BYTE) +OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_DETECTING) +OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 0, JOF_JUMP|JOF_DETECTING) /* The switch bytecodes have variable length. */ OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|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, 10, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) /* Lexical closure constructor. */ OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST) @@ -193,65 +165,61 @@ OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL, 3, 1, 0, 0, JOF_CONST| OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_IMPORT) /* Push object literal. */ -OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_CONST) +OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 12, JOF_CONST) /* Pop value and discard it. */ -OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) +OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 0, JOF_BYTE) /* Convert value to number, for unary +. */ -OPDEF(JSOP_POS, 82, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) +OPDEF(JSOP_POS, 82, "pos", "+", 1, 1, 1, 10, JOF_BYTE) /* Trap into debugger for breakpoint, etc. */ OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast get/set ops for function arguments and local variables. */ -OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) -OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 19, JOF_QVAR |JOF_NAME) -OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 12, JOF_QARG |JOF_NAME) +OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 1, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 12, JOF_QVAR |JOF_NAME) +OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 1, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) /* Push unsigned 16-bit int constant. */ -OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) +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, 0, JOF_BYTE) -OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) -OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 3, JOF_CONST|JOF_PROP|JOF_DETECTING) -OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 3, JOF_BYTE |JOF_ELEM|JOF_DETECTING) +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|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) /* Fast inc/dec ops for args and local vars. */ -OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC) -OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC) -OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC) -OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) - -/* - * Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL. - */ -OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC) +OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC) +OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC) +OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC) +OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 10, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 10, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) /* ECMA-compliant for/in ops. */ -OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_FOR) -OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) +OPDEF(JSOP_TOOBJECT, 103,"toobject", NULL, 1, 1, 1, 0, JOF_BYTE) +OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_FOR) +OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 0, JOF_CONST|JOF_PROP|JOF_FOR) +OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 4, 0, JOF_BYTE |JOF_ELEM|JOF_FOR) OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) /* ECMA-compliant assignment ops. */ OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) /* Exception handling ops. */ OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) /* 'in' and 'instanceof' ops. */ -OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE|JOF_LEFTASSOC) /* debugger op */ OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) @@ -276,12 +244,12 @@ OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) /* * ECMA-compliant call to eval op */ -OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16) +OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 11, JOF_UINT16) /* * ECMA-compliant helper for 'for (x[i] in o)' loops. */ -OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 1, JOF_BYTE |JOF_SET|JOF_ASSIGNING) /* * Getter and setter prefix bytecodes. These modify the next bytecode, either @@ -299,22 +267,22 @@ OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST| 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, 16, JOF_CONST) +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, 16, JOF_CONST) +OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 12, JOF_CONST) /* - * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately - * after to throw away the exception value. + * Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA + * Edition 3 catch variables. */ -OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) +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) /* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */ -OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 11, JOF_UINT16|JOF_SET|JOF_ASSIGNING) /* * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN @@ -325,6 +293,9 @@ OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Swap the top two stack elements. + * N.B. JSOP_SWAP doesn't swap the corresponding pc stack generating pcs, as + * they're not needed for the current use of preserving the top-of-stack return + * value when popping scopes while returning from catch blocks. */ OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) @@ -333,8 +304,8 @@ OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. * JSOP_ARGCNT returns fp->argc. */ -OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) -OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) +OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 12, JOF_QARG |JOF_NAME) +OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 12, JOF_BYTE) /* * Define a local function object as a local variable. @@ -345,10 +316,10 @@ OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXC /* Extended jumps. */ OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 3, JOF_JUMPX|JOF_DETECTING) +OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) +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, 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) @@ -359,52 +330,52 @@ OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUP OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) -/* Set pending exception from the stack, to trigger rethrow. */ -OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) +/* Set 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) OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) /* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ -OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) +OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME) +OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) +OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC) +OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) +OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) /* Regular expression literal requiring special "fork on exec" handling. */ -OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_CONST) +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, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_CONST|JOF_XMLNAME) -OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_CONST|JOF_XMLNAME) +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, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) -OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) -OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 18, JOF_BYTE) -OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) +OPDEF(JSOP_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, 19, JOF_CONST) -OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST) -OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) +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 @@ -412,67 +383,14 @@ OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE | * 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, 16, JOF_UINT24) -OPDEF(JSOP_LITERAL, 189,"literal", NULL, 4, 0, 1, 19, JOF_UINT24) +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, 0, JOF_LITOPX) +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, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* - * Stop interpretation, emitted at end of script to save the threaded bytecode - * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). - */ -OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Get an extant property or element value, throwing ReferenceError if the - * identified property does not exist. - */ -OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETXELEM, 197,"getxelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) - -/* - * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). - */ -OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) - -/* - * Block-local scope support. - */ -OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST) -OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) -OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET) -OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC) -OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_LOCALDEC, 206,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) - -/* - * Iterator, generator, and array comprehension support. - */ -OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) -OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) - -OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE) - -/* - * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). - */ -OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) - -/* - * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, - * which must be moved down when the block pops. - */ -OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, 0, 0, 1, JOF_UINT16) +OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 1, JOF_CONST|JOF_PROP) diff --git a/lib/javascript/jsosdep.h b/lib/javascript/jsosdep.h index a2661448..e9c4a553 100644 --- a/lib/javascript/jsosdep.h +++ b/lib/javascript/jsosdep.h @@ -62,10 +62,7 @@ /* * Get OS specific header information. */ -#if defined(XP_MACOSX) || defined(DARWIN) -#define JS_HAVE_LONG_LONG - -#elif defined(AIXV3) || defined(AIX) +#if defined(AIXV3) || defined(AIX) #define JS_HAVE_LONG_LONG #elif defined(BSDI) diff --git a/lib/javascript/jsotypes.h b/lib/javascript/jsotypes.h index 38d72869..ede1221b 100644 --- a/lib/javascript/jsotypes.h +++ b/lib/javascript/jsotypes.h @@ -67,7 +67,7 @@ typedef JSIntn intn; #else /* SVR4 typedef of uint is commonly found on UNIX machines. */ -#if defined(XP_UNIX) && !defined(__QNXNTO__) +#ifdef XP_UNIX #include #else typedef JSUintn uint; diff --git a/lib/javascript/jsparse.c b/lib/javascript/jsparse.c index 132e2ad2..1438f35a 100644 --- a/lib/javascript/jsparse.c +++ b/lib/javascript/jsparse.c @@ -1,5 +1,4 @@ /* -*- 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 @@ -59,7 +58,6 @@ #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" -#include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" @@ -80,10 +78,6 @@ #include "jsxml.h" #endif -#if JS_HAS_DESTRUCTURING -#include "jsdhash.h" -#endif - /* * JS parsers, from lowest to highest precedence. * @@ -98,12 +92,10 @@ typedef JSParseNode * JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax); -typedef JSParseNode * -JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot); - static JSParser FunctionStmt; +#if JS_HAS_LEXICAL_CLOSURE static JSParser FunctionExpr; +#endif static JSParser Statements; static JSParser Statement; static JSParser Variables; @@ -122,7 +114,7 @@ static JSParser AddExpr; static JSParser MulExpr; static JSParser UnaryExpr; static JSMemberParser MemberExpr; -static JSPrimaryParser PrimaryExpr; +static JSParser PrimaryExpr; /* * Insist that the next token be of type tt, or report errno and return null. @@ -246,8 +238,9 @@ NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, pn->pn_op = JSOP_NOP; pn->pn_arity = arity; pn->pn_next = NULL; +#if JS_HAS_XML_SUPPORT pn->pn_ts = ts; - pn->pn_source = NULL; +#endif return pn; } @@ -322,8 +315,9 @@ NewBinary(JSContext *cx, JSTokenType tt, pn->pn_left = left; pn->pn_right = right; pn->pn_next = NULL; +#if JS_HAS_XML_SUPPORT pn->pn_ts = NULL; - pn->pn_source = NULL; +#endif return pn; } @@ -357,69 +351,20 @@ CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) return TOK_ERROR; } CURRENT_TOKEN(ts).t_op = op; - if (JS_HAS_STRICT_OPTION(cx)) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DEPRECATED_USAGE, - name)) { - return TOK_ERROR; - } + name = js_AtomToPrintableString(cx, atom); + if (!name || + !js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DEPRECATED_USAGE, + name)) { + return TOK_ERROR; } return tt; } #endif -static void -MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, - JSStackFrame *newfp) -{ - /* - * Always push a new frame if the current frame is special, so that - * Variables gets the correct variables object: the one from the special - * frame's caller. - */ - if (oldfp && - oldfp->varobj && - oldfp->scopeChain == chain && - !(oldfp->flags & JSFRAME_SPECIAL)) { - return; - } - - memset(newfp, 0, sizeof *newfp); - - /* Default to sharing the same variables object and scope chain. */ - newfp->varobj = newfp->scopeChain = chain; - if (cx->options & JSOPTION_VAROBJFIX) { - while ((chain = JS_GetParent(cx, chain)) != NULL) - newfp->varobj = chain; - } - newfp->down = oldfp; - if (oldfp) { - /* - * In the case of eval and debugger frames, we need to dig down and find - * the real variables objects and function that our new stack frame is - * going to use. - */ - newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | - JSFRAME_SCRIPT_OBJECT); - while (oldfp->flags & JSFRAME_SPECIAL) { - oldfp = oldfp->down; - if (!oldfp) - break; - } - if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) { - newfp->varobj = oldfp->varobj; - newfp->vars = oldfp->vars; - newfp->fun = oldfp->fun; - } - } - cx->fp = newfp; -} - /* * Parse a top-level JS script. */ @@ -436,7 +381,20 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) * the one passed to us. */ fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); + 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; + } /* * Protect atoms from being collected by a GC activation, which might @@ -488,7 +446,20 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, * the one passed to us. */ fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); + 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; + } flags = cx->fp->flags; cx->fp->flags = flags | (JS_HAS_COMPILE_N_GO_OPTION(cx) @@ -516,17 +487,14 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, #endif /* - * No need to emit bytecode here -- Statements already has, for each + * No need to emit code here -- Statements already has, for each * statement in turn. Search for TCF_COMPILING in Statements, below. * That flag is set for every tc == &cg->treeContext, and it implies * that the tc can be downcast to a cg and used to emit code during * parsing, rather than at the end of the parse phase. - * - * Nowadays the threaded interpreter needs a stop instruction, so we - * do have to emit that here. */ JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); - ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; + ok = JS_TRUE; } #ifdef METER_PARSENODES @@ -543,10 +511,9 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, } /* - * Insist on a final return before control flows out of pn. Try to be a bit - * smart about loops: do {...; return e2;} while(0) at the end of a function - * that contains an early return e1 will get a strict warning. Similarly for - * iloops: while (true){...} is treated as though ... returns. + * Insist on a final return before control flows out of pn, but don't be too + * smart about loops (do {...; return e2;} while(0) at the end of a function + * that contains an early return e1 will get a strict-option-only warning). */ #define ENDS_IN_OTHER 0 #define ENDS_IN_RETURN 1 @@ -555,8 +522,8 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, static int HasFinalReturn(JSParseNode *pn) { - JSParseNode *pn2, *pn3; uintN rv, rv2, hasDefault; + JSParseNode *pn2, *pn3; switch (pn->pn_type) { case TOK_LC: @@ -565,46 +532,16 @@ HasFinalReturn(JSParseNode *pn) return HasFinalReturn(PN_LAST(pn)); case TOK_IF: - if (!pn->pn_kid3) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); - - case TOK_WHILE: - pn2 = pn->pn_left; - if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; - - case TOK_DO: - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_PRIMARY) { - if (pn2->pn_op == JSOP_FALSE) - return HasFinalReturn(pn->pn_left); - if (pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - } - if (pn2->pn_type == TOK_NUMBER) { - if (pn2->pn_dval == 0) - return HasFinalReturn(pn->pn_left); - return ENDS_IN_RETURN; - } - return ENDS_IN_OTHER; - - case TOK_FOR: - pn2 = pn->pn_left; - if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; + rv = HasFinalReturn(pn->pn_kid2); + if (pn->pn_kid3) + rv &= HasFinalReturn(pn->pn_kid3); + return rv; +#if JS_HAS_SWITCH_STATEMENT case TOK_SWITCH: rv = ENDS_IN_RETURN; hasDefault = ENDS_IN_OTHER; - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_LEXICALSCOPE) - pn2 = pn2->pn_expr; - for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { + for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) { if (pn2->pn_type == TOK_DEFAULT) hasDefault = ENDS_IN_RETURN; pn3 = pn2->pn_right; @@ -620,6 +557,7 @@ HasFinalReturn(JSParseNode *pn) /* If a final switch has no default case, we judge it harshly. */ rv &= hasDefault; return rv; +#endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_BREAK: return ENDS_IN_BREAK; @@ -631,9 +569,9 @@ HasFinalReturn(JSParseNode *pn) return ENDS_IN_RETURN; case TOK_COLON: - case TOK_LEXICALSCOPE: return HasFinalReturn(pn->pn_expr); +#if JS_HAS_EXCEPTIONS case TOK_THROW: return ENDS_IN_RETURN; @@ -647,22 +585,17 @@ HasFinalReturn(JSParseNode *pn) /* Else check the try block and any and all catch statements. */ rv = HasFinalReturn(pn->pn_kid1); - if (pn->pn_kid2) { - JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); - for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) - rv &= HasFinalReturn(pn2); - } + if (pn->pn_kid2) + rv &= HasFinalReturn(pn->pn_kid2); return rv; case TOK_CATCH: - /* Check this catch block's body. */ - return HasFinalReturn(pn->pn_kid3); - - case TOK_LET: - /* Non-binary let statements are let declarations. */ - if (pn->pn_arity != PN_BINARY) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_right); + /* Check this block's code and iterate over further catch blocks. */ + rv = HasFinalReturn(pn->pn_kid3); + for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2) + rv &= HasFinalReturn(pn2->pn_kid3); + return rv; +#endif default: return ENDS_IN_OTHER; @@ -670,29 +603,33 @@ HasFinalReturn(JSParseNode *pn) } static JSBool -ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, - uintN anonerrnum) +ReportNoReturnValue(JSContext *cx, JSTokenStream *ts) { JSFunction *fun; - const char *name; + JSBool ok; fun = cx->fp->fun; if (fun->atom) { - name = js_AtomToPrintableString(cx, fun->atom); + char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom)); + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_NO_RETURN_VALUE, name); } else { - errnum = anonerrnum; - name = NULL; + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_ANON_NO_RETURN_VALUE); } - return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum, - name); + return ok; } static JSBool CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) { - return HasFinalReturn(pn) == ENDS_IN_RETURN || - ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); + return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts); } static JSParseNode * @@ -701,8 +638,7 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, { JSStackFrame *fp, frame; JSObject *funobj; - JSStmtInfo stmtInfo; - uintN oldflags, firstLine; + uintN oldflags; JSParseNode *pn; fp = cx->fp; @@ -718,52 +654,17 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, cx->fp = &frame; } - /* - * Set interpreted early so js_EmitTree can test it to decide whether to - * eliminate useless expressions. - */ - fun->flags |= JSFUN_INTERPRETED; - - js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); - stmtInfo.flags = SIF_BODY_BLOCK; - oldflags = tc->flags; tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); tc->flags |= TCF_IN_FUNCTION; - - /* - * Save the body's first line, and store it in pn->pn_pos.begin.lineno - * later, because we may have not peeked in ts yet, so Statements won't - * acquire a valid pn->pn_pos.begin from the current token. - */ - firstLine = ts->lineno; pn = Statements(cx, ts, tc); - js_PopStatement(tc); - /* Check for falling off the end of a function that returns a value. */ if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { if (!CheckFinalReturn(cx, ts, pn)) pn = NULL; } - /* - * If we have a parse tree in pn and a code generator in tc, emit this - * function's code. We must do this here, not in js_CompileFunctionBody, - * in order to detect TCF_IN_FUNCTION among tc->flags. - */ - if (pn) { - pn->pn_pos.begin.lineno = firstLine; - if ((tc->flags & TCF_COMPILING)) { - JSCodeGenerator *cg = (JSCodeGenerator *) tc; - - if (!js_FoldConstants(cx, pn, tc) || - !js_EmitFunctionBytecode(cx, cg, pn)) { - pn = NULL; - } - } - } - cx->fp = fp; tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); return pn; @@ -781,6 +682,7 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) JSStackFrame *fp, frame; JSObject *funobj; JSParseNode *pn; + JSBool ok; JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); @@ -807,21 +709,26 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) : JSFRAME_COMPILING; cx->fp = &frame; - /* - * Farble the body so that it looks like a block statement to js_EmitTree, - * which is called beneath FunctionBody; see Statements, further below in - * this file. FunctionBody pushes a STMT_BLOCK record around its call to - * Statements, so Statements will not compile each statement as it loops - * to save JSParseNode space -- it will not compile at all, only build a - * JSParseNode tree. - * - * Therefore we must fold constants, allocate try notes, and generate code - * for this function, including a stop opcode at the end. - */ + /* Ensure that the body looks like a block statement to js_EmitTree. */ CURRENT_TOKEN(ts).type = TOK_LC; pn = FunctionBody(cx, ts, fun, &funcg.treeContext); - if (pn && !js_NewScriptFromCG(cx, &funcg, fun)) - pn = NULL; + if (!pn) { + ok = JS_FALSE; + } else { + /* + * No need to emit code here -- Statements (via FunctionBody) already + * has. See similar comment in js_CompileTokenStream, and bug 108257. + */ + fun->u.script = js_NewScriptFromCG(cx, &funcg, fun); + if (!fun->u.script) { + ok = JS_FALSE; + } else { + fun->interpreted = JS_TRUE; + if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + ok = JS_TRUE; + } + } /* Restore saved state and release code generation arenas. */ cx->fp = fp; @@ -829,227 +736,24 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) js_FinishCodeGenerator(cx, &funcg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); - return pn != NULL; + return ok; } -/* - * Parameter block types for the several Binder functions. We use a common - * helper function signature in order to share code among destructuring and - * simple variable declaration parsers. In the destructuring case, the binder - * function is called indirectly from the variable declaration parser by way - * of CheckDestructuring and its friends. - */ -typedef struct BindData BindData; - -typedef JSBool -(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); - -struct BindData { - JSParseNode *pn; /* error source coordinate */ - JSTokenStream *ts; /* fallback if pn is null */ - JSObject *obj; /* the variable object */ - JSOp op; /* prolog bytecode or nop */ - Binder binder; /* binder, discriminates u */ - union { - struct { - JSFunction *fun; /* must come first! see next */ - } arg; - struct { - JSFunction *fun; /* this overlays u.arg.fun */ - JSClass *clasp; - JSPropertyOp getter; - JSPropertyOp setter; - uintN attrs; - } var; - struct { - jsuint index; - uintN overflow; - } let; - } u; -}; - -/* - * Given BindData *data and JSREPORT_* flags, expand to the second and third - * actual parameters to js_ReportCompileErrorNumber. Prefer reporting via pn - * to reporting via ts, for better destructuring error pointers. - */ -#define BIND_DATA_REPORT_ARGS(data, flags) \ - (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \ - ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags) - -static JSBool -BumpFormalCount(JSContext *cx, JSFunction *fun) -{ - if (fun->nargs == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - return JS_FALSE; - } - fun->nargs++; - return JS_TRUE; -} - -static JSBool -BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - uintN dupflag; - JSFunction *fun; - const char *name; - - obj = data->obj; - ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (!ok) - return JS_FALSE; - - dupflag = 0; - if (prop) { - JS_ASSERT(pobj == obj); - name = js_AtomToPrintableString(cx, atom); - - /* - * A duplicate parameter name, a "feature" required by ECMA-262. - * We force a duplicate node on the SCOPE_LAST_PROP(scope) list - * with the same id, distinguished by the SPROP_IS_DUPLICATE flag, - * and not mapped by an entry in scope. - */ - ok = name && - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name); - - OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!ok) - return JS_FALSE; - - dupflag = SPROP_IS_DUPLICATE; - } - - fun = data->u.arg.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - fun->nargs)) { - return JS_FALSE; - } - - return BumpFormalCount(cx, fun); -} - -static JSBool -BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) -{ - JSFunction *fun; - - /* - * Can't increase fun->nvars in an active frame, so insist that getter is - * js_GetLocalVariable, not js_GetCallVariable or anything else. - */ - if (data->u.var.getter != js_GetLocalVariable) - return JS_TRUE; - - /* - * Don't bind a variable with the hidden name 'arguments', per ECMA-262. - * Instead 'var arguments' always restates the predefined property of the - * activation objects with unhidden name 'arguments'. Assignment to such - * a variable must be handled specially. - */ - if (atom == cx->runtime->atomState.argumentsAtom) - return JS_TRUE; - - fun = data->u.var.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - data->u.var.getter, data->u.var.setter, - SPROP_INVALID_SLOT, - data->u.var.attrs | JSPROP_SHARED, - SPROP_HAS_SHORTID, fun->u.i.nvars)) { - return JS_FALSE; - } - if (fun->u.i.nvars == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_VARS); - return JS_FALSE; - } - fun->u.i.nvars++; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING -/* - * Forward declaration to maintain top-down presentation. - */ -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt); - -static JSBool -BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, - JSTreeContext *tc) -{ - JSAtomListElement *ale; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - const char *name; - - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - ALE_SET_JSOP(ale, data->op); - } - - fun = data->u.var.fun; - obj = data->obj; - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - - if (prop) { - JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj)); - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name)) { - return JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - if (!BindLocalVariable(cx, data, atom)) - return JS_FALSE; - } - return JS_TRUE; -} -#endif /* JS_HAS_DESTRUCTURING */ - static JSParseNode * FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda) { JSOp op, prevop; JSParseNode *pn, *body, *result; - JSTokenType tt; - JSAtom *funAtom, *objAtom; + JSAtom *funAtom, *objAtom, *argAtom; JSStackFrame *fp; JSObject *varobj, *pobj; JSAtomListElement *ale; JSProperty *prop; JSFunction *fun; + uintN dupflag; + JSBool ok; JSTreeContext funtc; -#if JS_HAS_DESTRUCTURING - JSParseNode *item, *list = NULL; -#endif /* Make a TOK_FUNCTION node. */ #if JS_HAS_GETTER_SETTER @@ -1060,15 +764,14 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; /* Scan the optional function name into funAtom. */ - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_NAME) { - funAtom = CURRENT_TOKEN(ts).t_atom; - } else { - funAtom = NULL; - js_UngetToken(ts); + 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; @@ -1102,15 +805,16 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; } } - if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) + if (tc->topStmt && prevop == JSOP_DEFVAR) tc->flags |= TCF_FUN_CLOSURE_VS_VAR; } else { ale = js_IndexAtom(cx, funAtom, &tc->decls); if (!ale) return NULL; } - ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE); + ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN); +#if JS_HAS_LEXICAL_CLOSURE /* * A function nested at top level inside another's body needs only a * local variable to bind its name to its value, and not an activation @@ -1119,7 +823,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a * JSOP_GETVAR bytecode). */ - if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { + if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) { JSScopeProperty *sprop; /* @@ -1153,17 +857,18 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, js_SetLocalVariable, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, - sflags, fp->fun->u.i.nvars)) { + sflags, fp->fun->nvars)) { return NULL; } - if (fp->fun->u.i.nvars == JS_BITMASK(16)) { + if (fp->fun->nvars == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_VARS); return NULL; } - fp->fun->u.i.nvars++; + fp->fun->nvars++; } } +#endif } fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, @@ -1175,6 +880,13 @@ 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. @@ -1189,111 +901,59 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (!objAtom) return NULL; - /* Initialize early for possible flags mutation via DestructuringExpr. */ - TREE_CONTEXT_INIT(&funtc); - /* 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)) { - BindData data; - - data.pn = NULL; - data.ts = ts; - data.obj = fun->object; - data.op = JSOP_NOP; - data.binder = BindArg; - data.u.arg.fun = fun; - do { - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - { - JSParseNode *lhs, *rhs; - jsint slot; - - /* - * A destructuring formal parameter turns into one or more - * local variables initialized from properties of a single - * anonymous positional parameter, so here we must tweak our - * binder and its data. - */ - data.op = JSOP_DEFVAR; - data.binder = BindDestructuringArg; - data.u.var.clasp = &js_FunctionClass; - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - data.u.var.attrs = JSPROP_PERMANENT; - - /* - * Temporarily transfer the owneship of the recycle list to - * funtc. See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; - lhs = DestructuringExpr(cx, &data, &funtc, tt); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - if (!lhs) - return NULL; - - /* - * Restore the formal parameter binder in case there are more - * non-destructuring formals in the parameter list. - */ - data.binder = BindArg; - - /* - * Adjust fun->nargs to count the single anonymous positional - * parameter that is to be destructured. - */ - slot = fun->nargs; - if (!BumpFormalCount(cx, fun)) - return NULL; - - /* - * Synthesize a destructuring assignment from the single - * anonymous positional parameter into the destructuring - * left-hand-side expression and accumulate it in list. - */ - rhs = NewParseNode(cx, ts, PN_NAME, tc); - if (!rhs) - return NULL; - rhs->pn_type = TOK_NAME; - rhs->pn_op = JSOP_GETARG; - rhs->pn_atom = cx->runtime->atomState.emptyAtom; - rhs->pn_expr = NULL; - rhs->pn_slot = slot; - rhs->pn_attrs = 0; - - item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); - if (!item) - return NULL; - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_COMMA; - PN_INIT_LIST(list); - } - PN_APPEND(list, item); - break; - } -#endif /* JS_HAS_DESTRUCTURING */ - - case TOK_NAME: - if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc)) - return NULL; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_FORMAL); + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL); + argAtom = CURRENT_TOKEN(ts).t_atom; + pobj = NULL; + if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + &pobj, &prop)) { return NULL; } + dupflag = 0; + if (prop) { + ok = JS_TRUE; + if (pobj == fun->object && + ((JSScopeProperty *) prop)->getter == js_GetArgument) { + const char *name = js_AtomToPrintableString(cx, argAtom); + + /* + * A duplicate parameter name. We force a duplicate node + * on the SCOPE_LAST_PROP(scope) list with the same id, + * distinguished by the SPROP_IS_DUPLICATE flag, and not + * mapped by an entry in scope. + */ + ok = name && + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DUPLICATE_FORMAL, + name); + + dupflag = SPROP_IS_DUPLICATE; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) + return NULL; + prop = NULL; + } + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + 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)); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); @@ -1302,62 +962,15 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; - /* - * Temporarily transfer the owneship of the recycle list to funtc. - * See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; + TREE_CONTEXT_INIT(&funtc); body = FunctionBody(cx, ts, fun, &funtc); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - if (!body) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; -#if JS_HAS_DESTRUCTURING - /* - * If there were destructuring formal parameters, prepend the initializing - * comma expression that we synthesized to body. If the body is a lexical - * scope node, we must make a special TOK_BODY node, to prepend the formal - * parameter destructuring code without bracing the decompilation of the - * function body's lexical scope. - */ - if (list) { - if (body->pn_arity != PN_LIST) { - JSParseNode *block; - - JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE); - JS_ASSERT(body->pn_arity == PN_NAME); - - block = NewParseNode(cx, ts, PN_LIST, tc); - if (!block) - return NULL; - block->pn_type = TOK_BODY; - block->pn_pos = body->pn_pos; - PN_INIT_LIST_1(block, body); - - body = block; - } - - item = NewParseNode(cx, ts, PN_UNARY, tc); - if (!item) - return NULL; - - item->pn_type = TOK_SEMI; - item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; - item->pn_kid = list; - item->pn_next = body->pn_head; - body->pn_head = item; - if (body->pn_tail == &body->pn_head) - body->pn_tail = &item->pn_next; - ++body->pn_count; - } -#endif - +#if JS_HAS_LEXICAL_CLOSURE /* * If we collected flags that indicate nested heavyweight functions, or * this function contains heavyweight-making statements (references to @@ -1371,20 +984,19 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } else { /* * If this function is a named statement function not at top-level - * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then - * our enclosing function, if any, must be heavyweight. - * - * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, - * so it won't be set here. Assert that it's not. We have to check - * it later, in js_EmitTree, after js_EmitFunctionBody has traversed - * the function's body + * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that + * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our + * enclosing function, if any, must be heavyweight. */ - JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); - if (!lambda && funAtom && !AT_TOP_LEVEL(tc)) + if ((!lambda && funAtom && tc->topStmt) || + (funtc.flags & TCF_FUN_USES_NONLOCALS)) { tc->flags |= TCF_FUN_HEAVYWEIGHT; + } } +#endif result = pn; +#if JS_HAS_LEXICAL_CLOSURE if (lambda) { /* * ECMA ed. 3 standard: function expression, possibly anonymous. @@ -1404,7 +1016,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, result->pn_pos = pn->pn_pos; result->pn_kid = pn; op = JSOP_ANONFUNOBJ; - } else if (!AT_TOP_LEVEL(tc)) { + } else if (tc->topStmt) { /* * ECMA ed. 3 extension: a function expression statement not at the * top level, e.g., in a compound statement such as the "then" part @@ -1412,9 +1024,9 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * sub-statement. */ op = JSOP_CLOSURE; - } else { + } else +#endif op = JSOP_NOP; - } pn->pn_funAtom = objAtom; pn->pn_op = op; @@ -1431,11 +1043,13 @@ FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return FunctionDef(cx, ts, tc, JS_FALSE); } +#if JS_HAS_LEXICAL_CLOSURE static JSParseNode * FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { return FunctionDef(cx, ts, tc, JS_TRUE); } +#endif /* * Parse the statements in a block, creating a TOK_LC node that lists the @@ -1445,7 +1059,7 @@ FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) static JSParseNode * Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - JSParseNode *pn, *pn2, *saveBlock; + JSParseNode *pn, *pn2; JSTokenType tt; CHECK_RECURSION(); @@ -1453,8 +1067,6 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn; PN_INIT_LIST(pn); ts->flags |= TSF_OPERAND; @@ -1468,10 +1080,6 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } ts->flags |= TSF_OPERAND; - /* Detect a function statement for the TOK_LC case in Statement. */ - if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) - tc->flags |= TCF_HAS_FUNCTION_STMT; - /* If compiling top-level statements, emit as we go to save space. */ if (!tc->topStmt && (tc->flags & TCF_COMPILING)) { if (cx->fp->fun && @@ -1505,16 +1113,6 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) PN_APPEND(pn, pn2); } } - - /* - * Handle the case where there was a let declaration under this block. If - * it replaced tc->blockNode with a new block node then we must refresh pn - * and then restore tc->blockNode. - */ - if (tc->blockNode != pn) - pn = tc->blockNode; - tc->blockNode = saveBlock; - ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; @@ -1580,6 +1178,7 @@ static JSBool MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) { JSAtom *label; +#if JS_HAS_LABEL_STATEMENT JSTokenType tt; tt = js_PeekTokenSameLine(cx, ts); @@ -1591,6 +1190,9 @@ MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) } else { label = NULL; } +#else + label = NULL; +#endif pn->pn_atom = label; return JS_TRUE; } @@ -1622,12 +1224,9 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; - ts->flags |= TSF_KEYWORD_IS_NAME; if (js_MatchToken(cx, ts, TOK_STAR)) { pn2->pn_op = JSOP_IMPORTALL; pn2->pn_atom = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; } else { MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); pn2->pn_op = JSOP_GETPROP; @@ -1635,7 +1234,6 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_slot = -1; pn2->pn_attrs = 0; } - ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2->pn_expr = pn; pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; @@ -1677,802 +1275,8 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } #endif /* JS_HAS_EXPORT_IMPORT */ -static JSBool -BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *blockObj; - JSScopeProperty *sprop; - JSAtomListElement *ale; - - blockObj = data->obj; - sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { - const char *name; - - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT((uint16)sprop->shortid < data->u.let.index); - } - - name = js_AtomToPrintableString(cx, atom); - if (name) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (ale && ALE_JSOP(ale) == JSOP_DEFCONST) - ? js_const_str - : "variable", - name); - } - return JS_FALSE; - } - - if (data->u.let.index == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), - data->u.let.overflow); - return JS_FALSE; - } - - /* Use JSPROP_ENUMERATE to aid the disassembler. */ - return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, - (intN)data->u.let.index++, - NULL); -} - -static JSBool -BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSStmtInfo *stmt; - JSAtomListElement *ale; - JSOp op, prevop; - const char *name; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - JSPropertyOp getter, setter; - JSScopeProperty *sprop; - - stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - op = data->op; - if ((stmt && stmt->type != STMT_WITH) || ale) { - prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; - if (JS_HAS_STRICT_OPTION(cx) - ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR - : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - (op != JSOP_DEFCONST && - prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | - JSREPORT_STRICT - : JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (prevop == JSOP_DEFFUN || - prevop == JSOP_CLOSURE) - ? js_function_str - : (prevop == JSOP_DEFCONST) - ? js_const_str - : js_var_str, - name)) { - return JS_FALSE; - } - } - if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) - tc->flags |= TCF_FUN_CLOSURE_VS_VAR; - } - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - } - ALE_SET_JSOP(ale, op); - - fun = data->u.var.fun; - obj = data->obj; - if (!fun) { - /* Don't lookup global variables at compile time. */ - prop = NULL; - } else { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &pobj, &prop)) { - return JS_FALSE; - } - } - - ok = JS_TRUE; - getter = data->u.var.getter; - setter = data->u.var.setter; - - if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *)prop; - if (sprop->getter == js_GetArgument) { - name = js_AtomToPrintableString(cx, atom); - if (!name) { - ok = JS_FALSE; - } else if (op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_PARAM, - name); - ok = JS_FALSE; - } else { - getter = js_GetArgument; - setter = js_SetArgument; - ok = js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_VAR_HIDES_ARG, - name); - } - } else { - JS_ASSERT(getter == js_GetLocalVariable); - - if (fun) { - /* Not an argument, must be a redeclared local var. */ - if (data->u.var.clasp == &js_FunctionClass) { - JS_ASSERT(sprop->getter == js_GetLocalVariable); - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else if (data->u.var.clasp == &js_CallClass) { - if (sprop->getter == js_GetCallVariable) { - /* - * Referencing a name introduced by a var statement in - * the enclosing function. Check that the slot number - * we have is in range. - */ - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else { - /* - * A variable introduced through another eval: don't - * use the special getters and setters since we can't - * allocate a slot in the frame. - */ - getter = sprop->getter; - setter = sprop->setter; - } - } - - /* Override the old getter and setter, to handle eval. */ - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, - 0, sprop->attrs, - getter, setter); - if (!sprop) - ok = JS_FALSE; - } - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - /* - * Property not found in current variable scope: we have not seen this - * variable before. Define a new local variable by adding a property - * to the function's scope, allocating one slot in the function's vars - * frame. Global variables and any locals declared in with statement - * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes - * generated for slot-less vars. - */ - sprop = NULL; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - - if (cx->fp->scopeChain == obj && - !js_InWithStatement(tc) && - !BindLocalVariable(cx, data, atom)) { - return JS_FALSE; - } - } - return ok; -} - -#if JS_HAS_DESTRUCTURING - -static JSBool -BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, - JSTreeContext *tc) -{ - JSAtom *atom; - - /* - * Destructuring is a form of assignment, so just as for an initialized - * simple variable, we must check for assignment to 'arguments' and flag - * the enclosing function (if any) as heavyweight. - */ - JS_ASSERT(pn->pn_type == TOK_NAME); - atom = pn->pn_atom; - if (atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - - data->pn = pn; - if (!data->binder(cx, data, atom, tc)) - return JS_FALSE; - data->pn = NULL; - - /* - * Select the appropriate name-setting opcode, which may be specialized - * further for local variable and argument slot optimizations. At this - * point, we can't select the optimal final opcode, yet we must preserve - * the CONST bit and convey "set", not "get". - */ - pn->pn_op = (data->op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - pn->pn_attrs = data->u.var.attrs; - return JS_TRUE; -} - -/* - * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any - * LHS expression except a destructuring initialiser, and R is on the stack. - * Because R is already evaluated, the usual LHS-specialized bytecodes won't - * work. After pushing R[P] we need to evaluate Q's "reference base" QB and - * then push its property name QN. At this point the stack looks like - * - * [... R, R[P], QB, QN] - * - * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes - * its operands with left-hand side above right-hand side: - * - * [rval, lval, xval] - * - * and pops all three values, setting lval[xval] = rval. But we cannot select - * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, - * which can be optimized further. So we select JSOP_SETNAME. - */ -static JSBool -BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - switch (pn->pn_type) { - case TOK_NAME: - if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - /* FALL THROUGH */ - case TOK_DOT: - case TOK_LB: - pn->pn_op = JSOP_SETNAME; - break; - -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL); - pn->pn_op = JSOP_SETCALL; - break; -#endif - -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (pn->pn_op == JSOP_XMLNAME) { - pn->pn_op = JSOP_BINDXMLNAME; - break; - } - /* FALL THROUGH */ -#endif - - default: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_LEFTSIDE_OF_ASS); - return JS_FALSE; - } - - return JS_TRUE; -} - -typedef struct FindPropValData { - uint32 numvars; /* # of destructuring vars in left side */ - uint32 maxstep; /* max # of steps searching right side */ - JSDHashTable table; /* hash table for O(1) right side search */ -} FindPropValData; - -typedef struct FindPropValEntry { - JSDHashEntryHdr hdr; - JSParseNode *pnkey; - JSParseNode *pnval; -} FindPropValEntry; - -#define ASSERT_VALID_PROPERTY_KEY(pnkey) \ - JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ - ((pnkey)->pn_type == TOK_NUMBER || \ - (pnkey)->pn_type == TOK_STRING || \ - (pnkey)->pn_type == TOK_NAME)) - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -HashFindPropValKey(JSDHashTable *table, const void *key) -{ - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return (pnkey->pn_type == TOK_NUMBER) - ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ - JSDOUBLE_LO32(pnkey->pn_dval)) - : (JSDHashNumber) pnkey->pn_atom->number; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -MatchFindPropValEntry(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const FindPropValEntry *fpve = (const FindPropValEntry *)entry; - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return pnkey->pn_type == fpve->pnkey->pn_type && - ((pnkey->pn_type == TOK_NUMBER) - ? pnkey->pn_dval == fpve->pnkey->pn_dval - : pnkey->pn_atom == fpve->pnkey->pn_atom); -} - -static const JSDHashTableOps FindPropValOps = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - HashFindPropValKey, - MatchFindPropValEntry, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -#define STEP_HASH_THRESHOLD 10 -#define BIG_DESTRUCTURING 5 -#define BIG_OBJECT_INIT 20 - -static JSParseNode * -FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) -{ - FindPropValEntry *entry; - JSParseNode *pnhit, *pnprop, *pnkey; - uint32 step; - - /* If we have a hash table, use it as the sole source of truth. */ - if (data->table.ops) { - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); - return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; - } - - /* If pn is not an object initialiser node, we can't do anything here. */ - if (pn->pn_type != TOK_RC) - return NULL; - - /* - * We must search all the way through pn's list, to handle the case of an - * id duplicated for two or more property initialisers. - */ - pnhit = NULL; - step = 0; - ASSERT_VALID_PROPERTY_KEY(pnid); - if (pnid->pn_type == TOK_NUMBER) { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == TOK_NUMBER && - pnkey->pn_dval == pnid->pn_dval) { - pnhit = pnprop; - } - ++step; - } - } - } else { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == pnid->pn_type && - pnkey->pn_atom == pnid->pn_atom) { - pnhit = pnprop; - } - ++step; - } - } - } - if (!pnhit) - return NULL; - - /* Hit via full search -- see whether it's time to create the hash table. */ - JS_ASSERT(!data->table.ops); - if (step > data->maxstep) { - data->maxstep = step; - if (step >= STEP_HASH_THRESHOLD && - data->numvars >= BIG_DESTRUCTURING && - pn->pn_count >= BIG_OBJECT_INIT && - JS_DHashTableInit(&data->table, &FindPropValOps, pn, - sizeof(FindPropValEntry), pn->pn_count)) { - - for (pn = pn->pn_head; pn; pn = pn->pn_next) { - ASSERT_VALID_PROPERTY_KEY(pn->pn_left); - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pn->pn_left, - JS_DHASH_ADD); - entry->pnval = pn->pn_right; - } - } - } - return pnhit->pn_right; -} - -/* - * If data is null, the caller is AssignExpr and instead of binding variables, - * we specialize lvalues in the propery value positions of the left-hand side. - * If right is null, just check for well-formed lvalues. - */ -static JSBool -CheckDestructuring(JSContext *cx, BindData *data, - JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSBool ok; - FindPropValData fpvd; - JSParseNode *lhs, *rhs, *pn, *pn2; - - if (left->pn_type == TOK_ARRAYCOMP) { - js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_COMP_LEFTSIDE); - return JS_FALSE; - } - - ok = JS_TRUE; - fpvd.table.ops = NULL; - lhs = left->pn_head; - if (lhs && lhs->pn_type == TOK_DEFSHARP) { - pn = lhs; - goto no_var_name; - } - - if (left->pn_type == TOK_RB) { - rhs = (right && right->pn_type == left->pn_type) - ? right->pn_head - : NULL; - - while (lhs) { - pn = lhs, pn2 = rhs; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - if (pn2) { - while (pn2->pn_type == TOK_RP) - pn2 = pn2->pn_kid; - } - } - - /* Nullary comma is an elision; binary comma is an expression.*/ - if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - ok = CheckDestructuring(cx, data, pn, pn2, tc); - } else { - if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - } - if (!ok) - goto out; - } - - lhs = lhs->pn_next; - if (rhs) - rhs = rhs->pn_next; - } - } else { - JS_ASSERT(left->pn_type == TOK_RC); - fpvd.numvars = left->pn_count; - fpvd.maxstep = 0; - rhs = NULL; - - while (lhs) { - JS_ASSERT(lhs->pn_type == TOK_COLON); - pn = lhs->pn_right; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - } - - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (right) { - rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); - if (rhs && !data) { - while (rhs->pn_type == TOK_RP) - rhs = rhs->pn_kid; - } - } - - ok = CheckDestructuring(cx, data, pn, rhs, tc); - } else if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - if (!ok) - goto out; - - lhs = lhs->pn_next; - } - } - -out: - if (fpvd.table.ops) - JS_DHashTableFinish(&fpvd.table); - return ok; - -no_var_name: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - ok = JS_FALSE; - goto out; -} - -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt) -{ - JSParseNode *pn; - - pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE); - if (!pn) - return NULL; - if (!CheckDestructuring(cx, data, pn, NULL, tc)) - return NULL; - return pn; -} - -#endif /* JS_HAS_DESTRUCTURING */ - extern const char js_with_statement_str[]; -static JSParseNode * -ContainsStmt(JSParseNode *pn, JSTokenType tt) -{ - JSParseNode *pn2, *pnt; - - if (!pn) - return NULL; - if (pn->pn_type == tt) - return pn; - switch (pn->pn_arity) { - case PN_LIST: - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pnt = ContainsStmt(pn2, tt); - if (pnt) - return pnt; - } - break; - case PN_TERNARY: - pnt = ContainsStmt(pn->pn_kid1, tt); - if (pnt) - return pnt; - pnt = ContainsStmt(pn->pn_kid2, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_kid3, tt); - case PN_BINARY: - /* - * Limit recursion if pn is a binary expression, which can't contain a - * var statement. - */ - if (pn->pn_op != JSOP_NOP) - return NULL; - pnt = ContainsStmt(pn->pn_left, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_right, tt); - case PN_UNARY: - if (pn->pn_op != JSOP_NOP) - return NULL; - return ContainsStmt(pn->pn_kid, tt); - case PN_NAME: - return ContainsStmt(pn->pn_expr, tt); - default:; - } - return NULL; -} - -static JSParseNode * -ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParser operandParser) -{ - JSTokenType tt, tt2; - JSParseNode *pn, *pn2; - - tt = CURRENT_TOKEN(ts).type; - if (!(tc->flags & TCF_IN_FUNCTION)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_RETURN_OR_YIELD, -#if JS_HAS_GENERATORS - (tt == TOK_YIELD) ? js_yield_str : -#endif - js_return_str); - return NULL; - } - - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - -#if JS_HAS_GENERATORS - if (tt == TOK_YIELD) - tc->flags |= TCF_FUN_IS_GENERATOR; -#endif - - /* This is ugly, but we don't want to require a semicolon. */ - ts->flags |= TSF_OPERAND; - tt2 = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt2 == TOK_ERROR) - return NULL; - - if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC -#if JS_HAS_GENERATORS - && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP)) -#endif - ) { - pn2 = operandParser(cx, ts, tc); - if (!pn2) - return NULL; -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_EXPR; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - } else { -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_VOID; - pn->pn_kid = NULL; - } - - if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { - /* As in Python (see PEP-255), disallow return v; in generators. */ - ReportBadReturn(cx, ts, JSREPORT_ERROR, - JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); - return NULL; - } - - if (JS_HAS_STRICT_OPTION(cx) && - (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && - !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, - JSMSG_ANON_NO_RETURN_VALUE)) { - return NULL; - } - - return pn; -} - -static JSParseNode * -PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSStmtInfo *stmtInfo) -{ - JSParseNode *pn; - JSObject *obj; - JSAtom *atom; - - pn = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn) - return NULL; - - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - js_PushBlockScope(tc, stmtInfo, atom, -1); - pn->pn_type = TOK_LEXICALSCOPE; - pn->pn_op = JSOP_LEAVEBLOCK; - pn->pn_atom = atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - return pn; -} - -#if JS_HAS_BLOCK_SCOPE - -static JSParseNode * -LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) -{ - JSParseNode *pn, *pnblock, *pnlet; - JSStmtInfo stmtInfo; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); - - /* Create the let binary node. */ - pnlet = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pnlet) - return NULL; - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); - - /* This is a let block or expression of the form: let (a, b, c) .... */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - pn = pnblock; - pn->pn_expr = pnlet; - - pnlet->pn_left = Variables(cx, ts, tc); - if (!pnlet->pn_left) - return NULL; - pnlet->pn_left->pn_extra = PNX_POPVAR; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); - - ts->flags |= TSF_OPERAND; - if (statement && !js_MatchToken(cx, ts, TOK_LC)) { - /* - * If this is really an expression in let statement guise, then we - * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop - * the return value of the expression. - */ - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_num = -1; - pn->pn_kid = pnblock; - - statement = JS_FALSE; - } - ts->flags &= ~TSF_OPERAND; - - if (statement) { - pnlet->pn_right = Statements(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); - } else { - /* - * Change pnblock's opcode to the variant that propagates the last - * result down after popping the block, and clear statement. - */ - pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; - pnlet->pn_right = Expr(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - } - - js_PopStatement(tc); - return pn; -} - -#endif /* JS_HAS_BLOCK_SCOPE */ - static JSParseNode * Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { @@ -2543,10 +1347,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_FUNCTION: #if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_DBLCOLON) + if (js_PeekToken(cx, ts) == TOK_DBLCOLON) goto expression; #endif return FunctionStmt(cx, ts, tc); @@ -2563,16 +1364,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; - ts->flags |= TSF_OPERAND; if (js_MatchToken(cx, ts, TOK_ELSE)) { - ts->flags &= ~TSF_OPERAND; stmtInfo.type = STMT_ELSE; pn3 = Statement(cx, ts, tc); if (!pn3) return NULL; pn->pn_pos.end = pn3->pn_pos.end; } else { - ts->flags &= ~TSF_OPERAND; pn3 = NULL; pn->pn_pos.end = pn2->pn_pos.end; } @@ -2582,9 +1380,10 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_kid3 = pn3; return pn; +#if JS_HAS_SWITCH_STATEMENT case TOK_SWITCH: { - JSParseNode *pn5, *saveBlock; + JSParseNode *pn5; JSBool seenDefault = JS_FALSE; pn = NewParseNode(cx, ts, PN_BINARY, tc); @@ -2604,8 +1403,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn2; PN_INIT_LIST(pn2); js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); @@ -2680,22 +1477,14 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn3->pn_right = pn4; } - /* - * Handle the case where there was a let declaration in any case in - * the switch body, but not within an inner block. If it replaced - * tc->blockNode with a new block node then we must refresh pn2 and - * then restore tc->blockNode. - */ - if (tc->blockNode != pn2) - pn2 = tc->blockNode; - tc->blockNode = saveBlock; js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_left = pn1; - pn->pn_right = pn2; + pn->pn_kid1 = pn1; + pn->pn_kid2 = pn2; return pn; } +#endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_WHILE: pn = NewParseNode(cx, ts, PN_BINARY, tc); @@ -2714,6 +1503,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_right = pn2; return pn; +#if JS_HAS_DO_WHILE_LOOP case TOK_DO: pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) @@ -2740,79 +1530,60 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return pn; } break; +#endif /* JS_HAS_DO_WHILE_LOOP */ case TOK_FOR: - { -#if JS_HAS_BLOCK_SCOPE - JSParseNode *pnlet; - JSStmtInfo blockInfo; - - pnlet = NULL; -#endif - /* A FOR node is binary, left is loop control and right is the body. */ pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); - pn->pn_op = JSOP_FORIN; +#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 { + /* Set pn1 to a var list or an initializing expression. */ +#if JS_HAS_IN_OPERATOR /* - * Set pn1 to a var list or an initializing expression. - * * Set the TCF_IN_FOR_INIT flag during parsing of the first clause * of the for statement. This flag will be used by the RelExpr * production; if it is set, then the 'in' keyword will not be * recognized as an operator, leaving it available to be parsed as - * part of a for/in loop. - * - * A side effect of this restriction is that (unparenthesized) - * expressions involving an 'in' operator are illegal in the init - * clause of an ordinary for loop. + * part of a for/in loop. A side effect of this restriction is + * that (unparenthesized) expressions involving an 'in' operator + * are illegal in the init clause of an ordinary for loop. */ tc->flags |= TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ if (tt == TOK_VAR) { (void) js_GetToken(cx, ts); pn1 = Variables(cx, ts, tc); -#if JS_HAS_BLOCK_SCOPE - } else if (tt == TOK_LET) { - (void) js_GetToken(cx, ts); - if (js_PeekToken(cx, ts) == TOK_LP) { - pn1 = LetBlock(cx, ts, tc, JS_FALSE); - tt = TOK_LEXICALSCOPE; - } else { - pnlet = PushLexicalScope(cx, ts, tc, &blockInfo); - if (!pnlet) - return NULL; - pn1 = Variables(cx, ts, tc); - } -#endif } else { pn1 = Expr(cx, ts, tc); - if (pn1) { - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - } } +#if JS_HAS_IN_OPERATOR tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ if (!pn1) return NULL; } @@ -2827,26 +1598,12 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) stmtInfo.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ - JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); - if (TOKEN_TYPE_IS_DECL(tt) - ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST -#if JS_HAS_DESTRUCTURING - || (pn->pn_op == JSOP_FORIN && - (pn1->pn_head->pn_type == TOK_RC || - (pn1->pn_head->pn_type == TOK_RB && - pn1->pn_head->pn_count != 2) || - (pn1->pn_head->pn_type == TOK_ASSIGN && - (pn1->pn_head->pn_left->pn_type != TOK_RB || - pn1->pn_head->pn_left->pn_count != 2)))) -#endif - ) + 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_DESTRUCTURING - ((pn->pn_op == JSOP_FORIN) - ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2) - : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) && -#endif #if JS_HAS_LVALUE_RETURN pn1->pn_type != TOK_LP && #endif @@ -2861,17 +1618,13 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; } - if (TOKEN_TYPE_IS_DECL(tt)) { + if (pn1->pn_type == TOK_VAR) { /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */ pn1->pn_extra |= PNX_FORINVAR; - /* - * Generate a final POP only if the variable is a simple name - * (which means it is not a destructuring left-hand side) and - * it has an initializer. - */ + /* Generate a final POP only if the var has an initializer. */ pn2 = pn1->pn_head; - if (pn2->pn_type == TOK_NAME && pn2->pn_expr) + if (pn2->pn_expr) pn1->pn_extra |= PNX_POPVAR; } else { pn2 = pn1; @@ -2885,31 +1638,10 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif } - switch (pn2->pn_type) { - case TOK_NAME: - /* Beware 'for (arguments in ...)' with or without a 'var'. */ - if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - -#if JS_HAS_DESTRUCTURING - case TOK_ASSIGN: - pn2 = pn2->pn_left; - JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC); - /* FALL THROUGH */ - case TOK_RB: - case TOK_RC: - /* Check for valid lvalues in var-less destructuring for-in. */ - if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc)) - return NULL; - - /* Destructuring for-in requires [key, value] enumeration. */ - if (pn->pn_op != JSOP_FOREACH) - pn->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - default:; + /* Beware 'for (arguments in ...)' with or without a 'var'. */ + if (pn2->pn_type == TOK_NAME && + pn2->pn_atom == cx->runtime->atomState.argumentsAtom) { + tc->flags |= TCF_FUN_HEAVYWEIGHT; } /* Parse the object expression as the right operand of 'in'. */ @@ -2918,9 +1650,10 @@ 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; - pn->pn_op = JSOP_NOP; +#endif /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); @@ -2967,50 +1700,42 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn2) return NULL; pn->pn_right = pn2; + js_PopStatement(tc); /* Record the absolute line number for source note emission. */ pn->pn_pos.end = pn2->pn_pos.end; - -#if JS_HAS_BLOCK_SCOPE - if (pnlet) { - js_PopStatement(tc); - pnlet->pn_expr = pn; - pn = pnlet; - } -#endif - js_PopStatement(tc); 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 *catchList, *lastCatch; - + JSParseNode *catchtail = NULL; /* * try nodes are ternary. * kid1 is the try Statement - * kid2 is the catch node list or null + * kid2 is the catch node * kid3 is the finally Statement * * catch nodes are ternary. - * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) - * kid2 is the catch guard or null if no guard - * kid3 is the catch block + * kid1 is the discriminant + * kid2 is the next catch node, or NULL + * kid3 is the catch block (on kid3 so that we can always append a + * new catch pn on catchtail->kid2) * - * catch lvalue nodes are either: - * TOK_NAME for a single identifier - * TOK_RB or TOK_RC for a destructuring left-hand side + * catch discriminant nodes are binary + * atom is the receptacle + * expr is the discriminant code * - * finally nodes are TOK_LC Statement lists. + * finally nodes are unary (just the finally expression) */ pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; pn->pn_op = JSOP_NOP; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); @@ -3021,128 +1746,69 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); js_PopStatement(tc); - catchList = NULL; - tt = js_GetToken(cx, ts); - if (tt == TOK_CATCH) { - catchList = NewParseNode(cx, ts, PN_LIST, tc); - if (!catchList) + catchtail = pn; + while (js_PeekToken(cx, ts) == TOK_CATCH) { + /* check for another catch after unconditional catch */ + if (catchtail != pn && !catchtail->pn_kid1->pn_expr) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_CATCH_AFTER_GENERAL); return NULL; - catchList->pn_type = TOK_RESERVED; - PN_INIT_LIST(catchList); - lastCatch = NULL; + } - do { - JSParseNode *pnblock; - BindData data; + /* + * legal catch forms are: + * catch (v) + * catch (v if ) + * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) + */ + (void) js_GetToken(cx, ts); /* eat `catch' */ + pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); + if (!pn2) + return NULL; - /* Check for another catch after unconditional catch. */ - if (lastCatch && !lastCatch->pn_kid2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_AFTER_GENERAL); - return NULL; - } + /* + * We use a PN_NAME for the discriminant (catchguard) node + * with the actual discriminant code in the initializer spot + */ + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER); + pn3 = NewParseNode(cx, ts, PN_NAME, tc); + if (!pn3) + return NULL; - /* - * Create a lexical scope node around the whole catch clause, - * including the head. - */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - stmtInfo.type = STMT_CATCH; - - /* - * Legal catch forms are: - * catch (lhs) - * catch (lhs if ) - * where lhs is a name or a destructuring left-hand side. - * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) - */ - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pnblock->pn_expr = pn2; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); - - /* - * Contrary to ECMA Ed. 3, the catch variable is lexically - * scoped, not a property of a new Object instance. This is - * an intentional change that anticipates ECMA Ed. 4. - */ - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pn3 = DestructuringExpr(cx, &data, tc, tt); - if (!pn3) - return NULL; - break; -#endif - - case TOK_NAME: - label = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, label, tc)) - return NULL; - - pn3 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn3) - return NULL; - pn3->pn_atom = label; - pn3->pn_expr = NULL; - pn3->pn_slot = 0; - pn3->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_IDENTIFIER); - return NULL; - } - - pn2->pn_kid1 = pn3; - pn2->pn_kid2 = NULL; + pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn3->pn_expr = NULL; #if JS_HAS_CATCH_GUARD - /* - * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') - * to avoid conflicting with the JS2/ECMAv4 type annotation - * catchguard syntax. - */ - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2->pn_kid2 = Expr(cx, ts, tc); - if (!pn2->pn_kid2) - return NULL; - } -#endif - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); - pn2->pn_kid3 = Statements(cx, ts, tc); - if (!pn2->pn_kid3) + /* + * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to + * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax. + */ + if (js_PeekToken(cx, ts) == TOK_IF) { + (void)js_GetToken(cx, ts); /* eat `if' */ + pn3->pn_expr = Expr(cx, ts, tc); + if (!pn3->pn_expr) return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); - js_PopStatement(tc); + } +#endif + pn2->pn_kid1 = pn3; - PN_APPEND(catchList, pnblock); - lastCatch = pn2; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - } while (tt == TOK_CATCH); + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); + + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); + js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1); + stmtInfo.label = pn3->pn_atom; + pn2->pn_kid3 = Statements(cx, ts, tc); + if (!pn2->pn_kid3) + return NULL; + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); + js_PopStatement(tc); + + catchtail = catchtail->pn_kid2 = pn2; } - pn->pn_kid2 = catchList; + catchtail->pn_kid2 = NULL; - if (tt == TOK_FINALLY) { + if (js_MatchToken(cx, ts, TOK_FINALLY)) { tc->tryCount++; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); @@ -3152,10 +1818,9 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); js_PopStatement(tc); } else { - js_UngetToken(ts); pn->pn_kid3 = NULL; } - if (!catchList && !pn->pn_kid3) { + if (!pn->pn_kid2 && !pn->pn_kid3) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY); return NULL; @@ -3200,6 +1865,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSMSG_FINALLY_WITHOUT_TRY); return NULL; +#endif /* JS_HAS_EXCEPTIONS */ + case TOK_BREAK: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) @@ -3216,7 +1883,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSMSG_LABEL_NOT_FOUND); return NULL; } - if (stmt->type == STMT_LABEL && stmt->atom == label) + if (stmt->type == STMT_LABEL && stmt->label == label) break; } } else { @@ -3252,7 +1919,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) return NULL; } if (stmt->type == STMT_LABEL) { - if (stmt->atom == label) { + if (stmt->label == label) { if (!stmt2 || !STMT_IS_LOOP(stmt2)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | @@ -3313,140 +1980,47 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_extra |= PNX_POPVAR; break; -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - { - JSStmtInfo **sip; - JSObject *obj; - JSAtom *atom; - - /* Check for a let statement or let expression. */ - if (js_PeekToken(cx, ts) == TOK_LP) { - pn = LetBlock(cx, ts, tc, JS_TRUE); - if (!pn || pn->pn_op == JSOP_LEAVEBLOCK) - return pn; - - /* Let expressions require automatic semicolon insertion. */ - JS_ASSERT(pn->pn_type == TOK_SEMI || - pn->pn_op == JSOP_LEAVEBLOCKEXPR); - break; - } - - /* - * This is a let declaration. We must convert the nearest JSStmtInfo - * that is a block or a switch body to be our scope statement. Further - * let declarations in this block will find this scope statement and - * use the same block object. If we are the first let declaration in - * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a - * scope statement) then we also need to set tc->blockNode to be our - * TOK_LEXICALSCOPE. - */ - sip = &tc->topScopeStmt; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (STMT_MAYBE_SCOPE(stmt)) - break; - if (stmt == *sip) - sip = &stmt->downScope; - } - - if (stmt && (stmt->flags & SIF_SCOPE)) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom)); - obj = tc->blockChain; - } else { - if (!stmt) { - /* - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749 - * - * This is a hard case that requires more work. In particular, - * in many cases, we're trying to emit code as we go. However, - * this means that we haven't necessarily finished processing - * all let declarations in the implicit top-level block when - * we emit a reference to one of them. For now, punt on this - * and pretend this is a var declaration. - */ - CURRENT_TOKEN(ts).type = TOK_VAR; - CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR; - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra |= PNX_POPVAR; - break; - } - - /* Convert the block statement into a scope statement. */ - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - /* - * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked - * list stack, if it isn't already there. If it is there, but it - * lacks the SIF_SCOPE flag, it must be a try, catch, or finally - * block. - */ - JS_ASSERT(!(stmt->flags & SIF_SCOPE)); - stmt->flags |= SIF_SCOPE; - if (stmt != *sip) { - JS_ASSERT(!stmt->downScope); - JS_ASSERT(stmt->type == STMT_BLOCK || - stmt->type == STMT_SWITCH || - stmt->type == STMT_TRY || - stmt->type == STMT_FINALLY); - stmt->downScope = *sip; - *sip = stmt; - } else { - JS_ASSERT(stmt->type == STMT_CATCH); - JS_ASSERT(stmt->downScope); - } - - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - tc->blockChain = obj; - stmt->atom = atom; - -#ifdef DEBUG - pn1 = tc->blockNode; - JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE); -#endif - - /* Create a new lexical scope node for these statements. */ - pn1 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn1) - return NULL; - - pn1->pn_type = TOK_LEXICALSCOPE; - pn1->pn_op = JSOP_LEAVEBLOCK; - pn1->pn_pos = tc->blockNode->pn_pos; - pn1->pn_atom = atom; - pn1->pn_expr = tc->blockNode; - pn1->pn_slot = -1; - pn1->pn_attrs = 0; - tc->blockNode = pn1; - } - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra = PNX_POPVAR; - break; - } -#endif /* JS_HAS_BLOCK_SCOPE */ - case TOK_RETURN: - pn = ReturnOrYield(cx, ts, tc, Expr); + if (!(tc->flags & TCF_IN_FUNCTION)) { + js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_RETURN); + return NULL; + } + pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; + + /* This is ugly, but we don't want to require a semicolon. */ + ts->flags |= TSF_OPERAND; + tt = js_PeekTokenSameLine(cx, ts); + ts->flags &= ~TSF_OPERAND; + if (tt == TOK_ERROR) + return NULL; + + if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + tc->flags |= TCF_RETURN_EXPR; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + } else { + tc->flags |= TCF_RETURN_VOID; + pn->pn_kid = NULL; + } + + if (JS_HAS_STRICT_OPTION(cx) && + (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) { + /* + * We must be in a frame with a non-native function, because + * we're compiling one. + */ + if (!ReportNoReturnValue(cx, ts)) + return NULL; + } break; case TOK_LC: - { - uintN oldflags; - - oldflags = tc->flags; - tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT; js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); pn = Statements(cx, ts, tc); if (!pn) @@ -3454,18 +2028,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); js_PopStatement(tc); - - /* - * If we contain a function statement and our container is top-level - * or another block, flag pn to preserve braces when decompiling. - */ - if ((tc->flags & TCF_HAS_FUNCTION_STMT) && - (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) { - pn->pn_extra |= PNX_NEEDBRACES; - } - tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS)); return pn; - } case TOK_EOL: case TOK_SEMI: @@ -3532,7 +2095,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) } label = pn2->pn_atom; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_LABEL && stmt->atom == label) { + if (stmt->type == STMT_LABEL && stmt->label == label) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL); @@ -3543,18 +2106,11 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* Push a label struct and parse the statement. */ js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); - stmtInfo.atom = label; + stmtInfo.label = label; pn = Statement(cx, ts, tc); if (!pn) return NULL; - /* Normalize empty statement to empty block for the decompiler. */ - if (pn->pn_type == TOK_SEMI && !pn->pn_kid) { - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); - } - /* Pop the label, set pn_expr, and return early. */ js_PopStatement(tc); pn2->pn_type = TOK_COLON; @@ -3574,9 +2130,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* Check termination of this primitive statement. */ if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { @@ -3593,123 +2147,105 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) static JSParseNode * Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - JSTokenType tt; - JSBool let; - JSStmtInfo *scopeStmt; - BindData data; JSParseNode *pn, *pn2; + JSObject *obj, *pobj; JSStackFrame *fp; + JSFunction *fun; + JSClass *clasp; + JSPropertyOp getter, setter, currentGetter, currentSetter; JSAtom *atom; - - /* - * The three options here are: - * - TOK_LET: We are parsing a let declaration. - * - TOK_LP: We are parsing the head of a let block. - * - Otherwise, we're parsing var declarations. - */ - tt = CURRENT_TOKEN(ts).type; - let = (tt == TOK_LET || tt == TOK_LP); - JS_ASSERT(let || tt == TOK_VAR); - - /* Make sure that Statement set the tree context up correctly. */ - scopeStmt = tc->topScopeStmt; - if (let) { - while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) { - JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt)); - scopeStmt = scopeStmt->downScope; - } - JS_ASSERT(scopeStmt); - } - - data.pn = NULL; - data.ts = ts; - data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op; - data.binder = let ? BindLet : BindVarOrConst; - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_op = data.op; - PN_INIT_LIST(pn); + JSAtomListElement *ale; + JSOp prevop; + JSProperty *prop; + JSScopeProperty *sprop; + JSBool ok; /* * The tricky part of this code is to create special parsenode opcodes for * getting and setting variables (which will be stored as special slots in - * the frame). The most complicated case is an eval() inside a function. + * the frame). The complex special case is an eval() inside a function. * If the evaluated string references variables in the enclosing function, * then we need to generate the special variable opcodes. We determine - * this by looking up the variable's id in the current variable object. - * Fortunately, we can avoid doing this for let declared variables. + * this by looking up the variable id in the current variable scope. */ - fp = cx->fp; - if (let) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom)); - data.obj = tc->blockChain; - data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj); - data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS; - } else { - data.obj = fp->varobj; - data.u.var.fun = fp->fun; - data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); - if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { - /* We are compiling code inside a function */ - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) { - /* We are compiling code from an eval inside a function */ - data.u.var.getter = js_GetCallVariable; - data.u.var.setter = js_SetCallVariable; - } else { - data.u.var.getter = data.u.var.clasp->getProperty; - data.u.var.setter = data.u.var.clasp->setProperty; - } + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR); + pn = NewParseNode(cx, ts, PN_LIST, tc); + if (!pn) + return NULL; + pn->pn_op = CURRENT_TOKEN(ts).t_op; + PN_INIT_LIST(pn); - data.u.var.attrs = (data.op == JSOP_DEFCONST) - ? JSPROP_PERMANENT | JSPROP_READONLY - : JSPROP_PERMANENT; + /* + * Skip eval and debugger frames when looking for the function whose code + * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION + * will be set in tc->flags, and we can be sure fp->fun is the function to + * use. But if a function calls eval, the string argument is treated as a + * Program (per ECMA), so TCF_IN_FUNCTION won't be set. + * + * What's more, when the following code is reached from eval, cx->fp->fun + * is eval's JSFunction (a native function), so we need to skip its frame. + * We should find the scripted caller's function frame just below it, but + * we code a loop out of paranoia. + */ + for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down) + continue; + obj = fp->varobj; + fun = fp->fun; + clasp = OBJ_GET_CLASS(cx, obj); + if (fun && clasp == &js_FunctionClass) { + /* We are compiling code inside a function */ + getter = js_GetLocalVariable; + setter = js_SetLocalVariable; + } else if (fun && clasp == &js_CallClass) { + /* We are compiling code from an eval inside a function */ + getter = js_GetCallVariable; + setter = js_SetCallVariable; + } else { + getter = clasp->getProperty; + setter = clasp->setProperty; } + ok = JS_TRUE; do { - tt = js_GetToken(cx, ts); -#if JS_HAS_DESTRUCTURING - if (tt == TOK_LB || tt == TOK_LC) { - pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); - if (!pn2) - return NULL; - - if ((tc->flags & TCF_IN_FOR_INIT) && - js_PeekToken(cx, ts) == TOK_IN) { - if (!CheckDestructuring(cx, &data, pn2, NULL, tc)) - return NULL; - PN_APPEND(pn, pn2); - continue; - } - - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, - pn2, AssignExpr(cx, ts, tc), - tc); - if (!pn2 || - !CheckDestructuring(cx, &data, - pn2->pn_left, pn2->pn_right, - tc)) { - return NULL; - } - PN_APPEND(pn, pn2); - continue; - } -#endif - - if (tt != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } + currentGetter = getter; + currentSetter = setter; + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; + + ATOM_LIST_SEARCH(ale, &tc->decls, atom); + if (ale) { + prevop = ALE_JSOP(ale); + 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, + (pn->pn_op != JSOP_DEFCONST && + prevop != JSOP_DEFCONST) + ? JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT + : JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REDECLARED_VAR, + (prevop == JSOP_DEFFUN || + prevop == JSOP_CLOSURE) + ? js_function_str + : (prevop == JSOP_DEFCONST) + ? js_const_str + : js_var_str, + name)) { + return NULL; + } + } + if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) + tc->flags |= TCF_FUN_CLOSURE_VS_VAR; + } else { + ale = js_IndexAtom(cx, atom, &tc->decls); + if (!ale) + return NULL; + } + ALE_SET_JSOP(ale, pn->pn_op); pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) @@ -3718,31 +2254,148 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_atom = atom; pn2->pn_expr = NULL; pn2->pn_slot = -1; - pn2->pn_attrs = let ? 0 : data.u.var.attrs; + pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST) + ? JSPROP_PERMANENT | JSPROP_READONLY + : JSPROP_PERMANENT; PN_APPEND(pn, pn2); - if (js_MatchToken(cx, ts, TOK_ASSIGN)) { - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2->pn_expr = AssignExpr(cx, ts, tc); - if (!pn2->pn_expr) + if (!fun) { + /* 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, ATOM_TO_JSID(atom), &pobj, &prop)) return NULL; - pn2->pn_op = (!let && data.op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - if (!let && atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; } + if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *)prop; + if (sprop->getter == js_GetArgument) { + const char *name = js_AtomToPrintableString(cx, atom); + if (!name) { + ok = JS_FALSE; + } else if (pn->pn_op == JSOP_DEFCONST) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_REDECLARED_PARAM, + name); + ok = JS_FALSE; + } else { + currentGetter = js_GetArgument; + currentSetter = js_SetArgument; + ok = js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_VAR_HIDES_ARG, + name); + } + } else { + if (fun) { + /* Not an argument, must be a redeclared local var. */ + if (clasp == &js_FunctionClass) { + JS_ASSERT(sprop->getter == js_GetLocalVariable); + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + (uint16) sprop->shortid < fun->nvars); + } else if (clasp == &js_CallClass) { + if (sprop->getter == js_GetCallVariable) { + /* + * Referencing a variable introduced by a var + * statement in the enclosing function. Check + * that the slot number we have is in range. + */ + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + (uint16) sprop->shortid < fun->nvars); + } else { + /* + * A variable introduced through another eval: + * don't use the special getters and setters + * since we can't allocate a slot in the frame. + */ + currentGetter = sprop->getter; + currentSetter = sprop->setter; + } + } + + /* Override the old getter and setter, to handle eval. */ + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, + 0, sprop->attrs, + currentGetter, + currentSetter); + if (!sprop) + ok = JS_FALSE; + } + } + } else { + /* + * Property not found in current variable scope: we have not seen + * this variable before. Define a new local variable by adding a + * property to the function's scope, allocating one slot in the + * function's frame. Global variables and any locals declared in + * with statement bodies are handled at runtime, by script prolog + * JSOP_DEFVAR bytecodes generated for slot-less vars. + */ + sprop = NULL; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = NULL; + } + if (currentGetter == js_GetCallVariable) { + /* Can't increase fun->nvars in an active frame! */ + currentGetter = clasp->getProperty; + currentSetter = clasp->setProperty; + } + if (currentGetter == js_GetLocalVariable && + atom != cx->runtime->atomState.argumentsAtom && + fp->scopeChain == obj && + !js_InWithStatement(tc)) { + if (!js_AddHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + currentGetter, currentSetter, + SPROP_INVALID_SLOT, + pn2->pn_attrs | JSPROP_SHARED, + SPROP_HAS_SHORTID, fun->nvars)) { + return NULL; + } + if (fun->nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); + return NULL; + } + fun->nvars++; + } + } + + if (js_MatchToken(cx, ts, TOK_ASSIGN)) { + if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_VAR_INIT); + ok = JS_FALSE; + } else { + pn2->pn_expr = AssignExpr(cx, ts, tc); + if (!pn2->pn_expr) { + ok = JS_FALSE; + } else { + pn2->pn_op = (pn->pn_op == JSOP_DEFCONST) + ? JSOP_SETCONST + : JSOP_SETNAME; + if (atom == cx->runtime->atomState.argumentsAtom) + tc->flags |= TCF_FUN_HEAVYWEIGHT; + } + } + } + + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) + return NULL; } while (js_MatchToken(cx, ts, TOK_COMMA)); pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; return pn; - -bad_var_init: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_VAR_INIT); - return NULL; } static JSParseNode * @@ -3759,15 +2412,6 @@ Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) PN_INIT_LIST_1(pn2, pn); pn = pn2; do { -#if JS_HAS_GENERATORS - pn2 = PN_LAST(pn); - if (pn2->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return NULL; - } -#endif pn2 = AssignExpr(cx, ts, tc); if (!pn2) return NULL; @@ -3787,15 +2431,6 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) CHECK_RECURSION(); -#if JS_HAS_GENERATORS - ts->flags |= TSF_OPERAND; - if (js_MatchToken(cx, ts, TOK_YIELD)) { - ts->flags &= ~TSF_OPERAND; - return ReturnOrYield(cx, ts, tc, AssignExpr); - } - ts->flags &= ~TSF_OPERAND; -#endif - pn = CondExpr(cx, ts, tc); if (!pn) return NULL; @@ -3830,22 +2465,8 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) case TOK_LB: pn2->pn_op = JSOP_SETELEM; break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_DESTRUCT_ASS); - return NULL; - } - pn = AssignExpr(cx, ts, tc); - if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc)) - return NULL; - return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc); -#endif #if JS_HAS_LVALUE_RETURN case TOK_LP: - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); pn2->pn_op = JSOP_SETCALL; break; #endif @@ -3862,15 +2483,17 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSMSG_BAD_LEFTSIDE_OF_ASS); return NULL; } - - return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); + pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); + return pn; } static JSParseNode * CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn1, *pn2, *pn3; +#if JS_HAS_IN_OPERATOR uintN oldflags; +#endif /* JS_HAS_IN_OPERATOR */ pn = OrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { @@ -3878,6 +2501,7 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; +#if JS_HAS_IN_OPERATOR /* * Always accept the 'in' operator in the middle clause of a ternary, * where it's unambiguous, even if we might be parsing the init of a @@ -3885,8 +2509,11 @@ CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) */ oldflags = tc->flags; tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ pn2 = AssignExpr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); +#endif /* JS_HAS_IN_OPERATOR */ if (!pn2) return NULL; @@ -3982,6 +2609,7 @@ RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) JSParseNode *pn; JSTokenType tt; JSOp op; +#if JS_HAS_IN_OPERATOR uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT; /* @@ -3989,22 +2617,30 @@ RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * so unset the flag that prohibits recognizing it. */ tc->flags &= ~TCF_IN_FOR_INIT; +#endif /* JS_HAS_IN_OPERATOR */ pn = ShiftExpr(cx, ts, tc); while (pn && - (js_MatchToken(cx, ts, TOK_RELOP) || + (js_MatchToken(cx, ts, TOK_RELOP) +#if JS_HAS_IN_OPERATOR /* * Recognize the 'in' token as an operator only if we're not * currently in the init expr of a for loop. */ - (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) || - js_MatchToken(cx, ts, TOK_INSTANCEOF))) { + || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) +#endif /* JS_HAS_IN_OPERATOR */ +#if JS_HAS_INSTANCEOF + || js_MatchToken(cx, ts, TOK_INSTANCEOF) +#endif /* JS_HAS_INSTANCEOF */ + )) { tt = CURRENT_TOKEN(ts).type; op = CURRENT_TOKEN(ts).t_op; pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc); } +#if JS_HAS_IN_OPERATOR /* Restore previous state of inForInit flag. */ tc->flags |= inForInitFlag; +#endif /* JS_HAS_IN_OPERATOR */ return pn; } @@ -4082,7 +2718,7 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, return kid; } -static const char incop_name_str[][10] = {"increment", "decrement"}; +static const char *incop_name_str[] = {"increment", "decrement"}; static JSBool SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, @@ -4205,9 +2841,7 @@ UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* Don't look across a newline boundary for a postfix incop. */ if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; if (tt == TOK_INC || tt == TOK_DEC) { (void) js_GetToken(cx, ts); pn2 = NewParseNode(cx, ts, PN_UNARY, tc); @@ -4238,14 +2872,6 @@ ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *argNode = AssignExpr(cx, ts, tc); if (!argNode) return JS_FALSE; -#if JS_HAS_GENERATORS - if (argNode->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, argNode, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return JS_FALSE; - } -#endif PN_APPEND(listNode, argNode); } while (js_MatchToken(cx, ts, TOK_COMMA)); @@ -4269,9 +2895,11 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, /* Check for new expression first. */ ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); + tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_NEW) { + (void) js_GetToken(cx, ts); + pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; @@ -4291,7 +2919,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; } else { - pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); + pn = PrimaryExpr(cx, ts, tc); if (!pn) return NULL; @@ -4307,8 +2935,9 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2->pn_arity = PN_UNARY; pn2->pn_kid = pn; pn2->pn_next = NULL; +#if JS_HAS_XML_SUPPORT pn2->pn_ts = ts; - pn2->pn_source = NULL; +#endif pn = pn2; } } @@ -4318,13 +2947,8 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; #if JS_HAS_XML_SUPPORT - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); + pn3 = PrimaryExpr(cx, ts, tc); if (!pn3) return NULL; tt = pn3->pn_type; @@ -4349,9 +2973,6 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, RecycleTree(group, tc); pn2->pn_type = TOK_FILTER; pn2->pn_op = JSOP_FILTER; - - /* A filtering predicate is like a with statement. */ - tc->flags |= TCF_FUN_HEAVYWEIGHT; } else { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, @@ -4363,9 +2984,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2->pn_right = pn3; } #else - ts->flags |= TSF_KEYWORD_IS_NAME; MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2->pn_op = JSOP_GETPROP; pn2->pn_expr = pn; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; @@ -4377,17 +2996,14 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); + 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_QNAMEPART; + pn3->pn_op = JSOP_STRING; } else if (!TOKEN_TYPE_IS_XML(tt)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, @@ -4466,6 +3082,7 @@ BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) uintN oldflags; JSParseNode *pn; +#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 @@ -4473,8 +3090,11 @@ BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) */ 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; } @@ -4524,9 +3144,9 @@ EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * PropertySelector :: [ Expression ] * PropertySelector * - * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics - * for that rule to result in a name node, but ECMA-357 extends the grammar - * to include PrimaryExpression: QualifiedIdentifier, we must factor further: + * 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 @@ -4541,7 +3161,7 @@ EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * PrimaryExpression: * Identifier QualifiedSuffix * - * We hoist the :: match into callers of QualifiedSuffix, in order to tweak + * We hoists the :: match into callers of QualifiedSuffix, in order to tweak * PropertySelector vs. Identifier pn_arity, pn_op, and other members. */ static JSParseNode * @@ -4584,9 +3204,7 @@ QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, if (pn->pn_op == JSOP_QNAMEPART) pn->pn_op = JSOP_NAME; - ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_STAR || tt == TOK_NAME) { /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ pn2->pn_op = JSOP_QNAMECONST; @@ -4639,9 +3257,7 @@ AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn) return NULL; pn->pn_op = JSOP_TOATTRNAME; - ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_STAR || tt == TOK_NAME) { pn2 = QualifiedIdentifier(cx, ts, tc); } else if (tt == TOK_LB) { @@ -4691,7 +3307,7 @@ XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) } /* - * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, + * 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. @@ -4942,8 +3558,6 @@ XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt; JSAtom *startAtom, *endAtom; - CHECK_RECURSION(); - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) @@ -5034,13 +3648,11 @@ XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; } if (endAtom && startAtom && endAtom != startAtom) { - JSString *str = ATOM_TO_STRING(startAtom); - /* End vs. start tag name mismatch: point to the tag name. */ - js_ReportCompileErrorNumberUC(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_XML_TAG_NAME_MISMATCH, - JSSTRING_CHARS(str)); + ++pn2->pn_pos.begin.index; + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_XML_TAG_NAME_MISMATCH); return NULL; } @@ -5118,7 +3730,21 @@ js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, * the one passed to us. */ fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); + 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); @@ -5145,11 +3771,14 @@ js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, #endif /* JS_HAS_XMLSUPPORT */ static JSParseNode * -PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot) +PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { + JSTokenType tt; JSParseNode *pn, *pn2, *pn3; - JSOp op; +#if JS_HAS_GETTER_SETTER + JSAtom *atom; + JSRuntime *rt; +#endif #if JS_HAS_SHARP_VARS JSParseNode *defsharp; @@ -5167,6 +3796,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, 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); @@ -5176,11 +3809,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #endif switch (tt) { +#if JS_HAS_LEXICAL_CLOSURE || JS_HAS_XML_SUPPORT case TOK_FUNCTION: #if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn2) return NULL; @@ -5190,17 +3822,18 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; break; } - ts->flags &= ~TSF_KEYWORD_IS_NAME; #endif pn = FunctionExpr(cx, ts, tc); if (!pn) return NULL; break; +#endif +#if JS_HAS_INITIALIZERS case TOK_LB: { JSBool matched; - jsuint index; + jsuint atomIndex; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) @@ -5219,8 +3852,8 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, matched = js_MatchToken(cx, ts, TOK_RB); ts->flags &= ~TSF_OPERAND; if (!matched) { - for (index = 0; ; index++) { - if (index == ARRAY_INIT_LIMIT) { + for (atomIndex = 0; ; atomIndex++) { + if (atomIndex == ATOM_INDEX_LIMIT) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG); @@ -5253,219 +3886,13 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } } -#if JS_HAS_GENERATORS - /* - * At this point, (index == 0 && pn->pn_count != 0) implies one - * element initialiser was parsed (possibly with a defsharp before - * the left bracket). - * - * An array comprehension of the form: - * - * [i * j for (i in o) for (j in p) if (i != j)] - * - * translates to roughly the following let expression: - * - * let (array = new Array, i, j) { - * for (i in o) let { - * for (j in p) - * if (i != j) - * array.push(i * j) - * } - * array - * } - * - * where array is a nameless block-local variable. The "roughly" - * means that an implementation may optimize away the array.push. - * An array comprehension opens exactly one block scope, no matter - * how many for heads it contains. - * - * Each let () {...} or for (let ...) ... compiles to: - * - * JSOP_ENTERBLOCK ... JSOP_LEAVEBLOCK - * - * where is a literal object representing the block scope, - * with properties, naming each var declared in the block. - * - * Each var declaration in a let-block binds a name in at - * compile time, and allocates a slot on the operand stack at - * runtime via JSOP_ENTERBLOCK. A block-local var is accessed - * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with - * JSOP_FORLOCAL. These ops all have an immediate operand, the - * local slot's stack index from fp->spbase. - * - * The array comprehension iteration step, array.push(i * j) in - * the example above, is done by ; JSOP_ARRAYCOMP , - * where is the index of array's stack slot. - */ - if (index == 0 && - pn->pn_count != 0 && - js_MatchToken(cx, ts, TOK_FOR)) { - JSParseNode **pnp, *pnexp, *pntop, *pnlet; - BindData data; - JSRuntime *rt; - JSStmtInfo stmtInfo; - JSAtom *atom; - - /* Relabel pn as an array comprehension node. */ - pn->pn_type = TOK_ARRAYCOMP; - - /* - * Remove the comprehension expression from pn's linked list - * and save it via pnexp. We'll re-install it underneath the - * ARRAYPUSH node after we parse the rest of the comprehension. - */ - pnexp = PN_LAST(pn); - JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2); - pn->pn_tail = (--pn->pn_count == 1) - ? &pn->pn_head->pn_next - : &pn->pn_head; - *pn->pn_tail = NULL; - - /* - * Make a parse-node and literal object representing the array - * comprehension's block scope. - */ - pntop = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pntop) - return NULL; - pnp = &pntop->pn_expr; - - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG; - - rt = cx->runtime; - do { - /* - * FOR node is binary, left is control and right is body. - * Use index to count each block-local let-variable on the - * left-hand side of IN. - */ - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - - pn2->pn_op = JSOP_FORIN; - if (js_MatchToken(cx, ts, TOK_NAME)) { - if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom) - pn2->pn_op = JSOP_FOREACH; - else - js_UngetToken(ts); - } - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pnlet = DestructuringExpr(cx, &data, tc, tt); - if (!pnlet) - return NULL; - - if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return NULL; - } - - /* Destructuring requires [key, value] enumeration. */ - if (pn2->pn_op != JSOP_FOREACH) - pn2->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - case TOK_NAME: - atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; - - /* - * Create a name node with op JSOP_NAME. We can't set - * op to JSOP_GETLOCAL here, because we don't yet know - * the block's depth in the operand stack frame. The - * code generator computes that, and it tries to bind - * all names to slots, so we must let it do the deed. - */ - pnlet = NewParseNode(cx, ts, PN_NAME, tc); - if (!pnlet) - return NULL; - pnlet->pn_op = JSOP_NAME; - pnlet->pn_atom = atom; - pnlet->pn_expr = NULL; - pnlet->pn_slot = -1; - pnlet->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS|JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } - - MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME); - pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet, - Expr(cx, ts, tc), tc); - if (!pn3) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - pn2->pn_left = pn3; - *pnp = pn2; - pnp = &pn2->pn_right; - } while (js_MatchToken(cx, ts, TOK_FOR)); - - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pn2->pn_kid1 = Condition(cx, ts, tc); - if (!pn2->pn_kid1) - return NULL; - pn2->pn_kid2 = NULL; - pn2->pn_kid3 = NULL; - *pnp = pn2; - pnp = &pn2->pn_kid2; - } - - pn2 = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_ARRAYPUSH; - pn2->pn_op = JSOP_ARRAYPUSH; - pn2->pn_kid = pnexp; - *pnp = pn2; - PN_APPEND(pn, pntop); - - js_PopStatement(tc); - } -#endif /* JS_HAS_GENERATORS */ - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); } pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; return pn; } -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - pn = LetBlock(cx, ts, tc, JS_FALSE); - if (!pn) - return NULL; - break; -#endif - case TOK_LC: - { - JSBool afterComma; - pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; @@ -5479,38 +3906,32 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #endif PN_INIT_LIST(pn); - afterComma = JS_FALSE; - for (;;) { - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - switch (tt) { - case TOK_NUMBER: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; - break; - case TOK_NAME: -#if JS_HAS_GETTER_SETTER - { - JSAtom *atom; - JSRuntime *rt; + if (!js_MatchToken(cx, ts, TOK_RC)) { + do { + JSOp op; + tt = js_GetToken(cx, ts); + switch (tt) { + case TOK_NUMBER: + pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); + if (pn3) + pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; + break; + case TOK_NAME: +#if JS_HAS_GETTER_SETTER atom = CURRENT_TOKEN(ts).t_atom; rt = cx->runtime; if (atom == rt->atomState.getAtom || atom == rt->atomState.setAtom) { op = (atom == rt->atomState.getAtom) - ? JSOP_GETTER - : JSOP_SETTER; + ? JSOP_GETTER + : JSOP_SETTER; if (js_MatchToken(cx, ts, TOK_NAME)) { pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; pn3->pn_expr = NULL; - pn3->pn_slot = -1; - pn3->pn_attrs = 0; /* We have to fake a 'function' token here. */ CURRENT_TOKEN(ts).t_op = JSOP_NOP; @@ -5521,68 +3942,58 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } } /* else fall thru ... */ - } #endif - case TOK_STRING: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; - break; - case TOK_RC: - if (afterComma && - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_TRAILING_COMMA)) { + case TOK_STRING: + 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, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_TRAILING_COMMA)) { + return NULL; + } + goto end_obj_init; + default: + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_BAD_PROP_ID); + return NULL; + } + + tt = js_GetToken(cx, ts); +#if JS_HAS_GETTER_SETTER + if (tt == TOK_NAME) { + tt = CheckGetterOrSetter(cx, ts, TOK_COLON); + if (tt == TOK_ERROR) return NULL; } - goto end_obj_init; - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_PROP_ID); - return NULL; - } - - tt = js_GetToken(cx, ts); -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_COLON); - if (tt == TOK_ERROR) +#endif + if (tt != TOK_COLON) { + js_ReportCompileErrorNumber(cx, ts, + JSREPORT_TS | JSREPORT_ERROR, + JSMSG_COLON_AFTER_ID); return NULL; - } -#endif - if (tt != TOK_COLON) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_COLON_AFTER_ID); - return NULL; - } - op = CURRENT_TOKEN(ts).t_op; - pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc); + } + op = CURRENT_TOKEN(ts).t_op; + pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), + tc); #if JS_HAS_GETTER_SETTER - skip: + skip: #endif - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); + if (!pn2) + return NULL; + PN_APPEND(pn, pn2); + } while (js_MatchToken(cx, ts, TOK_COMMA)); - tt = js_GetToken(cx, ts); - if (tt == TOK_RC) - goto end_obj_init; - if (tt != TOK_COMMA) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CURLY_AFTER_LIST); - return NULL; - } - afterComma = JS_TRUE; + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST); } - end_obj_init: + end_obj_init: pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; return pn; - } #if JS_HAS_SHARP_VARS case TOK_DEFSHARP: @@ -5593,9 +4004,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; defsharp->pn_kid = NULL; defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; goto again; case TOK_USESHARP: @@ -5607,6 +4015,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, notsharp = JS_TRUE; break; #endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ case TOK_LP: pn = NewParseNode(cx, ts, PN_UNARY, tc); @@ -5617,29 +4026,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - if (pn2->pn_type == TOK_RP || - (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec && - !afterDot)) { - /* - * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly - * to help the decompiler look ahead from a JSOP_ENDINIT to see a - * JSOP_GROUP followed by a POP or POPV. That sequence means the - * parentheses are mandatory, to disambiguate object initialisers - * as expression statements from block statements. - * - * Also drop pn if pn2 is a member or a primary expression of any - * kind. This is required to avoid generating a JSOP_GROUP that - * will null the |obj| interpreter register, causing |this| in any - * call of that member expression to bind to the global object. - */ - pn->pn_kid = NULL; - RecycleTree(pn, tc); - pn = pn2; - } else { - pn->pn_type = TOK_RP; - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_kid = pn2; - } + pn->pn_type = TOK_RP; + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + pn->pn_kid = pn2; break; #if JS_HAS_XML_SUPPORT @@ -5696,28 +4085,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_XML_SUPPORT if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - if (afterDot) { - JSString *str; - - /* - * Here PrimaryExpr is called after '.' or '..' and we - * just scanned .name:: or ..name:: . This is the only - * case where a keyword after '.' or '..' is not - * treated as a property name. - */ - str = ATOM_TO_STRING(pn->pn_atom); - tt = js_CheckKeyword(JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (tt == TOK_FUNCTION) { - pn->pn_arity = PN_NULLARY; - pn->pn_type = TOK_FUNCTION; - } else if (tt != TOK_EOF) { - js_ReportCompileErrorNumber( - cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_KEYWORD_NOT_NS); - return NULL; - } - } pn = QualifiedSuffix(cx, ts, pn, tc); if (!pn) return NULL; @@ -5732,17 +4099,22 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, } else { JSAtomListElement *ale; JSStackFrame *fp; - JSBool loopy; + JSStmtInfo *stmt; /* Measure optimizable global variable uses. */ ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); if (ale && !(fp = cx->fp)->fun && fp->scopeChain == fp->varobj && - js_IsGlobalReference(tc, pn->pn_atom, &loopy)) { + !js_InWithStatement(tc) && + !js_InCatchBlock(tc, pn->pn_atom)) { tc->globalUses++; - if (loopy) - tc->loopyGlobalUses++; + for (stmt = tc->topStmt; stmt; stmt = stmt->down) { + if (STMT_IS_LOOP(stmt)) { + tc->loopyGlobalUses++; + break; + } + } } } } @@ -5797,6 +4169,43 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return pn; } +static JSBool +ContainsVarStmt(JSParseNode *pn) +{ + JSParseNode *pn2; + + if (!pn) + return JS_FALSE; + switch (pn->pn_arity) { + case PN_LIST: + if (pn->pn_type == TOK_VAR) + return JS_TRUE; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (ContainsVarStmt(pn2)) + return JS_TRUE; + } + break; + case PN_TERNARY: + return ContainsVarStmt(pn->pn_kid1) || + ContainsVarStmt(pn->pn_kid2) || + ContainsVarStmt(pn->pn_kid3); + case PN_BINARY: + /* + * Limit recursion if pn is a binary expression, which can't contain a + * var statement. + */ + if (pn->pn_op != JSOP_NOP) + return JS_FALSE; + return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right); + case PN_UNARY: + if (pn->pn_op != JSOP_NOP) + return JS_FALSE; + return ContainsVarStmt(pn->pn_kid); + default:; + } + return JS_FALSE; +} + /* * Fold from one constant type to another. * XXX handles only strings and numbers for now @@ -6081,45 +4490,6 @@ FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) #endif /* JS_HAS_XML_SUPPORT */ -static JSBool -StartsWith(JSParseNode *pn, JSTokenType tt) -{ -#define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO - -recur: - if (pn->pn_type == tt) - return JS_TRUE; - switch (pn->pn_arity) { - case PN_FUNC: - return tt == TOK_FUNCTION; - case PN_LIST: - if (pn->pn_head) - TAIL_RECURSE(pn->pn_head); - break; - case PN_TERNARY: - if (pn->pn_kid1) - TAIL_RECURSE(pn->pn_kid1); - break; - case PN_BINARY: - if (pn->pn_left) - TAIL_RECURSE(pn->pn_left); - break; - case PN_UNARY: - /* A parenthesized expression starts with a left parenthesis. */ - if (pn->pn_type == TOK_RP) - return tt == TOK_LP; - if (pn->pn_kid) - TAIL_RECURSE(pn->pn_kid); - break; - case PN_NAME: - if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT) - TAIL_RECURSE(pn->pn_expr); - /* FALL THROUGH */ - } - return JS_FALSE; -#undef TAIL_RECURSE -} - JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { @@ -6245,14 +4615,12 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) switch (pn->pn_type) { case TOK_IF: - if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR)) + if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3)) break; /* FALL THROUGH */ case TOK_HOOK: /* Reduce 'if (C) T; else E' into T for true C, E for false. */ - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; switch (pn1->pn_type) { case TOK_NUMBER: if (pn1->pn_dval == 0) @@ -6276,50 +4644,19 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } if (pn2) { - /* - * pn2 is the then- or else-statement subtree to compile. Take - * care not to expose an object initialiser, which would be parsed - * as a block, to the Statement parser via eval(uneval(e)) where e - * is '1 ? {p:2, q:3}[i] : r;' or the like. - */ - if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) { - pn->pn_type = TOK_RP; - pn->pn_arity = PN_UNARY; - pn->pn_kid = pn2; - } else { - PN_MOVE_NODE(pn, pn2); - } - } - if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) { - /* - * False condition and no else, or an empty then-statement was - * moved up over pn. Either way, make pn an empty block (not an - * empty statement, which does not decompile, even when labeled). - * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid - * or an empty statement for a child. - */ - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); + /* pn2 is the then- or else-statement subtree to compile. */ + PN_MOVE_NODE(pn, pn2); + } else { + /* False condition and no else: make pn an empty statement. */ + pn->pn_type = TOK_SEMI; + pn->pn_arity = PN_UNARY; + pn->pn_kid = NULL; } RecycleTree(pn2, tc); if (pn3 && pn3 != pn2) RecycleTree(pn3, tc); break; - case TOK_ASSIGN: - /* - * Compound operators such as *= should be subject to folding, in case - * the left-hand side is constant, and so that the decompiler produces - * the same string that you get from decompiling a script or function - * compiled from that same string. As with +, += is special. - */ - if (pn->pn_op == JSOP_NOP) - break; - if (pn->pn_op != JSOP_ADD) - goto do_binary_op; - /* FALL THROUGH */ - case TOK_PLUS: if (pn->pn_arity == PN_LIST) { size_t length, length2; @@ -6422,8 +4759,6 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { if (!FoldType(cx, pn2, TOK_NUMBER)) return JS_FALSE; - } - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { /* XXX fold only if all operands convert to number */ if (pn2->pn_type != TOK_NUMBER) break; @@ -6455,8 +4790,6 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) break; case TOK_UNARYOP: - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; if (pn1->pn_type == TOK_NUMBER) { jsdouble d; int32 i; diff --git a/lib/javascript/jsparse.h b/lib/javascript/jsparse.h index 7c23927e..24b27bdc 100644 --- a/lib/javascript/jsparse.h +++ b/lib/javascript/jsparse.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -82,10 +81,7 @@ JS_BEGIN_EXTERN_C * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null * TOK_SWITCH binary pn_left: discriminant * pn_right: list of TOK_CASE nodes, with at most one - * TOK_DEFAULT node, or if there are let bindings - * in the top level of the switch body's cases, a - * TOK_LEXICALSCOPE node that contains the list of - * TOK_CASE nodes. + * TOK_DEFAULT node * TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT * TOK_DEFAULT pn_right: TOK_LC node for this case's statements * pn_val: constant value if lookup or table switch @@ -105,13 +101,11 @@ JS_BEGIN_EXTERN_C * pn_right: body * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception * TOK_TRY ternary pn_kid1: try block - * pn_kid2: null or TOK_RESERVED list of - * TOK_LEXICALSCOPE nodes, each with pn_expr pointing - * to a TOK_CATCH node - * pn_kid3: null or finally block - * TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_RB, or TOK_RC catch var node - * (TOK_RB or TOK_RC if destructuring) - * pn_kid2: null or the catch guard expression + * pn_kid2: catch blocks or null + * pn_kid3: finally block or null + * TOK_CATCH ternary pn_kid1: PN_NAME node for catch var (with pn_expr + * null or the catch guard expression) + * pn_kid2: more catch blocks or null * pn_kid3: catch block statements * TOK_BREAK name pn_atom: label or null * TOK_CONTINUE name pn_atom: label or null @@ -189,8 +183,6 @@ 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 @@ -243,21 +235,6 @@ JS_BEGIN_EXTERN_C * translates to: * * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) - * - * - * - * Label Variant Members - * ----- ------- ------- - * TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR - * pn_atom: block object - * pn_expr: block body - * TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements - * if pn_count is 2, first element is #n=[...] - * last element is block enclosing for loop(s) - * and optionally if-guarded TOK_ARRAYPUSH - * pn_extra: stack slot, used during code gen - * TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP - * pn_kid: array comprehension expression */ typedef enum JSParseNodeArity { PN_FUNC = -3, @@ -286,7 +263,7 @@ struct JSParseNode { JSParseNode *head; /* first node in list */ JSParseNode **tail; /* ptr to ptr to last node in list */ uint32 count; /* number of nodes in list */ - uint32 extra; /* extra flags, see below */ + uint32 extra; /* extra comma flag for [1,2,,] */ } list; struct { /* ternary: if, for(;;), ?: */ JSParseNode *kid1; /* condition, discriminant, etc. */ @@ -315,8 +292,9 @@ struct JSParseNode { jsdouble dval; /* aligned numeric literal value */ } pn_u; JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ - JSTokenStream *pn_ts; /* token stream for error reports */ - JSAtom *pn_source; /* saved source for decompilation */ +#if JS_HAS_XML_SUPPORT + JSTokenStream *pn_ts; /* token stream for XML error reports */ +#endif }; #define pn_funAtom pn_u.func.funAtom @@ -350,8 +328,6 @@ struct JSParseNode { 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 */ -#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */ -#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ /* * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off diff --git a/lib/javascript/jsprf.c b/lib/javascript/jsprf.c index 416c16c8..ba7c0f03 100644 --- a/lib/javascript/jsprf.c +++ b/lib/javascript/jsprf.c @@ -402,9 +402,9 @@ 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. + /* + * 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); @@ -997,9 +997,9 @@ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) case 's': if(type == TYPE_INT16) { - /* - * This would do a simple string/byte conversion - * if JS_C_STRINGS_ARE_UTF8 is not defined. + /* + * 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); diff --git a/lib/javascript/jsproto.tbl b/lib/javascript/jsproto.tbl deleted file mode 100644 index 18f23559..00000000 --- a/lib/javascript/jsproto.tbl +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80 ft=c: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey 1.7 work in progress, released - * February 14, 2006. - * - * The Initial Developer of the Original Code is - * Brendan Eich - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient 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 "jsconfig.h" - -#if JS_HAS_SCRIPT_OBJECT -# define SCRIPT_INIT js_InitScriptClass -#else -# define SCRIPT_INIT js_InitNullClass -#endif - -#if JS_HAS_XML_SUPPORT -# define XML_INIT js_InitXMLClass -# define NAMESPACE_INIT js_InitNamespaceClass -# define QNAME_INIT js_InitQNameClass -# define ANYNAME_INIT js_InitAnyNameClass -# define ATTRIBUTE_INIT js_InitAttributeNameClass -#else -# define XML_INIT js_InitNullClass -# define NAMESPACE_INIT js_InitNullClass -# define QNAME_INIT js_InitNullClass -# define ANYNAME_INIT js_InitNullClass -# define ATTRIBUTE_INIT js_InitNullClass -#endif - -#if JS_HAS_GENERATORS -# define GENERATOR_INIT js_InitIteratorClasses -#else -# define GENERATOR_INIT js_InitNullClass -#endif - -#if JS_HAS_FILE_OBJECT -# define FILE_INIT js_InitFileClass -#else -# define FILE_INIT js_InitNullClass -#endif - -/* - * Enumerator codes in the second column must not change -- they are part of - * the JS XDR API. - */ -JS_PROTO(Null, 0, js_InitNullClass) -JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses) -JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses) -JS_PROTO(Array, 3, js_InitArrayClass) -JS_PROTO(Boolean, 4, js_InitBooleanClass) -JS_PROTO(Call, 5, js_InitCallClass) -JS_PROTO(Date, 6, js_InitDateClass) -JS_PROTO(Math, 7, js_InitMathClass) -JS_PROTO(Number, 8, js_InitNumberClass) -JS_PROTO(String, 9, js_InitStringClass) -JS_PROTO(RegExp, 10, js_InitRegExpClass) -JS_PROTO(Script, 11, SCRIPT_INIT) -JS_PROTO(XML, 12, XML_INIT) -JS_PROTO(Namespace, 13, NAMESPACE_INIT) -JS_PROTO(QName, 14, QNAME_INIT) -JS_PROTO(AnyName, 15, ANYNAME_INIT) -JS_PROTO(AttributeName, 16, ATTRIBUTE_INIT) -JS_PROTO(Error, 17, js_InitExceptionClasses) -JS_PROTO(InternalError, 18, js_InitExceptionClasses) -JS_PROTO(EvalError, 19, js_InitExceptionClasses) -JS_PROTO(RangeError, 20, js_InitExceptionClasses) -JS_PROTO(ReferenceError, 21, js_InitExceptionClasses) -JS_PROTO(SyntaxError, 22, js_InitExceptionClasses) -JS_PROTO(TypeError, 23, js_InitExceptionClasses) -JS_PROTO(URIError, 24, js_InitExceptionClasses) -JS_PROTO(Generator, 25, GENERATOR_INIT) -JS_PROTO(Iterator, 26, js_InitIteratorClasses) -JS_PROTO(StopIteration, 27, js_InitIteratorClasses) -JS_PROTO(UnusedProto28, 28, js_InitNullClass) -JS_PROTO(File, 29, FILE_INIT) -JS_PROTO(Block, 30, js_InitBlockClass) - -#undef SCRIPT_INIT -#undef XML_INIT -#undef NAMESPACE_INIT -#undef QNAME_INIT -#undef ANYNAME_INIT -#undef ATTRIBUTE_INIT -#undef GENERATOR_INIT -#undef FILE_INIT diff --git a/lib/javascript/jsprvtd.h b/lib/javascript/jsprvtd.h index f71b9a50..ed12f1bd 100644 --- a/lib/javascript/jsprvtd.h +++ b/lib/javascript/jsprvtd.h @@ -91,11 +91,11 @@ typedef uint32 jsatomid; typedef struct JSArgumentFormatMap JSArgumentFormatMap; typedef struct JSCodeGenerator JSCodeGenerator; typedef struct JSDependentString JSDependentString; +typedef struct JSGCLockHashEntry JSGCLockHashEntry; +typedef struct JSGCRootHashEntry JSGCRootHashEntry; typedef struct JSGCThing JSGCThing; -typedef struct JSGenerator JSGenerator; typedef struct JSParseNode JSParseNode; typedef struct JSSharpObjectMap JSSharpObjectMap; -typedef struct JSThread JSThread; typedef struct JSToken JSToken; typedef struct JSTokenPos JSTokenPos; typedef struct JSTokenPtr JSTokenPtr; @@ -116,6 +116,7 @@ typedef struct JSRegExpStatics JSRegExpStatics; typedef struct JSScope JSScope; typedef struct JSScopeOps JSScopeOps; typedef struct JSScopeProperty JSScopeProperty; +typedef struct JSStackFrame JSStackFrame; typedef struct JSStackHeader JSStackHeader; typedef struct JSStringBuffer JSStringBuffer; typedef struct JSSubString JSSubString; @@ -178,7 +179,7 @@ typedef void * 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 structure in the 'before' invocation and return a pointer to that + * 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 diff --git a/lib/javascript/jspubtd.h b/lib/javascript/jspubtd.h index 4e8c92a6..e9da9278 100644 --- a/lib/javascript/jspubtd.h +++ b/lib/javascript/jspubtd.h @@ -70,7 +70,6 @@ typedef enum JSVersion { JSVERSION_ECMA_3 = 148, JSVERSION_1_5 = 150, JSVERSION_1_6 = 160, - JSVERSION_1_7 = 170, JSVERSION_DEFAULT = 0, JSVERSION_UNKNOWN = -1 } JSVersion; @@ -91,14 +90,6 @@ typedef enum JSType { JSTYPE_LIMIT } JSType; -/* Dense index into cached prototypes and class atoms for standard objects. */ -typedef enum JSProtoKey { -#define JS_PROTO(name,code,init) JSProto_##name = code, -#include "jsproto.tbl" -#undef JS_PROTO - JSProto_LIMIT -} JSProtoKey; - /* JSObjectOps.checkAccess mode enumeration. */ typedef enum JSAccessMode { JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ @@ -140,7 +131,6 @@ typedef struct JSXMLObjectOps JSXMLObjectOps; typedef struct JSRuntime JSRuntime; typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ typedef struct JSScript JSScript; -typedef struct JSStackFrame JSStackFrame; typedef struct JSString JSString; typedef struct JSXDRState JSXDRState; typedef struct JSExceptionState JSExceptionState; @@ -539,27 +529,6 @@ typedef JSBool /* Callbacks and their arguments. */ -typedef enum JSContextOp { - JSCONTEXT_NEW, - JSCONTEXT_DESTROY -} JSContextOp; - -/* - * The possible values for contextOp when the runtime calls the callback are: - * JSCONTEXT_NEW JS_NewContext succesfully created a new JSContext - * instance. The callback can initialize the instance as - * required. If the callback returns false, the instance - * will be destroyed and JS_NewContext returns null. In - * this case the callback is not called again. - * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The - * callback may perform its own cleanup and must always - * return true. - * Any other value For future compatibility the callback must do nothing - * and return true in this case. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSContextCallback)(JSContext *cx, uintN contextOp); - typedef enum JSGCStatus { JSGC_BEGIN, JSGC_END, @@ -577,24 +546,6 @@ typedef void (* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report); -/* - * Possible exception types. These types are part of a JSErrorFormatString - * structure. They define which error to throw in case of a runtime error. - * JSEXN_NONE marks an unthrowable error. - */ -typedef enum JSExnType { - JSEXN_NONE = -1, - JSEXN_ERR, - JSEXN_INTERNALERR, - JSEXN_EVALERR, - JSEXN_RANGEERR, - JSEXN_REFERENCEERR, - JSEXN_SYNTAXERR, - JSEXN_TYPEERR, - JSEXN_URIERR, - JSEXN_LIMIT -} JSExnType; - typedef struct JSErrorFormatString { /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ const char *format; diff --git a/lib/javascript/jsregexp.c b/lib/javascript/jsregexp.c index 5d2fce48..e9400eb0 100644 --- a/lib/javascript/jsregexp.c +++ b/lib/javascript/jsregexp.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: + * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -63,6 +63,8 @@ #include "jsscan.h" #include "jsstr.h" +#if JS_HAS_REGEXPS + /* Note : contiguity of 'simple opcodes' is important for SimpleMatch() */ typedef enum REOp { REOP_EMPTY = 0, /* match rest of input against rest of r.e. */ @@ -931,12 +933,10 @@ CalculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, localMax = 0xB; break; case 'c': - if (src < end && RE_IS_LETTER(*src)) { + if (src + 1 < end && RE_IS_LETTER(src[1])) localMax = (jschar) (*src++ & 0x1F); - } else { - --src; + else localMax = '\\'; - } break; case 'x': nDigits = 2; @@ -1023,11 +1023,6 @@ lexHex: localMax = *src++; break; } - if (state->flags & JSREG_FOLD) { - c = JS_MAX(upcase((jschar) localMax), downcase((jschar) localMax)); - if (c > localMax) - localMax = c; - } if (inRange) { if (rangeStart > localMax) { JS_ReportErrorNumber(state->context, @@ -1046,6 +1041,11 @@ lexHex: } } } + if (state->flags & JSREG_FOLD) { + c = JS_MAX(upcase((jschar)localMax), downcase((jschar)localMax)); + if (c > localMax) + localMax = c; + } if (localMax > max) max = localMax; } @@ -1252,7 +1252,7 @@ ParseTerm(CompilerState *state) goto doFlat; /* Control letter */ case 'c': - if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { + if (state->cp + 1 < state->cpend && RE_IS_LETTER(state->cp[1])) { c = (jschar) (*state->cp++ & 0x1F); } else { /* back off to accepting the original '\' as a literal */ @@ -1927,10 +1927,7 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts, state.context = cx; state.tokenStream = ts; - state.cp = js_UndependString(cx, str); - if (!state.cp) - goto out; - state.cpbegin = state.cp; + state.cpbegin = state.cp = JSSTRING_CHARS(str); state.cpend = state.cp + len; state.flags = flags; state.parenCount = 0; @@ -1969,8 +1966,6 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts, re = NULL; goto out; } - for (i = 0; i < re->classCount; i++) - re->classList[i].converted = JS_FALSE; } else { re->classList = NULL; } @@ -2070,11 +2065,8 @@ PushBackTrackState(REGlobalData *gData, REOp op, btincr = JS_ROUNDUP(btincr, btsize); JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, &gData->pool, btsize, btincr); - if (!gData->backTrackStack) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; + if (!gData->backTrackStack) return NULL; - } gData->backTrackStackSize = btsize + btincr; result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); } @@ -2260,11 +2252,8 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet) byteLength = (charSet->length >> 3) + 1; charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); - if (!charSet->u.bits) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; + if (!charSet->u.bits) return JS_FALSE; - } memset(charSet->u.bits, 0, byteLength); if (src == end) @@ -2302,7 +2291,7 @@ ProcessCharSet(REGlobalData *gData, RECharSet *charSet) thisCh = 0xB; break; case 'c': - if (src < end && JS_ISWORD(*src)) { + if (src + 1 < end && JS_ISWORD(src[1])) { thisCh = (jschar)(*src++ & 0x1F); } else { --src; @@ -2476,13 +2465,12 @@ ReallocStateStack(REGlobalData *gData) /* * Apply the current op against the given input to see if it's going to match - * or fail. Return false if we don't get a match, true if we do. If updatecp is - * true, then update the current state's cp. Always update startpc to the next - * op. + * or fail. Return false if we don't get a match, true if we do and update the + * state of the input and pc if the update flag is true. */ static REMatchState * SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, - jsbytecode **startpc, JSBool updatecp) + jsbytecode **startpc, JSBool update) { REMatchState *result = NULL; jschar matchCh; @@ -2667,9 +2655,10 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, JS_ASSERT(JS_FALSE); } if (result) { - if (!updatecp) + if (update) + *startpc = pc; + else x->cp = startcp; - *startpc = pc; return result; } x->cp = startcp; @@ -2681,7 +2670,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) { REMatchState *result = NULL; REBackTrackData *backTrackData; - jsbytecode *nextpc, *testpc; + jsbytecode *nextpc; REOp nextop; RECapture *cap; REProgState *curState; @@ -2692,19 +2681,6 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) jschar matchCh1, matchCh2; RECharSet *charSet; - JSBranchCallback onbranch = gData->cx->branchCallback; - uintN onbranchCalls = 0; -#define ONBRANCH_CALLS_MASK 127 -#define CHECK_BRANCH() \ - JS_BEGIN_MACRO \ - if (onbranch && \ - (++onbranchCalls & ONBRANCH_CALLS_MASK) == 0 && \ - !(*onbranch)(gData->cx, NULL)) { \ - gData->ok = JS_FALSE; \ - return NULL; \ - } \ - JS_END_MACRO - JSBool anchor; jsbytecode *pc = gData->regexp->program; REOp op = (REOp) *pc++; @@ -2755,8 +2731,9 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) goto doAlt; charSet = &gData->regexp->classList[k]; - if (!charSet->converted && !ProcessCharSet(gData, charSet)) - return NULL; + if (!charSet->converted) + if (!ProcessCharSet(gData, charSet)) + return NULL; matchCh1 = *x->cp; k = matchCh1 >> 3; if ((charSet->length == 0 || @@ -2806,7 +2783,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) continue; /* - * Occurs at (successful) end of REOP_ALT, + * Occurs at (succesful) end of REOP_ALT, */ case REOP_JUMP: --gData->stateStackTop; @@ -2815,7 +2792,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) continue; /* - * Occurs at last (successful) end of REOP_ALT, + * Occurs at last (succesful) end of REOP_ALT, */ case REOP_ENDALT: --gData->stateStackTop; @@ -2852,9 +2829,8 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ pc += ARG_LEN; /* start of ASSERT child */ op = (REOp) *pc++; - testpc = pc; if (REOP_IS_SIMPLE(op) && - !SimpleMatch(gData, x, op, &testpc, JS_FALSE)) { + !SimpleMatch(gData, x, op, &pc, JS_FALSE)) { result = NULL; break; } @@ -2874,10 +2850,9 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) nextpc = pc + GET_OFFSET(pc); pc += ARG_LEN; op = (REOp) *pc++; - testpc = pc; if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && - SimpleMatch(gData, x, op, &testpc, JS_FALSE) && - *testpc == REOP_ASSERTNOTTEST) { + SimpleMatch(gData, x, op, &pc, JS_FALSE) && + pc == nextpc) { result = NULL; break; } @@ -2889,9 +2864,8 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, - nextpc, x, x->cp, 0, 0)) { + nextpc, x, x->cp, 0, 0)) return NULL; - } continue; case REOP_ASSERTTEST: @@ -2984,7 +2958,6 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) continue; case REOP_REPEAT: - CHECK_BRANCH(); --curState; do { --gData->stateStackTop; @@ -3082,7 +3055,6 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x) continue; case REOP_MINIMALREPEAT: - CHECK_BRANCH(); --gData->stateStackTop; --curState; @@ -3231,19 +3203,19 @@ InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) &gData->pool, INITIAL_BACKTRACK); if (!gData->backTrackStack) - goto bad; - + return NULL; gData->backTrackSP = gData->backTrackStack; gData->cursz = 0; + gData->stateStackLimit = INITIAL_STATESTACK; JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, &gData->pool, sizeof(REProgState) * INITIAL_STATESTACK); if (!gData->stateStack) - goto bad; - + return NULL; gData->stateStackTop = 0; + gData->cx = cx; gData->regexp = re; gData->ok = JS_TRUE; @@ -3253,21 +3225,14 @@ InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture)); if (!result) - goto bad; + return NULL; - for (i = 0; i < re->classCount; i++) { - if (!re->classList[i].converted && - !ProcessCharSet(gData, &re->classList[i])) { - return NULL; - } - } + for (i = 0; i < re->classCount; i++) + if (!re->classList[i].converted) + if (!ProcessCharSet(gData, &re->classList[i])) + return NULL; return result; - -bad: - JS_ReportOutOfMemory(cx); - gData->ok = JS_FALSE; - return NULL; } JSBool @@ -3306,10 +3271,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4); x = InitMatch(cx, &gData, re); - if (!x) { - ok = JS_FALSE; - goto out; - } + if (!x) + return JS_FALSE; x->cp = cp; /* @@ -3359,15 +3322,15 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JS_PropertyStub, JS_PropertyStub, \ JSPROP_ENUMERATE, NULL); \ if (!ok) { \ - cx->weakRoots.newborn[GCX_OBJECT] = NULL; \ - cx->weakRoots.newborn[GCX_STRING] = NULL; \ + cx->newborn[GCX_OBJECT] = NULL; \ + cx->newborn[GCX_STRING] = NULL; \ goto out; \ } \ } matchstr = js_NewStringCopyN(cx, cp, matchlen, 0); if (!matchstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; ok = JS_FALSE; goto out; } @@ -3404,8 +3367,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, res->moreLength * sizeof(JSSubString)); } if (!morepar) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; ok = JS_FALSE; goto out; } @@ -3428,8 +3391,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index, parsub->length, 0); if (!parstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; ok = JS_FALSE; goto out; } @@ -3438,8 +3401,8 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JSPROP_ENUMERATE, NULL); } if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; + cx->newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_STRING] = NULL; goto out; } } @@ -3466,14 +3429,28 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, res->lastMatch.chars = cp; res->lastMatch.length = matchlen; - - /* - * For JS1.3 and ECMAv2, emulate Perl5 exactly: - * - * js1.3 "hi", "hi there" "hihitherehi therebye" - */ - res->leftContext.chars = JSSTRING_CHARS(str); - res->leftContext.length = start + gData.skipped; + 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" + * pseudo-context. On "hi there bye", the following would result: + * + * Language while(/ /g){print("$`");} s/ /$`/g + * perl4.036 "hi", "there" "hihitherehi therebye" + * perl5 "hi", "hi there" "hihitherehi therebye" + * js1.2 "hi", "there" "hihitheretherebye" + */ + res->leftContext.chars = JSSTRING_CHARS(str) + start; + res->leftContext.length = gData.skipped; + } else { + /* + * For JS1.3 and ECMAv2, emulate Perl5 exactly: + * + * js1.3 "hi", "hi there" "hihitherehi therebye" + */ + res->leftContext.chars = JSSTRING_CHARS(str); + res->leftContext.length = start + gData.skipped; + } res->rightContext.chars = ep; res->rightContext.length = gData.cpend - ep; @@ -3785,14 +3762,13 @@ regexp_mark(JSContext *cx, JSObject *obj, void *arg) { JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj); if (re) - GC_MARK(cx, re->source, "source"); + JS_MarkGCThing(cx, re->source, "source", arg); return 0; } JSClass js_RegExpClass = { js_RegExp_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, JS_PropertyStub, regexp_getProperty, regexp_setProperty, JS_EnumerateStub, JS_ResolveStub, @@ -4031,10 +4007,8 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } } else { str = js_ValueToString(cx, argv[0]); - if (!str) { - ok = JS_FALSE; + if (!str) goto out; - } argv[0] = STRING_TO_JSVAL(str); } @@ -4156,14 +4130,12 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); if (!re) return NULL; - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); + 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)) { + if (!obj || !JS_SetPrivate(cx, obj, re) || !js_SetLastIndex(cx, obj, 0)) { js_DestroyRegExp(cx, re); obj = NULL; } - if (obj && !js_SetLastIndex(cx, obj, 0)) - obj = NULL; JS_POP_TEMP_ROOT(cx, &tvr); return obj; } @@ -4180,7 +4152,7 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) return NULL; re = JS_GetPrivate(cx, obj); if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } HOLD_REGEXP(cx, re); @@ -4204,3 +4176,5 @@ js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex) return js_NewNumberValue(cx, lastIndex, &v) && JS_SetReservedSlot(cx, obj, 0, v); } + +#endif /* JS_HAS_REGEXPS */ diff --git a/lib/javascript/jsscan.c b/lib/javascript/jsscan.c index a5be9655..c043799d 100644 --- a/lib/javascript/jsscan.c +++ b/lib/javascript/jsscan.c @@ -74,81 +74,133 @@ #include "jsxml.h" #endif -#define JS_KEYWORD(keyword, type, op, version) \ - const char js_##keyword##_str[] = #keyword; -#include "jskeyword.tbl" -#undef JS_KEYWORD +#define RESERVE_JAVA_KEYWORDS +#define RESERVE_ECMA_KEYWORDS -struct keyword { - const char *chars; /* C string with keyword text */ +#define MAX_KEYWORD_LENGTH 12 + +static struct keyword { + const char *name; JSTokenType tokentype; /* JSTokenType */ JSOp op; /* JSOp */ JSVersion version; /* JSVersion */ +} keywords[] = { + {"break", TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT}, + {"case", TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT}, + {"continue", TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT}, + {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}, + {"export", TOK_EXPORT, JSOP_NOP, JSVERSION_1_2}, + {js_false_str, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT}, + {"for", TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT}, + {js_function_str, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT}, + {"if", TOK_IF, JSOP_NOP, JSVERSION_DEFAULT}, + {js_in_str, TOK_IN, JSOP_IN, JSVERSION_DEFAULT}, + {js_new_str, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT}, + {js_null_str, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT}, + {"return", TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT}, + {"switch", TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT}, + {js_this_str, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT}, + {js_true_str, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT}, + {js_typeof_str, TOK_UNARYOP, JSOP_TYPEOF,JSVERSION_DEFAULT}, + {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}, + +#if JS_HAS_CONST + {js_const_str, TOK_VAR, JSOP_DEFCONST,JSVERSION_DEFAULT}, +#else + {js_const_str, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#if JS_HAS_EXCEPTIONS + {"try", TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT}, + {"catch", TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT}, + {"finally", TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT}, + {"throw", TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT}, +#else + {"try", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"catch", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"finally", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"throw", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#if JS_HAS_INSTANCEOF + {js_instanceof_str, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_1_4}, +#else + {js_instanceof_str, TOK_RESERVED, JSOP_NOP, JSVERSION_1_4}, +#endif + +#ifdef RESERVE_JAVA_KEYWORDS + {"abstract", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"boolean", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"byte", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"char", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"class", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"double", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"extends", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"final", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"float", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"goto", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"implements", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"import", TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT}, + {"int", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"interface", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"long", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"native", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"package", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"private", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"protected", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"public", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"short", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"static", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"super", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"synchronized", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"throws", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"transient", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, + {"volatile", TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT}, +#endif + +#ifdef RESERVE_ECMA_KEYWORDS + {"enum", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, +#endif + +#if JS_HAS_DEBUGGER_KEYWORD + {"debugger", TOK_DEBUGGER, JSOP_NOP, JSVERSION_1_3}, +#elif defined(RESERVE_ECMA_KEYWORDS) + {"debugger", TOK_RESERVED, JSOP_NOP, JSVERSION_1_3}, +#endif + {0, TOK_EOF, JSOP_NOP, JSVERSION_DEFAULT} }; -static const struct keyword keyword_defs[] = { -#define JS_KEYWORD(keyword, type, op, version) \ - {js_##keyword##_str, type, op, version}, -#include "jskeyword.tbl" -#undef JS_KEYWORD -}; - -#define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0]) - -static const struct keyword * -FindKeyword(const jschar *s, size_t length) +JSBool +js_InitScanner(JSContext *cx) { - register size_t i; - const struct keyword *kw; - const char *chars; + struct keyword *kw; + size_t length; + JSAtom *atom; - JS_ASSERT(length != 0); - -#define JSKW_LENGTH() length -#define JSKW_AT(column) s[column] -#define JSKW_GOT_MATCH(index) i = (index); goto got_match; -#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; -#define JSKW_NO_MATCH() goto no_match; -// #include "jsautokw.h" -#undef JSKW_NO_MATCH -#undef JSKW_TEST_GUESS -#undef JSKW_GOT_MATCH -#undef JSKW_AT -#undef JSKW_LENGTH - - got_match: - return &keyword_defs[i]; - - test_guess: - kw = &keyword_defs[i]; - chars = kw->chars; - do { - if (*s++ != (unsigned char)(*chars++)) - goto no_match; - } while (--length != 0); - return kw; - - no_match: - return NULL; -} - -JSTokenType -js_CheckKeyword(const jschar *str, size_t length) -{ - const struct keyword *kw; - - JS_ASSERT(length != 0); - kw = FindKeyword(str, length); - return kw ? kw->tokentype : TOK_EOF; + for (kw = keywords; kw->name; kw++) { + 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); + } + return JS_TRUE; } JS_FRIEND_API(void) js_MapKeywords(void (*mapfun)(const char *)) { - size_t i; + struct keyword *kw; - for (i = 0; i != KEYWORD_COUNT; ++i) - mapfun(keyword_defs[i].chars); + for (kw = keywords; kw->name; kw++) + mapfun(kw->name); } JSTokenStream * @@ -190,13 +242,9 @@ GrowTokenBuf(JSStringBuffer *sb, size_t newlength) JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); } else { length = PTRDIFF(sb->limit, base, jschar); - if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) { - base = NULL; - } else { - tbsize = (length + 1) * sizeof(jschar); - length += length + 1; - JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); - } + tbsize = (length + 1) * sizeof(jschar); + length += length + 1; + JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); } if (!base) { JS_ReportOutOfMemory(cx); @@ -531,11 +579,12 @@ ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, uintN errorNumber, JSErrorReport *report, JSBool charArgs, va_list ap) { - JSTempValueRooter linetvr; JSString *linestr = NULL; JSTokenStream *ts = NULL; JSCodeGenerator *cg = NULL; +#if JS_HAS_XML_SUPPORT JSParseNode *pn = NULL; +#endif JSErrorReporter onError; JSTokenPos *tp; JSStackFrame *fp; @@ -554,7 +603,7 @@ ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, return JS_FALSE; } - JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr); + js_AddRoot(cx, &linestr, "error line buffer"); switch (flags & JSREPORT_HANDLE) { case JSREPORT_TS: @@ -563,139 +612,146 @@ ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, 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); - /* - * We are typically called with non-null ts and null cg from jsparse.c. - * We can be called with null ts from the regexp compilation functions. - * The code generator (jsemit.c) may pass null ts and non-null cg. - */ - do { - if (ts) { - report->filename = ts->filename; - if (pn) { - report->lineno = pn->pn_pos.begin.lineno; - if (report->lineno != ts->lineno) - break; - } - report->lineno = ts->lineno; - linestr = js_NewStringCopyN(cx, ts->linebuf.base, - PTRDIFF(ts->linebuf.limit, - ts->linebuf.base, - jschar), - 0); - linetvr.u.string = linestr; - report->linebuf = linestr - ? JS_GetStringBytes(linestr) - : NULL; - tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos; - if (pn) - tp = &pn->pn_pos; - - /* - * 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; - } - + onError = cx->errorReporter; + if (onError) { /* - * 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. + * We are typically called with non-null ts and null cg from jsparse.c. + * We can be called with null ts from the regexp compilation functions. + * The code generator (jsemit.c) may pass null ts and non-null cg. */ - 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); + 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; } - } - } while (0); - /* - * If there's a runtime exception type associated with this error - * number, set that as the pending exception. For errors occuring at - * compile time, this is very likely to be a JSEXN_SYNTAXERR. - * - * If an exception is thrown but not caught, the JSREPORT_EXCEPTION - * flag will be set in report.flags. Proper behavior for an error - * reporter is to ignore a report with this flag for all but top-level - * compilation errors. The exception will remain pending, and so long - * as the non-top-level "load", "eval", or "compile" native function - * returns false, the top-level reporter will eventually receive the - * uncaught exception report. - * - * XXX it'd probably be best if there was only one call to this - * function, but there seem to be two error reporter call points. - */ - onError = cx->errorReporter; + if (cg) { + report->filename = cg->filename; + report->lineno = CG_CURRENT_LINE(cg); + break; + } - /* - * 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)) - onError = NULL; - } + /* + * 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); - /* - * 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 && !JSREPORT_IS_WARNING(flags)) - onError = NULL; - - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; +#if JS_HAS_ERROR_EXCEPTIONS + /* + * If there's a runtime exception type associated with this error + * number, set that as the pending exception. For errors occuring at + * compile time, this is very likely to be a JSEXN_SYNTAXERR. + * + * If an exception is thrown but not caught, the JSREPORT_EXCEPTION + * flag will be set in report.flags. Proper behavior for an error + * reporter is to ignore a report with this flag for all but top-level + * compilation errors. The exception will remain pending, and so long + * as the non-top-level "load", "eval", or "compile" native function + * returns false, the top-level reporter will eventually receive the + * uncaught exception report. + * + * XXX it'd probably be best if there was only one call to this + * function, but there seem to be two error reporter call points. + */ /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular error reporter. + * 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 (hook && !hook(cx, message, report, - cx->runtime->debugErrorHookData)) { - onError = NULL; + if (!ts || !(ts->flags & TSF_ERROR)) { + if (js_ErrorToException(cx, message, report)) + onError = NULL; } + + /* + * 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 && !JSREPORT_IS_WARNING(flags)) + onError = NULL; +#endif + if (onError) { + JSDebugErrorHook hook = cx->runtime->debugErrorHook; + + /* + * 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); } - if (onError) - (*onError)(cx, message, report); if (message) JS_free(cx, message); if (report->ucmessage) JS_free(cx, (void *)report->ucmessage); - JS_POP_TEMP_ROOT(cx, &linetvr); + js_RemoveRoot(cx->runtime, &linestr); if (ts && !JSREPORT_IS_WARNING(flags)) { /* Set the error flag to suppress spurious reports. */ @@ -721,7 +777,7 @@ js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, &report, JS_TRUE, ap); va_end(ap); - /* + /* * We have to do this here because js_ReportCompileErrorNumberUC doesn't * need to do this. */ @@ -745,7 +801,7 @@ js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, 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); @@ -1021,8 +1077,10 @@ js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; - if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)) - return TOK_EOL; + JS_ASSERT(ts->lookahead == 0 || + 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; @@ -1077,7 +1135,6 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) JSToken *tp; JSAtom *atom; JSBool hadUnicodeEscape; - const struct keyword *kw; #define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base) #define TOKENBUF_LENGTH() PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) @@ -1096,6 +1153,10 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) #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) + return TOK_ERROR; + /* Check for a pushed-back token resulting from mismatching lookahead. */ while (ts->lookahead != 0) { JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); @@ -1106,10 +1167,6 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) return tt; } - /* If there was a fatal error, keep returning TOK_ERROR. */ - if (ts->flags & TSF_ERROR) - return TOK_ERROR; - #if JS_HAS_XML_SUPPORT if (ts->flags & TSF_XMLTEXTMODE) { tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */ @@ -1304,33 +1361,35 @@ retry: } UngetChar(ts, c); - /* - * Check for keywords unless we saw Unicode escape or parser asks - * to ignore keywords. - */ - if (!hadUnicodeEscape && - !(ts->flags & TSF_KEYWORD_IS_NAME) && - TOKENBUF_OK() && - (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) { + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + if (!hadUnicodeEscape && ATOM_KEYWORD(atom)) { + 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, - kw->chars)) { + JSMSG_RESERVED_ID, buf)) { goto error; } - } else if (kw->version <= JSVERSION_NUMBER(cx)) { + } else if (JS_VERSION_IS_ECMA(cx) || + kw->version <= (cx->version & JSVERSION_MASK)) { tt = kw->tokentype; tp->t_op = (JSOp) kw->op; goto out; } } - - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; tp->t_op = JSOP_NAME; tp->t_atom = atom; tt = TOK_NAME; @@ -1581,7 +1640,11 @@ retry: case '=': if (MatchChar(ts, c)) { +#if JS_HAS_TRIPLE_EQOPS tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; +#else + tp->t_op = cx->jsop_eq; +#endif tt = TOK_EQOP; } else { tp->t_op = JSOP_NOP; @@ -1591,7 +1654,11 @@ retry: case '!': if (MatchChar(ts, '=')) { +#if JS_HAS_TRIPLE_EQOPS tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; +#else + tp->t_op = cx->jsop_ne; +#endif tt = TOK_EQOP; } else { tp->t_op = JSOP_NOT; @@ -1704,21 +1771,19 @@ retry: } ADD_TO_TOKENBUF(c); } - if (targetLength == 0) - goto bad_xml_markup; - if (!TOKENBUF_OK()) - goto error; if (contentIndex < 0) { atom = cx->runtime->atomState.emptyAtom; } else { + if (!TOKENBUF_OK()) + goto error; atom = js_AtomizeChars(cx, &TOKENBUF_CHAR(contentIndex), TOKENBUF_LENGTH() - contentIndex, 0); if (!atom) goto error; + TRIM_TOKENBUF(targetLength); } - TRIM_TOKENBUF(targetLength); tp->t_atom2 = atom; tt = TOK_XMLPI; @@ -1878,6 +1943,7 @@ skipline: goto retry; } +#if JS_HAS_REGEXPS if (ts->flags & TSF_OPERAND) { JSObject *obj; uintN flags; @@ -1953,6 +2019,7 @@ skipline: tt = TOK_OBJECT; break; } +#endif /* JS_HAS_REGEXPS */ tp->t_op = JSOP_DIV; tt = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP; @@ -2012,7 +2079,7 @@ skipline: if (!JS7_ISDEC(c)) break; n = 10 * n + JS7_UNDEC(c); - if (n >= UINT16_LIMIT) { + if (n >= ATOM_INDEX_LIMIT) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SHARPVAR_TOO_BIG); @@ -2087,6 +2154,8 @@ void js_UngetToken(JSTokenStream *ts) { JS_ASSERT(ts->lookahead < NTOKENS_MASK); + if (ts->flags & TSF_ERROR) + return; ts->lookahead++; ts->cursor = (ts->cursor - 1) & NTOKENS_MASK; } diff --git a/lib/javascript/jsscan.h b/lib/javascript/jsscan.h index 08cb0956..ee39bbc0 100644 --- a/lib/javascript/jsscan.h +++ b/lib/javascript/jsscan.h @@ -44,18 +44,12 @@ */ #include #include -#include "jsconfig.h" #include "jsopcode.h" #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C -#define JS_KEYWORD(keyword, type, op, version) \ - extern const char js_##keyword##_str[]; -#include "jskeyword.tbl" -#undef JS_KEYWORD - typedef enum JSTokenType { TOK_ERROR = -1, /* well-known as the only code < EOF */ TOK_EOF = 0, /* end of file */ @@ -131,13 +125,6 @@ typedef enum JSTokenType { TOK_FILTER = 76, /* XML filtering predicate op (.()) */ TOK_XMLELEM = 77, /* XML element node type (no token) */ TOK_XMLLIST = 78, /* XML list node type (no token) */ - TOK_YIELD = 79, /* yield from generator function */ - TOK_ARRAYCOMP = 80, /* array comprehension initialiser */ - TOK_ARRAYPUSH = 81, /* array push within comprehension */ - TOK_LEXICALSCOPE = 82, /* block scope AST node label */ - TOK_LET = 83, /* let keyword */ - TOK_BODY = 84, /* synthetic body of function with - destructuring formal parameters */ TOK_RESERVED, /* reserved keywords */ TOK_LIMIT /* domain size */ } JSTokenType; @@ -148,12 +135,6 @@ typedef enum JSTokenType { #define TOKEN_TYPE_IS_XML(tt) \ (tt == TOK_AT || tt == TOK_DBLCOLON || tt == TOK_ANYNAME) -#if JS_HAS_BLOCK_SCOPE -# define TOKEN_TYPE_IS_DECL(tt) ((tt) == TOK_VAR || (tt) == TOK_LET) -#else -# define TOKEN_TYPE_IS_DECL(tt) ((tt) == TOK_VAR) -#endif - struct JSStringBuffer { jschar *base; jschar *limit; /* length limit for quick bounds check */ @@ -291,9 +272,6 @@ struct JSTokenStream { */ #define TSF_IN_HTML_COMMENT 0x2000 -/* Ignore keywords and return TOK_NAME instead to the parser. */ -#define TSF_KEYWORD_IS_NAME 0x4000 - /* Unicode separators that are treated as line terminators, in addition to \n, \r */ #define LINE_SEPARATOR 0x2028 #define PARA_SEPARATOR 0x2029 @@ -323,14 +301,10 @@ extern JS_FRIEND_API(int) js_fgets(char *buf, int size, FILE *file); /* - * If the given char array forms JavaScript keyword, return corresponding - * token. Otherwise return TOK_EOF. + * Initialize the scanner, installing JS keywords into cx's global scope. */ -extern JSTokenType -js_CheckKeyword(const jschar *chars, size_t length); - -#define js_IsKeyword(chars, length) \ - (js_CheckKeyword(chars, length) != TOK_EOF) +extern JSBool +js_InitScanner(JSContext *cx); /* * Friend-exported API entry point to call a mapping function on each reserved diff --git a/lib/javascript/jsscope.c b/lib/javascript/jsscope.c index 49b55a6f..4f951238 100644 --- a/lib/javascript/jsscope.c +++ b/lib/javascript/jsscope.c @@ -1,5 +1,4 @@ /* -*- 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 @@ -126,7 +125,9 @@ CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) JS_ReportOutOfMemory(cx); return JS_FALSE; } - js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + + /* 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) { @@ -149,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 @@ -623,13 +625,10 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, */ JS_RUNTIME_METER(rt, duplicatePropTreeNodes); } - if (sweptChunk) { - chunk = sweptChunk; - } else { - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; - } + + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; parent->kids = CHUNK_TO_KIDS(chunk); chunk->kids[0] = sprop; childp = &chunk->kids[1]; @@ -644,7 +643,7 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, } /* NB: Called with the runtime lock held. */ -static PropTreeKidsChunk * +static void RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) { JSPropertyTreeEntry *entry; @@ -695,9 +694,9 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) *chunkp = NULL; if (!list) parent->kids = NULL; - return lastChunk; + DestroyPropTreeKidsChunk(rt, lastChunk); } - return NULL; + return; } } @@ -709,7 +708,6 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) parent->kids = NULL; } } - return NULL; } /* @@ -874,7 +872,6 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; uint32 size, splen, i; int change; - JSTempValueRooter tvr; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); CHECK_ANCESTOR_LINE(scope, JS_TRUE); @@ -1144,9 +1141,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, */ if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && js_FindWatchPoint(cx->runtime, scope, id)) { - JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); setter = js_WrapWatchedSetter(cx, id, attrs, setter); - JS_POP_TEMP_ROOT(cx, &tvr); if (!setter) goto fail_overwrite; } @@ -1333,10 +1328,8 @@ js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) } /* First, if sprop is unshared and not cleared, free its slot number. */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + if (SPROP_HAS_VALID_SLOT(sprop, scope)) js_FreeSlot(cx, scope->object, sprop->slot); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); - } /* Next, remove id by setting its entry to a removed or free sentinel. */ if (SPROP_HAD_COLLISION(stored)) { @@ -1387,67 +1380,6 @@ js_ClearScope(JSContext *cx, JSScope *scope) free(scope->table); SCOPE_CLR_MIDDLE_DELETE(scope); InitMinimalScope(scope); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); -} - -void -js_MarkId(JSContext *cx, jsid id) -{ - if (JSID_IS_ATOM(id)) - GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); - else if (JSID_IS_OBJECT(id)) - GC_MARK(cx, JSID_TO_OBJECT(id), "id"); - else - JS_ASSERT(JSID_IS_INT(id)); -} - -#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS -# include "jsprf.h" -#endif - -void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) -{ - sprop->flags |= SPROP_MARK; - MARK_ID(cx, sprop->id); - -#if JS_HAS_GETTER_SETTER - if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { -#ifdef GC_MARK_DEBUG - char buf[64]; - char buf2[11]; - const char *id; - - if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - - id = (atom && ATOM_IS_STRING(atom)) - ? JS_GetStringBytes(ATOM_TO_STRING(atom)) - : "unknown"; - } else if (JSID_IS_INT(sprop->id)) { - JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); - id = buf2; - } else { - id = ""; - } -#endif - - if (sprop->attrs & JSPROP_GETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_getter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); - } - if (sprop->attrs & JSPROP_SETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_setter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); - } - } -#endif /* JS_HAS_GETTER_SETTER */ } #ifdef DUMP_SCOPE_STATS @@ -1511,6 +1443,8 @@ js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, return JS_DHASH_NEXT; } +#include "jsprf.h" + static void DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) { @@ -1559,7 +1493,7 @@ js_SweepScopeProperties(JSRuntime *rt) JSArena **ap, *a; JSScopeProperty *limit, *sprop, *parent, *kids, *kid; uintN liveCount; - PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; + PropTreeKidsChunk *chunk, *nextChunk; uintN i; #ifdef DUMP_SCOPE_STATS @@ -1622,7 +1556,7 @@ js_SweepScopeProperties(JSRuntime *rt) } /* Ok, sprop is garbage to collect: unlink it from its parent. */ - freeChunk = RemovePropertyTreeChild(rt, sprop); + RemovePropertyTreeChild(rt, sprop); /* * Take care to reparent all sprop's kids to their grandparent. @@ -1638,16 +1572,11 @@ js_SweepScopeProperties(JSRuntime *rt) * chunks to be reused by the grandparent, which removes the * need for InsertPropertyTreeChild to malloc a new KidsChunk. * - * If sprop does not have chunky kids, then we rely on the - * RemovePropertyTreeChild call above (which removed sprop from - * its parent) either leaving one free entry, or else returning - * the now-unused chunk to us so we can reuse it. - * * We also require the grandparent to have either no kids or else * chunky kids. A single non-chunky kid would force a new chunk to * be malloced in some cases (if sprop had a single non-chunky * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that - * RemovePropertyTreeChild never converts a single-entry chunky + * RemovePropertyTreeChild never converts a single entry chunky * kid back to a non-chunky kid, so we are assured of correct * behaviour. */ @@ -1685,16 +1614,18 @@ js_SweepScopeProperties(JSRuntime *rt) } } if (!chunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ + /* The chunk wasn't reused so we can free it */ DestroyPropTreeKidsChunk(rt, chunk); } } while ((chunk = nextChunk) != NULL); } else { kid = kids; - if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { + if (!InsertPropertyTreeChild(rt, parent, kid, NULL)) { /* - * This can happen only if we failed to add an entry - * to the root property hash table. + * 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; @@ -1702,11 +1633,6 @@ js_SweepScopeProperties(JSRuntime *rt) } } - if (freeChunk && !freeChunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ - DestroyPropTreeKidsChunk(rt, freeChunk); - } - /* Clear id so we know (above) that sprop is on the freelist. */ sprop->id = JSVAL_NULL; FREENODE_INSERT(rt->propertyFreeList, sprop); diff --git a/lib/javascript/jsscope.h b/lib/javascript/jsscope.h index 0565d4d8..0f8a837c 100644 --- a/lib/javascript/jsscope.h +++ b/lib/javascript/jsscope.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -204,7 +203,7 @@ struct JSScope { JSObject *object; /* object that owns this scope */ uint8 flags; /* flags, see below */ int8 hashShift; /* multiplicative hash shift */ - uint16 spare; /* reserved */ + 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 */ @@ -305,26 +304,26 @@ struct JSScopeProperty { #define SPROP_INVALID_SLOT 0xffffffff -#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) -#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) +#define SPROP_HAS_VALID_SLOT(sprop, scope) \ + ((sprop)->slot < (scope)->map.freeslot) #define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) #define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) -/* - * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). - */ +#define SPROP_CALL_GETTER(cx,sprop,getter,obj,obj2,vp) \ + (!(getter) || \ + (getter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) +#define SPROP_CALL_SETTER(cx,sprop,setter,obj,obj2,vp) \ + (!(setter) || \ + (setter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + #define SPROP_GET(cx,sprop,obj,obj2,vp) \ (((sprop)->attrs & JSPROP_GETTER) \ ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ 0, 0, vp) \ - : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + : SPROP_CALL_GETTER(cx, sprop, (sprop)->getter, obj, obj2, vp)) -/* - * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && - * !(sprop->attrs & JSPROP_GETTER)). - */ #define SPROP_SET(cx,sprop,obj,obj2,vp) \ (((sprop)->attrs & JSPROP_SETTER) \ ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ @@ -333,7 +332,7 @@ struct JSScopeProperty { : ((sprop)->attrs & JSPROP_GETTER) \ ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ - : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + : SPROP_CALL_SETTER(cx, sprop, (sprop)->setter, obj, obj2, vp)) /* Macro for common expression to test for shared permanent attributes. */ #define SPROP_IS_SHARED_PERMANENT(sprop) \ @@ -381,19 +380,7 @@ js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); extern void js_ClearScope(JSContext *cx, JSScope *scope); -/* - * These macros used to inline short code sequences, but they grew over time. - * We retain them for internal backward compatibility, and in case one or both - * ever shrink to inline-able size. - */ -#define MARK_ID(cx,id) js_MarkId(cx, id) -#define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) - -extern void -js_MarkId(JSContext *cx, jsid id); - -extern void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); +#define MARK_SCOPE_PROPERTY(sprop) ((sprop)->flags |= SPROP_MARK) extern void js_SweepScopeProperties(JSRuntime *rt); diff --git a/lib/javascript/jsscript.c b/lib/javascript/jsscript.c index 73298a42..6fc92de9 100644 --- a/lib/javascript/jsscript.c +++ b/lib/javascript/jsscript.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: + * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -67,50 +67,20 @@ static const char js_script_exec[] = "Script.prototype.exec"; static const char js_script_compile[] = "Script.prototype.compile"; -/* - * This routine requires that obj has been locked previously. - */ -static jsint -GetScriptExecDepth(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); - return JSVAL_TO_INT(v); -} - -static void -AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) -{ - jsint execDepth; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), - INT_TO_JSVAL(execDepth + delta)); - JS_UNLOCK_OBJ(cx, obj); -} - #if JS_HAS_TOSOURCE static JSBool script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - uint32 indent; JSScript *script; size_t i, j, k, n; char buf[16]; jschar *s, *t; + uint32 indent; JSString *str; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; - - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); /* Let n count the source string length, j the "front porch" length. */ @@ -121,6 +91,9 @@ script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, k = 0; s = NULL; /* quell GCC overwarning */ } else { + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; str = JS_DecompileScript(cx, script, "Script.prototype.toSource", (uintN)indent); if (!str) @@ -160,14 +133,10 @@ static JSBool script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - uint32 indent; JSScript *script; + uint32 indent; JSString *str; - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; script = (JSScript *) JS_GetPrivate(cx, obj); @@ -176,6 +145,9 @@ script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_TRUE; } + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; str = JS_DecompileScript(cx, script, "Script.prototype.toString", (uintN)indent); if (!str) @@ -188,15 +160,13 @@ static JSBool script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JSScript *oldscript, *script; + JSStackFrame *fp, *caller; JSString *str; JSObject *scopeobj; - jsval v; - JSScript *script, *oldscript; - JSStackFrame *fp, *caller; const char *file; uintN line; JSPrincipals *principals; - jsint execDepth; /* Make sure obj is a Script object. */ if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) @@ -206,40 +176,42 @@ 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; + caller = JS_GetScriptedCaller(cx, fp); + JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); + scopeobj = NULL; if (argc >= 2) { if (!js_ValueToObject(cx, argv[1], &scopeobj)) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(scopeobj); } - - /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); - if (caller) { - if (!scopeobj) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; - fp->scopeChain = scopeobj; /* for the compiler's benefit */ - } + if (!scopeobj) + scopeobj = caller->scopeChain; + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } } else { file = NULL; line = 0; @@ -267,32 +239,15 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (!script) return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); + /* Swap script for obj's old script, if any. */ + if (!JS_SetPrivate(cx, obj, script)) { + js_DestroyScript(cx, script); return JS_FALSE; } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - if (oldscript) js_DestroyScript(cx, oldscript); script->object = obj; - js_CallNewScriptHook(cx, script, NULL); - out: /* Return the object. */ *rval = OBJECT_TO_JSVAL(obj); @@ -302,13 +257,16 @@ out: static JSBool script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JSScript *script; JSObject *scopeobj, *parent; JSStackFrame *fp, *caller; - JSScript *script; - JSBool ok; + JSPrincipals *principals; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; scopeobj = NULL; if (argc) { @@ -333,7 +291,7 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) caller = JS_GetScriptedCaller(cx, fp); if (caller && !caller->varobj) { /* Called from a lightweight function. */ - JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); + JS_ASSERT(caller->fun && !(caller->fun->flags & JSFUN_HEAVYWEIGHT)); /* Scope chain links from Call object to callee's parent. */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); @@ -348,9 +306,7 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * Load caller->scopeChain after the conditional js_GetCallObject * call above, which resets scopeChain as well as varobj. */ - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; + scopeobj = caller->scopeChain; } else { /* * Called from native code, so we don't know what scope object to @@ -368,97 +324,98 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!scopeobj) return JS_FALSE; - /* Keep track of nesting depth for the script. */ - AdjustScriptExecDepth(cx, obj, 1); - - /* Must get to out label after this */ - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) { - ok = JS_FALSE; - goto out; - } - /* Belt-and-braces: check that this script object has access to scopeobj. */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, - CLASS_ATOM(cx, Script)); - if (!ok) - goto out; + principals = script->principals; + if (!js_CheckPrincipalsAccess(cx, scopeobj, principals, js_script_exec)) + return JS_FALSE; - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); - -out: - AdjustScriptExecDepth(cx, obj, -1); - return ok; + return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); } #if JS_HAS_XDR static JSBool -XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +XDRAtomListElement(JSXDRState *xdr, JSAtomListElement *ale) { - JSContext *cx; - uint32 natoms, i, index; - JSAtom **atoms; - - cx = xdr->cx; + jsval value; + jsatomid index; if (xdr->mode == JSXDR_ENCODE) - natoms = (uint32)map->length; + value = ATOM_KEY(ALE_ATOM(ale)); - if (!JS_XDRUint32(xdr, &natoms)) + index = ALE_INDEX(ale); + if (!JS_XDRUint32(xdr, &index)) + return JS_FALSE; + ALE_SET_INDEX(ale, index); + + if (!JS_XDRValue(xdr, &value)) return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { - atoms = map->vector; - } else { - if (natoms == 0) { - atoms = NULL; - } else { - atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); - if (!atoms) - return JS_FALSE; -#ifdef DEBUG - memset(atoms, 0, (size_t)natoms * sizeof *atoms); -#endif - } - - map->vector = atoms; - map->length = natoms; - } - - for (i = 0; i != natoms; ++i) { - if (xdr->mode == JSXDR_ENCODE) - index = i; - if (!JS_XDRUint32(xdr, &index)) - goto bad; - - /* - * Assert that, when decoding, the read index is valid and points to - * an unoccupied element of atoms array. - */ - JS_ASSERT(index < natoms); - JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); - if (!js_XDRAtom(xdr, &atoms[index])) - goto bad; - } - - return JS_TRUE; - - bad: if (xdr->mode == JSXDR_DECODE) { - JS_free(cx, atoms); - map->vector = NULL; - map->length = 0; + if (!ALE_SET_ATOM(ale, js_AtomizeValue(xdr->cx, value, 0))) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +{ + uint32 length; + uintN i; + JSBool ok; + + if (xdr->mode == JSXDR_ENCODE) + length = map->length; + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + JSContext *cx; + void *mark; + JSAtomList al; + JSAtomListElement *ale; + + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + ATOM_LIST_INIT(&al); + for (i = 0; i < length; i++) { + JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); + if (!ale || + !XDRAtomListElement(xdr, ale)) { + if (!ale) + JS_ReportOutOfMemory(cx); + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + ALE_SET_NEXT(ale, al.list); + al.count++; + al.list = ale; + } + ok = js_InitAtomMap(cx, map, &al); + JS_ARENA_RELEASE(&cx->tempPool, mark); + return ok; } - return JS_FALSE; + if (xdr->mode == JSXDR_ENCODE) { + JSAtomListElement ale; + + for (i = 0; i < map->length; i++) { + ALE_SET_ATOM(&ale, map->vector[i]); + ALE_SET_INDEX(&ale, i); + if (!XDRAtomListElement(xdr, &ale)) + return JS_FALSE; + } + } + return JS_TRUE; } JSBool js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) { JSContext *cx; - JSScript *script, *newscript, *oldscript; + JSScript *script, *newscript; uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; uint32 prologLength, version; JSBool filenameWasSaved; @@ -477,21 +434,15 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) * nsrcnotes and ntrynotes fields to come before everything except magic, * length, prologLength, and version, so that srcnote and trynote storage * can be allocated as part of the JSScript (along with bytecode storage). - * - * So far, the magic number has not changed for every jsopcode.tbl change. - * We stipulate forward compatibility by requiring old bytecodes never to - * change or go away (modulo a few exceptions before the XDR interfaces - * evolved, and a few exceptions during active trunk development). With - * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new - * magic number (_5) so that we know to append JSOP_STOP to old scripts - * when deserializing. */ if (xdr->mode == JSXDR_ENCODE) magic = JSXDR_MAGIC_SCRIPT_CURRENT; if (!JS_XDRUint32(xdr, &magic)) return JS_FALSE; - JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); - if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { + if (magic != JSXDR_MAGIC_SCRIPT_4 && + magic != JSXDR_MAGIC_SCRIPT_3 && + magic != JSXDR_MAGIC_SCRIPT_2 && + magic != JSXDR_MAGIC_SCRIPT_1) { if (!hasMagic) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); @@ -544,11 +495,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) } if (xdr->mode == JSXDR_DECODE) { - size_t alloclength = length; - if (magic < JSXDR_MAGIC_SCRIPT_5) - ++alloclength; /* add a byte for JSOP_STOP */ - - script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); + script = js_NewScript(cx, length, nsrcnotes, ntrynotes); if (!script) return JS_FALSE; if (magic >= JSXDR_MAGIC_SCRIPT_2) { @@ -569,32 +516,18 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) * in the (DECODE and magic < _4) case must point at a temporary vector * allocated just below. */ - oldscript = xdr->script; - xdr->script = script; if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || !XDRAtomMap(xdr, &script->atomMap)) { goto error; } - if (magic < JSXDR_MAGIC_SCRIPT_5) { + if (magic < JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + goto error; if (xdr->mode == JSXDR_DECODE) { - /* - * Append JSOP_STOP to old scripts, to relieve the interpreter - * from having to bounds-check pc. Also take care to increment - * length, as it is used below and must count all bytecode. - */ - script->code[length++] = JSOP_STOP; - } - - if (magic < JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) + notes = (jssrcnote *) JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); + if (!notes) goto error; - if (xdr->mode == JSXDR_DECODE) { - notes = (jssrcnote *) - JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); - if (!notes) - goto error; - } } } @@ -698,8 +631,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) tn->length = (ptrdiff_t) catchLength; tn->catchStart = (ptrdiff_t) catchStart; } - - xdr->script = oldscript; return JS_TRUE; error: @@ -794,7 +725,6 @@ script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSString *str; void *buf; uint32 len; - jsval v; JSScript *script, *oldscript; JSBool ok, hasMagic; @@ -844,26 +774,13 @@ script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, goto out; } - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); + /* Swap script for obj's old script, if any. */ + oldscript = (JSScript *) JS_GetPrivate(cx, obj); + ok = JS_SetPrivate(cx, obj, script); + if (!ok) { + JS_free(cx, script); goto out; } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - if (oldscript) js_DestroyScript(cx, oldscript); @@ -932,20 +849,13 @@ script_mark(JSContext *cx, JSObject *obj, void *arg) script = (JSScript *) JS_GetPrivate(cx, obj); if (script) - js_MarkScript(cx, script); + js_MarkScript(cx, script, arg); return 0; } -#if !JS_HAS_SCRIPT_OBJECT -const char js_Script_str[] = "Script"; - -#define JSProto_Script JSProto_Object -#endif - JS_FRIEND_DATA(JSClass) js_ScriptClass = { js_Script_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | - JSCLASS_HAS_RESERVED_SLOTS(1), + JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, NULL, NULL, script_call, NULL,/*XXXbe xdr*/ @@ -969,10 +879,6 @@ Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ *rval = OBJECT_TO_JSVAL(obj); } - - if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) - return JS_FALSE; - return script_compile(cx, obj, argc, argv, rval); } @@ -1106,9 +1012,6 @@ js_FreeRuntimeScriptState(JSRuntime *rt) { ScriptFilenamePrefix *sfp; - if (!rt->scriptFilenameTable) - return; - while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; JS_REMOVE_LINK(&sfp->links); @@ -1118,9 +1021,6 @@ js_FreeRuntimeScriptState(JSRuntime *rt) } #ifdef DEBUG_brendan -#define DEBUG_SFTBL -#endif -#ifdef DEBUG_SFTBL size_t sftbl_savings = 0; #endif @@ -1139,7 +1039,7 @@ SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) hash = JS_HashString(filename); hep = JS_HashTableRawLookup(table, hash, filename); sfe = (ScriptFilenameEntry *) *hep; -#ifdef DEBUG_SFTBL +#ifdef DEBUG_brendan if (sfe) sftbl_savings += strlen(sfe->filename); #endif @@ -1293,15 +1193,12 @@ js_script_filename_marker(JSHashEntry *he, intN i, void *arg) } void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) +js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags) { JSCList *head, *link; ScriptFilenamePrefix *sfp; - if (!rt->scriptFilenameTable) - return; - - if (keepAtoms) { + if (gcflags & GC_KEEP_ATOMS) { JS_HashTableEnumerateEntries(rt->scriptFilenameTable, js_script_filename_marker, rt); @@ -1328,17 +1225,12 @@ js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) void js_SweepScriptFilenames(JSRuntime *rt) { - if (!rt->scriptFilenameTable) - return; - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, js_script_filename_sweeper, rt); #ifdef DEBUG_notme -#ifdef DEBUG_SFTBL printf("script filename table savings so far: %u\n", sftbl_savings); #endif -#endif } JSScript * @@ -1410,17 +1302,6 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) if (script->trynotes) js_FinishTakingTryNotes(cx, cg, script->trynotes); - /* - * We initialize fun->u.script to be the script constructed above - * so that the debugger has a valid FUN_SCRIPT(fun). - */ - if (fun) { - JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); - fun->u.i.script = script; - if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) - fun->flags |= JSFUN_HEAVYWEIGHT; - } - /* Tell the debugger about this compiled script. */ js_CallNewScriptHook(cx, script, fun); return script; @@ -1467,13 +1348,11 @@ js_DestroyScript(JSContext *cx, JSScript *script) js_FreeAtomMap(cx, &script->atomMap); if (script->principals) JSPRINCIPALS_DROP(cx, script->principals); - if (JS_GSN_CACHE(cx).script == script) - JS_CLEAR_GSN_CACHE(cx); JS_free(cx, script); } void -js_MarkScript(JSContext *cx, JSScript *script) +js_MarkScript(JSContext *cx, JSScript *script, void *arg) { JSAtomMap *map; uintN i, length; @@ -1483,86 +1362,28 @@ js_MarkScript(JSContext *cx, JSScript *script) length = map->length; vector = map->vector; for (i = 0; i < length; i++) - GC_MARK_ATOM(cx, vector[i]); + GC_MARK_ATOM(cx, vector[i], arg); if (script->filename) js_MarkScriptFilename(script->filename); } -typedef struct GSNCacheEntry { - JSDHashEntryHdr hdr; - jsbytecode *pc; - jssrcnote *sn; -} GSNCacheEntry; - -#define GSN_CACHE_THRESHOLD 100 - jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) +js_GetSrcNote(JSScript *script, jsbytecode *pc) { - ptrdiff_t target, offset; - GSNCacheEntry *entry; - jssrcnote *sn, *result; - uintN nsrcnotes; - + jssrcnote *sn; + ptrdiff_t offset, target; target = PTRDIFF(pc, script->code, jsbytecode); if ((uint32)target >= script->length) return NULL; - - if (JS_GSN_CACHE(cx).script == script) { - JS_METER_GSN_CACHE(cx, hits); - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_LOOKUP); - return entry->sn; - } - - JS_METER_GSN_CACHE(cx, misses); offset = 0; - for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { - if (SN_IS_TERMINATOR(sn)) { - result = NULL; - break; - } + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { offset += SN_DELTA(sn); - if (offset == target && SN_IS_GETTABLE(sn)) { - result = sn; - break; - } + if (offset == target && SN_IS_GETTABLE(sn)) + return sn; } - - if (JS_GSN_CACHE(cx).script != script && - script->length >= GSN_CACHE_THRESHOLD) { - JS_CLEAR_GSN_CACHE(cx); - nsrcnotes = 0; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - if (SN_IS_GETTABLE(sn)) - ++nsrcnotes; - } - if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), - NULL, sizeof(GSNCacheEntry), nsrcnotes)) { - JS_GSN_CACHE(cx).table.ops = NULL; - } else { - pc = script->code; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - pc += SN_DELTA(sn); - if (SN_IS_GETTABLE(sn)) { - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_ADD); - entry->pc = pc; - entry->sn = sn; - } - } - JS_GSN_CACHE(cx).script = script; - JS_METER_GSN_CACHE(cx, fills); - } - } - - return result; + return NULL; } uintN @@ -1575,23 +1396,15 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) jssrcnote *sn; JSSrcNoteType type; - /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ - if (!pc) - return 0; - /* * Special case: function definition needs no line number note because * the function's script contains its starting line number. */ - if (*pc == JSOP_DEFFUN || - (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { - atom = js_GetAtom(cx, &script->atomMap, - (*pc == JSOP_DEFFUN) - ? GET_ATOM_INDEX(pc) - : GET_LITERAL_INDEX(pc)); + if (*pc == JSOP_DEFFUN) { + atom = GET_ATOM(cx, script, pc); fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); - JS_ASSERT(FUN_INTERPRETED(fun)); - return fun->u.i.script->lineno; + JS_ASSERT(fun->interpreted); + return fun->u.script->lineno; } /* @@ -1675,43 +1488,3 @@ js_GetScriptLineExtent(JSScript *script) } return 1 + lineno - script->lineno; } - -#if JS_HAS_GENERATORS - -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc) -{ - JSTryNote *tn; - ptrdiff_t off; - JSOp op2; - - tn = script->trynotes; - if (!tn) - return NULL; - - off = pc - script->main; - if (off < 0) - return NULL; - - JS_ASSERT(tn->catchStart != 0); - do { - if ((jsuword)(off - tn->start) < (jsuword)tn->length) { - /* - * We have a handler: is it the finally one, or a catch handler? - * - * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK - * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) - */ - pc = script->main + tn->catchStart; - JS_ASSERT(*pc == JSOP_SETSP); - op2 = pc[JSOP_SETSP_LENGTH]; - if (op2 != JSOP_ENTERBLOCK) { - JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); - return pc; - } - } - } while ((++tn)->catchStart != 0); - return NULL; -} - -#endif diff --git a/lib/javascript/jsscript.h b/lib/javascript/jsscript.h index 18ad373d..77f59ed7 100644 --- a/lib/javascript/jsscript.h +++ b/lib/javascript/jsscript.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -98,14 +97,6 @@ struct JSScript { catchpc = catchpc_; \ JS_END_MACRO -/* - * Find the innermost finally block that handles the given pc. This is a - * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used - * to implement generator.close(). - */ -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc); - extern JS_FRIEND_DATA(JSClass) js_ScriptClass; extern JSObject * @@ -148,7 +139,7 @@ extern void js_MarkScriptFilename(const char *filename); extern void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); +js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags); extern void js_SweepScriptFilenames(JSRuntime *rt); @@ -186,17 +177,10 @@ extern void js_DestroyScript(JSContext *cx, JSScript *script); extern void -js_MarkScript(JSContext *cx, JSScript *script); - -/* - * To perturb as little code as possible, we introduce a js_GetSrcNote lookup - * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes - * a macro that uses cx from its calls' lexical environments. - */ -#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) +js_MarkScript(JSContext *cx, JSScript *script, void *arg); extern jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); +js_GetSrcNote(JSScript *script, jsbytecode *pc); /* XXX need cx to lock function objects declared by prolog bytecodes. */ extern uintN diff --git a/lib/javascript/jsstr.c b/lib/javascript/jsstr.c index e38f6525..5e0bafd3 100644 --- a/lib/javascript/jsstr.c +++ b/lib/javascript/jsstr.c @@ -70,6 +70,10 @@ #include "jsregexp.h" #include "jsstr.h" +#if JS_HAS_REPLACE_LAMBDA +#include "jsinterp.h" +#endif + #define JSSTRDEP_RECURSION_LIMIT 100 size_t @@ -515,29 +519,24 @@ static JSPropertySpec string_props[] = { static JSBool str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - jsval v; JSString *str; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; - slot = JSVAL_TO_INT(id); - if (slot == STRING_LENGTH) { - if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { - /* Follow ECMA-262 by fetching intrinsic length of our string. */ - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - } else { - /* Preserve compatibility: convert obj to a string primitive. */ - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - } + /* + * 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)); - } return JS_TRUE; } @@ -546,13 +545,17 @@ str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) static JSBool str_enumerate(JSContext *cx, JSObject *obj) { - jsval v; JSString *str, *str1; size_t i, length; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); + /* 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_TRUE; + cx->newborn[GCX_STRING] = (JSGCThing *) str; length = JSSTRING_LENGTH(str); for (i = 0; i < length; i++) { @@ -572,16 +575,16 @@ static JSBool str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { - jsval v; JSString *str, *str1; jsint slot; if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) return JS_TRUE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_TRUE; + cx->newborn[GCX_STRING] = (JSGCThing *) str; slot = JSVAL_TO_INT(id); if ((size_t)slot < JSSTRING_LENGTH(str)) { @@ -600,8 +603,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSClass js_StringClass = { js_String_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_String), + 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 @@ -639,15 +641,11 @@ str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[16]; jschar *s, *t; - if (JSVAL_IS_STRING((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_STRING(v)) - return js_obj_toSource(cx, obj, argc, argv, rval); - } + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); if (!str) return JS_FALSE; @@ -681,10 +679,6 @@ str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; - if (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); @@ -697,10 +691,6 @@ 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 (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); @@ -744,10 +734,14 @@ str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, else if (end > length) end = length; if (end < begin) { - /* ECMA emulates old JDK1.0 java.lang.String.substring. */ - jsdouble tmp = begin; - begin = end; - end = tmp; + if (!JS_VERSION_IS_1_2(cx)) { + /* XXX emulate old JDK1.0 java.lang.String.substring. */ + jsdouble tmp = begin; + begin = end; + end = tmp; + } else { + end = begin; + } } } @@ -1110,6 +1104,7 @@ str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, /* * Perl-inspired string functions. */ +#if JS_HAS_REGEXPS typedef struct GlobData { uintN flags; /* inout: mode and flag bits, see below */ uintN optarg; /* in: index of optional flags argument */ @@ -1331,12 +1326,24 @@ static JSSubString * interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, size_t *skip) { + JSVersion version; JSRegExpStatics *res; jschar dc, *cp; uintN num, tmp; + JSString *str; JS_ASSERT(*dp == '$'); + /* + * Allow a real backslash (literal "\\" before "$1") to escape "$1", e.g. + * Do this only for versions strictly less than ECMAv3. + */ + 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; @@ -1345,22 +1352,35 @@ interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, res = &cx->regExpStatics; dc = dp[1]; if (JS7_ISDEC(dc)) { - /* ECMA-262 Edition 3: 1-9 or 01-99 */ - num = JS7_UNDEC(dc); - if (num > res->parenCount) - return NULL; + if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) { + if (dc == '0') + return NULL; - cp = dp + 2; - if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { - tmp = 10 * num + JS7_UNDEC(dc); - if (tmp <= res->parenCount) { - cp++; + /* Check for overflow to avoid gobbling arbitrary decimal digits. */ + num = 0; + cp = dp; + while (++cp < ep && (dc = *cp, JS7_ISDEC(dc))) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp < num) + break; num = tmp; } - } - if (num == 0) - return NULL; + } else { /* ECMA 3, 1-9 or 01-99 */ + num = JS7_UNDEC(dc); + if (num > res->parenCount) + return NULL; + cp = dp + 2; + if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp <= res->parenCount) { + cp++; + num = tmp; + } + } + if (num == 0) + return NULL; + } /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; *skip = cp - dp; @@ -1378,6 +1398,19 @@ interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, case '+': return &res->lastParen; case '`': + 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, + * not from the start of the target string. But Perl4 does start + * $` at the beginning of the target string when it is used in a + * substitution, so we emulate that special case here. + */ + str = rdata->base.str; + res->leftContext.chars = JSSTRING_CHARS(str); + res->leftContext.length = res->lastMatch.chars + - JSSTRING_CHARS(str); + } return &res->leftContext; case '\'': return &res->rightContext; @@ -1392,6 +1425,7 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) size_t replen, skip; jschar *dp, *ep; JSSubString *sub; +#if JS_HAS_REPLACE_LAMBDA JSObject *lambda; lambda = rdata->lambda; @@ -1500,6 +1534,7 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) cx->regExpStatics = save; return ok; } +#endif /* JS_HAS_REPLACE_LAMBDA */ repstr = rdata->repstr; replen = JSSTRING_LENGTH(repstr); @@ -1592,14 +1627,18 @@ 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; +#if JS_HAS_REPLACE_LAMBDA if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { lambda = JSVAL_TO_OBJECT(argv[1]); repstr = NULL; - } else { + } else +#endif + { if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) return JS_FALSE; repstr = JSVAL_TO_STRING(argv[1]); @@ -1611,7 +1650,10 @@ str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * to match in a "flat" sense (without regular expression metachars having * special meanings) UNLESS the first arg is a RegExp object. */ - rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; + rdata.base.flags = MODE_REPLACE | KEEP_REGEXP; + version = cx->version & JSVERSION_MASK; + if (version == JSVERSION_DEFAULT || version > JSVERSION_1_4) + rdata.base.flags |= FORCE_FLAT; rdata.base.optarg = 2; rdata.lambda = lambda; @@ -1681,6 +1723,7 @@ out: js_DestroyRegExp(cx, rdata.base.regexp); return ok; } +#endif /* JS_HAS_REGEXPS */ /* * Subroutine used by str_split to find the next split point in str, starting @@ -1696,8 +1739,8 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, JSSubString *sep) { jsint i, j, k; - size_t length; jschar *chars; + size_t length; /* * Stop if past end of string. If at end of string, we will compare the @@ -1711,12 +1754,48 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * limit argument (see str_split). */ i = *ip; - length = JSSTRING_LENGTH(str); - if ((size_t)i > length) + if ((size_t)i > JSSTRING_LENGTH(str)) return -1; + /* + * Perl4 special case for str.split(' '), only if the user has selected + * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s. + * Strange but true, apparently modeled after awk. + * + * NB: we set sep->length to the length of the w/s run, so we must test + * sep->chars[1] == 0 to make sure sep is just one space. + */ chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + if (JS_VERSION_IS_1_2(cx) && + !re && *sep->chars == ' ' && sep->chars[1] == 0) { + /* Skip leading whitespace if at front of str. */ + if (i == 0) { + while (JS_ISSPACE(chars[i])) + i++; + *ip = i; + } + + /* Don't delimit whitespace at end of string. */ + if ((size_t)i == length) + return -1; + + /* Skip over the non-whitespace chars. */ + while ((size_t)i < length && !JS_ISSPACE(chars[i])) + i++; + + /* Now skip the next run of whitespace. */ + j = i; + while ((size_t)j < length && JS_ISSPACE(chars[j])) + j++; + + /* Update sep->length to count delimiter chars. */ + sep->length = (size_t)(j - i); + return i; + } + +#if JS_HAS_REGEXPS /* * Match a regular expression against the separator at or above index i. * Call js_ExecuteRegExp with true for the test argument. On successful @@ -1750,8 +1829,13 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * bump past end of string -- our caller must do that by adding * sep->length to our return value. */ - if ((size_t)i == length) + if ((size_t)i == length) { + if (JS_VERSION_IS_1_2(cx)) { + sep->length = 1; + return i; + } return -1; + } i++; goto again; } @@ -1767,6 +1851,7 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, JS_ASSERT((size_t)i >= sep->length); return i - sep->length; } +#endif /* JS_HAS_REGEXPS */ /* * Deviate from ECMA by never splitting an empty string by any separator @@ -1780,9 +1865,21 @@ find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, * Special case: if sep is the empty string, split str into one character * substrings. Let our caller worry about whether to split once at end of * string into an empty substring. + * + * For 1.2 compatibility, at the end of the string, we return the length as + * the result, and set the separator length to 1 -- this allows the caller + * to include an additional null string at the end of the substring list. */ - if (sep->length == 0) + if (sep->length == 0) { + if (JS_VERSION_IS_1_2(cx)) { + if ((size_t)i == length) { + sep->length = 1; + return i; + } + return i + 1; + } return ((size_t)i == length) ? -1 : i + 1; + } /* * Now that we know sep is non-empty, search starting at i in str for an @@ -1829,14 +1926,16 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) v = STRING_TO_JSVAL(str); ok = JS_SetElement(cx, arrayobj, 0, &v); } else { +#if JS_HAS_REGEXPS if (JSVAL_IS_REGEXP(cx, argv[0])) { re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); sep = &tmp; /* Set a magic value so we can detect a successful re match. */ sep->chars = NULL; - sep->length = 0; - } else { + } else +#endif + { JSString *str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; @@ -1877,7 +1976,7 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (!JS_SetElement(cx, arrayobj, len, &v)) return JS_FALSE; len++; - +#if JS_HAS_REGEXPS /* * Imitate perl's feature of including parenthesized substrings * that matched part of the delimiter in the new array, after the @@ -1902,7 +2001,7 @@ str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } sep->chars = NULL; } - +#endif i = j + sep->length; if (!JS_VERSION_IS_ECMA(cx)) { /* @@ -1967,6 +2066,7 @@ str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } #endif /* JS_HAS_PERL_SUBSTR */ +#if JS_HAS_SEQUENCE_OPS /* * Python-esque sequence operations. */ @@ -2046,6 +2146,7 @@ str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) *rval = STRING_TO_JSVAL(str); return JS_TRUE; } +#endif /* JS_HAS_SEQUENCE_OPS */ #if JS_HAS_STR_HTML_HELPERS /* @@ -2061,14 +2162,10 @@ tagify(JSContext *cx, JSObject *obj, jsval *argv, size_t beglen, endlen, parlen, taglen; size_t i, j; - if (JSVAL_IS_STRING((jsval)obj)) { - str = JSVAL_TO_STRING((jsval)obj); - } else { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - } + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); if (!end) end = begin; @@ -2219,70 +2316,56 @@ str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE - {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,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,JSFUN_THISP_STRING,0}, - {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, - {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, + {js_toString_str, str_toString, 0,0,0}, + {js_valueOf_str, str_valueOf, 0,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). */ - {"match", str_match, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,2}, - {"search", str_search, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"split", str_split, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, +#if JS_HAS_REGEXPS + {"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,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, + {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE,0}, #endif /* Python-esque sequence methods. */ - {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, +#if JS_HAS_SEQUENCE_OPS + {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE,0}, + {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE,0}, +#endif /* HTML string methods. */ #if JS_HAS_STR_HTML_HELPERS - {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, - {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, - {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, - {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, - {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, - {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, - {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, + {"bold", str_bold, 0,0,0}, + {"italics", str_italics, 0,0,0}, + {"fixed", str_fixed, 0,0,0}, + {"fontsize", str_fontsize, 1,0,0}, + {"fontcolor", str_fontcolor, 1,0,0}, + {"link", str_link, 1,0,0}, + {"anchor", str_anchor, 1,0,0}, + {"strike", str_strike, 0,0,0}, + {"small", str_small, 0,0,0}, + {"big", str_big, 0,0,0}, + {"blink", str_blink, 0,0,0}, + {"sup", str_sup, 0,0,0}, + {"sub", str_sub, 0,0,0}, #endif {0,0,0,0,0} @@ -2318,7 +2401,6 @@ str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uint16 code; JSString *str; - JS_ASSERT(argc < ARRAY_INIT_LIMIT); chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; @@ -2344,6 +2426,43 @@ static JSFunctionSpec string_static_methods[] = { {0,0,0,0,0} }; +static JSHashTable *deflated_string_cache; +#ifdef DEBUG +static uint32 deflated_string_cache_bytes; +#endif +#ifdef JS_THREADSAFE +static JSLock *deflated_string_cache_lock; +#endif + +JSBool +js_InitStringGlobals(void) +{ +#ifdef JS_THREADSAFE + /* Must come through here once in primordial thread to init safely! */ + if (!deflated_string_cache_lock) { + deflated_string_cache_lock = JS_NEW_LOCK(); + if (!deflated_string_cache_lock) + return JS_FALSE; + } +#endif + return JS_TRUE; +} + +void +js_FreeStringGlobals() +{ + if (deflated_string_cache) { + JS_HashTableDestroy(deflated_string_cache); + deflated_string_cache = NULL; + } +#ifdef JS_THREADSAFE + if (deflated_string_cache_lock) { + JS_DESTROY_LOCK(deflated_string_cache_lock); + deflated_string_cache_lock = NULL; + } +#endif +} + JSBool js_InitRuntimeStringState(JSContext *cx) { @@ -2352,38 +2471,21 @@ js_InitRuntimeStringState(JSContext *cx) JSAtom *atom; rt = cx->runtime; - - /* Initialize string cache */ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = JS_NEW_LOCK(); - if (!rt->deflatedStringCacheLock) - return JS_FALSE; -#endif + JS_ASSERT(!rt->emptyString); /* Make a permanently locked empty string. */ - JS_ASSERT(!rt->emptyString); empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); if (!empty) - goto bad; + return JS_FALSE; /* Atomize it for scripts that use '' + x to convert x to string. */ atom = js_AtomizeString(cx, empty, ATOM_PINNED); if (!atom) - goto bad; + return JS_FALSE; rt->emptyString = empty; rt->atomState.emptyAtom = atom; - return JS_TRUE; - - bad: -#ifdef JS_THREADSAFE - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; -#endif - return JS_FALSE; - } void @@ -2395,21 +2497,6 @@ js_FinishRuntimeStringState(JSContext *cx) rt->emptyString = NULL; } -void -js_FinishDeflatedStringCache(JSRuntime *rt) -{ - if (rt->deflatedStringCache) { - JS_HashTableDestroy(rt->deflatedStringCache); - rt->deflatedStringCache = NULL; - } -#ifdef JS_THREADSAFE - if (rt->deflatedStringCacheLock) { - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; - } -#endif -} - JSObject * js_InitStringClass(JSContext *cx, JSObject *obj) { @@ -2586,26 +2673,26 @@ js_hash_string_pointer(const void *key) } void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) +js_PurgeDeflatedStringCache(JSString *str) { JSHashNumber hash; JSHashEntry *he, **hep; - if (!rt->deflatedStringCache) + if (!deflated_string_cache) return; hash = js_hash_string_pointer(str); - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); + JS_ACQUIRE_LOCK(deflated_string_cache_lock); + hep = JS_HashTableRawLookup(deflated_string_cache, hash, str); he = *hep; if (he) { #ifdef DEBUG - rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); + deflated_string_cache_bytes -= JSSTRING_LENGTH(str); #endif free(he->value); - JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); + JS_HashTableRawRemove(deflated_string_cache, hep, he); } - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + JS_RELEASE_LOCK(deflated_string_cache_lock); } void @@ -2632,7 +2719,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str) free(str->chars); } if (valid) { - js_PurgeDeflatedStringCache(rt, str); + js_PurgeDeflatedStringCache(str); str->chars = NULL; } str->length = 0; @@ -2651,24 +2738,24 @@ js_StringToObject(JSContext *cx, JSString *str) } JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) +js_ValueToPrintableString(JSContext *cx, jsval v) { JSString *str; const char *bytes; - str = v2sfun(cx, v); + str = js_ValueToString(cx, v); if (!str) return NULL; str = js_QuoteString(cx, str, 0); if (!str) return NULL; - bytes = js_GetStringBytes(cx->runtime, str); + bytes = js_GetStringBytes(str); if (!bytes) JS_ReportOutOfMemory(cx); return bytes; } -JS_FRIEND_API(JSString *) +JSString * js_ValueToString(JSContext *cx, jsval v) { JSObject *obj; @@ -2695,7 +2782,7 @@ js_ValueToString(JSContext *cx, jsval v) return str; } -JS_FRIEND_API(JSString *) +JSString * js_ValueToSource(JSContext *cx, jsval v) { JSTempValueRooter tvr; @@ -2746,13 +2833,6 @@ js_CompareStrings(JSString *str1, JSString *str2) const jschar *s1, *s2; intN cmp; - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); n = JS_MIN(l1, l2); @@ -2764,36 +2844,6 @@ js_CompareStrings(JSString *str1, JSString *str2) return (intN)(l1 - l2); } -JSBool -js_EqualStrings(JSString *str1, JSString *str2) -{ - size_t n; - const jschar *s1, *s2; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return JS_TRUE; - - n = JSSTRING_LENGTH(str1); - if (n != JSSTRING_LENGTH(str2)) - return JS_FALSE; - - if (n == 0) - return JS_TRUE; - - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - do { - if (*s1 != *s2) - return JS_FALSE; - ++s1, ++s2; - } while (--n != 0); - - return JS_TRUE; -} - size_t js_strlen(const jschar *s) { @@ -3026,7 +3076,7 @@ bufferTooSmall: return JS_FALSE; } -#else +#else /* !JS_C_STRINGS_ARE_UTF8 */ JSBool js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, @@ -3109,34 +3159,34 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t length) return bytes; } -#endif +#endif /* !JS_C_STRINGS_ARE_UTF8 */ static JSHashTable * -GetDeflatedStringCache(JSRuntime *rt) +GetDeflatedStringCache(void) { JSHashTable *cache; - cache = rt->deflatedStringCache; + cache = deflated_string_cache; if (!cache) { cache = JS_NewHashTable(8, js_hash_string_pointer, JS_CompareValues, JS_CompareValues, NULL, NULL); - rt->deflatedStringCache = cache; + deflated_string_cache = cache; } return cache; } JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) +js_SetStringBytes(JSString *str, char *bytes, size_t length) { JSHashTable *cache; JSBool ok; JSHashNumber hash; JSHashEntry **hep; - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + JS_ACQUIRE_LOCK(deflated_string_cache_lock); - cache = GetDeflatedStringCache(rt); + cache = GetDeflatedStringCache(); if (!cache) { ok = JS_FALSE; } else { @@ -3146,25 +3196,25 @@ js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; #ifdef DEBUG if (ok) - rt->deflatedStringCacheBytes += length; + deflated_string_cache_bytes += length; #endif } - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + JS_RELEASE_LOCK(deflated_string_cache_lock); return ok; } char * -js_GetStringBytes(JSRuntime *rt, JSString *str) +js_GetStringBytes(JSString *str) { JSHashTable *cache; char *bytes; JSHashNumber hash; JSHashEntry *he, **hep; - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + JS_ACQUIRE_LOCK(deflated_string_cache_lock); - cache = GetDeflatedStringCache(rt); + cache = GetDeflatedStringCache(); if (!cache) { bytes = NULL; } else { @@ -3183,7 +3233,7 @@ js_GetStringBytes(JSRuntime *rt, JSString *str) if (bytes) { if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { #ifdef DEBUG - rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); + deflated_string_cache_bytes += JSSTRING_LENGTH(str); #endif } else { free(bytes); @@ -3193,7 +3243,7 @@ js_GetStringBytes(JSRuntime *rt, JSString *str) } } - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + JS_RELEASE_LOCK(deflated_string_cache_lock); return bytes; } diff --git a/lib/javascript/jsstr.h b/lib/javascript/jsstr.h index 708c69ac..658a87f9 100644 --- a/lib/javascript/jsstr.h +++ b/lib/javascript/jsstr.h @@ -287,16 +287,23 @@ typedef enum JSCharType { ? (c) + ((int32)JS_CCODE(c) >> 22) \ : (c))) -/* - * Shorthands for ASCII (7-bit) decimal and hex conversion. - * Manually inline isdigit for performance; MSVC doesn't do this for us. - */ -#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +/* Shorthands for ASCII (7-bit) decimal and hex conversion. */ +#define JS7_ISDEC(c) ((c) < 128 && isdigit(c)) #define JS7_UNDEC(c) ((c) - '0') #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) -#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') +#define JS7_UNHEX(c) (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a') #define JS7_ISLET(c) ((c) < 128 && isalpha(c)) +/* Initialize truly global state associated with JS strings. */ +extern JSBool +js_InitStringGlobals(void); + +extern void +js_FreeStringGlobals(void); + +extern void +js_PurgeDeflatedStringCache(JSString *str); + /* Initialize per-runtime string state for the first context in the runtime. */ extern JSBool js_InitRuntimeStringState(JSContext *cx); @@ -304,9 +311,6 @@ js_InitRuntimeStringState(JSContext *cx); extern void js_FinishRuntimeStringState(JSContext *cx); -extern void -js_FinishDeflatedStringCache(JSRuntime *rt); - /* Initialize the String class, returning its prototype object. */ extern JSClass js_StringClass; @@ -351,29 +355,21 @@ js_StringToObject(JSContext *cx, JSString *str); /* * Convert a value to a printable C string. */ -typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); - extern JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); - -#define js_ValueToPrintableString(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToString) - -#define js_ValueToPrintableSource(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToSource) +js_ValueToPrintableString(JSContext *cx, jsval v); /* * Convert a value to a string, returning null after reporting an error, * otherwise returning a new string reference. */ -extern JS_FRIEND_API(JSString *) +extern JSString * js_ValueToString(JSContext *cx, jsval v); /* * Convert a value to its source expression, returning null after reporting * an error, otherwise returning a new string reference. */ -extern JS_FRIEND_API(JSString *) +extern JSString * js_ValueToSource(JSContext *cx, jsval v); #ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ @@ -391,12 +387,6 @@ js_HashString(JSString *str); extern intN js_CompareStrings(JSString *str1, JSString *str2); -/* - * Test if strings are equal. - */ -extern JSBool -js_EqualStrings(JSString *str1, JSString *str2); - /* * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. @@ -447,42 +437,34 @@ js_DeflateString(JSContext *cx, const jschar *chars, size_t length); * '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. + * The destination length needs to be initialized with the buffer size, takes the number of chars moved. */ extern JSBool -js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, - jschar *chars, size_t* charsLength); +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. + * 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); +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t charsLength, char *bytes, size_t* length); /* * Associate bytes with str in the deflated string cache, returning true on * successful association, false on out of memory. */ extern JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); +js_SetStringBytes(JSString *str, char *bytes, size_t length); /* * Find or create a deflated string cache entry for str that contains its * characters chopped from Unicode code points into bytes. */ extern char * -js_GetStringBytes(JSRuntime *rt, JSString *str); - -/* Remove a deflated string cache entry associated with str if any. */ -extern void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); +js_GetStringBytes(JSString *str); JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, diff --git a/lib/javascript/jstypes.h b/lib/javascript/jstypes.h index 78f282cf..92e13f91 100644 --- a/lib/javascript/jstypes.h +++ b/lib/javascript/jstypes.h @@ -86,16 +86,6 @@ #define JS_DLL_CALLBACK #define JS_STATIC_DLL_CALLBACK(__x) static __x -#elif defined(XP_OS2) && defined(__declspec) - -#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_API(__type) __declspec(dllexport) __type -#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - #elif defined(WIN16) #ifdef _WINDLL @@ -119,7 +109,7 @@ #else /* Unix */ -#ifdef HAVE_VISIBILITY_ATTRIBUTE +#ifdef HAVE_VISIBILITY_PRAGMA #define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) #else #define JS_EXTERNAL_VIS @@ -141,16 +131,12 @@ # else # define JS_IMPORT_API(__x) __declspec(dllimport) __x # endif -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_API(__x) __declspec(dllimport) __x #else # define JS_IMPORT_API(__x) JS_EXPORT_API (__x) #endif #if defined(_WIN32) && !defined(__MWERKS__) # define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x #else # define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) #endif @@ -376,11 +362,7 @@ typedef ptrdiff_t JSPtrdiff; ** A type for pointer difference. Variables of this type are suitable ** for storing a pointer or pointer sutraction. ************************************************************************/ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSUint64 JSUptrdiff; -#else typedef unsigned long JSUptrdiff; -#endif /************************************************************************ ** TYPES: JSBool @@ -398,20 +380,15 @@ typedef JSIntn JSBool; ** TYPES: JSPackedBool ** DESCRIPTION: ** Use JSPackedBool within structs where bitfields are not desireable -** but minimum and consistent overhead matters. +** but minimum and consistant overhead matters. ************************************************************************/ typedef JSUint8 JSPackedBool; /* ** A JSWord is an integer that is the same size as a void* */ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSInt64 JSWord; -typedef JSUint64 JSUword; -#else typedef long JSWord; typedef unsigned long JSUword; -#endif #include "jsotypes.h" @@ -439,26 +416,6 @@ typedef unsigned long JSUword; #define JS_UNLIKELY(x) (x) #endif -/*********************************************************************** -** MACROS: JS_ARRAY_LENGTH -** JS_ARRAY_END -** DESCRIPTION: -** Macros to get the number of elements and the pointer to one past the -** last element of a C array. Use them like this: -** -** jschar buf[10], *s; -** JSString *str; -** ... -** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; -** ... -** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); -** ... -** -***********************************************************************/ - -#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) -#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) - JS_END_EXTERN_C #endif /* jstypes_h___ */ diff --git a/lib/javascript/jsutil.c b/lib/javascript/jsutil.c index 1bb9f939..d7fab0f8 100644 --- a/lib/javascript/jsutil.c +++ b/lib/javascript/jsutil.c @@ -57,7 +57,8 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) #if defined(WIN32) DebugBreak(); exit(3); -#elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) +#endif +#if defined(XP_OS2) asm("int $3"); #endif abort(); @@ -67,6 +68,7 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) #define __USE_GNU 1 #include +#include #include #include "jshash.h" #include "jsprf.h" @@ -74,9 +76,9 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; static JSCallsite * -CallTree(void **bp) +CallTree(uint32 *bp) { - void **bpup, **bpdown, *pc; + uint32 *bpup, *bpdown, pc; JSCallsite *parent, *site, **csp; Dl_info info; int ok, offset; @@ -86,9 +88,9 @@ CallTree(void **bp) /* Reverse the stack frame list to avoid recursion. */ bpup = NULL; for (;;) { - bpdown = (void**) bp[0]; - bp[0] = (void*) bpup; - if ((void**) bpdown[0] < bpdown) + bpdown = (uint32*) bp[0]; + bp[0] = (uint32) bpup; + if ((uint32*) bpdown[0] < bpdown) break; bpup = bp; bp = bpdown; @@ -97,8 +99,8 @@ CallTree(void **bp) /* Reverse the stack again, finding and building a path in the tree. */ parent = &js_calltree_root; do { - bpup = (void**) bp[0]; - bp[0] = (void*) bpdown; + bpup = (uint32*) bp[0]; + bp[0] = (uint32) bpdown; pc = bp[1]; csp = &parent->kids; @@ -126,7 +128,7 @@ CallTree(void **bp) * XXX static syms are masked by nearest lower global */ info.dli_fname = info.dli_sname = NULL; - ok = dladdr(pc, &info); + ok = dladdr((void*) pc, &info); if (ok < 0) { fprintf(stderr, "dladdr failed!\n"); return NULL; @@ -170,23 +172,15 @@ CallTree(void **bp) JSCallsite * JS_Backtrace(int skip) { - void **bp, **bpdown; + jmp_buf jb; + uint32 *bp, *bpdown; + + setjmp(jb); /* Stack walking code adapted from Kipp's "leaky". */ -#if defined(__i386) - __asm__( "movl %%ebp, %0" : "=g"(bp)); -#elif defined(__x86_64__) - __asm__( "movq %%rbp, %0" : "=g"(bp)); -#else - /* - * It would be nice if this worked uniformly, but at least on i386 and - * x86_64, it stopped working with gcc 4.1, because it points to the - * end of the saved registers instead of the start. - */ - bp = (void**) __builtin_frame_address(0); -#endif + bp = (uint32*) jb[0].__jmpbuf[JB_BP]; while (--skip >= 0) { - bpdown = (void**) *bp++; + bpdown = (uint32*) *bp++; if (bpdown < bp) break; bp = bpdown; diff --git a/lib/javascript/jsutil.h b/lib/javascript/jsutil.h index efcb614c..5e26c16a 100644 --- a/lib/javascript/jsutil.h +++ b/lib/javascript/jsutil.h @@ -63,18 +63,6 @@ JS_Assert(const char *s, const char *file, JSIntn ln); #endif /* defined(DEBUG) */ -/* - * Compile-time assert. "condition" must be a constant expression. - * The macro should be used only once per source line in places where - * a "typedef" declaration is allowed. - */ -#define JS_STATIC_ASSERT(condition) \ - JS_STATIC_ASSERT_IMPL(condition, __LINE__) -#define JS_STATIC_ASSERT_IMPL(condition, line) \ - JS_STATIC_ASSERT_IMPL2(condition, line) -#define JS_STATIC_ASSERT_IMPL2(condition, line) \ - typedef int js_static_assert_line_##line[(condition) ? 1 : -1] - /* ** Abort the process in a non-graceful manner. This will cause a core file, ** call to the debugger or other moral equivalent as well as causing the diff --git a/lib/javascript/jsxdrapi.c b/lib/javascript/jsxdrapi.c index 2855c608..0c9aecee 100644 --- a/lib/javascript/jsxdrapi.c +++ b/lib/javascript/jsxdrapi.c @@ -233,7 +233,6 @@ JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) xdr->numclasses = xdr->maxclasses = 0; xdr->reghash = NULL; xdr->userdata = NULL; - xdr->script = NULL; } JS_PUBLIC_API(JSXDRState *) @@ -411,60 +410,46 @@ JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) return JS_XDRCString(xdr, sp); } -static JSBool -XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) -{ - uint32 i, padlen, nbytes; - jschar *raw; - - nbytes = nchars * sizeof(jschar); - padlen = nbytes % JSXDR_ALIGN; - if (padlen) { - padlen = JSXDR_ALIGN - padlen; - nbytes += padlen; - } - if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) - return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { - for (i = 0; i != nchars; i++) - raw[i] = JSXDR_SWAB16(chars[i]); - if (padlen) - memset((char *)raw + nbytes - padlen, 0, padlen); - } else if (xdr->mode == JSXDR_DECODE) { - for (i = 0; i != nchars; i++) - chars[i] = JSXDR_SWAB16(raw[i]); - } - return JS_TRUE; -} - /* * Convert between a JS (Unicode) string and the XDR representation. */ JS_PUBLIC_API(JSBool) JS_XDRString(JSXDRState *xdr, JSString **strp) { - uint32 nchars; - jschar *chars; + uint32 i, len, padlen, nbytes; + jschar *chars = NULL, *raw; if (xdr->mode == JSXDR_ENCODE) - nchars = JSSTRING_LENGTH(*strp); - if (!JS_XDRUint32(xdr, &nchars)) + len = JSSTRING_LENGTH(*strp); + if (!JS_XDRUint32(xdr, &len)) return JS_FALSE; + nbytes = len * sizeof(jschar); if (xdr->mode == JSXDR_DECODE) { - chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); - if (!chars) + if (!(chars = (jschar *) JS_malloc(xdr->cx, nbytes + sizeof(jschar)))) return JS_FALSE; } else { chars = JSSTRING_CHARS(*strp); } - if (!XDRChars(xdr, chars, nchars)) + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) goto bad; - if (xdr->mode == JSXDR_DECODE) { - chars[nchars] = 0; - *strp = JS_NewUCString(xdr->cx, chars, nchars); - if (!*strp) + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i < len; i++) + raw[i] = JSXDR_SWAB16(chars[i]); + if (padlen) + memset((char *)raw + nbytes - padlen, 0, padlen); + } else if (xdr->mode == JSXDR_DECODE) { + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + chars[len] = 0; + + if (!(*strp = JS_NewUCString(xdr->cx, chars, len))) goto bad; } return JS_TRUE; @@ -488,32 +473,18 @@ JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) return JS_XDRString(xdr, strp); } -static JSBool -XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) +JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp) { jsdpun u; if (xdr->mode == JSXDR_ENCODE) - u.d = *dp; + u.d = **dp; if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *dp = u.d; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) -{ - jsdouble d; - - if (xdr->mode == JSXDR_ENCODE) - d = **dpp; - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; if (xdr->mode == JSXDR_DECODE) { - *dpp = JS_NewDouble(xdr->cx, d); - if (!*dpp) + *dp = JS_NewDouble(xdr->cx, u.d); + if (!*dp) return JS_FALSE; } return JS_TRUE; @@ -523,9 +494,22 @@ JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) #define JSVAL_XDRNULL 0x8 #define JSVAL_XDRVOID 0xA -static JSBool -XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) +JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp) { + uint32 type; + + if (xdr->mode == JSXDR_ENCODE) { + if (JSVAL_IS_NULL(*vp)) + type = JSVAL_XDRNULL; + else if (JSVAL_IS_VOID(*vp)) + type = JSVAL_XDRVOID; + else + type = JSVAL_TAG(*vp); + } + if (!JS_XDRUint32(xdr, &type)) + return JS_FALSE; + switch (type) { case JSVAL_XDRNULL: *vp = JSVAL_NULL; @@ -589,139 +573,6 @@ XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) return JS_TRUE; } -JS_PUBLIC_API(JSBool) -JS_XDRValue(JSXDRState *xdr, jsval *vp) -{ - uint32 type; - - if (xdr->mode == JSXDR_ENCODE) { - if (JSVAL_IS_NULL(*vp)) - type = JSVAL_XDRNULL; - else if (JSVAL_IS_VOID(*vp)) - type = JSVAL_XDRVOID; - else - type = JSVAL_TAG(*vp); - } - return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); -} - -JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) -{ - jsval v; - uint32 type; - jsdouble d; - JSAtom *atom; - - if (xdr->mode == JSXDR_ENCODE) { - v = ATOM_KEY(*atomp); - return JS_XDRValue(xdr, &v); - } - - /* - * Inline JS_XDRValue when decoding to avoid ceation of GC things when - * then corresponding atom already exists. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &type)) - return JS_FALSE; - if (type == JSVAL_STRING) - return js_XDRStringAtom(xdr, atomp); - - if (type == JSVAL_DOUBLE) { - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; - atom = js_AtomizeDouble(xdr->cx, d, 0); - } else { - if (!XDRValueBody(xdr, type, &v)) - return JS_FALSE; - atom = js_AtomizeValue(xdr->cx, v, 0); - } - - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - JSString *str; - uint32 nchars; - JSAtom *atom; - JSContext *cx; - void *mark; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - str = ATOM_TO_STRING(*atomp); - return JS_XDRString(xdr, &str); - } - - /* - * Inline JS_XDRString when decoding to avoid JSString allocation - * for already existing atoms. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, - nchars * sizeof(jschar)); - if (!chars) - JS_ReportOutOfMemory(cx); - else if (XDRChars(xdr, chars, nchars)) - atom = js_AtomizeChars(cx, chars, nchars, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -/* - * FIXME: This performs lossy conversion and we need to switch to - * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. - */ -JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - char *bytes; - uint32 nbytes; - JSAtom *atom; - JSContext *cx; - void *mark; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); - return JS_XDRCString(xdr, &bytes); - } - - /* - * Inline JS_XDRCString when decoding not to malloc temporary buffer - * just to free it after atomization. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nbytes)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, - nbytes * sizeof *bytes); - if (!bytes) - JS_ReportOutOfMemory(cx); - else if (JS_XDRBytes(xdr, bytes, nbytes)) - atom = js_Atomize(cx, bytes, nbytes, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - JS_PUBLIC_API(JSBool) JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) { diff --git a/lib/javascript/jsxdrapi.h b/lib/javascript/jsxdrapi.h index 35d9918a..874a62ee 100644 --- a/lib/javascript/jsxdrapi.h +++ b/lib/javascript/jsxdrapi.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -115,7 +114,6 @@ struct JSXDRState { uintN maxclasses; void *reghash; void *userdata; - JSScript *script; }; extern JS_PUBLIC_API(void) @@ -188,35 +186,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); #define JSXDR_MAGIC_SCRIPT_2 0xdead0002 #define JSXDR_MAGIC_SCRIPT_3 0xdead0003 #define JSXDR_MAGIC_SCRIPT_4 0xdead0004 -#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 - -/* - * Bytecode version number. Decrement the second term whenever JS bytecode - * changes incompatibly. - * - * This version number should be XDR'ed once near the front of any file or - * larger storage unit containing XDR'ed bytecode and other data, and checked - * before deserialization of bytecode. If the saved version does not match - * the current version, abort deserialization and invalidate the file. - */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) - -/* - * Library-private functions. - */ -extern JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); - -/* - * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy - * conversion. Do not use it in the new code! See bug 325202. - */ -extern JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_4 JS_END_EXTERN_C diff --git a/lib/javascript/jsxml.c b/lib/javascript/jsxml.c index 1266255b..e7cde2ed 100644 --- a/lib/javascript/jsxml.c +++ b/lib/javascript/jsxml.c @@ -116,6 +116,8 @@ static struct { /* * 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"; @@ -191,7 +193,8 @@ namespace_finalize(JSContext *cx, JSObject *obj) } static void -namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) +namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len, + void *arg) { uint32 i; JSXMLNamespace *ns; @@ -205,8 +208,10 @@ namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) JS_snprintf(buf, sizeof buf, "%s=%s", ns->prefix ? JS_GetStringBytes(ns->prefix) : "", JS_GetStringBytes(ns->uri)); +#else + const char *buf = NULL; #endif - GC_MARK(cx, ns, buf); + JS_MarkGCThing(cx, ns, buf, arg); } } } @@ -217,7 +222,7 @@ namespace_mark(JSContext *cx, JSObject *obj, void *arg) JSXMLNamespace *ns; ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - GC_MARK(cx, ns, "private"); + JS_MarkGCThing(cx, ns, js_private_str, arg); return 0; } @@ -234,21 +239,21 @@ namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) *bp = JS_FALSE; } else { ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); - *bp = js_EqualStrings(ns->uri, ns2->uri); + *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 | - JSCLASS_HAS_CACHED_PROTO(JSProto_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, NULL, - NULL, NULL, NULL, NULL + namespace_equality, + NULL, NULL, + JSCLASS_NO_RESERVED_MEMBERS }; #define NAMESPACE_ATTRS \ @@ -300,11 +305,11 @@ js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, } void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns, void *arg) { - GC_MARK(cx, ns->object, "object"); - GC_MARK(cx, ns->prefix, "prefix"); - GC_MARK(cx, ns->uri, "uri"); + 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 @@ -337,7 +342,7 @@ js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) } obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, ns)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } ns->object = obj; @@ -410,7 +415,7 @@ qname_mark(JSContext *cx, JSObject *obj, void *arg) JSXMLQName *qn; qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - GC_MARK(cx, qn, "private"); + JS_MarkGCThing(cx, qn, js_private_str, arg); return 0; } @@ -419,9 +424,9 @@ qname_identity(JSXMLQName *qna, JSXMLQName *qnb) { if (!qna->uri ^ !qnb->uri) return JS_FALSE; - if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) + if (qna->uri && js_CompareStrings(qna->uri, qnb->uri)) return JS_FALSE; - return js_EqualStrings(qna->localName, qnb->localName); + return !js_CompareStrings(qna->localName, qnb->localName); } static JSBool @@ -444,14 +449,14 @@ qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { { "QName", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | - JSCLASS_HAS_CACHED_PROTO(JSProto_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, NULL, - NULL, NULL, NULL, NULL + qname_equality, + NULL, NULL, + JSCLASS_NO_RESERVED_MEMBERS }; /* @@ -461,9 +466,7 @@ JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { * in this implementation. */ JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { - js_AttributeName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), + 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, @@ -471,9 +474,7 @@ JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { }; JS_FRIEND_DATA(JSClass) js_AnyNameClass = { - js_AnyName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), + 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, @@ -568,12 +569,12 @@ js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, } void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn, void *arg) { - GC_MARK(cx, qn->object, "object"); - GC_MARK(cx, qn->uri, "uri"); - GC_MARK(cx, qn->prefix, "prefix"); - GC_MARK(cx, qn->localName, "localName"); + 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 @@ -606,7 +607,7 @@ js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) } obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } qn->object = obj; @@ -631,7 +632,7 @@ js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } @@ -852,7 +853,9 @@ QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * (see below after this function). */ obj = js_NewObject(cx, - JS_ValueToFunction(cx, argv[-2])->clasp, + argv + ? JS_ValueToFunction(cx, argv[-2])->clasp + : &js_QNameClass.base, NULL, NULL); if (!obj) return JS_FALSE; @@ -968,13 +971,13 @@ namespace_identity(const void *a, const void *b) const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsa->prefix && nsb->prefix) { - if (!js_EqualStrings(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_EqualStrings(nsa->uri, nsb->uri); + return !js_CompareStrings(nsa->uri, nsb->uri); } static JSBool @@ -1041,7 +1044,7 @@ static void XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) { while (cursor) { - GC_MARK(cx, cursor->root, "cursor->root"); + GC_MARK(cx, cursor->root, "cursor->root", NULL); cursor = cursor->next; } } @@ -1423,7 +1426,7 @@ ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { + if (ns->prefix && !js_CompareStrings(ns->prefix, prefix)) { uri = ns->uri; break; } @@ -1463,7 +1466,7 @@ ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, break; } } - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + prefix = NULL; } localName = str; } @@ -1508,13 +1511,6 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, JSXMLNamespace *ns; JSXMLQName *qn, *attrjqn; JSXMLClass xml_class; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_OVER_RECURSED); - return NULL; - } #define PN2X_SKIP_CHILD ((JSXML *) 1) @@ -1524,7 +1520,7 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, * garbage collection. */ xml = NULL; - if (!js_EnterLocalRootScope(cx)) + if (!JS_EnterLocalRootScope(cx)) return NULL; switch (pn->pn_type) { case TOK_XMLELEM: @@ -1533,6 +1529,8 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, 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; @@ -1760,8 +1758,8 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, for (j = 0; j < i; j++) { attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); attrjqn = attrj->name; - if (js_EqualStrings(attrjqn->uri, qn->uri) && - js_EqualStrings(attrjqn->localName, qn->localName)) { + 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, @@ -1840,7 +1838,7 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, goto syntax; } - js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); + JS_LeaveLocalRootScope(cx); if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) return NULL; return xml; @@ -1855,7 +1853,7 @@ syntax: js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP); fail: - js_LeaveLocalRootScope(cx); + JS_LeaveLocalRootScope(cx); return NULL; } @@ -1868,9 +1866,9 @@ GetXMLSetting(JSContext *cx, const char *name, jsval *vp) { jsval v; - if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) + if (!js_FindConstructor(cx, NULL, js_XML_str, &v)) return JS_FALSE; - if (!VALUE_IS_FUNCTION(cx, v)) { + if (!JSVAL_IS_FUNCTION(cx, v)) { *vp = JSVAL_VOID; return JS_TRUE; } @@ -1981,15 +1979,13 @@ ParseXMLSource(JSContext *cx, JSString *src) js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); offset += urilen; dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, - &dstlen); + 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); + js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen); chars [offset + dstlen] = 0; mark = JS_ARENA_MARK(&cx->tempPool); @@ -2197,11 +2193,11 @@ ToXMLList(JSContext *cx, jsval v) xml = NULL; length = 0; } else { - if (!js_EnterLocalRootScope(cx)) + if (!JS_EnterLocalRootScope(cx)) return NULL; xml = ParseXMLSource(cx, str); if (!xml) { - js_LeaveLocalRootScope(cx); + JS_LeaveLocalRootScope(cx); return NULL; } length = JSXML_LENGTH(xml); @@ -2212,7 +2208,9 @@ ToXMLList(JSContext *cx, jsval v) list = (JSXML *) JS_GetPrivate(cx, listobj); for (i = 0; i < length; i++) { kid = OrphanXMLChild(cx, xml, i); - if (!kid || !Append(cx, list, kid)) { + if (!kid) + return NULL; + if (!Append(cx, list, kid)) { listobj = NULL; break; } @@ -2220,7 +2218,7 @@ ToXMLList(JSContext *cx, jsval v) } if (xml) - js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); + JS_LeaveLocalRootScope(cx); return listobj; bad: @@ -2474,7 +2472,7 @@ GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) qn->prefix ? js_ValueToPrintableString(cx, STRING_TO_JSVAL(qn->prefix)) - : js_type_strs[JSTYPE_VOID]); + : js_type_str[JSTYPE_VOID]); return NULL; } @@ -2513,10 +2511,10 @@ GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) * This spec bug leads to ToXMLString results that duplicate the * declared namespace. */ - if (js_EqualStrings(ns->uri, qn->uri) && + if (!js_CompareStrings(ns->uri, qn->uri) && (ns->prefix == qn->prefix || ((ns->prefix && qn->prefix) - ? js_EqualStrings(ns->prefix, qn->prefix) + ? !js_CompareStrings(ns->prefix, qn->prefix) : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { match = ns; break; @@ -2550,20 +2548,6 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) JS_ASSERT(!IS_EMPTY(uri)); - /* - * If there are no *declared* namespaces, skip all collision detection and - * return a short prefix quickly; an example of such a situation: - * - * var x = ; - * var n = new Namespace("http://example.com/"); - * x.@n::att = "val"; - * x.toXMLString(); - * - * This is necessary for various log10 uses below to be valid. - */ - if (decls->length == 0) - return JS_NewStringCopyZ(cx, "a"); - /* * Try peeling off the last filename suffix or pathname component till * we have a valid XML name. This heuristic will prefer "xul" given @@ -2575,49 +2559,31 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) while (--cp > start) { if (*cp == '.' || *cp == '/' || *cp == ':') { ++cp; - length = PTRDIFF(end, cp, jschar); - if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) + if (IsXMLName(cp, PTRDIFF(end, cp, jschar))) break; end = --cp; } } length = PTRDIFF(end, cp, jschar); - /* - * If the namespace consisted only of non-XML names or names that begin - * case-insensitively with "xml", arbitrarily create a prefix consisting - * of 'a's of size length (allowing dp-calculating code to work with or - * without this branch executing) plus the space for storing a hyphen and - * the serial number (avoiding reallocation if a collision happens). - */ - bp = (jschar *) cp; - newlength = length; - if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { - newlength = length + 2 + (size_t) log10(decls->length); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - - bp[newlength] = 0; - for (i = 0; i < newlength; i++) - bp[i] = 'a'; - } - /* * Now search through decls looking for a collision. If we collide with * an existing prefix, start tacking on a hyphen and a serial number. */ serial = 0; + 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) == newlength && - !memcmp(JSSTRING_CHARS(ns->prefix), bp, - newlength * sizeof(jschar))) { - if (bp == cp) { + 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)); @@ -2641,7 +2607,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) } } while (!done); - if (bp == cp) { + if (!bp) { offset = PTRDIFF(cp, start, jschar); prefix = js_NewDependentString(cx, uri, offset, length, 0); } else { @@ -2659,8 +2625,8 @@ namespace_match(const void *a, const void *b) const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsb->prefix) - return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); - return js_EqualStrings(nsa->uri, nsb->uri); + 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 */ @@ -2747,7 +2713,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } /* After this point, control must flow through label out: to exit. */ - if (!js_EnterLocalRootScope(cx)) + if (!JS_EnterLocalRootScope(cx)) return NULL; /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ @@ -2877,6 +2843,34 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, * 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) { @@ -2925,34 +2919,6 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, if (attr) goto out; - /* 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 18: handle point tags. */ n = xml->xml_kids.length; if (n == 0) { @@ -3012,7 +2978,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); out: - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + JS_LeaveLocalRootScope(cx); if (!str && STRING_BUFFER_OK(&sb)) js_FinishStringBuffer(&sb); XMLArrayFinish(cx, &decls); @@ -3032,9 +2998,9 @@ ToXMLString(JSContext *cx, jsval v) if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_CONVERSION, - js_type_strs[JSVAL_IS_NULL(v) - ? JSTYPE_NULL - : JSTYPE_VOID]); + js_type_str[JSVAL_IS_NULL(v) + ? JSTYPE_NULL + : JSTYPE_VOID]); return NULL; } @@ -3108,7 +3074,12 @@ ToAttributeName(JSContext *cx, jsval v) if (!qn) return NULL; - JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); + /* + * 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) @@ -3184,7 +3155,7 @@ out: atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; if (qn->uri && atom && (qn->uri == ATOM_TO_STRING(atom) || - js_EqualStrings(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 { @@ -3214,7 +3185,7 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) match = NULL; for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { + if (ns2 && !js_CompareStrings(ns2->uri, ns->uri)) { match = ns2; break; } @@ -3231,13 +3202,13 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (ns2 && ns2->prefix && - js_EqualStrings(ns2->prefix, ns->prefix)) { + !js_CompareStrings(ns2->prefix, ns->prefix)) { match = ns2; m = i; break; } } - if (match && !js_EqualStrings(match->uri, ns->uri)) { + if (match && js_CompareStrings(match->uri, ns->uri)) { ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, JSXMLNamespace); JS_ASSERT(ns2 == match); @@ -3299,7 +3270,7 @@ DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) JSBool ok; /* Our caller may not be protecting newborns with a local root scope. */ - if (!js_EnterLocalRootScope(cx)) + if (!JS_EnterLocalRootScope(cx)) return NULL; copy = DeepCopyInLRS(cx, xml, flags); if (copy) { @@ -3314,7 +3285,7 @@ DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) if (!ok) copy = NULL; } - js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); + JS_LeaveLocalRootScope(cx); return copy; } @@ -3499,9 +3470,9 @@ MatchAttrName(JSXMLQName *nameqn, JSXML *attr) JSXMLQName *attrqn = attr->name; return (IS_STAR(nameqn->localName) || - js_EqualStrings(attrqn->localName, nameqn->localName)) && + !js_CompareStrings(attrqn->localName, nameqn->localName)) && (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri)); + !js_CompareStrings(attrqn->uri, nameqn->uri)); } static JSBool @@ -3509,10 +3480,10 @@ MatchElemName(JSXMLQName *nameqn, JSXML *elem) { return (IS_STAR(nameqn->localName) || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->localName, nameqn->localName))) && + !js_CompareStrings(elem->name->localName, nameqn->localName))) && (!nameqn->uri || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->uri, nameqn->uri))); + !js_CompareStrings(elem->name->uri, nameqn->uri))); } /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ @@ -3576,7 +3547,7 @@ Descendants(JSContext *cx, JSXML *xml, jsval id) * DescendantsHelper use local roots. */ list->name = nameqn; - if (!js_EnterLocalRootScope(cx)) + if (!JS_EnterLocalRootScope(cx)) return NULL; if (xml->xml_class == JSXML_CLASS_LIST) { ok = JS_TRUE; @@ -3591,7 +3562,7 @@ Descendants(JSContext *cx, JSXML *xml, jsval id) } else { ok = DescendantsHelper(cx, xml, nameqn, list); } - js_LeaveLocalRootScopeWithResult(cx, (jsval) list); + JS_LeaveLocalRootScope(cx); if (!ok) return NULL; list->name = NULL; @@ -3632,8 +3603,8 @@ retry: vqn = vxml->name; if (qn) { *bp = vqn && - js_EqualStrings(qn->localName, vqn->localName) && - js_EqualStrings(qn->uri, vqn->uri); + !js_CompareStrings(qn->localName, vqn->localName) && + !js_CompareStrings(qn->uri, vqn->uri); } else { *bp = vqn == NULL; } @@ -3641,7 +3612,7 @@ retry: return JS_TRUE; if (JSXML_HAS_VALUE(xml)) { - *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + *bp = !js_CompareStrings(xml->xml_value, vxml->xml_value); } else if (xml->xml_kids.length != vxml->xml_kids.length) { *bp = JS_FALSE; } else { @@ -3683,7 +3654,7 @@ retry: vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); if (!vattr) continue; - *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + *bp = !js_CompareStrings(attr->xml_value, vattr->xml_value); } } } @@ -3918,7 +3889,7 @@ DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSXML *xml, *kid, *parent; JSBool isIndex; JSXMLArray *array; - uint32 length, index, kidIndex, deleteCount; + uint32 length, index, deleteCount; JSXMLQName *nameqn; jsid funid; JSObject *nameobj, *kidobj; @@ -3955,10 +3926,9 @@ DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!DeleteProperty(cx, parent->object, id, vp)) return JS_FALSE; } else { - kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, - NULL); - JS_ASSERT(kidIndex != XML_NOT_FOUND); - if (!IndexToIdVal(cx, kidIndex, &id)) + 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; @@ -4023,6 +3993,40 @@ out: 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) { @@ -4043,72 +4047,34 @@ SyncInScopeNamespaces(JSContext *cx, JSXML *xml) return JS_TRUE; } -static JSBool -GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, - JSBool attributes, JSXML *list) -{ - JSXMLArray *array; - JSXMLNameMatcher matcher; - JSXMLArrayCursor cursor; - JSXML *kid; - JSBool ok; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (attributes) { - 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 (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = SyncInScopeNamespaces(cx, kid); - if (!ok) - goto out; - } - ok = Append(cx, list, kid); - if (!ok) - goto out; - } - } - ok = JS_TRUE; - - out: - XMLArrayCursorFinish(&cursor); - return ok; -} - /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXML *xml, *list, *kid; uint32 index; - JSObject *kidobj, *listobj; + JSObject *kidobj, *listobj, *nameobj; JSXMLQName *nameqn; jsid funid; - jsval roots[2]; - JSTempValueRooter tvr; - JSBool attributes; + JSBool ok; JSXMLArrayCursor cursor; + jsval kidval; + JSXMLArray *array; + JSXMLNameMatcher matcher; xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); if (!xml) return JS_TRUE; - if (js_IdIsIndex(id, &index)) { - if (xml->xml_class != JSXML_CLASS_LIST) { - *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; - } else { +#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)) { /* - * ECMA-357 9.2.1.1 starts here. - * * Erratum: 9.2 is not completely clear that indexed properties * correspond to kids, but that's what it seems to say, and it's * what any sane user would want. @@ -4127,61 +4093,133 @@ GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } else { *vp = JSVAL_VOID; } + return JS_TRUE; } - return JS_TRUE; - } - /* - * ECMA-357 9.2.1.1/9.1.1.1 qname case. - */ - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return js_GetXMLFunction(cx, obj, funid, vp); + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return GetFunction(cx, obj, xml, funid, vp); - roots[0] = OBJECT_TO_JSVAL(nameqn->object); - JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); + /* + * 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) { - roots[1] = OBJECT_TO_JSVAL(listobj); - tvr.count++; + /* + * 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; - list = (JSXML *) JS_GetPrivate(cx, listobj); - attributes = (OBJ_GET_CLASS(cx, nameqn->object) == - &js_AttributeNameClass); - - if (xml->xml_class == JSXML_CLASS_LIST) { XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT && - !GetNamedProperty(cx, kid, nameqn, attributes, list)) { - listobj = NULL; - break; + 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 { - if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) - listobj = NULL; + } + } 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; + /* - * 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. + * 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. */ - list->xml_target = xml; - list->xml_targetprop = nameqn; - *vp = OBJECT_TO_JSVAL(listobj); + 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); + } + } } - JS_POP_TEMP_ROOT(cx, &tvr); - return listobj != NULL; + /* 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 * @@ -4249,7 +4287,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) } /* Control flow after here must exit via label out. */ - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); @@ -4329,8 +4367,6 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) ok = GetProperty(cx, rxml->object, id, &attrval); if (!ok) goto out; - if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ - goto out; attrobj = JSVAL_TO_OBJECT(attrval); attr = (JSXML *) JS_GetPrivate(cx, attrobj); if (JSXML_LENGTH(attr) != 0) @@ -4422,9 +4458,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (parent) { /* 2(e)(i). */ - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - goto bad; + parentobj = parent->object; ok = PutProperty(cx, parentobj, id, vp); if (!ok) goto out; @@ -4666,9 +4700,9 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!attr) continue; attrqn = attr->name; - if (js_EqualStrings(attrqn->localName, nameqn->localName) && + if (!js_CompareStrings(attrqn->localName, nameqn->localName) && (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri))) { + !js_CompareStrings(attrqn->uri, nameqn->uri))) { if (!match) { match = attr; } else { @@ -4851,7 +4885,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) out: JS_POP_TEMP_ROOT(cx, &tvr); - js_LeaveLocalRootScope(cx); + JS_LeaveLocalRootScope(cx); return ok; type_error: @@ -5030,7 +5064,7 @@ xml_finalize(JSContext *cx, JSObject *obj) } static void -xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) +xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len, void *arg) { uint32 i; JSXML *elt; @@ -5059,8 +5093,10 @@ xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, buf, &dstlen); } +#else + const char *buf = NULL; #endif - GC_MARK(cx, elt, buf); + JS_MarkGCThing(cx, elt, buf, arg); } } } @@ -5070,25 +5106,18 @@ xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) * 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), js_FindProperty (in jsobj.c, called from - * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a - * JSScopeProperty here is when an unqualified name or XML name is being - * accessed or when "name in xml" is called. + * 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 keeps the JSOP_NAME code in js_Interpret happy by - * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). + * 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. - * - * FIXME This clashes with the function namespace implementation which also - * uses native properties. Effectively after xml_lookupProperty any property - * stored previously using assignments to xml.function::name will be removed. - * We partially workaround the problem in js_GetXMLFunction. There we take - * advantage of the fact that typically function:: is used to access the - * functions from XML.prototype. So when js_GetProperty returns a non-function - * property, we assume that it represents the result of GetProperty setter - * hiding the function and use an extra prototype chain lookup to recover it. - * For a proper solution see bug 355257. */ static JSBool xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, @@ -5118,7 +5147,7 @@ xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp) { - if (VALUE_IS_FUNCTION(cx, value) || getter || setter || + 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, @@ -5295,8 +5324,8 @@ xml_mark(JSContext *cx, JSObject *obj, void *arg) JSXML *xml; xml = (JSXML *) JS_GetPrivate(cx, obj); - GC_MARK(cx, xml, "private"); - return js_Mark(cx, obj, NULL); + JS_MarkGCThing(cx, xml, js_private_str, arg); + return js_Mark(cx, obj, arg); } static void @@ -5346,20 +5375,66 @@ again: 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); - /* - * As our callers have a bad habit of passing a pointer to an unrooted - * local value as vp, we use a proper root here. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) - obj = NULL; - *vp = tvr.u.value; + /* 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 @@ -5458,34 +5533,34 @@ xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) ((vxml->xml_class == JSXML_CLASS_TEXT || vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && HasSimpleContent(xml))) { - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (ok) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); vstr = js_ValueToString(cx, v); ok = str && vstr; if (ok) - *bp = js_EqualStrings(str, vstr); - js_LeaveLocalRootScope(cx); + *bp = !js_CompareStrings(str, vstr); + JS_LeaveLocalRootScope(cx); } } else { ok = XMLEquals(cx, xml, vxml, bp); } } } else { - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (ok) { if (HasSimpleContent(xml)) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); vstr = js_ValueToString(cx, v); ok = str && vstr; if (ok) - *bp = js_EqualStrings(str, vstr); + *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_EqualStrings(str, JSVAL_TO_STRING(v)); + *bp = !js_CompareStrings(str, JSVAL_TO_STRING(v)); } else { ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); if (ok) { @@ -5497,7 +5572,7 @@ xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) } else { *bp = JS_FALSE; } - js_LeaveLocalRootScope(cx); + JS_LeaveLocalRootScope(cx); } } return ok; @@ -5510,7 +5585,7 @@ xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) JSObject *listobj, *robj; JSXML *list, *lxml, *rxml; - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; @@ -5542,7 +5617,7 @@ xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) *vp = OBJECT_TO_JSVAL(listobj); out: - js_LeaveLocalRootScopeWithResult(cx, *vp); + JS_LeaveLocalRootScope(cx); return ok; } @@ -5572,8 +5647,7 @@ xml_getObjectOps(JSContext *cx, JSClass *clasp) } JS_FRIEND_DATA(JSClass) js_XMLClass = { - js_XML_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), + 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, @@ -5595,40 +5669,6 @@ CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, return JSVAL_TO_OBJECT(rval); } -static JSXML * -StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) -{ - JSXML *xml; - JSFunction *fun; - - JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); - - xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); - if (!xml || xml->xml_class != JSXML_CLASS_LIST) - return xml; - - if (xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - *objp = js_GetXMLObject(cx, xml); - if (!*objp) - return NULL; - argv[-1] = OBJECT_TO_JSVAL(*objp); - return xml; - } - } - - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); - if (fun) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NON_LIST_XML_METHOD, - JS_GetFunctionName(fun), numBuf); - } - return NULL; -} - #define XML_METHOD_PROLOG \ JS_BEGIN_MACRO \ xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ @@ -5636,14 +5676,6 @@ StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) return JS_FALSE; \ JS_END_MACRO -#define NON_LIST_XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = StartNonListXMLMethod(cx, &obj, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ - JS_END_MACRO - static JSBool xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -5652,7 +5684,7 @@ xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSObject *nsobj; JSXMLNamespace *ns; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); @@ -5680,7 +5712,7 @@ xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval name, v; JSObject *vobj; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; @@ -5741,22 +5773,7 @@ xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return ok; } -static JSXML * -xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) -{ - JSObject *listobj; - JSXML *list; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - return list; -} - +/* XML and XMLList */ static JSBool xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, jsval *rval) @@ -5788,23 +5805,26 @@ xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, return GetProperty(cx, obj, name, rval); } -/* XML and XMLList */ 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 *kidobj; + JSObject *listobj, *kidobj; XML_METHOD_PROLOG; name = argv[0]; if (xml->xml_class == JSXML_CLASS_LIST) { /* ECMA-357 13.5.4.4 */ - list = xml_list_helper(cx, xml, rval); - if (!list) + 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); @@ -5828,12 +5848,7 @@ xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return !kid; } - /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ - if (!xml_child_helper(cx, obj, xml, name, rval)) - return JS_FALSE; - if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) - return JS_FALSE; - return JS_TRUE; + return xml_child_helper(cx, obj, xml, name, rval); } static JSBool @@ -5843,7 +5858,7 @@ xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSXML *xml, *parent; uint32 i, n; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; parent = xml->parent; if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); @@ -5874,16 +5889,20 @@ 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; - JSObject *kidobj; jsval v; XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) + 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) { @@ -5891,17 +5910,14 @@ xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 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); + ok = JS_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_comments(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); + 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)); @@ -5951,7 +5967,7 @@ xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, break; } XMLArrayCursorFinish(&cursor); - if (kid && !eq) + if (kid) return JS_FALSE; } else { if (!xml_equality(cx, obj, value, &eq)) @@ -6001,9 +6017,9 @@ xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval name, v; JSXMLQName *nameqn; jsid funid; + JSObject *listobj, *kidobj; JSBool ok; JSXMLArrayCursor cursor; - JSObject *kidobj; uint32 i, n; XML_METHOD_PROLOG; @@ -6013,12 +6029,15 @@ xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nameqn->object); - list = xml_list_helper(cx, xml, rval); - if (!list) + 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; @@ -6027,17 +6046,14 @@ xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_elements(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); + 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)); @@ -6160,10 +6176,10 @@ mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) namespace_mark_vector(cx, (JSXMLNamespace **)tmp->array.vector, - tmp->array.length); + 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"); + GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value", NULL); } static void @@ -6231,8 +6247,8 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); if (ns2 && ((ns2->prefix && ns->prefix) - ? js_EqualStrings(ns2->prefix, ns->prefix) - : js_EqualStrings(ns2->uri, ns->uri))) { + ? !js_CompareStrings(ns2->prefix, ns->prefix) + : !js_CompareStrings(ns2->uri, ns->uri))) { break; } } @@ -6257,7 +6273,7 @@ xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSTempRootedNSArray namespaces; JSBool ok; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; InitTempNSArray(cx, &namespaces); ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && @@ -6274,7 +6290,7 @@ xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval arg; uint32 i; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; @@ -6309,7 +6325,7 @@ xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval arg; uint32 i; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; @@ -6357,7 +6373,7 @@ xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { JSXML *xml; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; return JS_TRUE; } @@ -6368,7 +6384,7 @@ xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSXML *xml; JSObject *nameobj; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (!xml->name) { *rval = JSVAL_NULL; } else { @@ -6392,8 +6408,11 @@ xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSXMLNamespace *ns; JSObject *nsobj; - NON_LIST_XML_METHOD_PROLOG; - if (argc == 0 && !JSXML_HAS_NAME(xml)) { + 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; } @@ -6423,7 +6442,7 @@ xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, ns = NULL; for (i = 0, length = inScopeNSes.array.length; i < length; i++) { ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); - if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) + if (ns && ns->prefix && !js_CompareStrings(ns->prefix, prefix)) break; ns = NULL; } @@ -6455,8 +6474,8 @@ xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uint32 i, n; JSXMLNamespace *ns; - NON_LIST_XML_METHOD_PROLOG; - if (JSXML_HAS_VALUE(xml)) + 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. */ @@ -6520,7 +6539,7 @@ xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSXML *xml; JSString *str; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); if (!str) return JS_FALSE; @@ -6657,9 +6676,9 @@ xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, jsval name, v; JSXMLQName *nameqn; jsid funid; + JSObject *listobj, *kidobj; JSBool ok; JSXMLArrayCursor cursor; - JSObject *kidobj; uint32 i, n; XML_METHOD_PROLOG; @@ -6669,12 +6688,15 @@ xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nameqn->object); - list = xml_list_helper(cx, xml, rval); - if (!list) + 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; @@ -6683,17 +6705,14 @@ xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); + 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)); @@ -6711,7 +6730,7 @@ xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && (IS_STAR(nameqn->localName) || - js_EqualStrings(nameqn->localName, kid->name->localName))) { + !js_CompareStrings(nameqn->localName, kid->name->localName))) { ok = Append(cx, list, kid); if (!ok) break; @@ -6728,7 +6747,7 @@ xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, { JSXML *xml; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; @@ -6767,10 +6786,10 @@ namespace_full_match(const void *a, const void *b) const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsa->prefix && nsb->prefix && - !js_EqualStrings(nsa->prefix, nsb->prefix)) { + js_CompareStrings(nsa->prefix, nsb->prefix)) { return JS_FALSE; } - return js_EqualStrings(nsa->uri, nsb->uri); + return !js_CompareStrings(nsa->uri, nsb->uri); } static JSBool @@ -6817,7 +6836,7 @@ xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSObject *nsobj; JSXMLNamespace *ns; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; *rval = OBJECT_TO_JSVAL(obj); if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; @@ -6844,7 +6863,7 @@ xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSObject *nameobj; JSXMLQName *nameqn; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; *rval = OBJECT_TO_JSVAL(obj); if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; @@ -6900,9 +6919,6 @@ static JSBool xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!StartNonListXMLMethod(cx, &obj, argv)) - return JS_FALSE; - if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), &argv[0])) { return JS_FALSE; @@ -6921,7 +6937,7 @@ xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSXMLQName *nameqn; JSString *namestr; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; @@ -6955,7 +6971,7 @@ xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) uint32 i, n; JSXMLNamespace *ns; - NON_LIST_XML_METHOD_PROLOG; + XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; @@ -7034,7 +7050,7 @@ xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) nsarray = &nsowner->xml_namespaces; for (i = 0, n = nsarray->length; i < n; i++) { ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); - if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { + if (ns && !js_CompareStrings(ns->uri, nameqn->uri)) { nameqn->prefix = ns->prefix; return JS_TRUE; } @@ -7057,9 +7073,11 @@ xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSXMLNamespace *ns; jsval qnargv[2]; - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) + 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)) @@ -7098,32 +7116,33 @@ 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; - JSObject *kidobj; jsval v; XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) + 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); + ok = JS_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_text(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); + 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)); @@ -7174,7 +7193,7 @@ xml_toString_helper(JSContext *cx, JSXML *xml) return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); str = cx->runtime->emptyString; - js_EnterLocalRootScope(cx); + JS_EnterLocalRootScope(cx); XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class != JSXML_CLASS_COMMENT && @@ -7190,7 +7209,7 @@ xml_toString_helper(JSContext *cx, JSXML *xml) } } XMLArrayCursorFinish(&cursor); - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + JS_LeaveLocalRootScope(cx); return str; } @@ -7218,46 +7237,46 @@ xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } static JSFunctionSpec xml_methods[] = { - {"addNamespace", xml_addNamespace, 1,0,0}, - {"appendChild", xml_appendChild, 1,0,0}, - {js_attribute_str, xml_attribute, 1,0,0}, - {"attributes", xml_attributes, 0,0,0}, - {"child", xml_child, 1,0,0}, - {"childIndex", xml_childIndex, 0,0,0}, - {"children", xml_children, 0,0,0}, - {"comments", xml_comments, 0,0,0}, - {"contains", xml_contains, 1,0,0}, - {"copy", xml_copy, 0,0,0}, - {"descendants", xml_descendants, 1,0,0}, - {"elements", xml_elements, 1,0,0}, - {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, - {"hasComplexContent", xml_hasComplexContent, 1,0,0}, - {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, - {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, - {"insertChildAfter", xml_insertChildAfter, 2,0,0}, - {"insertChildBefore", xml_insertChildBefore, 2,0,0}, - {js_length_str, xml_length, 0,0,0}, - {js_localName_str, xml_localName, 0,0,0}, - {js_name_str, xml_name, 0,0,0}, - {js_namespace_str, xml_namespace, 1,0,0}, - {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, - {"nodeKind", xml_nodeKind, 0,0,0}, - {"normalize", xml_normalize, 0,0,0}, - {js_xml_parent_str, xml_parent, 0,0,0}, - {"processingInstructions",xml_processingInstructions,1,0,0}, - {"prependChild", xml_prependChild, 1,0,0}, - {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, - {"removeNamespace", xml_removeNamespace, 1,0,0}, - {"replace", xml_replace, 2,0,0}, - {"setChildren", xml_setChildren, 1,0,0}, - {"setLocalName", xml_setLocalName, 1,0,0}, - {"setName", xml_setName, 1,0,0}, - {"setNamespace", xml_setNamespace, 1,0,0}, - {js_text_str, xml_text, 0,0,0}, - {js_toString_str, xml_toString, 0,0,0}, - {js_toXMLString_str, xml_toXMLString, 0,0,0}, - {js_toSource_str, xml_toXMLString, 0,0,0}, - {js_valueOf_str, xml_valueOf, 0,0,0}, + {"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} }; @@ -7483,44 +7502,53 @@ js_NewXML(JSContext *cx, JSXMLClass xml_class) return xml; } -void -js_MarkXML(JSContext *cx, JSXML *xml) +static void +xml_mark_tail(JSContext *cx, JSXML *xml, void *arg) { - GC_MARK(cx, xml->object, "object"); - GC_MARK(cx, xml->name, "name"); - GC_MARK(cx, xml->parent, "xml_parent"); - - if (JSXML_HAS_VALUE(xml)) { - GC_MARK(cx, xml->xml_value, "value"); - return; - } - - xml_mark_vector(cx, - (JSXML **) xml->xml_kids.vector, - xml->xml_kids.length); - XMLArrayCursorMark(cx, xml->xml_kids.cursors); XMLArrayTrim(&xml->xml_kids); if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_target) - GC_MARK(cx, xml->xml_target, "target"); + JS_MarkGCThing(cx, xml->xml_target, "target", arg); if (xml->xml_targetprop) - GC_MARK(cx, xml->xml_targetprop, "targetprop"); + JS_MarkGCThing(cx, xml->xml_targetprop, "targetprop", arg); } else { namespace_mark_vector(cx, (JSXMLNamespace **) xml->xml_namespaces.vector, - xml->xml_namespaces.length); + 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); + 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) { @@ -7574,7 +7602,7 @@ js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) xml = js_NewXML(cx, xml_class); if (!xml) return NULL; - JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(xml), &tvr); obj = js_GetXMLObject(cx, xml); JS_POP_TEMP_ROOT(cx, &tvr); return obj; @@ -7587,7 +7615,7 @@ NewXMLObject(JSContext *cx, JSXML *xml) obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, xml)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; + cx->newborn[GCX_OBJECT] = NULL; return NULL; } METER(xml_stats.xmlobj); @@ -7661,6 +7689,7 @@ JSObject * js_InitXMLClass(JSContext *cx, JSObject *obj) { JSObject *proto, *pobj, *ctor; + JSFunctionSpec *fs; JSFunction *fun; JSXML *xml; JSProperty *prop; @@ -7673,11 +7702,27 @@ js_InitXMLClass(JSContext *cx, JSObject *obj) /* Define the XML class constructor and prototype. */ proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, - NULL, xml_methods, + 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; @@ -7701,7 +7746,7 @@ js_InitXMLClass(JSContext *cx, JSObject *obj) 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(VALUE_IS_FUNCTION(cx, cval)); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, cval)); /* Set default settings. */ ctor = JSVAL_TO_OBJECT(cval); @@ -7745,51 +7790,32 @@ js_GetFunctionNamespace(JSContext *cx, jsval *vp) /* An invalid URI, for internal use only, guaranteed not to collide. */ static const char anti_uri[] = "@mozilla.org/js/function"; - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ rt = cx->runtime; obj = rt->functionNamespaceObject; if (!obj) { - JS_LOCK_GC(rt); - obj = rt->functionNamespaceObject; - if (!obj) { - JS_UNLOCK_GC(rt); - atom = js_Atomize(cx, js_function_str, 8, 0); - JS_ASSERT(atom); - prefix = ATOM_TO_STRING(atom); + atom = js_Atomize(cx, js_function_str, 8, 0); + JS_ASSERT(atom); + prefix = ATOM_TO_STRING(atom); - /* - * Note that any race to atomize anti_uri here is resolved by - * the atom table code, such that at most one atom for anti_uri - * is created. We store in rt->atomState.lazy unconditionally, - * since we are guaranteed to overwrite either null or the same - * atom pointer. - */ - atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); - if (!atom) - return JS_FALSE; - rt->atomState.lazy.functionNamespaceURIAtom = atom; + 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; + 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); - - JS_LOCK_GC(rt); - if (!rt->functionNamespaceObject) - rt->functionNamespaceObject = obj; - else - obj = rt->functionNamespaceObject; - } - JS_UNLOCK_GC(rt); + /* + * 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; @@ -7875,7 +7901,7 @@ js_SetDefaultXMLNamespace(JSContext *cx, jsval v) return JS_FALSE; } } else { - JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); + JS_ASSERT(fp->fun && !(fp->fun->flags & JSFUN_HEAVYWEIGHT)); } fp->xmlNamespace = JSVAL_TO_OBJECT(v); return JS_TRUE; @@ -7924,7 +7950,7 @@ js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) * Reallocating str (because we know it has no other references) requires * purging any deflated string cached for it. */ - js_PurgeDeflatedStringCache(cx->runtime, str); + js_PurgeDeflatedStringCache(str); str->chars = chars; str->length = newlen; @@ -7970,69 +7996,36 @@ js_GetAnyName(JSContext *cx, jsval *vp) JSRuntime *rt; JSObject *obj; JSXMLQName *qn; - JSBool ok; - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ rt = cx->runtime; obj = rt->anynameObject; if (!obj) { - JS_LOCK_GC(rt); - obj = rt->anynameObject; - if (!obj) { - JS_UNLOCK_GC(rt); + qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + if (!qn) + return JS_FALSE; - /* - * Protect multiple newborns created below, in the do-while(0) - * loop used to ensure that we leave this local root scope. - */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - do { - qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, - ATOM_TO_STRING(rt->atomState.starAtom)); - if (!qn) { - ok = JS_FALSE; - break; - } - - obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; - break; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - /* - * Avoid entraining any Object.prototype found via cx's scope - * chain or global object. This loses the default toString, - * but no big deal: we want to customize toString anyway for - * clearer diagnostics. - */ - if (!JS_DefineFunction(cx, obj, js_toString_str, - anyname_toString, 0, 0)) { - ok = JS_FALSE; - break; - } - OBJ_SET_PROTO(cx, obj, NULL); - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - } while (0); - - js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); - if (!ok) - return JS_FALSE; - - JS_LOCK_GC(rt); - if (!rt->anynameObject) - rt->anynameObject = obj; - else - obj = rt->anynameObject; + obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return JS_FALSE; } - JS_UNLOCK_GC(rt); + 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; @@ -8089,54 +8082,6 @@ js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) return GetProperty(cx, obj, name, vp); } -JSBool -js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *target; - JSXML *xml; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJECT_IS_XML(cx, obj)); - - /* After this point, control must flow through label out: to exit. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); - - /* - * See comments before xml_lookupProperty about the need for the proto - * chain lookup. - */ - target = obj; - for (;;) { - ok = js_GetProperty(cx, target, id, vp); - if (!ok) - goto out; - if (VALUE_IS_FUNCTION(cx, *vp)) { - ok = JS_TRUE; - goto out; - } - target = OBJ_GET_PROTO(cx, target); - if (target == NULL) - break; - tvr.u.object = target; - } - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (HasSimpleContent(xml)) { - /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), - &tvr.u.object); - if (!ok) - goto out; - JS_ASSERT(tvr.u.object); - ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - JSBool js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) { @@ -8193,24 +8138,18 @@ js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) { JSBool ok, match; JSStackFrame *fp; - uint32 flags; JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; JSXML *xml, *list, *result, *kid; JSXMLArrayCursor cursor; - ok = js_EnterLocalRootScope(cx); + ok = JS_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; /* All control flow after this point must exit via label out or bad. */ - *vp = JSVAL_NULL; fp = cx->fp; - flags = fp->flags; - fp->flags = flags | JSFRAME_FILTERING; - scobj = js_GetScopeChain(cx, fp); + scobj = fp->scopeChain; withobj = NULL; - if (!scobj) - goto bad; xml = GetPrivate(cx, obj, "filtering predicate operator"); if (!xml) goto bad; @@ -8259,12 +8198,11 @@ js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) *vp = OBJECT_TO_JSVAL(resobj); out: - fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); if (withobj) { fp->scopeChain = scobj; JS_SetPrivate(cx, withobj, NULL); } - js_LeaveLocalRootScopeWithResult(cx, *vp); + JS_LeaveLocalRootScope(cx); return ok; bad: ok = JS_FALSE; diff --git a/lib/javascript/jsxml.h b/lib/javascript/jsxml.h index 71e591ac..294e66c9 100644 --- a/lib/javascript/jsxml.h +++ b/lib/javascript/jsxml.h @@ -64,7 +64,7 @@ js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared); extern void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns, void *arg); extern void js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); @@ -88,7 +88,7 @@ js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName); extern void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn, void *arg); extern void js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); @@ -131,7 +131,7 @@ struct JSXMLArrayCursor { /* * 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, @@ -203,7 +203,7 @@ extern JSXML * js_NewXML(JSContext *cx, JSXMLClass xml_class); extern void -js_MarkXML(JSContext *cx, JSXML *xml); +js_MarkXML(JSContext *cx, JSXML *xml, void *arg); extern void js_FinalizeXML(JSContext *cx, JSXML *xml); @@ -292,9 +292,6 @@ 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_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - extern JSBool js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); diff --git a/lib/javascript/liveconnect/nsCLiveconnectFactory.cpp b/lib/javascript/liveconnect/nsCLiveconnectFactory.cpp index 69f9e147..164e8634 100644 --- a/lib/javascript/liveconnect/nsCLiveconnectFactory.cpp +++ b/lib/javascript/liveconnect/nsCLiveconnectFactory.cpp @@ -54,8 +54,40 @@ #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID); static NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID); +/*+++++++++++++++++++++++++++++++++++++++++++++++++ + * NSGetFactory: + * Provides entry point to liveconnect dll. + +++++++++++++++++++++++++++++++++++++++++++++++++*/ + +extern "C" NS_EXPORT nsresult +NSGetFactory(nsISupports* serviceMgr, + const nsCID &aClass, + const char *aClassName, + const char *aContractID, + nsIFactory **aFactory) +{ + + if (!aClass.Equals(kCLiveconnectCID)) { + return NS_ERROR_FACTORY_NOT_LOADED; // XXX right error? + } + nsCLiveconnectFactory* factory = new nsCLiveconnectFactory(); + if (factory == NULL) + return NS_ERROR_OUT_OF_MEMORY; + factory->AddRef(); + *aFactory = factory; + return NS_OK; +} + +extern "C" NS_EXPORT PRBool +NSCanUnload(nsISupports* serviceMgr) +{ + return PR_FALSE; +} + extern "C" NS_EXPORT nsresult JSJ_RegisterLiveConnectFactory() { @@ -73,7 +105,27 @@ JSJ_RegisterLiveConnectFactory() return NS_ERROR_OUT_OF_MEMORY; } -NS_IMPL_ISUPPORTS1(nsCLiveconnectFactory, nsIFactory) +//////////////////////////////////////////////////////////////////////////// +// from nsISupports + +NS_METHOD +nsCLiveconnectFactory::QueryInterface(const nsIID& aIID, void** aInstancePtr) +{ + PR_ASSERT(NULL != aInstancePtr); + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + if (aIID.Equals(kIFactoryIID) || + aIID.Equals(kISupportsIID)) { + *aInstancePtr = (void*) this; + AddRef(); + return NS_OK; + } + return NS_NOINTERFACE; +} + +NS_IMPL_ADDREF(nsCLiveconnectFactory) +NS_IMPL_RELEASE(nsCLiveconnectFactory) //////////////////////////////////////////////////////////////////////////// // from nsIFactory: @@ -86,7 +138,8 @@ nsCLiveconnectFactory::CreateInstance(nsISupports *aOuter, REFNSIID aIID, void * *aResult = NULL; - NS_ENSURE_PROPER_AGGREGATION(aOuter, aIID); + if (aOuter && !aIID.Equals(kISupportsIID)) + return NS_ERROR_INVALID_ARG; nsCLiveconnect* liveconnect = new nsCLiveconnect(aOuter); if (liveconnect == NULL) diff --git a/lib/javascript/plify_jsdhash.sed b/lib/javascript/plify_jsdhash.sed index eff4901c..b58e7fde 100644 --- a/lib/javascript/plify_jsdhash.sed +++ b/lib/javascript/plify_jsdhash.sed @@ -28,6 +28,5 @@ s/JS_PUBLIC_API(\([^()]*\))/\1/ s/JS_DLL_CALLBACK/PR_CALLBACK/ s/JS_STATIC_DLL_CALLBACK/PR_STATIC_CALLBACK/ s/JS_NewDHashTable/PL_NewDHashTable/ -s/JS_ASSERT(0)/NS_NOTREACHED("0")/ -s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ +s/JS_ASSERT(\(.*\));/NS_ASSERTION(\1, "PLDHash precondition");/ s/JS_/PR_/g diff --git a/lib/javascript/rules.mk b/lib/javascript/rules.mk index 8d484db9..e04a8edf 100644 --- a/lib/javascript/rules.mk +++ b/lib/javascript/rules.mk @@ -177,7 +177,7 @@ endif +$(LOOP_OVER_DIRS) clean: - rm -rf $(OBJS) $(GARBAGE) + rm -rf $(OBJS) @cd fdlibm; $(MAKE) -f Makefile.ref clean clobber: