mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 04:27:41 +00:00
Merge tracemonkey to mozilla-central.
This commit is contained in:
commit
b55965f33d
@ -59,6 +59,10 @@ ifndef JS_DISABLE_SHELL
|
||||
DIRS += shell
|
||||
endif
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += jsapi-tests
|
||||
endif
|
||||
|
||||
MODULE = js
|
||||
LIBRARY_NAME = mozjs
|
||||
STATIC_LIBRARY_NAME = js_static
|
||||
@ -333,7 +337,7 @@ ifdef MOZ_SYNC_BUILD_FILES
|
||||
# belongs in the other as well. If the change isn't right for both
|
||||
# places, then that's something to bring up with the other developers.
|
||||
#
|
||||
# Some files are reasonable to diverge; for example,
|
||||
# Some files are reasonable to diverge; for example,
|
||||
# js/config/autoconf.mk.in doesn't need most of the stuff in
|
||||
# config/autoconf.mk.in.
|
||||
check-sync-dirs = $(PYTHON) $(srcdir)/config/check-sync-dirs.py
|
||||
@ -344,9 +348,8 @@ endif
|
||||
|
||||
ifdef ENABLE_JIT
|
||||
check::
|
||||
$(wildcard $(RUN_TEST_PROGRAM)) $(DIST)/bin/js$(BIN_SUFFIX) -j -e \
|
||||
"var gSrcdir='$(call normalizepath,$(srcdir))'; var gSkipSlowTests=true; var gReportSummary=false;" \
|
||||
$(srcdir)/trace-test.js
|
||||
$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) $(srcdir)/trace-test/trace-test.py \
|
||||
-x slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX)
|
||||
endif
|
||||
|
||||
DIST_GARBAGE = config.cache config.log config.status \
|
||||
|
@ -5215,6 +5215,7 @@ MAKEFILES="
|
||||
Makefile
|
||||
shell/Makefile
|
||||
lirasm/Makefile
|
||||
jsapi-tests/Makefile
|
||||
config/Makefile
|
||||
config/autoconf.mk
|
||||
config/mkdepend/Makefile
|
||||
|
@ -1,72 +1,78 @@
|
||||
/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */
|
||||
static struct {
|
||||
jsbytecode any_obj[34];
|
||||
jsbytecode obj_any[36];
|
||||
jsbytecode any_obj[36];
|
||||
jsbytecode obj_any[38];
|
||||
} equality_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*12*/ JSOP_CALL, 0, 1,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*18*/ JSOP_GOTO, 0, 4,
|
||||
/*21*/ JSOP_POP,
|
||||
/*22*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*25*/ JSOP_CALL, 0, 0,
|
||||
/*28*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*30*/ JSOP_SWAP,
|
||||
/*31*/ JSOP_POP,
|
||||
/*32*/ JSOP_IMACOP,
|
||||
/*33*/ JSOP_STOP,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_DUP,
|
||||
/*20*/ JSOP_GOTO, 0, 4,
|
||||
/*23*/ JSOP_POP,
|
||||
/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*27*/ JSOP_CALL, 0, 0,
|
||||
/*30*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*32*/ JSOP_SWAP,
|
||||
/*33*/ JSOP_POP,
|
||||
/*34*/ JSOP_IMACOP,
|
||||
/*35*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_SWAP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 9*/ JSOP_SWAP,
|
||||
/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*13*/ JSOP_CALL, 0, 1,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*19*/ JSOP_GOTO, 0, 4,
|
||||
/*22*/ JSOP_POP,
|
||||
/*23*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*26*/ JSOP_CALL, 0, 0,
|
||||
/*29*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*31*/ JSOP_SWAP,
|
||||
/*32*/ JSOP_POP,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*19*/ JSOP_POP,
|
||||
/*20*/ JSOP_DUP,
|
||||
/*21*/ JSOP_GOTO, 0, 4,
|
||||
/*24*/ JSOP_POP,
|
||||
/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*28*/ JSOP_CALL, 0, 0,
|
||||
/*31*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*33*/ JSOP_SWAP,
|
||||
/*34*/ JSOP_IMACOP,
|
||||
/*35*/ JSOP_STOP,
|
||||
/*34*/ JSOP_POP,
|
||||
/*35*/ JSOP_SWAP,
|
||||
/*36*/ JSOP_IMACOP,
|
||||
/*37*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode any_obj[34];
|
||||
jsbytecode any_obj[36];
|
||||
jsbytecode obj_any[38];
|
||||
jsbytecode obj_obj[68];
|
||||
jsbytecode obj_obj[72];
|
||||
} binary_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER),
|
||||
/*12*/ JSOP_CALL, 0, 1,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*18*/ JSOP_GOTO, 0, 4,
|
||||
/*21*/ JSOP_POP,
|
||||
/*22*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*25*/ JSOP_CALL, 0, 0,
|
||||
/*28*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*30*/ JSOP_SWAP,
|
||||
/*31*/ JSOP_POP,
|
||||
/*32*/ JSOP_IMACOP,
|
||||
/*33*/ JSOP_STOP,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_DUP,
|
||||
/*20*/ JSOP_GOTO, 0, 4,
|
||||
/*23*/ JSOP_POP,
|
||||
/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*27*/ JSOP_CALL, 0, 0,
|
||||
/*30*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*32*/ JSOP_SWAP,
|
||||
/*33*/ JSOP_POP,
|
||||
/*34*/ JSOP_IMACOP,
|
||||
/*35*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_SWAP,
|
||||
@ -96,80 +102,65 @@ static struct {
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 9*/ JSOP_SWAP,
|
||||
/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER),
|
||||
/*13*/ JSOP_CALL, 0, 1,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*19*/ JSOP_GOTO, 0, 4,
|
||||
/*22*/ JSOP_POP,
|
||||
/*23*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*26*/ JSOP_CALL, 0, 0,
|
||||
/*29*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*31*/ JSOP_SWAP,
|
||||
/*32*/ JSOP_POP,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*19*/ JSOP_POP,
|
||||
/*20*/ JSOP_DUP,
|
||||
/*21*/ JSOP_GOTO, 0, 4,
|
||||
/*24*/ JSOP_POP,
|
||||
/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*28*/ JSOP_CALL, 0, 0,
|
||||
/*31*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*33*/ JSOP_SWAP,
|
||||
/*34*/ JSOP_DUP,
|
||||
/*35*/ JSOP_DUP,
|
||||
/*36*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*39*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/*42*/ JSOP_SWAP,
|
||||
/*43*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER),
|
||||
/*46*/ JSOP_CALL, 0, 1,
|
||||
/*49*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*52*/ JSOP_GOTO, 0, 4,
|
||||
/*55*/ JSOP_POP,
|
||||
/*56*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*59*/ JSOP_CALL, 0, 0,
|
||||
/*62*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*64*/ JSOP_SWAP,
|
||||
/*65*/ JSOP_POP,
|
||||
/*66*/ JSOP_IMACOP,
|
||||
/*67*/ JSOP_STOP,
|
||||
/*34*/ JSOP_POP,
|
||||
/*35*/ JSOP_SWAP,
|
||||
/*36*/ JSOP_DUP,
|
||||
/*37*/ JSOP_DUP,
|
||||
/*38*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*41*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/*44*/ JSOP_SWAP,
|
||||
/*45*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER),
|
||||
/*48*/ JSOP_CALL, 0, 1,
|
||||
/*51*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*54*/ JSOP_POP,
|
||||
/*55*/ JSOP_DUP,
|
||||
/*56*/ JSOP_GOTO, 0, 4,
|
||||
/*59*/ JSOP_POP,
|
||||
/*60*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*63*/ JSOP_CALL, 0, 0,
|
||||
/*66*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*68*/ JSOP_SWAP,
|
||||
/*69*/ JSOP_POP,
|
||||
/*70*/ JSOP_IMACOP,
|
||||
/*71*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode any_obj[34];
|
||||
jsbytecode obj_any[36];
|
||||
jsbytecode obj_obj[68];
|
||||
jsbytecode any_obj[36];
|
||||
jsbytecode obj_any[38];
|
||||
jsbytecode obj_obj[72];
|
||||
} add_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*12*/ JSOP_CALL, 0, 1,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*18*/ JSOP_GOTO, 0, 4,
|
||||
/*21*/ JSOP_POP,
|
||||
/*22*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*25*/ JSOP_CALL, 0, 0,
|
||||
/*28*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*30*/ JSOP_SWAP,
|
||||
/*31*/ JSOP_POP,
|
||||
/*32*/ JSOP_ADD,
|
||||
/*33*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_SWAP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 9*/ JSOP_SWAP,
|
||||
/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*13*/ JSOP_CALL, 0, 1,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*19*/ JSOP_GOTO, 0, 4,
|
||||
/*22*/ JSOP_POP,
|
||||
/*23*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*26*/ JSOP_CALL, 0, 0,
|
||||
/*29*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*31*/ JSOP_SWAP,
|
||||
/*32*/ JSOP_POP,
|
||||
/*33*/ JSOP_SWAP,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_DUP,
|
||||
/*20*/ JSOP_GOTO, 0, 4,
|
||||
/*23*/ JSOP_POP,
|
||||
/*24*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*27*/ JSOP_CALL, 0, 0,
|
||||
/*30*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*32*/ JSOP_SWAP,
|
||||
/*33*/ JSOP_POP,
|
||||
/*34*/ JSOP_ADD,
|
||||
/*35*/ JSOP_STOP,
|
||||
},
|
||||
@ -178,108 +169,141 @@ static struct {
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 9*/ JSOP_SWAP,
|
||||
/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*13*/ JSOP_CALL, 0, 1,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*19*/ JSOP_GOTO, 0, 4,
|
||||
/*22*/ JSOP_POP,
|
||||
/*23*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*26*/ JSOP_CALL, 0, 0,
|
||||
/*29*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*31*/ JSOP_SWAP,
|
||||
/*32*/ JSOP_POP,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*19*/ JSOP_POP,
|
||||
/*20*/ JSOP_DUP,
|
||||
/*21*/ JSOP_GOTO, 0, 4,
|
||||
/*24*/ JSOP_POP,
|
||||
/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*28*/ JSOP_CALL, 0, 0,
|
||||
/*31*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*33*/ JSOP_SWAP,
|
||||
/*34*/ JSOP_DUP,
|
||||
/*35*/ JSOP_DUP,
|
||||
/*36*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*39*/ JSOP_IFPRIMTOP, 0, 16,
|
||||
/*42*/ JSOP_SWAP,
|
||||
/*43*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*46*/ JSOP_CALL, 0, 1,
|
||||
/*49*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/*52*/ JSOP_GOTO, 0, 4,
|
||||
/*55*/ JSOP_POP,
|
||||
/*56*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*59*/ JSOP_CALL, 0, 0,
|
||||
/*62*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*64*/ JSOP_SWAP,
|
||||
/*65*/ JSOP_POP,
|
||||
/*66*/ JSOP_ADD,
|
||||
/*67*/ JSOP_STOP,
|
||||
/*34*/ JSOP_POP,
|
||||
/*35*/ JSOP_SWAP,
|
||||
/*36*/ JSOP_ADD,
|
||||
/*37*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_SWAP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 6*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/* 9*/ JSOP_SWAP,
|
||||
/*10*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*13*/ JSOP_CALL, 0, 1,
|
||||
/*16*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*19*/ JSOP_POP,
|
||||
/*20*/ JSOP_DUP,
|
||||
/*21*/ JSOP_GOTO, 0, 4,
|
||||
/*24*/ JSOP_POP,
|
||||
/*25*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*28*/ JSOP_CALL, 0, 0,
|
||||
/*31*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*33*/ JSOP_SWAP,
|
||||
/*34*/ JSOP_POP,
|
||||
/*35*/ JSOP_SWAP,
|
||||
/*36*/ JSOP_DUP,
|
||||
/*37*/ JSOP_DUP,
|
||||
/*38*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*41*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/*44*/ JSOP_SWAP,
|
||||
/*45*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_VOID),
|
||||
/*48*/ JSOP_CALL, 0, 1,
|
||||
/*51*/ JSOP_IFPRIMTOP, 0, 17,
|
||||
/*54*/ JSOP_POP,
|
||||
/*55*/ JSOP_DUP,
|
||||
/*56*/ JSOP_GOTO, 0, 4,
|
||||
/*59*/ JSOP_POP,
|
||||
/*60*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*63*/ JSOP_CALL, 0, 0,
|
||||
/*66*/ JSOP_PRIMTOP, (JSTYPE_VOID),
|
||||
/*68*/ JSOP_SWAP,
|
||||
/*69*/ JSOP_POP,
|
||||
/*70*/ JSOP_ADD,
|
||||
/*71*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode sign[39];
|
||||
jsbytecode sign[41];
|
||||
} unary_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 21,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 23,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_NUMBER),
|
||||
/*12*/ JSOP_CALL, 0, 1,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 6,
|
||||
/*18*/ JSOP_GOTO, 0, 9,
|
||||
/*21*/ JSOP_SWAP,
|
||||
/*22*/ JSOP_POP,
|
||||
/*23*/ JSOP_GOTO, 0, 14,
|
||||
/*26*/ JSOP_POP,
|
||||
/*27*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*30*/ JSOP_CALL, 0, 0,
|
||||
/*33*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*35*/ JSOP_SWAP,
|
||||
/*36*/ JSOP_POP,
|
||||
/*37*/ JSOP_IMACOP,
|
||||
/*38*/ JSOP_STOP,
|
||||
/*15*/ JSOP_IFPRIMTOP, 0, 8,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_DUP,
|
||||
/*20*/ JSOP_GOTO, 0, 9,
|
||||
/*23*/ JSOP_SWAP,
|
||||
/*24*/ JSOP_POP,
|
||||
/*25*/ JSOP_GOTO, 0, 14,
|
||||
/*28*/ JSOP_POP,
|
||||
/*29*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/*32*/ JSOP_CALL, 0, 0,
|
||||
/*35*/ JSOP_PRIMTOP, (JSTYPE_NUMBER),
|
||||
/*37*/ JSOP_SWAP,
|
||||
/*38*/ JSOP_POP,
|
||||
/*39*/ JSOP_IMACOP,
|
||||
/*40*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode String[36];
|
||||
jsbytecode String[38];
|
||||
} call_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 13,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_CALL, 0, 0,
|
||||
/*12*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/*15*/ JSOP_GOTO, 0, 4,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*22*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING),
|
||||
/*25*/ JSOP_CALL, 0, 1,
|
||||
/*28*/ JSOP_PRIMTOP, (JSTYPE_STRING),
|
||||
/*30*/ JSOP_SWAP,
|
||||
/*31*/ JSOP_POP,
|
||||
/*32*/ JSOP_CALL, 0, 1,
|
||||
/*35*/ JSOP_STOP,
|
||||
/*12*/ JSOP_IFPRIMTOP, 0, 20,
|
||||
/*15*/ JSOP_POP,
|
||||
/*16*/ JSOP_DUP,
|
||||
/*17*/ JSOP_GOTO, 0, 4,
|
||||
/*20*/ JSOP_POP,
|
||||
/*21*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*24*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING),
|
||||
/*27*/ JSOP_CALL, 0, 1,
|
||||
/*30*/ JSOP_PRIMTOP, (JSTYPE_STRING),
|
||||
/*32*/ JSOP_SWAP,
|
||||
/*33*/ JSOP_POP,
|
||||
/*34*/ JSOP_CALL, 0, 1,
|
||||
/*37*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode String[36];
|
||||
jsbytecode String[38];
|
||||
} new_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_DUP,
|
||||
/* 2*/ JSOP_GETPROP, 0, COMMON_ATOM_INDEX(toString),
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 13,
|
||||
/* 5*/ JSOP_IFPRIMTOP, 0, 15,
|
||||
/* 8*/ JSOP_SWAP,
|
||||
/* 9*/ JSOP_CALL, 0, 0,
|
||||
/*12*/ JSOP_IFPRIMTOP, 0, 18,
|
||||
/*15*/ JSOP_GOTO, 0, 4,
|
||||
/*18*/ JSOP_POP,
|
||||
/*19*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*22*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING),
|
||||
/*25*/ JSOP_CALL, 0, 1,
|
||||
/*28*/ JSOP_PRIMTOP, (JSTYPE_STRING),
|
||||
/*30*/ JSOP_SWAP,
|
||||
/*31*/ JSOP_POP,
|
||||
/*32*/ JSOP_NEW, 0, 1,
|
||||
/*35*/ JSOP_STOP,
|
||||
/*12*/ JSOP_IFPRIMTOP, 0, 20,
|
||||
/*15*/ JSOP_POP,
|
||||
/*16*/ JSOP_DUP,
|
||||
/*17*/ JSOP_GOTO, 0, 4,
|
||||
/*20*/ JSOP_POP,
|
||||
/*21*/ JSOP_CALLPROP, 0, COMMON_ATOM_INDEX(valueOf),
|
||||
/*24*/ JSOP_STRING, 0, COMMON_TYPE_ATOM_INDEX(JSTYPE_STRING),
|
||||
/*27*/ JSOP_CALL, 0, 1,
|
||||
/*30*/ JSOP_PRIMTOP, (JSTYPE_STRING),
|
||||
/*32*/ JSOP_SWAP,
|
||||
/*33*/ JSOP_POP,
|
||||
/*34*/ JSOP_NEW, 0, 1,
|
||||
/*37*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
@ -627,66 +651,6 @@ static struct {
|
||||
/*11*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode setprop[15];
|
||||
jsbytecode setelem[15];
|
||||
} setelem_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_PICK, 3,
|
||||
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetProperty) & 0xff00) >> 8, ((JSBUILTIN_SetProperty) & 0xff),
|
||||
/* 6*/ JSOP_PICK, 4,
|
||||
/* 8*/ JSOP_PICK, 4,
|
||||
/*10*/ JSOP_CALL, 0, 2,
|
||||
/*13*/ JSOP_POP,
|
||||
/*14*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_DUP,
|
||||
/* 1*/ JSOP_PICK, 3,
|
||||
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
|
||||
/* 6*/ JSOP_PICK, 4,
|
||||
/* 8*/ JSOP_PICK, 4,
|
||||
/*10*/ JSOP_CALL, 0, 2,
|
||||
/*13*/ JSOP_POP,
|
||||
/*14*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode initprop[15];
|
||||
jsbytecode initelem[15];
|
||||
} initelem_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_PICK, 2,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetProperty) & 0xff00) >> 8, ((JSBUILTIN_SetProperty) & 0xff),
|
||||
/* 6*/ JSOP_PICK, 4,
|
||||
/* 8*/ JSOP_PICK, 4,
|
||||
/*10*/ JSOP_CALL, 0, 2,
|
||||
/*13*/ JSOP_POP,
|
||||
/*14*/ JSOP_STOP,
|
||||
},
|
||||
{
|
||||
/* 0*/ JSOP_PICK, 2,
|
||||
/* 2*/ JSOP_DUP,
|
||||
/* 3*/ JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
|
||||
/* 6*/ JSOP_PICK, 4,
|
||||
/* 8*/ JSOP_PICK, 4,
|
||||
/*10*/ JSOP_CALL, 0, 2,
|
||||
/*13*/ JSOP_POP,
|
||||
/*14*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
static struct {
|
||||
jsbytecode instanceof[9];
|
||||
} instanceof_imacros = {
|
||||
{
|
||||
/* 0*/ JSOP_CALLBUILTIN, ((JSBUILTIN_HasInstance) & 0xff00) >> 8, ((JSBUILTIN_HasInstance) & 0xff),
|
||||
/* 3*/ JSOP_PICK, 2,
|
||||
/* 5*/ JSOP_CALL, 0, 1,
|
||||
/* 8*/ JSOP_STOP,
|
||||
},
|
||||
};
|
||||
uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_NOP */
|
||||
0, /* JSOP_PUSH */
|
||||
@ -744,7 +708,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_GETPROP */
|
||||
0, /* JSOP_SETPROP */
|
||||
0, /* JSOP_GETELEM */
|
||||
2, /* JSOP_SETELEM */
|
||||
0, /* JSOP_SETELEM */
|
||||
0, /* JSOP_CALLNAME */
|
||||
3, /* JSOP_CALL */
|
||||
0, /* JSOP_NAME */
|
||||
@ -780,7 +744,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_NEWINIT */
|
||||
0, /* JSOP_ENDINIT */
|
||||
0, /* JSOP_INITPROP */
|
||||
2, /* JSOP_INITELEM */
|
||||
0, /* JSOP_INITELEM */
|
||||
0, /* JSOP_DEFSHARP */
|
||||
0, /* JSOP_USESHARP */
|
||||
0, /* JSOP_INCARG */
|
||||
@ -800,7 +764,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_SETNAME */
|
||||
0, /* JSOP_THROW */
|
||||
0, /* JSOP_IN */
|
||||
1, /* JSOP_INSTANCEOF */
|
||||
0, /* JSOP_INSTANCEOF */
|
||||
0, /* JSOP_DEBUGGER */
|
||||
0, /* JSOP_GOSUB */
|
||||
0, /* JSOP_RETSUB */
|
||||
@ -943,28 +907,25 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
|| x == JSOP_MOD \
|
||||
|| x == JSOP_NEG \
|
||||
|| x == JSOP_POS \
|
||||
|| x == JSOP_SETELEM \
|
||||
|| x == JSOP_CALL \
|
||||
|| x == JSOP_ITER \
|
||||
|| x == JSOP_NEXTITER \
|
||||
|| x == JSOP_APPLY \
|
||||
|| x == JSOP_NEW \
|
||||
|| x == JSOP_INITELEM \
|
||||
|| x == JSOP_INSTANCEOF \
|
||||
)
|
||||
jsbytecode*
|
||||
js_GetImacroStart(jsbytecode* pc) {
|
||||
if (size_t(pc - equality_imacros.any_obj) < 34) return equality_imacros.any_obj;
|
||||
if (size_t(pc - equality_imacros.obj_any) < 36) return equality_imacros.obj_any;
|
||||
if (size_t(pc - binary_imacros.any_obj) < 34) return binary_imacros.any_obj;
|
||||
if (size_t(pc - equality_imacros.any_obj) < 36) return equality_imacros.any_obj;
|
||||
if (size_t(pc - equality_imacros.obj_any) < 38) return equality_imacros.obj_any;
|
||||
if (size_t(pc - binary_imacros.any_obj) < 36) return binary_imacros.any_obj;
|
||||
if (size_t(pc - binary_imacros.obj_any) < 38) return binary_imacros.obj_any;
|
||||
if (size_t(pc - binary_imacros.obj_obj) < 68) return binary_imacros.obj_obj;
|
||||
if (size_t(pc - add_imacros.any_obj) < 34) return add_imacros.any_obj;
|
||||
if (size_t(pc - add_imacros.obj_any) < 36) return add_imacros.obj_any;
|
||||
if (size_t(pc - add_imacros.obj_obj) < 68) return add_imacros.obj_obj;
|
||||
if (size_t(pc - unary_imacros.sign) < 39) return unary_imacros.sign;
|
||||
if (size_t(pc - call_imacros.String) < 36) return call_imacros.String;
|
||||
if (size_t(pc - new_imacros.String) < 36) return new_imacros.String;
|
||||
if (size_t(pc - binary_imacros.obj_obj) < 72) return binary_imacros.obj_obj;
|
||||
if (size_t(pc - add_imacros.any_obj) < 36) return add_imacros.any_obj;
|
||||
if (size_t(pc - add_imacros.obj_any) < 38) return add_imacros.obj_any;
|
||||
if (size_t(pc - add_imacros.obj_obj) < 72) return add_imacros.obj_obj;
|
||||
if (size_t(pc - unary_imacros.sign) < 41) return unary_imacros.sign;
|
||||
if (size_t(pc - call_imacros.String) < 38) return call_imacros.String;
|
||||
if (size_t(pc - new_imacros.String) < 38) return new_imacros.String;
|
||||
if (size_t(pc - apply_imacros.apply0) < 8) return apply_imacros.apply0;
|
||||
if (size_t(pc - apply_imacros.apply1) < 12) return apply_imacros.apply1;
|
||||
if (size_t(pc - apply_imacros.apply2) < 16) return apply_imacros.apply2;
|
||||
@ -989,10 +950,5 @@ js_GetImacroStart(jsbytecode* pc) {
|
||||
if (size_t(pc - iter_imacros.for_each_native) < 10) return iter_imacros.for_each_native;
|
||||
if (size_t(pc - nextiter_imacros.custom_iter_next) < 12) return nextiter_imacros.custom_iter_next;
|
||||
if (size_t(pc - nextiter_imacros.native_iter_next) < 12) return nextiter_imacros.native_iter_next;
|
||||
if (size_t(pc - setelem_imacros.setprop) < 15) return setelem_imacros.setprop;
|
||||
if (size_t(pc - setelem_imacros.setelem) < 15) return setelem_imacros.setelem;
|
||||
if (size_t(pc - initelem_imacros.initprop) < 15) return initelem_imacros.initprop;
|
||||
if (size_t(pc - initelem_imacros.initelem) < 15) return initelem_imacros.initelem;
|
||||
if (size_t(pc - instanceof_imacros.instanceof) < 9) return instanceof_imacros.instanceof;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -47,6 +47,8 @@
|
||||
string void # any obj valueOf obj "void"
|
||||
call 1 # any obj rval
|
||||
ifprimtop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
1: pop # any obj obj
|
||||
2: callprop toString # any obj toString obj
|
||||
@ -68,6 +70,8 @@
|
||||
string void # any obj valueOf obj "void"
|
||||
call 1 # any obj lval
|
||||
ifprimtop 3 # any obj lval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
1: pop # any obj obj
|
||||
2: callprop toString # any obj toString obj
|
||||
@ -93,6 +97,8 @@
|
||||
string number # any obj valueOf obj "number"
|
||||
call 1 # any obj rval
|
||||
ifprimtop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
1: pop # any obj obj
|
||||
2: callprop toString # any obj toString obj
|
||||
@ -138,6 +144,8 @@
|
||||
string number # obj2 obj1 valueOf obj1 "number"
|
||||
call 1 # obj2 obj1 lval
|
||||
ifprimtop 3 # obj2 obj1 lval
|
||||
pop # obj2 obj1
|
||||
dup # obj2 obj1 obj1
|
||||
goto 2
|
||||
1: pop # obj2 obj1 obj1
|
||||
2: callprop toString # obj2 obj1 toString obj1
|
||||
@ -154,6 +162,8 @@
|
||||
string number # lval obj valueOf obj "number"
|
||||
call 1 # lval obj rval
|
||||
ifprimtop 6 # lval obj rval
|
||||
pop # lval obj
|
||||
dup # lval obj obj
|
||||
goto 5
|
||||
4: pop # lval obj obj
|
||||
5: callprop toString # lval obj toString obj
|
||||
@ -175,9 +185,11 @@
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
swap # any obj valueOf obj
|
||||
string void # lval obj valueOf obj "void"
|
||||
string void # any obj valueOf obj "void"
|
||||
call 1 # any obj rval
|
||||
ifprimtop 3 # any obj rval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
1: pop # any obj obj
|
||||
2: callprop toString # any obj toString obj
|
||||
@ -196,9 +208,11 @@
|
||||
getprop valueOf # any obj obj valueOf
|
||||
ifprimtop 1 # any obj obj valueOf
|
||||
swap # any obj valueOf obj
|
||||
string void # lval obj valueOf obj "void"
|
||||
string void # any obj valueOf obj "void"
|
||||
call 1 # any obj lval
|
||||
ifprimtop 3 # any obj lval
|
||||
pop # any obj
|
||||
dup # any obj obj
|
||||
goto 2
|
||||
1: pop # any obj obj
|
||||
2: callprop toString # any obj toString obj
|
||||
@ -218,18 +232,20 @@
|
||||
getprop valueOf # obj2 obj1 obj1 valueOf
|
||||
ifprimtop 1 # obj2 obj1 obj1 valueOf
|
||||
swap # obj2 obj1 valueOf obj1
|
||||
string void # lval obj valueOf obj "void"
|
||||
string void # obj2 obj1 valueOf obj1 "void"
|
||||
call 1 # obj2 obj1 lval
|
||||
ifprimtop 3 # obj2 obj1 lval
|
||||
pop # obj2 obj1
|
||||
dup # obj2 obj1 obj1
|
||||
goto 2
|
||||
1: pop # obj2 obj1 obj1
|
||||
2: callprop toString # obj2 obj toString obj1
|
||||
call 0 # obj2 obj lval
|
||||
primtop (JSTYPE_VOID) # obj2 obj lval
|
||||
2: callprop toString # obj2 obj1 toString obj1
|
||||
call 0 # obj2 obj1 lval
|
||||
primtop (JSTYPE_VOID) # obj2 obj1 lval
|
||||
3: swap # obj2 lval obj1
|
||||
pop # obj2 lval
|
||||
swap # lval obj2
|
||||
dup # lval obj1 obj1
|
||||
dup # lval obj obj
|
||||
dup # lval obj obj obj
|
||||
getprop valueOf # lval obj obj valueOf
|
||||
ifprimtop 4 # lval obj obj valueOf
|
||||
@ -237,6 +253,8 @@
|
||||
string void # lval obj valueOf obj "void"
|
||||
call 1 # lval obj rval
|
||||
ifprimtop 6 # lval obj rval
|
||||
pop # lval obj
|
||||
dup # lval obj obj
|
||||
goto 5
|
||||
4: pop # lval obj obj
|
||||
5: callprop toString # lval obj toString obj
|
||||
@ -261,6 +279,8 @@
|
||||
string number # obj valueOf obj "number"
|
||||
call 1 # obj lval
|
||||
ifprimtop 1 # obj lval
|
||||
pop # obj
|
||||
dup # obj obj
|
||||
goto 3
|
||||
1: swap # lval obj
|
||||
pop # lval
|
||||
@ -287,6 +307,8 @@
|
||||
swap # String this obj toString obj
|
||||
call 0 # String this obj rval
|
||||
ifprimtop 3 # String this obj rval
|
||||
pop # String this obj
|
||||
dup # String this obj obj
|
||||
goto 2
|
||||
1: pop # String this obj obj
|
||||
2: callprop valueOf # String this obj valueOf obj
|
||||
@ -311,6 +333,8 @@
|
||||
swap # String this obj toString obj
|
||||
call 0 # String this obj rval
|
||||
ifprimtop 3 # String this obj rval
|
||||
pop # String this obj
|
||||
dup # String this obj obj
|
||||
goto 2
|
||||
1: pop # String this obj obj
|
||||
2: callprop valueOf # String this obj valueOf obj
|
||||
@ -672,66 +696,3 @@
|
||||
.end
|
||||
|
||||
.end nextiter
|
||||
|
||||
.igroup setelem JSOP_SETELEM
|
||||
|
||||
.imacro setprop # obj name val
|
||||
dup # obj name val val
|
||||
pick 3 # name val val obj
|
||||
callbuiltin (JSBUILTIN_SetProperty) # name val val fun obj
|
||||
pick 4 # val val fun obj name
|
||||
pick 4 # val fun obj name val
|
||||
call 2 # val junk
|
||||
pop # val
|
||||
stop
|
||||
.end
|
||||
|
||||
.imacro setelem # obj i val
|
||||
dup # obj i val val
|
||||
pick 3 # i val val obj
|
||||
callbuiltin (JSBUILTIN_SetElement) # i val val fun obj
|
||||
pick 4 # val val fun obj i
|
||||
pick 4 # val fun obj i val
|
||||
call 2 # val junk
|
||||
pop # val
|
||||
stop
|
||||
.end
|
||||
|
||||
.end setelem
|
||||
|
||||
.igroup initelem JSOP_INITELEM
|
||||
|
||||
.imacro initprop # obj i val
|
||||
pick 2 # i val obj
|
||||
dup # i val obj obj
|
||||
callbuiltin (JSBUILTIN_SetProperty) # i val obj fun obj
|
||||
pick 4 # val obj fun obj i
|
||||
pick 4 # obj fun obj i val
|
||||
call 2 # obj junk
|
||||
pop # obj
|
||||
stop
|
||||
.end
|
||||
|
||||
.imacro initelem # obj i val
|
||||
pick 2 # i val obj
|
||||
dup # i val obj obj
|
||||
callbuiltin (JSBUILTIN_SetElement) # i val obj fun obj
|
||||
pick 4 # val obj fun obj i
|
||||
pick 4 # obj fun obj i val
|
||||
call 2 # obj junk
|
||||
pop # obj
|
||||
stop
|
||||
.end
|
||||
|
||||
.end initelem
|
||||
|
||||
.igroup instanceof JSOP_INSTANCEOF
|
||||
|
||||
.imacro instanceof # val obj
|
||||
callbuiltin (JSBUILTIN_HasInstance) # val fun obj
|
||||
pick 2 # fun obj val
|
||||
call 1 # bool
|
||||
stop
|
||||
.end
|
||||
|
||||
.end instanceof
|
||||
|
@ -54,3 +54,9 @@ JITSTAT(returnLoopExits)
|
||||
JITSTAT(mergedLoopExits)
|
||||
JITSTAT(noCompatInnerTrees)
|
||||
JITSTAT(blacklisted)
|
||||
JITSTAT(archIsIA32)
|
||||
JITSTAT(archIsAMD64)
|
||||
JITSTAT(archIs64BIT)
|
||||
JITSTAT(archIsARM)
|
||||
JITSTAT(archIsSPARC)
|
||||
JITSTAT(archIsPPC)
|
||||
|
@ -301,7 +301,7 @@ MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty ar
|
||||
MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements")
|
||||
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_SYNTAXERR, "invalid delete operand")
|
||||
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
|
||||
MSG_DEF(JSMSG_NULL_OR_UNDEFINED, 222, 2, JSEXN_TYPEERR, "{0} is {1}")
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 222, 2, JSEXN_TYPEERR, "{0} is {1}")
|
||||
MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK, 223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
|
||||
MSG_DEF(JSMSG_BAD_OBJECT_INIT, 224, 0, JSEXN_SYNTAXERR, "invalid object initializer")
|
||||
MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS, 225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
|
||||
|
70
js/src/jsapi-tests/Makefile.in
Normal file
70
js/src/jsapi-tests/Makefile.in
Normal file
@ -0,0 +1,70 @@
|
||||
# -*- Mode: makefile -*-
|
||||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Spidermonkey build system.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# The Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2009
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Jason Orendorff
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
PROGRAM = jsapi-tests$(BIN_SUFFIX)
|
||||
CPPSRCS = \
|
||||
tests.cpp \
|
||||
selfTest.cpp \
|
||||
testPropCache.cpp \
|
||||
testXDR.cpp \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += -DEXPORT_JS_API
|
||||
|
||||
LIBS = $(NSPR_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
|
||||
|
||||
LOCAL_INCLUDES += -I$(topsrcdir) -I..
|
||||
|
||||
ifdef _MSC_VER
|
||||
ifdef WINCE
|
||||
WIN32_EXE_LDFLAGS += -ENTRY:mainACRTStartup
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
check::
|
||||
$(wildcard $(RUN_TEST_PROGRAM)) $(DIST)/bin/jsapi-tests$(BIN_SUFFIX)
|
22
js/src/jsapi-tests/selfTest.cpp
Normal file
22
js/src/jsapi-tests/selfTest.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "tests.h"
|
||||
|
||||
BEGIN_TEST(selfTest_NaNsAreSame)
|
||||
{
|
||||
jsvalRoot v1(cx), v2(cx);
|
||||
EVAL("0/0", v1.addr()); // NaN
|
||||
CHECK_SAME(v1, v1);
|
||||
|
||||
EVAL("Math.sin('no')", v2.addr()); // also NaN
|
||||
CHECK_SAME(v1, v2);
|
||||
return true;
|
||||
}
|
||||
END_TEST(selfTest_NaNsAreSame)
|
||||
|
||||
BEGIN_TEST(selfTest_negativeZeroIsNotTheSameAsZero)
|
||||
{
|
||||
jsvalRoot negativeZero(cx);
|
||||
EVAL("-0", negativeZero.addr());
|
||||
CHECK(!sameValue(negativeZero, JSVAL_ZERO));
|
||||
return true;
|
||||
}
|
||||
END_TEST(selfTest_negativeZeroIsNotTheSameAsZero)
|
32
js/src/jsapi-tests/testPropCache.cpp
Normal file
32
js/src/jsapi-tests/testPropCache.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "tests.h"
|
||||
|
||||
static int g_counter;
|
||||
|
||||
static JSBool
|
||||
CounterAdd(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
{
|
||||
g_counter++;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSClass CounterClass = {
|
||||
"Counter", /* name */
|
||||
0, /* flags */
|
||||
CounterAdd, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
BEGIN_TEST(testPropCache_bug505798)
|
||||
{
|
||||
g_counter = 0;
|
||||
EXEC("var x = {};");
|
||||
CHECK(JS_DefineObject(cx, global, "y", &CounterClass, NULL, JSPROP_ENUMERATE));
|
||||
EXEC("var arr = [x, y];\n"
|
||||
"for (var i = 0; i < arr.length; i++)\n"
|
||||
" arr[i].p = 1;\n");
|
||||
knownFail = true;
|
||||
CHECK(g_counter == 1);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testPropCache_bug505798)
|
52
js/src/jsapi-tests/testXDR.cpp
Normal file
52
js/src/jsapi-tests/testXDR.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "tests.h"
|
||||
#include "jsxdrapi.h"
|
||||
|
||||
BEGIN_TEST(testXDR_bug506491)
|
||||
{
|
||||
const char *s =
|
||||
"function makeClosure(s, name, value) {\n"
|
||||
" eval(s);\n"
|
||||
" return let (n = name, v = value) function () { return String(v); };\n"
|
||||
"}\n"
|
||||
"var f = makeClosure('0;', 'status', 'ok');\n";
|
||||
|
||||
// compile
|
||||
JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
|
||||
CHECK(script);
|
||||
JSObject *scrobj = JS_NewScriptObject(cx, script);
|
||||
CHECK(scrobj);
|
||||
jsvalRoot v(cx, OBJECT_TO_JSVAL(scrobj));
|
||||
|
||||
// freeze
|
||||
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
|
||||
CHECK(w);
|
||||
CHECK(JS_XDRScript(w, &script));
|
||||
uint32 nbytes;
|
||||
void *p = JS_XDRMemGetData(w, &nbytes);
|
||||
CHECK(p);
|
||||
void *frozen = malloc(nbytes);
|
||||
CHECK(frozen);
|
||||
memcpy(frozen, p, nbytes);
|
||||
JS_XDRDestroy(w);
|
||||
|
||||
// thaw
|
||||
script = NULL;
|
||||
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
|
||||
JS_XDRMemSetData(r, frozen, nbytes);
|
||||
CHECK(JS_XDRScript(r, &script));
|
||||
JS_XDRDestroy(r); // this frees `frozen`
|
||||
|
||||
// execute
|
||||
jsvalRoot v2(cx);
|
||||
CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
|
||||
|
||||
// try to break the Block object that is the parent of f
|
||||
JS_GC(cx);
|
||||
|
||||
// confirm
|
||||
EVAL("f() === 'ok';\n", v2.addr());
|
||||
jsvalRoot trueval(cx, JSVAL_TRUE);
|
||||
CHECK_SAME(v2, trueval);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testXDR_bug506491)
|
79
js/src/jsapi-tests/tests.cpp
Normal file
79
js/src/jsapi-tests/tests.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- 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 JSAPI tests.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Jason Orendorff
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient 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 "tests.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
JSAPITest *JSAPITest::list;
|
||||
|
||||
int main()
|
||||
{
|
||||
int failures = 0;
|
||||
|
||||
for (JSAPITest *test = JSAPITest::list; test; test = test->next) {
|
||||
string name = test->name();
|
||||
|
||||
cout << name << endl;
|
||||
if (!test->init()) {
|
||||
cout << "TEST-UNEXPECTED-FAIL | " << name << " | Failed to initialize." << endl;
|
||||
failures++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test->run()) {
|
||||
cout << "TEST-PASS | " << name << " | ok" << endl;
|
||||
} else {
|
||||
cout << (test->knownFail ? "TEST-KNOWN-FAIL" : "TEST-UNEXPECTED-FAIL")
|
||||
<< " | " << name << " | " << test->messages() << endl;
|
||||
if (!test->knownFail)
|
||||
failures++;
|
||||
}
|
||||
test->uninit();
|
||||
}
|
||||
|
||||
if (failures) {
|
||||
cout << "\n" << failures << " unexpected failure" << (failures == 1 ? "." : "s.") << endl;
|
||||
return 1;
|
||||
}
|
||||
cout << "\nPassed." << endl;
|
||||
return 0;
|
||||
}
|
266
js/src/jsapi-tests/tests.h
Normal file
266
js/src/jsapi-tests/tests.h
Normal file
@ -0,0 +1,266 @@
|
||||
/* -*- 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 JSAPI tests.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Jason Orendorff
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
class jsvalRoot
|
||||
{
|
||||
public:
|
||||
explicit jsvalRoot(JSContext *context, jsval value = JSVAL_NULL)
|
||||
: cx(context), v(value)
|
||||
{
|
||||
if (!JS_AddRoot(cx, &v)) {
|
||||
fprintf(stderr, "Out of memory in jsvalRoot constructor, aborting\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
~jsvalRoot() { JS_RemoveRoot(cx, &v); }
|
||||
|
||||
operator jsval() const { return v; }
|
||||
|
||||
jsvalRoot & operator=(jsval value) {
|
||||
v = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
jsval * addr() { return &v; }
|
||||
|
||||
private:
|
||||
JSContext *cx;
|
||||
jsval v;
|
||||
};
|
||||
|
||||
class JSAPITest
|
||||
{
|
||||
public:
|
||||
static JSAPITest *list;
|
||||
JSAPITest *next;
|
||||
|
||||
JSRuntime *rt;
|
||||
JSContext *cx;
|
||||
JSObject *global;
|
||||
bool knownFail;
|
||||
std::string msgs;
|
||||
|
||||
JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false) {
|
||||
next = list;
|
||||
list = this;
|
||||
}
|
||||
|
||||
virtual ~JSAPITest() { uninit(); }
|
||||
|
||||
virtual bool init() {
|
||||
rt = createRuntime();
|
||||
if (!rt)
|
||||
return false;
|
||||
cx = createContext();
|
||||
if (!cx)
|
||||
return false;
|
||||
JS_BeginRequest(cx);
|
||||
global = createGlobal();
|
||||
return global != NULL;
|
||||
}
|
||||
|
||||
virtual void uninit() {
|
||||
if (cx) {
|
||||
JS_EndRequest(cx);
|
||||
JS_DestroyContext(cx);
|
||||
cx = NULL;
|
||||
}
|
||||
if (rt) {
|
||||
JS_DestroyRuntime(rt);
|
||||
rt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char * name() = 0;
|
||||
virtual bool run() = 0;
|
||||
|
||||
bool isNegativeZero(jsval v) {
|
||||
if (!JSVAL_IS_DOUBLE(v))
|
||||
return false;
|
||||
union {
|
||||
uint64 u64;
|
||||
jsdouble d;
|
||||
} pun;
|
||||
pun.d = *JSVAL_TO_DOUBLE(v);
|
||||
return pun.d == jsdouble(-0.0) && pun.u64 != uint64(0);
|
||||
}
|
||||
|
||||
bool isNaN(jsval v) {
|
||||
if (!JSVAL_IS_DOUBLE(v))
|
||||
return false;
|
||||
jsdouble d = *JSVAL_TO_DOUBLE(v);
|
||||
return d != d;
|
||||
}
|
||||
|
||||
bool sameValue(jsval v1, jsval v2) {
|
||||
if ((isNegativeZero(v1) && v2 == JSVAL_ZERO) ||
|
||||
(isNegativeZero(v2) && v1 == JSVAL_ZERO)) {
|
||||
return false;
|
||||
}
|
||||
if (isNaN(v1) && isNaN(v2))
|
||||
return true;
|
||||
return JS_StrictlyEqual(cx, v1, v2);
|
||||
}
|
||||
|
||||
#define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false)
|
||||
|
||||
bool exec(const char *bytes, const char *filename, int lineno) {
|
||||
jsvalRoot v(cx);
|
||||
return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, v.addr()) ||
|
||||
fail(bytes, filename, lineno);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
|
||||
|
||||
bool evaluate(const char *bytes, const char *filename, int lineno, jsval *vp) {
|
||||
return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, vp) ||
|
||||
fail(bytes, filename, lineno);
|
||||
}
|
||||
|
||||
std::string toSource(jsval v) {
|
||||
JSString *str = JS_ValueToSource(cx, v);
|
||||
if (str)
|
||||
return std::string(JS_GetStringBytes(str));
|
||||
JS_ClearPendingException(cx);
|
||||
return std::string("<<error converting value to string>>");
|
||||
}
|
||||
|
||||
#define CHECK_SAME(actual, expected) \
|
||||
do { \
|
||||
if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
|
||||
return false; \
|
||||
} while (false)
|
||||
|
||||
bool checkSame(jsval actual, jsval expected,
|
||||
const char *actualExpr, const char *expectedExpr,
|
||||
const char *filename, int lineno) {
|
||||
return sameValue(actual, expected) ||
|
||||
fail(std::string("CHECK_SAME failed: expected sameValue(") +
|
||||
actualExpr + ", " + expectedExpr + "), got !sameValue(" +
|
||||
toSource(actual) + ", " + toSource(expected) + ")", filename, lineno);
|
||||
}
|
||||
|
||||
#define CHECK(expr) \
|
||||
do { \
|
||||
if (!(expr)) \
|
||||
return fail("CHECK failed: " #expr, __FILE__, __LINE__); \
|
||||
} while (false)
|
||||
|
||||
bool fail(std::string msg = std::string(), const char *filename = "-", int lineno = 0) {
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
jsvalRoot v(cx);
|
||||
JS_GetPendingException(cx, v.addr());
|
||||
JS_ClearPendingException(cx);
|
||||
JSString *s = JS_ValueToString(cx, v);
|
||||
if (s)
|
||||
msg += JS_GetStringBytes(s);
|
||||
}
|
||||
fprintf(stderr, "%s:%d:%s\n", filename, lineno, msg.c_str());
|
||||
msgs += msg;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string messages() const { return msgs; }
|
||||
|
||||
protected:
|
||||
virtual JSRuntime * createRuntime() {
|
||||
return JS_NewRuntime(8L * 1024 * 1024);
|
||||
}
|
||||
|
||||
static void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
|
||||
fprintf(stderr, "%s:%u:%s\n",
|
||||
report->filename ? report->filename : "<no filename>",
|
||||
(unsigned int) report->lineno,
|
||||
message);
|
||||
}
|
||||
|
||||
virtual JSContext * createContext() {
|
||||
JSContext *cx = JS_NewContext(rt, 8192);
|
||||
if (!cx)
|
||||
return NULL;
|
||||
JS_SetOptions(cx, JSOPTION_VAROBJFIX);
|
||||
JS_SetVersion(cx, JSVERSION_LATEST);
|
||||
JS_SetErrorReporter(cx, &reportError);
|
||||
return cx;
|
||||
}
|
||||
|
||||
virtual JSClass * getGlobalClass() {
|
||||
static JSClass basicGlobalClass = {
|
||||
"global", JSCLASS_GLOBAL_FLAGS,
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
return &basicGlobalClass;
|
||||
}
|
||||
|
||||
virtual JSObject * createGlobal() {
|
||||
/* Create the global object. */
|
||||
JSObject *global = JS_NewObject(cx, getGlobalClass(), NULL, NULL);
|
||||
if (!global)
|
||||
return NULL;
|
||||
|
||||
/* Populate the global object with the standard globals,
|
||||
like Object and Array. */
|
||||
if (!JS_InitStandardClasses(cx, global))
|
||||
return NULL;
|
||||
return global;
|
||||
}
|
||||
};
|
||||
|
||||
#define BEGIN_TEST(testname) \
|
||||
class cls_##testname : public JSAPITest { \
|
||||
public: \
|
||||
virtual const char * name() { return #testname; } \
|
||||
virtual bool run()
|
||||
|
||||
#define END_TEST(testname) \
|
||||
}; \
|
||||
static cls_##testname cls_##testname##_instance;
|
||||
|
||||
|
@ -84,6 +84,7 @@
|
||||
#include "jsdbgapi.h"
|
||||
#include "prmjtime.h"
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#if JS_HAS_FILE_OBJECT
|
||||
#include "jsfile.h"
|
||||
@ -832,6 +833,12 @@ bad:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_CommenceRuntimeShutDown(JSRuntime *rt)
|
||||
{
|
||||
rt->gcFlushCodeCaches = true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_DestroyRuntime(JSRuntime *rt)
|
||||
{
|
||||
@ -1334,7 +1341,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
|
||||
/* Define a top-level property 'undefined' with the undefined value. */
|
||||
atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
|
||||
if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT,
|
||||
NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -1541,7 +1548,7 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,
|
||||
if (idstr == ATOM_TO_STRING(atom)) {
|
||||
*resolved = JS_TRUE;
|
||||
return obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JSPROP_PERMANENT, NULL);
|
||||
}
|
||||
|
||||
@ -1636,7 +1643,7 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
|
||||
atom = rt->atomState.typeAtoms[JSTYPE_VOID];
|
||||
if (!AlreadyHasOwnProperty(cx, obj, atom) &&
|
||||
!obj->defineProperty(cx, ATOM_TO_JSID(atom), JSVAL_VOID,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT,
|
||||
JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT,
|
||||
NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -2964,7 +2971,7 @@ DefinePropertyById(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
return !!js_DefineNativeProperty(cx, obj, id, value, getter, setter,
|
||||
attrs, flags, tinyid, NULL);
|
||||
}
|
||||
return obj->defineProperty(cx, id, value, getter, setter, attrs, NULL);
|
||||
return obj->defineProperty(cx, id, value, getter, setter, attrs, NULL);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -5195,15 +5202,6 @@ JS_IsAssigning(JSContext *cx)
|
||||
return (js_CodeSpec[*fp->regs->pc].format & JOF_ASSIGNING) != 0;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetCallReturnValue2(JSContext *cx, jsval v)
|
||||
{
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
cx->rval2 = v;
|
||||
cx->rval2set = JS_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSStackFrame *)
|
||||
JS_SaveFrameChain(JSContext *cx)
|
||||
{
|
||||
@ -5489,7 +5487,10 @@ JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
|
||||
JSONWriteCallback callback, void *data)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
return js_Stringify(cx, vp, replacer, space, callback, data);
|
||||
JSCharBuffer cb(cx);
|
||||
if (!js_Stringify(cx, vp, replacer, space, cb))
|
||||
return false;
|
||||
return callback(cb.begin(), cb.length(), data);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
|
@ -545,6 +545,9 @@ JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2);
|
||||
extern JS_PUBLIC_API(JSRuntime *)
|
||||
JS_NewRuntime(uint32 maxbytes);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_CommenceRuntimeShutDown(JSRuntime *rt);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_DestroyRuntime(JSRuntime *rt);
|
||||
|
||||
@ -2346,17 +2349,6 @@ JS_IsConstructing(JSContext *cx);
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
JS_IsAssigning(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Set the second return value, which should be a string or int jsval that
|
||||
* identifies a property in the returned object, to form an ECMA reference
|
||||
* type value (obj, id). Only native methods can return reference types,
|
||||
* and if the returned value is used on the left-hand side of an assignment
|
||||
* op, the identified property will be set. If the return value is in an
|
||||
* r-value, the interpreter just gets obj[id]'s value.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetCallReturnValue2(JSContext *cx, jsval v);
|
||||
|
||||
/*
|
||||
* Saving and restoring frame chains.
|
||||
*
|
||||
|
@ -955,6 +955,25 @@ js_Array_dense_setelem_int(JSContext* cx, JSObject* obj, jsint i, int32 j)
|
||||
return dense_grow(cx, obj, i, v);
|
||||
}
|
||||
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_int, CONTEXT, OBJECT, INT32, INT32, 0, 0)
|
||||
|
||||
JSBool FASTCALL
|
||||
js_Array_dense_setelem_double(JSContext* cx, JSObject* obj, jsint i, jsdouble d)
|
||||
{
|
||||
JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
|
||||
|
||||
jsval v;
|
||||
jsint j;
|
||||
|
||||
if (JS_LIKELY(JSDOUBLE_IS_INT(d, j) && INT_FITS_IN_JSVAL(j))) {
|
||||
v = INT_TO_JSVAL(j);
|
||||
} else {
|
||||
if (!js_NewDoubleInRootedValue(cx, d, &v))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return dense_grow(cx, obj, i, v);
|
||||
}
|
||||
JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_double, CONTEXT, OBJECT, INT32, DOUBLE, 0, 0)
|
||||
#endif
|
||||
|
||||
static JSBool
|
||||
@ -1331,9 +1350,9 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
|
||||
|
||||
/* Transfer ownership of buffer to returned string. */
|
||||
static inline JSBool
|
||||
BufferToString(JSContext *cx, JSCharVector &buf, jsval *rval)
|
||||
BufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval)
|
||||
{
|
||||
JSString *str = js_NewStringFromCharBuffer(cx, buf);
|
||||
JSString *str = js_NewStringFromCharBuffer(cx, cb);
|
||||
if (!str)
|
||||
return false;
|
||||
*rval = STRING_TO_JSVAL(str);
|
||||
@ -1368,28 +1387,28 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
||||
* This object will take responsibility for the jschar buffer until the
|
||||
* buffer is transferred to the returned JSString.
|
||||
*/
|
||||
JSCharVector buf(cx);
|
||||
JSCharBuffer cb(cx);
|
||||
|
||||
/* Cycles/joins are indicated by sharp objects. */
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (IS_SHARP(he)) {
|
||||
JS_ASSERT(sharpchars != 0);
|
||||
buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
|
||||
cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
|
||||
goto make_string;
|
||||
} else if (sharpchars) {
|
||||
MAKE_SHARP(he);
|
||||
buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
|
||||
cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
|
||||
}
|
||||
#else
|
||||
if (IS_SHARP(he)) {
|
||||
if (!js_AppendLiteral(buf, "[]"))
|
||||
if (!js_AppendLiteral(cb, "[]"))
|
||||
goto out;
|
||||
cx->free(sharpchars);
|
||||
goto make_string;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!buf.append('['))
|
||||
if (!cb.append('['))
|
||||
goto out;
|
||||
|
||||
jsuint length;
|
||||
@ -1419,23 +1438,23 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp)
|
||||
str->getCharsAndLength(chars, charlen);
|
||||
|
||||
/* Append element to buffer. */
|
||||
if (!buf.append(chars, charlen))
|
||||
if (!cb.append(chars, charlen))
|
||||
goto out;
|
||||
if (index + 1 != length) {
|
||||
if (!js_AppendLiteral(buf, ", "))
|
||||
if (!js_AppendLiteral(cb, ", "))
|
||||
goto out;
|
||||
} else if (hole) {
|
||||
if (!buf.append(','))
|
||||
if (!cb.append(','))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the buffer. */
|
||||
if (!buf.append(']'))
|
||||
if (!cb.append(']'))
|
||||
goto out;
|
||||
|
||||
make_string:
|
||||
if (!BufferToString(cx, buf, vp))
|
||||
if (!BufferToString(cx, cb, vp))
|
||||
goto out;
|
||||
|
||||
ok = true;
|
||||
@ -1514,7 +1533,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
* This object will take responsibility for the jschar buffer until the
|
||||
* buffer is transferred to the returned JSString.
|
||||
*/
|
||||
JSCharVector buf(cx);
|
||||
JSCharBuffer cb(cx);
|
||||
|
||||
jsuint length;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
@ -1542,19 +1561,19 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!js_ValueToCharBuffer(cx, *rval, buf))
|
||||
if (!js_ValueToCharBuffer(cx, *rval, cb))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Append the separator. */
|
||||
if (index + 1 != length) {
|
||||
if (!buf.append(sep, seplen))
|
||||
if (!cb.append(sep, seplen))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the buffer. */
|
||||
if (!BufferToString(cx, buf, rval))
|
||||
if (!BufferToString(cx, cb, rval))
|
||||
goto out;
|
||||
|
||||
ok = true;
|
||||
@ -2398,7 +2417,7 @@ js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
|
||||
JS_ASSERT(length <= js_DenseArrayCapacity(obj));
|
||||
|
||||
if (length == js_DenseArrayCapacity(obj)) {
|
||||
if (length >= ARRAY_INIT_LIMIT) {
|
||||
if (length > JS_ARGS_LENGTH_MAX) {
|
||||
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
return JS_FALSE;
|
||||
@ -3292,6 +3311,15 @@ array_every(JSContext *cx, uintN argc, jsval *vp)
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSBool
|
||||
array_isArray(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
*vp = BOOLEAN_TO_JSVAL(argc > 0 &&
|
||||
!JSVAL_IS_PRIMITIVE(vp[2]) &&
|
||||
OBJ_IS_ARRAY(cx, JSVAL_TO_OBJECT(vp[2])));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSPropertySpec array_props[] = {
|
||||
{js_length_str, -1, JSPROP_SHARED | JSPROP_PERMANENT,
|
||||
array_length_getter, array_length_setter},
|
||||
@ -3343,6 +3371,11 @@ static JSFunctionSpec array_methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSFunctionSpec array_static_methods[] = {
|
||||
JS_FN("isArray", array_isArray, 1,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
JSBool
|
||||
js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
@ -3433,7 +3466,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
|
||||
js_SlowArrayObjectOps.call = NULL;
|
||||
|
||||
proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
|
||||
array_props, array_methods, NULL, NULL);
|
||||
array_props, array_methods, NULL, array_static_methods);
|
||||
|
||||
/* Initialize the Array prototype object so it gets a length property. */
|
||||
if (!proto || !InitArrayObject(cx, proto, 0, NULL))
|
||||
|
@ -50,9 +50,6 @@ JS_BEGIN_EXTERN_C
|
||||
|
||||
#define ARRAY_CAPACITY_MIN 7
|
||||
|
||||
/* 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);
|
||||
|
||||
|
@ -559,15 +559,30 @@ js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
void
|
||||
js_TraceAtomState(JSTracer *trc, JSBool allAtoms)
|
||||
{
|
||||
JSAtomState *state;
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
JSAtomState *state = &rt->atomState;
|
||||
|
||||
state = &trc->context->runtime->atomState;
|
||||
if (allAtoms) {
|
||||
JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc);
|
||||
JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc);
|
||||
} else {
|
||||
JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc);
|
||||
}
|
||||
|
||||
if (rt->state != JSRTS_LANDING) {
|
||||
/*
|
||||
* Unit strings aren't in state->stringAtoms, so we mark any that have
|
||||
* been created on demand. This bloats more than strictly necessary but
|
||||
* we can't help that without putting unit atoms in state->stringAtoms,
|
||||
* which is too expensive.
|
||||
*/
|
||||
for (uintN i = 0; i < UNIT_STRING_LIMIT; i++) {
|
||||
if (JSString *str = rt->unitStrings[i]) {
|
||||
JS_SET_TRACING_INDEX(trc, "unit_string_atom", i);
|
||||
JS_CallTracer(trc, str, JSTRACE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static JSDHashOperator
|
||||
@ -673,6 +688,14 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
||||
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
|
||||
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
|
||||
|
||||
if (str->length() == 1) {
|
||||
jschar c = str->chars()[0];
|
||||
if (c < UNIT_STRING_LIMIT) {
|
||||
JSString *str = js_GetUnitStringForChar(cx, c);
|
||||
return str ? (JSAtom *) STRING_TO_JSVAL(str) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
state = &cx->runtime->atomState;
|
||||
table = &state->stringAtoms;
|
||||
|
||||
|
@ -164,9 +164,9 @@ js_BooleanToString(JSContext *cx, JSBool b)
|
||||
|
||||
/* This function implements E-262-3 section 9.8, toString. */
|
||||
JSBool
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf)
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb)
|
||||
{
|
||||
return b ? js_AppendLiteral(buf, "true") : js_AppendLiteral(buf, "false");
|
||||
return b ? js_AppendLiteral(cb, "true") : js_AppendLiteral(cb, "false");
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -74,7 +74,7 @@ extern JSString *
|
||||
js_BooleanToString(JSContext *cx, JSBool b);
|
||||
|
||||
extern JSBool
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf);
|
||||
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb);
|
||||
|
||||
extern JSBool
|
||||
js_ValueToBoolean(jsval v);
|
||||
|
@ -382,15 +382,6 @@ js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed)
|
||||
}
|
||||
JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
|
||||
|
||||
JSObject* FASTCALL
|
||||
js_Arguments(JSContext* cx, JSObject* parent, JSObject* cached)
|
||||
{
|
||||
if (cached)
|
||||
return cached;
|
||||
return js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);
|
||||
}
|
||||
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, OBJECT, 0, 0)
|
||||
|
||||
JSObject* FASTCALL
|
||||
js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent)
|
||||
{
|
||||
|
@ -453,6 +453,7 @@ JS_DECLARE_CALLINFO(js_NewInstance)
|
||||
/* Defined in jsarray.cpp. */
|
||||
JS_DECLARE_CALLINFO(js_Array_dense_setelem)
|
||||
JS_DECLARE_CALLINFO(js_Array_dense_setelem_int)
|
||||
JS_DECLARE_CALLINFO(js_Array_dense_setelem_double)
|
||||
JS_DECLARE_CALLINFO(js_NewEmptyArray)
|
||||
JS_DECLARE_CALLINFO(js_NewUninitializedArray)
|
||||
JS_DECLARE_CALLINFO(js_ArrayCompPush)
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jslock.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
@ -83,6 +84,7 @@ InitThreadData(JSThreadData *data)
|
||||
#ifdef JS_TRACER
|
||||
js_InitJIT(&data->traceMonitor);
|
||||
#endif
|
||||
js_InitRandom(data);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -114,11 +116,10 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
|
||||
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
|
||||
|
||||
/*
|
||||
* FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes),
|
||||
* but we can't yet, because traces may embed sprop and object references,
|
||||
* and we don't yet mark such embedded refs.
|
||||
* If we are about to regenerate shapes, we have to flush the JIT cache, too.
|
||||
*/
|
||||
tm->needFlush = JS_TRUE;
|
||||
if (cx->runtime->gcRegenShapes)
|
||||
tm->needFlush = JS_TRUE;
|
||||
|
||||
if (tm->recorder)
|
||||
tm->recorder->deepAbort();
|
||||
@ -267,6 +268,15 @@ thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static JSDHashOperator
|
||||
thread_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
|
||||
void *arg)
|
||||
{
|
||||
JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
|
||||
thread->data.mark((JSTracer *)arg);
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
JSBool
|
||||
@ -308,6 +318,16 @@ js_PurgeThreads(JSContext *cx)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js_TraceThreads(JSRuntime *rt, JSTracer *trc)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_DHashTableEnumerate(&rt->threads, thread_tracer, trc);
|
||||
#else
|
||||
rt->threadData.mark(trc);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
|
||||
* associated with scripts, so in addition to storing them in cx->options we
|
||||
@ -1657,13 +1677,13 @@ js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
|
||||
} else if (JSVAL_IS_VOID(v)) {
|
||||
ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
|
||||
js_GetErrorMessage, NULL,
|
||||
JSMSG_NULL_OR_UNDEFINED, bytes,
|
||||
JSMSG_UNEXPECTED_TYPE, bytes,
|
||||
js_undefined_str, NULL);
|
||||
} else {
|
||||
JS_ASSERT(JSVAL_IS_NULL(v));
|
||||
ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
|
||||
js_GetErrorMessage, NULL,
|
||||
JSMSG_NULL_OR_UNDEFINED, bytes,
|
||||
JSMSG_UNEXPECTED_TYPE, bytes,
|
||||
js_null_str, NULL);
|
||||
}
|
||||
|
||||
@ -1830,3 +1850,14 @@ js_GetCurrentBytecodePC(JSContext* cx)
|
||||
*/
|
||||
return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
|
||||
}
|
||||
|
||||
bool
|
||||
js_CurrentPCIsInImacro(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_TRACER
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
return (JS_ON_TRACE(cx) ? cx->bailExit->imacpc : cx->fp->imacpc) != NULL;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
@ -98,8 +98,11 @@ namespace nanojit {
|
||||
class Assembler;
|
||||
class CodeAlloc;
|
||||
class Fragment;
|
||||
class Fragmento;
|
||||
class LirBuffer;
|
||||
#ifdef DEBUG
|
||||
class LabelMap;
|
||||
#endif
|
||||
extern "C++" { template<typename K, typename V, typename H> class HashMap; }
|
||||
}
|
||||
class TraceRecorder;
|
||||
class VMAllocator;
|
||||
@ -114,6 +117,12 @@ typedef Queue<uint16> SlotList;
|
||||
#define FRAGMENT_TABLE_SIZE 512
|
||||
struct VMFragment;
|
||||
|
||||
#ifdef __cplusplus
|
||||
struct REHashKey;
|
||||
struct REHashFn;
|
||||
typedef nanojit::HashMap<REHashKey, nanojit::Fragment*, REHashFn> REHashMap;
|
||||
#endif
|
||||
|
||||
#define MONITOR_N_GLOBAL_STATES 4
|
||||
struct GlobalState {
|
||||
JSObject* globalObj;
|
||||
@ -141,10 +150,13 @@ struct JSTraceMonitor {
|
||||
JSContext *tracecx;
|
||||
|
||||
CLS(nanojit::LirBuffer) lirbuf;
|
||||
CLS(nanojit::Fragmento) fragmento;
|
||||
CLS(VMAllocator) allocator; // A chunk allocator for LIR.
|
||||
CLS(nanojit::CodeAlloc) codeAlloc; // A general allocator for native code.
|
||||
CLS(nanojit::Assembler) assembler;
|
||||
#ifdef DEBUG
|
||||
CLS(nanojit::LabelMap) labels;
|
||||
#endif
|
||||
|
||||
CLS(TraceRecorder) recorder;
|
||||
jsval *reservedDoublePool;
|
||||
jsval *reservedDoublePoolPtr;
|
||||
@ -180,10 +192,19 @@ struct JSTraceMonitor {
|
||||
CLS(nanojit::CodeAlloc) reCodeAlloc;
|
||||
CLS(nanojit::Assembler) reAssembler;
|
||||
CLS(nanojit::LirBuffer) reLirBuf;
|
||||
CLS(nanojit::Fragmento) reFragmento;
|
||||
CLS(REHashMap) reFragments;
|
||||
#ifdef DEBUG
|
||||
CLS(nanojit::LabelMap) reLabels;
|
||||
#endif
|
||||
|
||||
/* Keep a list of recorders we need to abort on cache flush. */
|
||||
CLS(TraceRecorder) abortStack;
|
||||
|
||||
/* Flush the JIT cache. */
|
||||
void flush();
|
||||
|
||||
/* Mark all objects baked into native code in the code cache. */
|
||||
void mark(JSTracer *trc);
|
||||
};
|
||||
|
||||
typedef struct InterpStruct InterpStruct;
|
||||
@ -244,6 +265,9 @@ struct JSThreadData {
|
||||
/* Property cache for faster call/get/set invocation. */
|
||||
JSPropertyCache propertyCache;
|
||||
|
||||
/* Random number generator state, used by jsmath.cpp. */
|
||||
int64 rngSeed;
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/* Trace-tree JIT recorder/interpreter state. */
|
||||
JSTraceMonitor traceMonitor;
|
||||
@ -268,6 +292,12 @@ struct JSThreadData {
|
||||
*/
|
||||
JSFreePointerListTask *deallocatorTask;
|
||||
#endif
|
||||
|
||||
void mark(JSTracer *trc) {
|
||||
#ifdef JS_TRACER
|
||||
traceMonitor.mark(trc);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -332,9 +362,6 @@ typedef enum JSRuntimeState {
|
||||
typedef enum JSBuiltinFunctionId {
|
||||
JSBUILTIN_ObjectToIterator,
|
||||
JSBUILTIN_CallIteratorNext,
|
||||
JSBUILTIN_SetProperty,
|
||||
JSBUILTIN_SetElement,
|
||||
JSBUILTIN_HasInstance,
|
||||
JSBUILTIN_LIMIT
|
||||
} JSBuiltinFunctionId;
|
||||
|
||||
@ -392,6 +419,7 @@ struct JSRuntime {
|
||||
uint32 gcTriggerFactor;
|
||||
size_t gcTriggerBytes;
|
||||
volatile JSBool gcIsNeeded;
|
||||
volatile JSBool gcFlushCodeCaches;
|
||||
|
||||
/*
|
||||
* NB: do not pack another flag here by claiming gcPadding unless the new
|
||||
@ -446,14 +474,6 @@ struct JSRuntime {
|
||||
*/
|
||||
JSSetSlotRequest *setSlotRequests;
|
||||
|
||||
/* Random number generator state, used by jsmath.c. */
|
||||
JSBool rngInitialized;
|
||||
int64 rngMultiplier;
|
||||
int64 rngAddend;
|
||||
int64 rngMask;
|
||||
int64 rngSeed;
|
||||
jsdouble rngDscale;
|
||||
|
||||
/* Well-known numbers held for use by this runtime's contexts. */
|
||||
jsdouble *jsNaN;
|
||||
jsdouble *jsNegativeInfinity;
|
||||
@ -948,21 +968,9 @@ struct JSContext {
|
||||
*/
|
||||
JSDHashTable *resolvingTable;
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
/*
|
||||
* Secondary return value from native method called on the left-hand side
|
||||
* of an assignment operator. The native should store the object in which
|
||||
* to set a property in *rval, and return the property's id expressed as a
|
||||
* jsval by calling JS_SetCallReturnValue2(cx, idval).
|
||||
*/
|
||||
jsval rval2;
|
||||
JSPackedBool rval2set;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* True if generating an error, to prevent runaway recursion.
|
||||
* NB: generatingError packs with rval2set, #if JS_HAS_LVALUE_RETURN;
|
||||
* with insideGCMarkCallback and with throwing below.
|
||||
* NB: generatingError packs with insideGCMarkCallback and throwing below.
|
||||
*/
|
||||
JSPackedBool generatingError;
|
||||
|
||||
@ -1306,6 +1314,9 @@ js_FinishThreads(JSRuntime *rt);
|
||||
extern void
|
||||
js_PurgeThreads(JSContext *cx);
|
||||
|
||||
extern void
|
||||
js_TraceThreads(JSRuntime *rt, JSTracer *trc);
|
||||
|
||||
/*
|
||||
* Ensures the JSOPTION_XML and JSOPTION_ANONFUNFIX bits of cx->options are
|
||||
* reflected in cx->version, since each bit must travel with a script that has
|
||||
@ -1587,6 +1598,9 @@ js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp);
|
||||
extern jsbytecode*
|
||||
js_GetCurrentBytecodePC(JSContext* cx);
|
||||
|
||||
extern bool
|
||||
js_CurrentPCIsInImacro(JSContext *cx);
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/*
|
||||
* Reconstruct the JS stack and clear cx->tracecx. We must be currently in a
|
||||
|
@ -634,7 +634,6 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.script = script;
|
||||
frame.regs = NULL;
|
||||
frame.callee = closure;
|
||||
frame.fun = fun;
|
||||
frame.argv = argv + 2;
|
||||
frame.down = js_GetTopStackFrame(cx);
|
||||
@ -672,7 +671,7 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
: wp->setter(cx, obj, userid, vp));
|
||||
if (injectFrame) {
|
||||
/* Evil code can cause us to have an arguments object. */
|
||||
ok &= frame.putActivationObjects(cx);
|
||||
frame.putActivationObjects(cx);
|
||||
cx->fp = frame.down;
|
||||
if (argv != smallv)
|
||||
cx->free(argv);
|
||||
@ -1019,8 +1018,8 @@ JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
|
||||
if (fp->fun) {
|
||||
callbacks = JS_GetSecurityCallbacks(cx);
|
||||
if (callbacks && callbacks->findObjectPrincipals) {
|
||||
if (FUN_OBJECT(fp->fun) != fp->callee)
|
||||
return callbacks->findObjectPrincipals(cx, fp->callee);
|
||||
if (FUN_OBJECT(fp->fun) != fp->callee())
|
||||
return callbacks->findObjectPrincipals(cx, fp->callee());
|
||||
/* FALL THROUGH */
|
||||
}
|
||||
}
|
||||
@ -1037,7 +1036,7 @@ JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
|
||||
|
||||
callbacks = JS_GetSecurityCallbacks(cx);
|
||||
if (callbacks && callbacks->findObjectPrincipals) {
|
||||
principals = callbacks->findObjectPrincipals(cx, fp->callee);
|
||||
principals = callbacks->findObjectPrincipals(cx, fp->callee());
|
||||
} else {
|
||||
principals = NULL;
|
||||
}
|
||||
@ -1166,9 +1165,9 @@ JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
|
||||
if (!fp->fun)
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee));
|
||||
JS_ASSERT(fp->callee->getAssignedPrivate() == fp->fun);
|
||||
return fp->callee;
|
||||
JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee()));
|
||||
JS_ASSERT(fp->callee()->getAssignedPrivate() == fp->fun);
|
||||
return fp->callee();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
@ -1180,7 +1179,7 @@ JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
return fp->callee;
|
||||
return fp->callee();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
|
@ -2446,9 +2446,7 @@ CheckSideEffects(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
#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;
|
||||
@ -3675,7 +3673,6 @@ EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
break;
|
||||
|
||||
default:
|
||||
#if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT
|
||||
{
|
||||
ptrdiff_t top;
|
||||
|
||||
@ -3688,7 +3685,7 @@ EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case JSOP_ENUMELEM:
|
||||
JS_ASSERT(0);
|
||||
}
|
||||
@ -4722,7 +4719,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
} else
|
||||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (pn3->pn_type == TOK_LP) {
|
||||
JS_ASSERT(pn3->pn_op == JSOP_SETCALL);
|
||||
if (!js_EmitTree(cx, cg, pn3))
|
||||
@ -4730,7 +4726,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0)
|
||||
return JS_FALSE;
|
||||
} else
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn3->pn_type == TOK_UNARYOP) {
|
||||
JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME);
|
||||
@ -5632,12 +5627,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
case TOK_RC:
|
||||
break;
|
||||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
|
||||
@ -5714,9 +5707,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
}
|
||||
break;
|
||||
case TOK_LB:
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
#endif
|
||||
@ -5768,9 +5759,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
EMIT_INDEX_OP(PN_OP(pn2), atomIndex);
|
||||
break;
|
||||
case TOK_LB:
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
#endif
|
||||
if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
@ -6032,7 +6021,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (!EmitElemOp(cx, pn2, op, cg))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
@ -6043,7 +6031,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (js_Emit1(cx, cg, op) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
|
||||
@ -6086,17 +6073,6 @@ 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:
|
||||
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))
|
||||
@ -6113,6 +6089,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (!useful) {
|
||||
off = noteIndex = -1;
|
||||
} else {
|
||||
if (pn2->pn_op == JSOP_SETCALL)
|
||||
pn2->pn_op = JSOP_CALL;
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
off = CG_OFFSET(cg);
|
||||
|
@ -281,7 +281,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
|
||||
if (fp->fun && fp->argv) {
|
||||
v = JSVAL_NULL;
|
||||
if (checkAccess &&
|
||||
!checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) {
|
||||
!checkAccess(cx, fp->callee(), callerid, JSACC_READ, &v)) {
|
||||
break;
|
||||
}
|
||||
valueCount += fp->argc;
|
||||
|
592
js/src/jsfun.cpp
592
js/src/jsfun.cpp
@ -49,6 +49,7 @@
|
||||
#include "jsapi.h"
|
||||
#include "jsarray.h"
|
||||
#include "jsatom.h"
|
||||
#include "jsbool.h"
|
||||
#include "jsbuiltins.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsversion.h"
|
||||
@ -79,35 +80,83 @@
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
||||
/* Generic function/call/arguments tinyids -- also reflected bit numbers. */
|
||||
enum {
|
||||
CALL_ARGUMENTS = -1, /* predefined arguments local variable */
|
||||
ARGS_LENGTH = -2, /* number of actual args, arity if inactive */
|
||||
ARGS_CALLEE = -3, /* reference from arguments to active funobj */
|
||||
FUN_ARITY = -4, /* number of formal parameters; desired argc */
|
||||
FUN_NAME = -5, /* function name, "" if anonymous */
|
||||
FUN_CALLER = -6 /* Function.prototype.caller, backward compat */
|
||||
};
|
||||
/*
|
||||
* Reserved slot structure for Arguments objects:
|
||||
*
|
||||
* JSSLOT_PRIVATE - the corresponding frame until the frame exits.
|
||||
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating
|
||||
* whether arguments.length was overwritten.
|
||||
* JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was
|
||||
* overwritten.
|
||||
* JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after
|
||||
* the frame exists. The slot's value will be JSVAL_HOLE
|
||||
* if arguments[i] was deleted or overwritten.
|
||||
*/
|
||||
const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
|
||||
const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2;
|
||||
const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3;
|
||||
|
||||
#if JSFRAME_OVERRIDE_BITS < 8
|
||||
# error "not enough override bits in JSStackFrame.flags!"
|
||||
#endif
|
||||
/* Number of extra fixed slots besides JSSLOT_PRIVATE. */
|
||||
const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START -
|
||||
JSSLOT_ARGS_LENGTH;
|
||||
|
||||
#define TEST_OVERRIDE_BIT(fp, tinyid) \
|
||||
((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
|
||||
/*
|
||||
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval.
|
||||
* Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that
|
||||
* we check first that the shift does not overflow uint32.
|
||||
*/
|
||||
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
|
||||
JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
|
||||
|
||||
#define SET_OVERRIDE_BIT(fp, tinyid) \
|
||||
((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
|
||||
static inline bool
|
||||
IsOverriddenArgsLength(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
jsval v = obj->fslots[JSSLOT_ARGS_LENGTH];
|
||||
return (JSVAL_TO_INT(v) & 1) != 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetOverriddenArgsLength(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
jsval v = obj->fslots[JSSLOT_ARGS_LENGTH];
|
||||
v = INT_TO_JSVAL(JSVAL_TO_INT(v) | 1);
|
||||
JS_ASSERT(JSVAL_IS_INT(v));
|
||||
obj->fslots[JSSLOT_ARGS_LENGTH] = v;
|
||||
}
|
||||
|
||||
static inline void
|
||||
InitArgsLengthSlot(JSObject *obj, uint32 argc)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(obj->fslots[JSSLOT_ARGS_LENGTH] == JSVAL_VOID);
|
||||
obj->fslots[JSSLOT_ARGS_LENGTH] = INT_TO_JSVAL(argc << 1);
|
||||
JS_ASSERT(!IsOverriddenArgsLength(obj));
|
||||
}
|
||||
|
||||
static inline uint32
|
||||
GetArgsLength(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
uint32 argc = uint32(JSVAL_TO_INT(obj->fslots[JSSLOT_ARGS_LENGTH])) >> 1;
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
return argc;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
|
||||
{
|
||||
JSObject *argsobj;
|
||||
|
||||
if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
|
||||
if (fp->flags & JSFRAME_OVERRIDE_ARGS) {
|
||||
JS_ASSERT(fp->callobj);
|
||||
return fp->callobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom),
|
||||
vp);
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
|
||||
return fp->callobj->getProperty(cx, id, vp);
|
||||
}
|
||||
argsobj = js_GetArgsObject(cx, fp);
|
||||
if (!argsobj)
|
||||
@ -116,98 +165,39 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
|
||||
{
|
||||
JSObject *argsobj;
|
||||
jsval bmapval, bmapint;
|
||||
size_t nbits, nbytes;
|
||||
jsbitmap *bitmap;
|
||||
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
(void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
|
||||
nbits = fp->argc;
|
||||
JS_ASSERT(slot < nbits);
|
||||
if (JSVAL_IS_VOID(bmapval)) {
|
||||
if (nbits <= JSVAL_INT_BITS) {
|
||||
bmapint = 0;
|
||||
bitmap = (jsbitmap *) &bmapint;
|
||||
} else {
|
||||
nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
|
||||
bitmap = (jsbitmap *) cx->malloc(nbytes);
|
||||
if (!bitmap)
|
||||
return JS_FALSE;
|
||||
memset(bitmap, 0, nbytes);
|
||||
bmapval = PRIVATE_TO_JSVAL(bitmap);
|
||||
JS_SetReservedSlot(cx, argsobj, 0, bmapval);
|
||||
}
|
||||
} else {
|
||||
if (nbits <= JSVAL_INT_BITS) {
|
||||
bmapint = JSVAL_TO_INT(bmapval);
|
||||
bitmap = (jsbitmap *) &bmapint;
|
||||
} else {
|
||||
bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
|
||||
}
|
||||
}
|
||||
JS_SET_BIT(bitmap, slot);
|
||||
if (bitmap == (jsbitmap *) &bmapint) {
|
||||
bmapval = INT_TO_JSVAL(bmapint);
|
||||
JS_SetReservedSlot(cx, argsobj, 0, bmapval);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* NB: Infallible predicate, false does not mean error/exception. */
|
||||
static JSBool
|
||||
ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
|
||||
{
|
||||
JSObject *argsobj;
|
||||
jsval bmapval, bmapint;
|
||||
jsbitmap *bitmap;
|
||||
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
(void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
|
||||
if (JSVAL_IS_VOID(bmapval))
|
||||
return JS_FALSE;
|
||||
if (fp->argc <= JSVAL_INT_BITS) {
|
||||
bmapint = JSVAL_TO_INT(bmapval);
|
||||
bitmap = (jsbitmap *) &bmapint;
|
||||
} else {
|
||||
bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
|
||||
}
|
||||
return JS_TEST_BIT(bitmap, slot) != 0;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
|
||||
{
|
||||
jsval val;
|
||||
JSObject *obj;
|
||||
uintN slot;
|
||||
|
||||
if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
|
||||
if (fp->flags & JSFRAME_OVERRIDE_ARGS) {
|
||||
JS_ASSERT(fp->callobj);
|
||||
if (!fp->callobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom),
|
||||
&val)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (JSVAL_IS_PRIMITIVE(val)) {
|
||||
obj = js_ValueToNonNullObject(cx, val);
|
||||
|
||||
jsid argumentsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
|
||||
jsval v;
|
||||
if (!fp->callobj->getProperty(cx, argumentsid, &v))
|
||||
return false;
|
||||
|
||||
JSObject *obj;
|
||||
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||
obj = js_ValueToNonNullObject(cx, v);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
} else {
|
||||
obj = JSVAL_TO_OBJECT(val);
|
||||
obj = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
return obj->getProperty(cx, id, vp);
|
||||
}
|
||||
|
||||
*vp = JSVAL_VOID;
|
||||
if (JSID_IS_INT(id)) {
|
||||
slot = (uintN) JSID_TO_INT(id);
|
||||
if (slot < fp->argc) {
|
||||
if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
|
||||
return JSVAL_TO_OBJECT(fp->argsobj)->getProperty(cx, id, vp);
|
||||
*vp = fp->argv[slot];
|
||||
uint32 arg = uint32(JSID_TO_INT(id));
|
||||
JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
if (arg < fp->argc) {
|
||||
if (argsobj) {
|
||||
jsval v = OBJ_GET_SLOT(cx, argsobj, JSSLOT_ARGS_COPY_START+arg);
|
||||
if (v == JSVAL_HOLE)
|
||||
return argsobj->getProperty(cx, id, vp);
|
||||
}
|
||||
*vp = fp->argv[arg];
|
||||
} else {
|
||||
/*
|
||||
* Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
|
||||
@ -221,17 +211,41 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
|
||||
* is null at this point, as it would be in the example, return
|
||||
* undefined in *vp.
|
||||
*/
|
||||
if (fp->argsobj)
|
||||
return JSVAL_TO_OBJECT(fp->argsobj)->getProperty(cx, id, vp);
|
||||
}
|
||||
} else {
|
||||
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
|
||||
if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
|
||||
return JSVAL_TO_OBJECT(fp->argsobj)->getProperty(cx, id, vp);
|
||||
*vp = INT_TO_JSVAL((jsint) fp->argc);
|
||||
if (argsobj)
|
||||
return argsobj->getProperty(cx, id, vp);
|
||||
}
|
||||
} else if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
|
||||
JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
if (argsobj && IsOverriddenArgsLength(argsobj))
|
||||
return argsobj->getProperty(cx, id, vp);
|
||||
*vp = INT_TO_JSVAL(jsint(fp->argc));
|
||||
}
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
|
||||
{
|
||||
JSObject *argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, parent, 0);
|
||||
if (!argsobj || !js_EnsureReservedSlots(cx, argsobj, argc))
|
||||
return NULL;
|
||||
|
||||
argsobj->fslots[JSSLOT_ARGS_CALLEE] = OBJECT_TO_JSVAL(callee);
|
||||
InitArgsLengthSlot(argsobj, argc);
|
||||
return argsobj;
|
||||
}
|
||||
|
||||
static void
|
||||
PutArguments(JSContext *cx, JSObject *argsobj, jsval *args)
|
||||
{
|
||||
uint32 argc = GetArgsLength(argsobj);
|
||||
JS_LOCK_OBJ(cx, argsobj);
|
||||
for (uint32 i = 0; i != argc; ++i) {
|
||||
jsval v = STOBJ_GET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i);
|
||||
if (v != JSVAL_HOLE)
|
||||
STOBJ_SET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i, args[i]);
|
||||
}
|
||||
JS_UNLOCK_OBJ(cx, argsobj);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -266,9 +280,11 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
JSObject *parent, *global = fp->scopeChain;
|
||||
while ((parent = OBJ_GET_PARENT(cx, global)) != NULL)
|
||||
global = parent;
|
||||
argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, global, 0);
|
||||
|
||||
JS_ASSERT(fp->argv);
|
||||
argsobj = NewArguments(cx, global, fp->argc, JSVAL_TO_OBJECT(fp->argv[-2]));
|
||||
if (!argsobj)
|
||||
return NULL;
|
||||
return argsobj;
|
||||
|
||||
/* Link the new object to fp so it can get actual argument values. */
|
||||
argsobj->setPrivate(fp);
|
||||
@ -276,76 +292,54 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
return argsobj;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
args_enumerate(JSContext *cx, JSObject *obj);
|
||||
|
||||
JSBool
|
||||
void
|
||||
js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *argsobj;
|
||||
jsval bmapval, rval;
|
||||
JSBool ok;
|
||||
JSRuntime *rt;
|
||||
|
||||
/*
|
||||
* Reuse args_enumerate here to reflect fp's actual arguments as indexed
|
||||
* elements of argsobj. Do this first, before clearing and freeing the
|
||||
* deleted argument slot bitmap, because args_enumerate depends on that.
|
||||
*/
|
||||
argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
ok = args_enumerate(cx, argsobj);
|
||||
|
||||
/*
|
||||
* Now clear the deleted argument number bitmap slot and free the bitmap,
|
||||
* if one was actually created due to 'delete arguments[0]' or similar.
|
||||
*/
|
||||
(void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
|
||||
if (!JSVAL_IS_VOID(bmapval)) {
|
||||
JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
|
||||
if (fp->argc > JSVAL_INT_BITS)
|
||||
cx->free(JSVAL_TO_PRIVATE(bmapval));
|
||||
}
|
||||
|
||||
/*
|
||||
* Now get the prototype properties so we snapshot fp->fun and fp->argc
|
||||
* before fp goes away.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
|
||||
&rval);
|
||||
ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
|
||||
&rval);
|
||||
ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
|
||||
&rval);
|
||||
ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
|
||||
&rval);
|
||||
|
||||
/*
|
||||
* Clear the private pointer to fp, which is about to go away (js_Invoke).
|
||||
* Do this last because the args_enumerate and js_GetProperty calls above
|
||||
* need to follow the private slot to find fp.
|
||||
*/
|
||||
JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj);
|
||||
JS_ASSERT(argsobj->getPrivate() == fp);
|
||||
PutArguments(cx, argsobj, fp->argv);
|
||||
argsobj->setPrivate(NULL);
|
||||
fp->argsobj = NULL;
|
||||
return ok;
|
||||
fp->argsobj = JSVAL_NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traced versions of js_GetArgsObject and js_PutArgsObject.
|
||||
*/
|
||||
JSObject * JS_FASTCALL
|
||||
js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee,
|
||||
JSObject* cached)
|
||||
{
|
||||
return cached
|
||||
? cached
|
||||
: NewArguments(cx, parent, argc, callee);
|
||||
}
|
||||
|
||||
JS_DEFINE_CALLINFO_5(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJECT, OBJECT, 0, 0)
|
||||
|
||||
/* FIXME change the return type to void. */
|
||||
JSBool JS_FASTCALL
|
||||
js_PutArguments(JSContext *cx, JSObject *argsobj, jsval *args)
|
||||
{
|
||||
JS_ASSERT(!argsobj->getPrivate());
|
||||
PutArguments(cx, argsobj, args);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, JSVALPTR, 0, 0)
|
||||
|
||||
static JSBool
|
||||
args_delProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
{
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (!fp)
|
||||
return JS_TRUE;
|
||||
JS_ASSERT(fp->argsobj);
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
if (JSVAL_IS_INT(idval)) {
|
||||
uintN arg = uintN(JSVAL_TO_INT(idval));
|
||||
if (arg < fp->argc && !MarkArgDeleted(cx, fp, arg))
|
||||
return false;
|
||||
if (arg < GetArgsLength(obj))
|
||||
OBJ_SET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg, JSVAL_HOLE);
|
||||
} else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
|
||||
SET_OVERRIDE_BIT(fp, ARGS_LENGTH);
|
||||
SetOverriddenArgsLength(obj);
|
||||
} else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) {
|
||||
SET_OVERRIDE_BIT(fp, ARGS_CALLEE);
|
||||
obj->fslots[JSSLOT_ARGS_CALLEE] = JSVAL_HOLE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -521,9 +515,7 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunctio
|
||||
static JSBool
|
||||
ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
{
|
||||
JSStackFrame *fp = (JSStackFrame *)
|
||||
JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
|
||||
if (!fp)
|
||||
if (!JS_InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
|
||||
return true;
|
||||
|
||||
if (JSVAL_IS_INT(idval)) {
|
||||
@ -532,14 +524,23 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
* prototype to point to another Arguments object with a bigger argc.
|
||||
*/
|
||||
uintN arg = uintN(JSVAL_TO_INT(idval));
|
||||
if (arg < fp->argc && !ArgWasDeleted(cx, fp, arg))
|
||||
*vp = fp->argv[arg];
|
||||
if (arg < GetArgsLength(obj)) {
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp) {
|
||||
*vp = fp->argv[arg];
|
||||
} else {
|
||||
jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg);
|
||||
if (v != JSVAL_HOLE)
|
||||
*vp = v;
|
||||
}
|
||||
}
|
||||
} else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
|
||||
if (!TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
|
||||
*vp = INT_TO_JSVAL((jsint)fp->argc);
|
||||
if (!IsOverriddenArgsLength(obj))
|
||||
*vp = INT_TO_JSVAL(GetArgsLength(obj));
|
||||
} else {
|
||||
JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.calleeAtom));
|
||||
if (!TEST_OVERRIDE_BIT(fp, ARGS_CALLEE)) {
|
||||
jsval v = obj->fslots[JSSLOT_ARGS_CALLEE];
|
||||
if (v != JSVAL_HOLE) {
|
||||
/*
|
||||
* If this function or one in it needs upvars that reach above it
|
||||
* in the scope chain, it must not be a null closure (it could be a
|
||||
@ -548,12 +549,12 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
* to reduce code size and tell debugger users the truth instead of
|
||||
* passing off a fibbing wrapper.
|
||||
*/
|
||||
if (fp->fun->needsWrapper()) {
|
||||
if (GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))->needsWrapper()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_OPTIMIZED_CLOSURE_LEAK);
|
||||
return false;
|
||||
}
|
||||
*vp = OBJECT_TO_JSVAL(fp->callee);
|
||||
*vp = v;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -562,27 +563,36 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
static JSBool
|
||||
ArgSetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
|
||||
{
|
||||
JSStackFrame *fp = (JSStackFrame *)
|
||||
JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
|
||||
if (!fp)
|
||||
if (!JS_InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
|
||||
return true;
|
||||
|
||||
if (JSVAL_IS_INT(idval)) {
|
||||
uintN arg = uintN(JSVAL_TO_INT(idval));
|
||||
if (FUN_INTERPRETED(fp->fun) &&
|
||||
arg < fp->argc &&
|
||||
!ArgWasDeleted(cx, fp, arg)) {
|
||||
fp->argv[arg] = *vp;
|
||||
if (arg < GetArgsLength(obj)) {
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp) {
|
||||
fp->argv[arg] = *vp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.lengthAtom) ||
|
||||
idval == ATOM_KEY(cx->runtime->atomState.calleeAtom));
|
||||
SET_OVERRIDE_BIT(fp,
|
||||
(idval == ATOM_KEY(cx->runtime->atomState.lengthAtom))
|
||||
? ARGS_LENGTH
|
||||
: ARGS_CALLEE);
|
||||
}
|
||||
return true;
|
||||
|
||||
/*
|
||||
* For simplicity we use delete/set to replace the property with one
|
||||
* backed by the default Object getter and setter. Note the we rely on
|
||||
* args_delete to clear the corresponding reserved slot so the GC can
|
||||
* collect its value.
|
||||
*/
|
||||
jsid id;
|
||||
if (!JS_ValueToId(cx, idval, &id))
|
||||
return false;
|
||||
|
||||
JSAutoTempValueRooter tvr(cx);
|
||||
return js_DeleteProperty(cx, obj, id, tvr.addr()) &&
|
||||
js_SetProperty(cx, obj, id, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -590,48 +600,35 @@ args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
|
||||
JSObject **objp)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
*objp = NULL;
|
||||
|
||||
JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
if (!fp)
|
||||
return true;
|
||||
JS_ASSERT(fp->argsobj);
|
||||
|
||||
jsid id;
|
||||
jsval v;
|
||||
jsid id = 0;
|
||||
if (JSVAL_IS_INT(idval)) {
|
||||
uintN arg = uintN(JSVAL_TO_INT(idval));
|
||||
if (arg >= fp->argc || ArgWasDeleted(cx, fp, arg))
|
||||
return true;
|
||||
id = INT_JSVAL_TO_JSID(idval);
|
||||
v = fp->argv[arg];
|
||||
} else {
|
||||
if (!JSVAL_IS_STRING(idval))
|
||||
return true;
|
||||
JSString *str = JSVAL_TO_STRING(idval);
|
||||
JSAtom *atom = cx->runtime->atomState.lengthAtom;
|
||||
if (str == ATOM_TO_STRING(atom)) {
|
||||
if (TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
|
||||
return true;
|
||||
v = INT_TO_JSVAL(fp->argc);
|
||||
} else {
|
||||
atom = cx->runtime->atomState.calleeAtom;
|
||||
if (str == ATOM_TO_STRING(atom)) {
|
||||
if (TEST_OVERRIDE_BIT(fp, ARGS_CALLEE))
|
||||
return true;
|
||||
JS_ASSERT(fp);
|
||||
v = JSVAL_NULL;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
uint32 arg = uint32(JSVAL_TO_INT(idval));
|
||||
if (arg < GetArgsLength(obj) &&
|
||||
OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg) != JSVAL_HOLE) {
|
||||
id = INT_JSVAL_TO_JSID(idval);
|
||||
}
|
||||
id = ATOM_TO_JSID(atom);
|
||||
} else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
|
||||
if (!IsOverriddenArgsLength(obj))
|
||||
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
|
||||
|
||||
} else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) {
|
||||
if (obj->fslots[JSSLOT_ARGS_CALLEE] != JSVAL_HOLE)
|
||||
id = ATOM_TO_JSID(cx->runtime->atomState.calleeAtom);
|
||||
}
|
||||
|
||||
/* XXX ECMA specs DontEnum, contrary to other array-like objects */
|
||||
if (!js_DefineProperty(cx, obj, id, v, ArgGetter, ArgSetter, 0, NULL))
|
||||
return false;
|
||||
*objp = obj;
|
||||
if (id != 0) {
|
||||
/*
|
||||
* XXX ECMA specs DontEnum even for indexed properties, contrary to
|
||||
* other array-like objects.
|
||||
*/
|
||||
if (!js_DefineProperty(cx, obj, id, JSVAL_VOID, ArgGetter, ArgSetter,
|
||||
JSPROP_SHARED, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
*objp = obj;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -640,19 +637,11 @@ args_enumerate(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
|
||||
JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
if (!fp)
|
||||
return JS_TRUE;
|
||||
JS_ASSERT(fp->argsobj);
|
||||
|
||||
/*
|
||||
* Trigger reflection in with value snapshot in args_resolve using a series
|
||||
* of js_LookupProperty calls. We handle length, callee, and the indexed
|
||||
* argument properties. We know that args_resolve covers all these cases
|
||||
* and creates direct properties of obj, but that it may fail to resolve
|
||||
* length or callee if overridden.
|
||||
* Trigger reflection in args_resolve using a series of js_LookupProperty
|
||||
* calls.
|
||||
*/
|
||||
int argc = int(fp->argc);
|
||||
int argc = int(GetArgsLength(obj));
|
||||
for (int i = -2; i != argc; i++) {
|
||||
jsid id = (i == -2)
|
||||
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
|
||||
@ -664,35 +653,14 @@ args_enumerate(JSContext *cx, JSObject *obj)
|
||||
JSProperty *prop;
|
||||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
||||
return false;
|
||||
|
||||
/* prop is null when the property was deleted. */
|
||||
if (prop)
|
||||
pobj->dropProperty(cx, prop);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool JS_FASTCALL
|
||||
js_PutArguments(JSContext *cx, JSObject *argsobj, uint32 length, JSObject *callee, jsval *args)
|
||||
{
|
||||
if (!js_DefineProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
|
||||
INT_TO_JSVAL(length), ArgGetter, ArgSetter, 0, NULL)) {
|
||||
return false;
|
||||
}
|
||||
if (!js_DefineProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
|
||||
OBJECT_TO_JSVAL(callee), ArgGetter, ArgSetter, 0, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uintN i = 0; i < length; ++i) {
|
||||
if (!js_DefineProperty(cx, argsobj, INT_TO_JSID(i), args[i],
|
||||
ArgGetter, ArgSetter, 0, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, UINT32, OBJECT, JSVALPTR, 0, 0)
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* If a generator-iterator's arguments or call object escapes, it needs to
|
||||
@ -701,9 +669,10 @@ JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, UINT32, OBJ
|
||||
static void
|
||||
args_or_call_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass ||
|
||||
STOBJ_GET_CLASS(obj) == &js_CallClass);
|
||||
|
||||
fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj);
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp && (fp->flags & JSFRAME_GENERATOR)) {
|
||||
JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj,
|
||||
"FRAME_TO_GENERATOR(fp)->obj");
|
||||
@ -713,6 +682,13 @@ args_or_call_trace(JSTracer *trc, JSObject *obj)
|
||||
# define args_or_call_trace NULL
|
||||
#endif
|
||||
|
||||
static uint32
|
||||
args_reserveSlots(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
|
||||
return GetArgsLength(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Arguments class is not initialized via JS_InitClass, and must not be,
|
||||
* because its name is "Object". Per ECMA, that causes instances of it to
|
||||
@ -726,7 +702,8 @@ args_or_call_trace(JSTracer *trc, JSObject *obj)
|
||||
*/
|
||||
JSClass js_ArgumentsClass = {
|
||||
js_Object_str,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ARGS_CLASS_FIXED_RESERVED_SLOTS) |
|
||||
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||
JS_PropertyStub, args_delProperty,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
@ -735,12 +712,12 @@ JSClass js_ArgumentsClass = {
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
JS_CLASS_TRACE(args_or_call_trace), NULL
|
||||
JS_CLASS_TRACE(args_or_call_trace), args_reserveSlots
|
||||
};
|
||||
|
||||
#define JSSLOT_CALLEE (JSSLOT_PRIVATE + 1)
|
||||
#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
|
||||
#define CALL_CLASS_FIXED_RESERVED_SLOTS 2
|
||||
const uint32 JSSLOT_CALLEE = JSSLOT_PRIVATE + 1;
|
||||
const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2;
|
||||
const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS = 2;
|
||||
|
||||
/*
|
||||
* A Declarative Environment object stores its active JSStackFrame pointer in
|
||||
@ -775,7 +752,7 @@ CheckForEscapingClosure(JSContext *cx, JSObject *obj, jsval *vp)
|
||||
if (fun->needsWrapper()) {
|
||||
js_LeaveTrace(cx);
|
||||
|
||||
JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp) {
|
||||
JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun);
|
||||
if (!wrapper)
|
||||
@ -832,8 +809,9 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
/* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
|
||||
fp->scopeChain = env;
|
||||
JS_ASSERT(fp->argv);
|
||||
if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
|
||||
OBJECT_TO_JSVAL(fp->callee),
|
||||
fp->argv[-2],
|
||||
CalleeGetter, NULL,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY,
|
||||
0, 0, NULL)) {
|
||||
@ -848,8 +826,9 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
}
|
||||
|
||||
callobj->setPrivate(fp);
|
||||
JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee));
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee));
|
||||
JS_ASSERT(fp->argv);
|
||||
JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fp->argv[-2])));
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, fp->argv[-2]);
|
||||
fp->callobj = callobj;
|
||||
|
||||
/*
|
||||
@ -876,20 +855,17 @@ GetCallObjectFunction(JSObject *obj)
|
||||
return GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v));
|
||||
}
|
||||
|
||||
JSBool
|
||||
void
|
||||
js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *callobj = fp->callobj;
|
||||
JS_ASSERT(callobj);
|
||||
|
||||
/*
|
||||
* Get the arguments object to snapshot fp's actual argument values.
|
||||
*/
|
||||
JSBool ok = JS_TRUE;
|
||||
/* Get the arguments object to snapshot fp's actual argument values. */
|
||||
if (fp->argsobj) {
|
||||
if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS))
|
||||
if (!(fp->flags & JSFRAME_OVERRIDE_ARGS))
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS, fp->argsobj);
|
||||
ok &= js_PutArgsObject(cx, fp);
|
||||
js_PutArgsObject(cx, fp);
|
||||
}
|
||||
|
||||
JSFunction *fun = fp->fun;
|
||||
@ -903,6 +879,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
|
||||
1 + CALL_CLASS_FIXED_RESERVED_SLOTS);
|
||||
if (n != 0) {
|
||||
JS_ASSERT(STOBJ_NSLOTS(callobj) >= JS_INITIAL_NSLOTS + n);
|
||||
n += JS_INITIAL_NSLOTS;
|
||||
JS_LOCK_OBJ(cx, callobj);
|
||||
memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
|
||||
@ -922,7 +899,6 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
callobj->setPrivate(NULL);
|
||||
fp->callobj = NULL;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -1004,10 +980,10 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
||||
if (kind == JSCPK_ARGUMENTS) {
|
||||
if (setter) {
|
||||
if (fp)
|
||||
SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS);
|
||||
fp->flags |= JSFRAME_OVERRIDE_ARGS;
|
||||
STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp);
|
||||
} else {
|
||||
if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
|
||||
if (fp && !(fp->flags & JSFRAME_OVERRIDE_ARGS)) {
|
||||
JSObject *argsobj;
|
||||
|
||||
argsobj = js_GetArgsObject(cx, fp);
|
||||
@ -1217,7 +1193,8 @@ call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
if (fp) {
|
||||
JS_ASSERT(fp->fun);
|
||||
*vp = OBJECT_TO_JSVAL(fp->callee);
|
||||
JS_ASSERT(fp->argv);
|
||||
*vp = fp->argv[-2];
|
||||
}
|
||||
}
|
||||
return JS_TRUE;
|
||||
@ -1247,6 +1224,15 @@ JS_FRIEND_DATA(JSClass) js_CallClass = {
|
||||
JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
|
||||
};
|
||||
|
||||
/* Generic function tinyids. */
|
||||
enum {
|
||||
FUN_ARGUMENTS = -1, /* predefined arguments local variable */
|
||||
FUN_LENGTH = -2, /* number of actual args, arity if inactive */
|
||||
FUN_ARITY = -3, /* number of formal parameters; desired argc */
|
||||
FUN_NAME = -4, /* function name, "" if anonymous */
|
||||
FUN_CALLER = -5 /* Function.prototype.caller, backward compat */
|
||||
};
|
||||
|
||||
static JSBool
|
||||
fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
@ -1261,7 +1247,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
|
||||
/*
|
||||
* Loop because getter and setter can be delegated from another class,
|
||||
* but loop only for ARGS_LENGTH because we must pretend that f.length
|
||||
* but loop only for FUN_LENGTH because we must pretend that f.length
|
||||
* is in each function instance f, per ECMA-262, instead of only in the
|
||||
* Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
|
||||
* to make it appear so).
|
||||
@ -1271,7 +1257,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
*
|
||||
* It's important to allow delegating objects, even though they inherit
|
||||
* this getter (fun_getProperty), to override arguments, arity, caller,
|
||||
* and name. If we didn't return early for slot != ARGS_LENGTH, we would
|
||||
* and name. If we didn't return early for slot != FUN_LENGTH, we would
|
||||
* clobber *vp with the native property value, instead of letting script
|
||||
* override that value in delegating objects.
|
||||
*
|
||||
@ -1281,7 +1267,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
*/
|
||||
while (!(fun = (JSFunction *)
|
||||
JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
|
||||
if (slot != ARGS_LENGTH)
|
||||
if (slot != FUN_LENGTH)
|
||||
return JS_TRUE;
|
||||
obj = OBJ_GET_PROTO(cx, obj);
|
||||
if (!obj)
|
||||
@ -1296,7 +1282,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
}
|
||||
|
||||
switch (slot) {
|
||||
case CALL_ARGUMENTS:
|
||||
case FUN_ARGUMENTS:
|
||||
/* Warn if strict about f.arguments or equivalent unqualified uses. */
|
||||
if (!JS_ReportErrorFlagsAndNumber(cx,
|
||||
JSREPORT_WARNING | JSREPORT_STRICT,
|
||||
@ -1313,7 +1299,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
}
|
||||
break;
|
||||
|
||||
case ARGS_LENGTH:
|
||||
case FUN_LENGTH:
|
||||
case FUN_ARITY:
|
||||
*vp = INT_TO_JSVAL((jsint)fun->nargs);
|
||||
break;
|
||||
@ -1341,7 +1327,8 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
*vp = OBJECT_TO_JSVAL(fp->down->callee);
|
||||
JS_ASSERT(fp->down->argv);
|
||||
*vp = fp->down->argv[-2];
|
||||
} else {
|
||||
*vp = JSVAL_NULL;
|
||||
}
|
||||
@ -1382,7 +1369,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
|
||||
|
||||
static JSPropertySpec function_props[] = {
|
||||
{js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
|
||||
{js_length_str, FUN_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
|
||||
@ -1394,7 +1381,7 @@ typedef struct LazyFunctionProp {
|
||||
|
||||
/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
|
||||
static LazyFunctionProp lazy_function_props[] = {
|
||||
{ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT},
|
||||
{ATOM_OFFSET(arguments), FUN_ARGUMENTS, JSPROP_PERMANENT},
|
||||
{ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
|
||||
{ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
|
||||
{ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
|
||||
@ -1715,8 +1702,8 @@ static JSBool
|
||||
fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
{
|
||||
jsval pval;
|
||||
|
||||
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &pval))
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
|
||||
if (!obj->getProperty(cx, id, &pval))
|
||||
return JS_FALSE;
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(pval)) {
|
||||
@ -1763,10 +1750,8 @@ fun_trace(JSTracer *trc, JSObject *obj)
|
||||
static void
|
||||
fun_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSFunction *fun;
|
||||
|
||||
/* Ignore newborn and cloned function objects. */
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
JSFunction *fun = (JSFunction *) obj->getPrivate();
|
||||
if (!fun || FUN_OBJECT(fun) != obj)
|
||||
return;
|
||||
|
||||
@ -2023,7 +2008,7 @@ js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
|
||||
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, JS_ARGS_LENGTH_MAX);
|
||||
invokevp = js_AllocStack(cx, 2 + argc, &mark);
|
||||
if (!invokevp)
|
||||
return JS_FALSE;
|
||||
@ -2068,8 +2053,8 @@ fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!js_GetLengthProperty(cx, aobj, &length))
|
||||
return JS_FALSE;
|
||||
|
||||
if (length >= ARRAY_INIT_LIMIT)
|
||||
length = ARRAY_INIT_LIMIT - 1;
|
||||
if (length > JS_ARGS_LENGTH_MAX)
|
||||
length = JS_ARGS_LENGTH_MAX;
|
||||
invokevp = js_AllocStack(cx, 2 + length, &mark);
|
||||
if (!invokevp)
|
||||
return JS_FALSE;
|
||||
@ -2116,7 +2101,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
const char *filename;
|
||||
JSBool ok;
|
||||
JSString *str, *arg;
|
||||
JSTokenStream ts;
|
||||
JSTokenStream ts(cx);
|
||||
JSPrincipals *principals;
|
||||
jschar *collected_args, *cp;
|
||||
void *mark;
|
||||
@ -2252,8 +2237,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
}
|
||||
|
||||
/* Initialize a tokenstream that reads from the given string. */
|
||||
if (!js_InitTokenStream(cx, &ts, collected_args, args_length,
|
||||
NULL, filename, lineno)) {
|
||||
if (!ts.init(cx, collected_args, args_length, NULL, filename, lineno)) {
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -2316,7 +2300,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_FORMAL);
|
||||
}
|
||||
js_CloseTokenStream(cx, &ts);
|
||||
ts.close(cx);
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
if (state != OK)
|
||||
return JS_FALSE;
|
||||
@ -2512,7 +2496,9 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
if (!obj->defineProperty(cx, ATOM_TO_JSID(atom), OBJECT_TO_JSVAL(FUN_OBJECT(fun)), gsop, gsop,
|
||||
if (!obj->defineProperty(cx, ATOM_TO_JSID(atom),
|
||||
OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
|
||||
gsop, gsop,
|
||||
attrs & ~JSFUN_FLAGS_MASK, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags);
|
||||
extern JSObject *
|
||||
js_GetCallObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
extern void
|
||||
js_PutCallObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
@ -316,7 +316,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp);
|
||||
extern JSObject *
|
||||
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
extern void
|
||||
js_PutArgsObject(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
|
@ -2871,10 +2871,8 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
|
||||
(fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags)));
|
||||
JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this");
|
||||
|
||||
if (fp->callee)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->callee, "callee");
|
||||
|
||||
if (fp->argv) {
|
||||
JS_CALL_VALUE_TRACER(trc, fp->argv[-2], "callee");
|
||||
nslots = fp->argc;
|
||||
skip = 0;
|
||||
if (fp->fun) {
|
||||
@ -3019,10 +3017,6 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
|
||||
/* Avoid keeping GC-ed junk stored in JSContext.exception. */
|
||||
acx->exception = JSVAL_NULL;
|
||||
}
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (acx->rval2set)
|
||||
JS_CALL_VALUE_TRACER(trc, acx->rval2, "rval2");
|
||||
#endif
|
||||
|
||||
for (sh = acx->stackHeaders; sh; sh = sh->down) {
|
||||
METER(trc->context->runtime->gcStats.stackseg++);
|
||||
@ -3116,6 +3110,8 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
|
||||
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
|
||||
js_TraceContext(trc, acx);
|
||||
|
||||
js_TraceThreads(rt, trc);
|
||||
|
||||
if (rt->gcExtraRootsTraceOp)
|
||||
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
|
||||
|
||||
|
@ -1103,6 +1103,8 @@ js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
|
||||
JSInterpreterHook hook;
|
||||
void *hookData;
|
||||
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
|
||||
/* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
|
||||
JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
|
||||
JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
|
||||
@ -1220,10 +1222,6 @@ have_fun:
|
||||
JSBool alreadyThrowing = cx->throwing;
|
||||
#endif
|
||||
JS_ASSERT(nslots == 0);
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
/* Set by JS_SetCallReturnValue2, used to return reference types. */
|
||||
cx->rval2set = JS_FALSE;
|
||||
#endif
|
||||
ok = ((JSFastNative) native)(cx, argc, vp);
|
||||
JS_RUNTIME_METER(cx->runtime, nativeCalls);
|
||||
#ifdef DEBUG_NOT_THROWING
|
||||
@ -1291,7 +1289,6 @@ have_fun:
|
||||
frame.callobj = NULL;
|
||||
frame.argsobj = NULL;
|
||||
frame.script = script;
|
||||
frame.callee = funobj;
|
||||
frame.fun = fun;
|
||||
frame.argc = argc;
|
||||
frame.argv = argv;
|
||||
@ -1364,10 +1361,6 @@ have_fun:
|
||||
JSBool alreadyThrowing = cx->throwing;
|
||||
#endif
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
/* Set by JS_SetCallReturnValue2, used to return reference types. */
|
||||
cx->rval2set = JS_FALSE;
|
||||
#endif
|
||||
ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
|
||||
JS_RUNTIME_METER(cx->runtime, nativeCalls);
|
||||
#ifdef DEBUG_NOT_THROWING
|
||||
@ -1394,7 +1387,7 @@ out:
|
||||
hook(cx, &frame, JS_FALSE, &ok, hookData);
|
||||
}
|
||||
|
||||
ok &= frame.putActivationObjects(cx);
|
||||
frame.putActivationObjects(cx);
|
||||
|
||||
*vp = frame.rval;
|
||||
|
||||
@ -1523,7 +1516,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
frame.callobj = down->callobj;
|
||||
frame.argsobj = down->argsobj;
|
||||
frame.varobj = down->varobj;
|
||||
frame.callee = down->callee;
|
||||
frame.fun = down->fun;
|
||||
frame.thisp = down->thisp;
|
||||
if (down->flags & JSFRAME_COMPUTED_THIS)
|
||||
@ -1542,7 +1534,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
obj = tmp;
|
||||
}
|
||||
frame.varobj = obj;
|
||||
frame.callee = NULL;
|
||||
frame.fun = NULL;
|
||||
frame.thisp = chain;
|
||||
frame.argc = 0;
|
||||
@ -2786,7 +2777,7 @@ js_Interpret(JSContext *cx)
|
||||
the recorder to be destroyed when we return. */
|
||||
if (tr) {
|
||||
if (tr->wasDeepAborted())
|
||||
tr->removeFragmentoReferences();
|
||||
tr->removeFragmentReferences();
|
||||
else
|
||||
tr->pushAbortStack();
|
||||
}
|
||||
@ -3027,7 +3018,7 @@ js_Interpret(JSContext *cx)
|
||||
/********************** Here we include the operations ***********************/
|
||||
#include "jsops.cpp"
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
#if !JS_THREADED_INTERP
|
||||
default:
|
||||
#endif
|
||||
|
@ -71,9 +71,9 @@ struct JSStackFrame {
|
||||
jsbytecode *imacpc; /* null or interpreter macro call pc */
|
||||
jsval *slots; /* variables, locals and operand stack */
|
||||
JSObject *callobj; /* lazily created Call object */
|
||||
jsval argsobj; /* lazily created arguments object, must be JSVAL_OBJECT */
|
||||
jsval argsobj; /* lazily created arguments object, must be
|
||||
JSVAL_OBJECT */
|
||||
JSObject *varobj; /* variables object, where vars go */
|
||||
JSObject *callee; /* function or script object */
|
||||
JSScript *script; /* script being interpreted */
|
||||
JSFunction *fun; /* function being called or null */
|
||||
JSObject *thisp; /* "this" pointer if in method */
|
||||
@ -132,21 +132,21 @@ struct JSStackFrame {
|
||||
|
||||
inline void assertValidStackDepth(uintN depth);
|
||||
|
||||
JSBool putActivationObjects(JSContext *cx) {
|
||||
void putActivationObjects(JSContext *cx) {
|
||||
/*
|
||||
* The order of calls here is important as js_PutCallObject needs to
|
||||
* access argsobj.
|
||||
*/
|
||||
JSBool ok;
|
||||
if (callobj) {
|
||||
ok = js_PutCallObject(cx, this);
|
||||
js_PutCallObject(cx, this);
|
||||
JS_ASSERT(!argsobj);
|
||||
} else if (argsobj) {
|
||||
ok = js_PutArgsObject(cx, this);
|
||||
} else {
|
||||
ok = JS_TRUE;
|
||||
js_PutArgsObject(cx, this);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
JSObject *callee() {
|
||||
return argv ? JSVAL_TO_OBJECT(argv[-2]) : NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@ -202,9 +202,7 @@ typedef struct JSInlineFrame {
|
||||
#define JSFRAME_YIELDING 0x40 /* js_Interpret dispatched JSOP_YIELD */
|
||||
#define JSFRAME_ITERATOR 0x80 /* trying to get an iterator for for-in */
|
||||
#define JSFRAME_GENERATOR 0x200 /* frame belongs to generator-iterator */
|
||||
|
||||
#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */
|
||||
#define JSFRAME_OVERRIDE_BITS 8
|
||||
#define JSFRAME_OVERRIDE_ARGS 0x400 /* overridden arguments local variable */
|
||||
|
||||
#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL)
|
||||
|
||||
|
@ -735,7 +735,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
/* Copy call-invariant script and function references. */
|
||||
gen->frame.script = fp->script;
|
||||
gen->frame.callee = fp->callee;
|
||||
gen->frame.fun = fp->fun;
|
||||
|
||||
/* Use slots to carve space out of gen->slots. */
|
||||
@ -1014,6 +1013,7 @@ js_InitIteratorClasses(JSContext *cx, JSObject *obj)
|
||||
if (!proto)
|
||||
return NULL;
|
||||
STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL);
|
||||
STOBJ_SET_SLOT(proto, JSSLOT_ITER_FLAGS, JSVAL_ZERO);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Initialize the generator internals if configured. */
|
||||
|
@ -237,8 +237,8 @@ math_ceil_kernel(jsdouble x)
|
||||
return ceil(x);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
math_ceil(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSBool
|
||||
js_math_ceil(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsdouble x, z;
|
||||
|
||||
@ -297,8 +297,8 @@ math_exp(JSContext *cx, uintN argc, jsval *vp)
|
||||
return js_NewNumberInRootedValue(cx, z, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
math_floor(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSBool
|
||||
js_math_floor(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsdouble x, z;
|
||||
|
||||
@ -335,8 +335,8 @@ math_log(JSContext *cx, uintN argc, jsval *vp)
|
||||
return js_NewNumberInRootedValue(cx, z, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
math_max(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSBool
|
||||
js_math_max(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsdouble x, z = *cx->runtime->jsNegativeInfinity;
|
||||
jsval *argv;
|
||||
@ -365,8 +365,8 @@ math_max(JSContext *cx, uintN argc, jsval *vp)
|
||||
return js_NewNumberInRootedValue(cx, z, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
math_min(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSBool
|
||||
js_math_min(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsdouble x, z = *cx->runtime->jsPositiveInfinity;
|
||||
jsval *argv;
|
||||
@ -427,90 +427,47 @@ math_pow(JSContext *cx, uintN argc, jsval *vp)
|
||||
return js_NewNumberInRootedValue(cx, z, vp);
|
||||
}
|
||||
|
||||
static const int64 RNG_MULTIPLIER = 0x5DEECE66DLL;
|
||||
static const int64 RNG_ADDEND = 0xBLL;
|
||||
static const int64 RNG_MASK = (1LL << 48) - 1;
|
||||
static const jsdouble RNG_DSCALE = jsdouble(1LL << 53);
|
||||
|
||||
/*
|
||||
* Math.random() support, lifted from java.util.Random.java.
|
||||
*/
|
||||
static void
|
||||
random_setSeed(JSRuntime *rt, int64 seed)
|
||||
static inline void
|
||||
random_setSeed(JSThreadData *data, int64 seed)
|
||||
{
|
||||
int64 tmp;
|
||||
|
||||
JSLL_I2L(tmp, 1000);
|
||||
JSLL_DIV(seed, seed, tmp);
|
||||
JSLL_XOR(tmp, seed, rt->rngMultiplier);
|
||||
JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
|
||||
data->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
|
||||
}
|
||||
|
||||
void
|
||||
js_random_init(JSRuntime *rt)
|
||||
js_InitRandom(JSThreadData *data)
|
||||
{
|
||||
int64 tmp, tmp2;
|
||||
|
||||
/* Do at most once. */
|
||||
if (rt->rngInitialized)
|
||||
return;
|
||||
rt->rngInitialized = JS_TRUE;
|
||||
|
||||
/* rt->rngMultiplier = 0x5DEECE66DL */
|
||||
JSLL_ISHL(tmp, 0x5, 32);
|
||||
JSLL_UI2L(tmp2, 0xDEECE66DL);
|
||||
JSLL_OR(rt->rngMultiplier, tmp, tmp2);
|
||||
|
||||
/* rt->rngAddend = 0xBL */
|
||||
JSLL_I2L(rt->rngAddend, 0xBL);
|
||||
|
||||
/* rt->rngMask = (1L << 48) - 1 */
|
||||
JSLL_I2L(tmp, 1);
|
||||
JSLL_SHL(tmp2, tmp, 48);
|
||||
JSLL_SUB(rt->rngMask, tmp2, tmp);
|
||||
|
||||
/* rt->rngDscale = (jsdouble)(1L << 53) */
|
||||
JSLL_SHL(tmp2, tmp, 53);
|
||||
JSLL_L2D(rt->rngDscale, tmp2);
|
||||
|
||||
/* Finally, set the seed from current time. */
|
||||
random_setSeed(rt, PRMJ_Now());
|
||||
random_setSeed(data, PRMJ_Now() / 1000);
|
||||
}
|
||||
|
||||
static uint32
|
||||
random_next(JSRuntime *rt, int bits)
|
||||
static inline uint64
|
||||
random_next(JSThreadData *data, int bits)
|
||||
{
|
||||
int64 nextseed, tmp;
|
||||
uint32 retval;
|
||||
|
||||
JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
|
||||
JSLL_ADD(nextseed, nextseed, rt->rngAddend);
|
||||
JSLL_AND(nextseed, nextseed, rt->rngMask);
|
||||
rt->rngSeed = nextseed;
|
||||
JSLL_USHR(tmp, nextseed, 48 - bits);
|
||||
JSLL_L2I(retval, tmp);
|
||||
return retval;
|
||||
uint64 nextseed = data->rngSeed * RNG_MULTIPLIER;
|
||||
nextseed += RNG_ADDEND;
|
||||
nextseed &= RNG_MASK;
|
||||
data->rngSeed = nextseed;
|
||||
return nextseed >> (48 - bits);
|
||||
}
|
||||
|
||||
jsdouble
|
||||
js_random_nextDouble(JSRuntime *rt)
|
||||
static inline jsdouble
|
||||
random_nextDouble(JSThreadData *data)
|
||||
{
|
||||
int64 tmp, tmp2;
|
||||
jsdouble d;
|
||||
|
||||
JSLL_ISHL(tmp, random_next(rt, 26), 27);
|
||||
JSLL_UI2L(tmp2, random_next(rt, 27));
|
||||
JSLL_ADD(tmp, tmp, tmp2);
|
||||
JSLL_L2D(d, tmp);
|
||||
return d / rt->rngDscale;
|
||||
return jsdouble((random_next(data, 26) << 27) + random_next(data, 27)) / RNG_DSCALE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
math_random(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
jsdouble z;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_LOCK_RUNTIME(rt);
|
||||
js_random_init(rt);
|
||||
z = js_random_nextDouble(rt);
|
||||
JS_UNLOCK_RUNTIME(rt);
|
||||
jsdouble z = random_nextDouble(JS_THREAD_DATA(cx));
|
||||
return js_NewNumberInRootedValue(cx, z, vp);
|
||||
}
|
||||
|
||||
@ -529,8 +486,8 @@ js_copysign(double x, double y)
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSBool
|
||||
math_round(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSBool
|
||||
js_math_round(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
jsdouble x, z;
|
||||
|
||||
@ -615,7 +572,6 @@ MATH_BUILTIN_1(atan)
|
||||
MATH_BUILTIN_1(sin)
|
||||
MATH_BUILTIN_1(cos)
|
||||
MATH_BUILTIN_1(sqrt)
|
||||
MATH_BUILTIN_1(floor)
|
||||
MATH_BUILTIN_1(tan)
|
||||
|
||||
static jsdouble FASTCALL
|
||||
@ -716,13 +672,9 @@ math_pow_tn(jsdouble d, jsdouble p)
|
||||
}
|
||||
|
||||
static jsdouble FASTCALL
|
||||
math_random_tn(JSRuntime* rt)
|
||||
math_random_tn(JSContext *cx)
|
||||
{
|
||||
JS_LOCK_RUNTIME(rt);
|
||||
js_random_init(rt);
|
||||
jsdouble z = js_random_nextDouble(rt);
|
||||
JS_UNLOCK_RUNTIME(rt);
|
||||
return z;
|
||||
return random_nextDouble(JS_THREAD_DATA(cx));
|
||||
}
|
||||
|
||||
static jsdouble FASTCALL
|
||||
@ -737,25 +689,33 @@ math_ceil_tn(jsdouble x)
|
||||
return math_ceil_kernel(x);
|
||||
}
|
||||
|
||||
static jsdouble FASTCALL
|
||||
math_floor_tn(jsdouble x)
|
||||
{
|
||||
return floor(x);
|
||||
}
|
||||
|
||||
JS_DEFINE_TRCINFO_1(math_acos,
|
||||
(1, (static, DOUBLE, math_acos_tn, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_asin,
|
||||
(1, (static, DOUBLE, math_asin_tn, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_atan2,
|
||||
(2, (static, DOUBLE, math_atan2_kernel, DOUBLE, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(js_math_floor,
|
||||
(1, (static, DOUBLE, math_floor_tn, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_log,
|
||||
(1, (static, DOUBLE, math_log_tn, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_max,
|
||||
JS_DEFINE_TRCINFO_1(js_math_max,
|
||||
(2, (static, DOUBLE, math_max_tn, DOUBLE, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_min,
|
||||
JS_DEFINE_TRCINFO_1(js_math_min,
|
||||
(2, (static, DOUBLE, math_min_tn, DOUBLE, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_pow,
|
||||
(2, (static, DOUBLE, math_pow_tn, DOUBLE, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_random,
|
||||
(1, (static, DOUBLE, math_random_tn, RUNTIME, 0, 0)))
|
||||
JS_DEFINE_TRCINFO_1(math_round,
|
||||
(1, (static, DOUBLE, math_random_tn, CONTEXT, 0, 0)))
|
||||
JS_DEFINE_TRCINFO_1(js_math_round,
|
||||
(1, (static, DOUBLE, math_round_tn, DOUBLE, 1, 1)))
|
||||
JS_DEFINE_TRCINFO_1(math_ceil,
|
||||
JS_DEFINE_TRCINFO_1(js_math_ceil,
|
||||
(1, (static, DOUBLE, math_ceil_tn, DOUBLE, 1, 1)))
|
||||
|
||||
#endif /* JS_TRACER */
|
||||
@ -769,16 +729,16 @@ static JSFunctionSpec math_static_methods[] = {
|
||||
JS_TN("asin", math_asin, 1, 0, math_asin_trcinfo),
|
||||
JS_TN("atan", math_atan, 1, 0, math_atan_trcinfo),
|
||||
JS_TN("atan2", math_atan2, 2, 0, math_atan2_trcinfo),
|
||||
JS_TN("ceil", math_ceil, 1, 0, math_ceil_trcinfo),
|
||||
JS_TN("ceil", js_math_ceil, 1, 0, js_math_ceil_trcinfo),
|
||||
JS_TN("cos", math_cos, 1, 0, math_cos_trcinfo),
|
||||
JS_TN("exp", math_exp, 1, 0, math_exp_trcinfo),
|
||||
JS_TN("floor", math_floor, 1, 0, math_floor_trcinfo),
|
||||
JS_TN("floor", js_math_floor, 1, 0, js_math_floor_trcinfo),
|
||||
JS_TN("log", math_log, 1, 0, math_log_trcinfo),
|
||||
JS_TN("max", math_max, 2, 0, math_max_trcinfo),
|
||||
JS_TN("min", math_min, 2, 0, math_min_trcinfo),
|
||||
JS_TN("max", js_math_max, 2, 0, js_math_max_trcinfo),
|
||||
JS_TN("min", js_math_min, 2, 0, js_math_min_trcinfo),
|
||||
JS_TN("pow", math_pow, 2, 0, math_pow_trcinfo),
|
||||
JS_TN("random", math_random, 0, 0, math_random_trcinfo),
|
||||
JS_TN("round", math_round, 1, 0, math_round_trcinfo),
|
||||
JS_TN("round", js_math_round, 1, 0, js_math_round_trcinfo),
|
||||
JS_TN("sin", math_sin, 1, 0, math_sin_trcinfo),
|
||||
JS_TN("sqrt", math_sqrt, 1, 0, math_sqrt_trcinfo),
|
||||
JS_TN("tan", math_tan, 1, 0, math_tan_trcinfo),
|
||||
|
@ -51,10 +51,22 @@ extern JSObject *
|
||||
js_InitMathClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern void
|
||||
js_random_init(JSRuntime *rt);
|
||||
js_InitRandom(JSThreadData *data);
|
||||
|
||||
extern jsdouble
|
||||
js_random_nextDouble(JSRuntime *rt);
|
||||
extern JSBool
|
||||
js_math_ceil(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_math_floor(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_math_max(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_math_min(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
extern JSBool
|
||||
js_math_round(JSContext *cx, uintN argc, jsval *vp);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
@ -866,7 +866,7 @@ js_NumberToString(JSContext *cx, jsdouble d)
|
||||
}
|
||||
|
||||
JSBool JS_FASTCALL
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb)
|
||||
{
|
||||
/* Convert to C-string. */
|
||||
static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE;
|
||||
@ -887,10 +887,10 @@ js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
*/
|
||||
size_t cstrlen = strlen(cstr);
|
||||
JS_ASSERT(cstrlen < arrSize);
|
||||
size_t sizeBefore = buf.size();
|
||||
if (!buf.growBy(cstrlen))
|
||||
size_t sizeBefore = cb.length();
|
||||
if (!cb.growBy(cstrlen))
|
||||
return JS_FALSE;
|
||||
jschar *appendBegin = buf.begin() + sizeBefore;
|
||||
jschar *appendBegin = cb.begin() + sizeBefore;
|
||||
#ifdef DEBUG
|
||||
size_t oldcstrlen = cstrlen;
|
||||
JSBool ok =
|
||||
@ -927,6 +927,14 @@ js_ValueToNumber(JSContext *cx, jsval *vp)
|
||||
* octal).
|
||||
*/
|
||||
str->getCharsAndEnd(bp, end);
|
||||
|
||||
/* ECMA doesn't allow signed hex numbers (bug 273467). */
|
||||
bp = js_SkipWhiteSpace(bp, end);
|
||||
if (bp + 2 < end && (*bp == '-' || *bp == '+') &&
|
||||
bp[1] == '0' && (bp[2] == 'X' || bp[2] == 'x')) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((!js_strtod(cx, bp, end, &ep, &d) ||
|
||||
js_SkipWhiteSpace(ep, end) != end) &&
|
||||
(!js_strtointeger(cx, bp, end, &ep, 0, &d) ||
|
||||
|
@ -194,7 +194,7 @@ js_NumberToString(JSContext *cx, jsdouble d);
|
||||
* append to the given buffer.
|
||||
*/
|
||||
extern JSBool JS_FASTCALL
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
|
||||
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb);
|
||||
|
||||
/*
|
||||
* Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an
|
||||
|
@ -1946,11 +1946,17 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
obj = js_ValueToNonNullObject(cx, vp[2]);
|
||||
if (!obj)
|
||||
if (JSVAL_IS_PRIMITIVE(vp[2])) {
|
||||
char *bytes = js_DecompileValueGenerator(cx, -argc, vp[2], NULL);
|
||||
if (!bytes)
|
||||
return JS_FALSE;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
|
||||
JS_free(cx, bytes);
|
||||
return JS_FALSE;
|
||||
vp[2] = OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
obj = JSVAL_TO_OBJECT(vp[2]);
|
||||
return obj->checkAccess(cx, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
|
||||
JSACC_PROTO, vp, &attrs);
|
||||
}
|
||||
@ -2284,7 +2290,7 @@ Detecting(JSContext *cx, jsbytecode *pc)
|
||||
script = cx->fp->script;
|
||||
endpc = script->code + script->length;
|
||||
for (;; pc += js_CodeSpec[op].length) {
|
||||
JS_ASSERT(pc < endpc);
|
||||
JS_ASSERT_IF(!cx->fp->imacpc, script->code <= pc && pc < endpc);
|
||||
|
||||
/* General case: a branch or equality op follows the access. */
|
||||
op = js_GetOpcode(cx, script, pc);
|
||||
@ -4254,7 +4260,8 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult,
|
||||
flags = JSREPORT_ERROR;
|
||||
} else {
|
||||
if (!JS_HAS_STRICT_OPTION(cx) ||
|
||||
(op != JSOP_GETPROP && op != JSOP_GETELEM)) {
|
||||
(op != JSOP_GETPROP && op != JSOP_GETELEM) ||
|
||||
js_CurrentPCIsInImacro(cx)) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -6115,8 +6122,8 @@ js_DumpStackFrame(JSStackFrame *fp)
|
||||
|
||||
for (; fp; fp = fp->down) {
|
||||
fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
|
||||
if (fp->callee)
|
||||
dumpValue(OBJECT_TO_JSVAL(fp->callee));
|
||||
if (fp->argv)
|
||||
dumpValue(fp->argv[-2]);
|
||||
else
|
||||
fprintf(stderr, "global frame, no callee");
|
||||
fputc('\n', stderr);
|
||||
@ -6188,8 +6195,8 @@ js_DumpStackFrame(JSStackFrame *fp)
|
||||
fprintf(stderr, " iterator");
|
||||
if (fp->flags & JSFRAME_GENERATOR)
|
||||
fprintf(stderr, " generator");
|
||||
if ((fp->flags >> JSFRAME_OVERRIDE_SHIFT) & JS_BITMASK(JSFRAME_OVERRIDE_BITS))
|
||||
fprintf(stderr, " override_bits(0x%x)", (fp->flags >> JSFRAME_OVERRIDE_SHIFT) & JS_BITMASK(JSFRAME_OVERRIDE_BITS));
|
||||
if (fp->flags & JSFRAME_OVERRIDE_ARGS)
|
||||
fprintf(stderr, " overridden_args");
|
||||
fputc('\n', stderr);
|
||||
|
||||
if (fp->scopeChain)
|
||||
|
303
js/src/json.cpp
303
js/src/json.cpp
@ -58,11 +58,43 @@
|
||||
#include "jsstdint.h"
|
||||
#include "jsutil.h"
|
||||
#include "jsxml.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
||||
struct JSONParser
|
||||
{
|
||||
JSONParser(JSContext *cx)
|
||||
: hexChar(), numHex(), statep(), stateStack(), rootVal(), objectStack(),
|
||||
objectKey(cx), buffer(cx)
|
||||
{}
|
||||
|
||||
static JSONParser *create(JSContext *cx) {
|
||||
JSONParser *jp = (JSONParser*) cx->calloc(sizeof(JSONParser));
|
||||
if (!jp)
|
||||
return NULL;
|
||||
return new(jp) JSONParser(cx);
|
||||
}
|
||||
|
||||
static void destroy(JSContext *cx, JSONParser *jp) {
|
||||
jp->~JSONParser();
|
||||
cx->free(jp);
|
||||
}
|
||||
|
||||
/* Used while handling \uNNNN in strings */
|
||||
jschar hexChar;
|
||||
uint8 numHex;
|
||||
|
||||
JSONParserState *statep;
|
||||
JSONParserState stateStack[JSON_MAX_DEPTH];
|
||||
jsval *rootVal;
|
||||
JSObject *objectStack;
|
||||
JSTempVector<jschar> objectKey;
|
||||
JSTempVector<jschar> buffer;
|
||||
};
|
||||
|
||||
JSClass js_JSONClass = {
|
||||
js_JSON_str,
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
|
||||
@ -85,46 +117,16 @@ js_json_parse(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSONParser *jp = js_BeginJSONParse(cx, vp);
|
||||
JSBool ok = jp != NULL;
|
||||
if (ok) {
|
||||
ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
s->getCharsAndLength(chars, length);
|
||||
ok = js_ConsumeJSONText(cx, jp, chars, length);
|
||||
ok &= js_FinishJSONParse(cx, jp, reviver);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
class WriterContext
|
||||
{
|
||||
public:
|
||||
WriterContext(JSContext *cx) : cx(cx), didWrite(JS_FALSE)
|
||||
{
|
||||
js_InitStringBuffer(&sb);
|
||||
}
|
||||
|
||||
~WriterContext()
|
||||
{
|
||||
js_FinishStringBuffer(&sb);
|
||||
}
|
||||
|
||||
JSStringBuffer sb;
|
||||
JSContext *cx;
|
||||
JSBool didWrite;
|
||||
};
|
||||
|
||||
static JSBool
|
||||
WriteCallback(const jschar *buf, uint32 len, void *data)
|
||||
{
|
||||
WriterContext *wc = static_cast<WriterContext*>(data);
|
||||
wc->didWrite = JS_TRUE;
|
||||
|
||||
js_AppendUCString(&wc->sb, buf, len);
|
||||
if (!STRING_BUFFER_OK(&wc->sb)) {
|
||||
JS_ReportOutOfMemory(wc->cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
@ -138,20 +140,19 @@ js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!JS_ConvertArguments(cx, argc, argv, "v / o v", vp, &replacer, &space))
|
||||
return JS_FALSE;
|
||||
|
||||
WriterContext wc(cx);
|
||||
JSCharBuffer cb(cx);
|
||||
|
||||
if (!js_Stringify(cx, vp, replacer, space, &WriteCallback, &wc))
|
||||
if (!js_Stringify(cx, vp, replacer, space, cb))
|
||||
return JS_FALSE;
|
||||
|
||||
// XXX This can never happen to nsJSON.cpp, but the JSON object
|
||||
// needs to support returning undefined. So this is a little awkward
|
||||
// for the API, because we want to support streaming writers.
|
||||
if (wc.didWrite) {
|
||||
JSStringBuffer *sb = &wc.sb;
|
||||
JSString *s = JS_NewUCStringCopyN(cx, sb->base, STRING_BUFFER_OFFSET(sb));
|
||||
if (!s)
|
||||
if (!cb.empty()) {
|
||||
JSString *str = js_NewStringFromCharBuffer(cx, cb);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
*vp = STRING_TO_JSVAL(s);
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
} else {
|
||||
*vp = JSVAL_VOID;
|
||||
}
|
||||
@ -174,69 +175,59 @@ js_TryJSON(JSContext *cx, jsval *vp)
|
||||
}
|
||||
|
||||
|
||||
static const jschar quote = jschar('"');
|
||||
static const jschar backslash = jschar('\\');
|
||||
static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
|
||||
static const jschar null_ucstr[] = {'n', 'u', 'l', 'l'};
|
||||
static const jschar true_ucstr[] = {'t', 'r', 'u', 'e'};
|
||||
static const jschar false_ucstr[] = {'f', 'a', 'l', 's', 'e'};
|
||||
static const char quote = '\"';
|
||||
static const char backslash = '\\';
|
||||
static const char unicodeEscape[] = "\\u00";
|
||||
|
||||
static JSBool
|
||||
write_string(JSContext *cx, JSONWriteCallback callback, void *data, const jschar *buf, uint32 len)
|
||||
write_string(JSContext *cx, JSCharBuffer &cb, const jschar *buf, uint32 len)
|
||||
{
|
||||
if (!callback("e, 1, data))
|
||||
if (!cb.append(quote))
|
||||
return JS_FALSE;
|
||||
|
||||
uint32 mark = 0;
|
||||
uint32 i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (buf[i] == quote || buf[i] == backslash) {
|
||||
if (!callback(&buf[mark], i - mark, data) || !callback(&backslash, 1, data) ||
|
||||
!callback(&buf[i], 1, data)) {
|
||||
if (!cb.append(&buf[mark], i - mark) || !cb.append(backslash) ||
|
||||
!cb.append(buf[i])) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
mark = i + 1;
|
||||
} else if (buf[i] <= 31 || buf[i] == 127) {
|
||||
if (!callback(&buf[mark], i - mark, data) || !callback(unicodeEscape, 4, data))
|
||||
if (!cb.append(&buf[mark], i - mark) ||
|
||||
!js_AppendLiteral(cb, unicodeEscape)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
char ubuf[3];
|
||||
size_t len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]);
|
||||
JS_ASSERT(len == 2);
|
||||
jschar wbuf[3];
|
||||
size_t wbufSize = JS_ARRAY_LENGTH(wbuf);
|
||||
if (!js_InflateStringToBuffer(cx, ubuf, len, wbuf, &wbufSize) ||
|
||||
!callback(wbuf, wbufSize, data)) {
|
||||
!cb.append(wbuf, wbufSize)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
mark = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mark < len && !callback(&buf[mark], len - mark, data))
|
||||
if (mark < len && !cb.append(&buf[mark], len - mark))
|
||||
return JS_FALSE;
|
||||
|
||||
return callback("e, 1, data);
|
||||
return cb.append(quote);
|
||||
}
|
||||
|
||||
class StringifyContext
|
||||
{
|
||||
public:
|
||||
StringifyContext(JSONWriteCallback callback, JSObject *replacer, void *data)
|
||||
: callback(callback), replacer(replacer), data(data), depth(0)
|
||||
{
|
||||
js_InitStringBuffer(&gap);
|
||||
}
|
||||
StringifyContext(JSContext *cx, JSCharBuffer &cb, JSObject *replacer)
|
||||
: cb(cb), gap(cx), replacer(replacer), depth(0)
|
||||
{}
|
||||
|
||||
~StringifyContext()
|
||||
{
|
||||
if (STRING_BUFFER_OK(&gap))
|
||||
js_FinishStringBuffer(&gap);
|
||||
}
|
||||
|
||||
JSONWriteCallback callback;
|
||||
JSStringBuffer gap;
|
||||
JSCharBuffer &cb;
|
||||
JSCharBuffer gap;
|
||||
JSObject *replacer;
|
||||
void *data;
|
||||
uint32 depth;
|
||||
};
|
||||
|
||||
@ -248,12 +239,11 @@ static JSBool Str(JSContext *cx, jsid id, JSObject *holder,
|
||||
static JSBool
|
||||
WriteIndent(JSContext *cx, StringifyContext *scx, uint32 limit)
|
||||
{
|
||||
if (STRING_BUFFER_OFFSET(&scx->gap) > 0) {
|
||||
jschar c = jschar('\n');
|
||||
if (!scx->callback(&c, 1, scx->data))
|
||||
if (!scx->gap.empty()) {
|
||||
if (!scx->cb.append('\n'))
|
||||
return JS_FALSE;
|
||||
for (uint32 i = 0; i < limit; i++) {
|
||||
if (!scx->callback(scx->gap.base, STRING_BUFFER_OFFSET(&scx->gap), scx->data))
|
||||
if (!scx->cb.append(scx->gap.begin(), scx->gap.end()))
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
@ -266,8 +256,7 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
{
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
jschar c = jschar('{');
|
||||
if (!scx->callback(&c, 1, scx->data))
|
||||
if (!scx->cb.append('{'))
|
||||
return JS_FALSE;
|
||||
|
||||
jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
|
||||
@ -360,11 +349,9 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
continue;
|
||||
|
||||
// output a comma unless this is the first member to write
|
||||
if (memberWritten) {
|
||||
c = jschar(',');
|
||||
ok = scx->callback(&c, 1, scx->data);
|
||||
if (!ok)
|
||||
break;
|
||||
if (memberWritten && !scx->cb.append(',')) {
|
||||
ok = JS_FALSE;
|
||||
break;
|
||||
}
|
||||
memberWritten = JS_TRUE;
|
||||
|
||||
@ -378,12 +365,14 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
break;
|
||||
}
|
||||
|
||||
ok = write_string(cx, scx->callback, scx->data, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
s->getCharsAndLength(chars, length);
|
||||
ok = write_string(cx, scx->cb, chars, length);
|
||||
if (!ok)
|
||||
break;
|
||||
|
||||
c = jschar(':');
|
||||
ok = scx->callback(&c, 1, scx->data);
|
||||
ok = scx->cb.append(':');
|
||||
if (!ok)
|
||||
break;
|
||||
|
||||
@ -405,9 +394,7 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1))
|
||||
return JS_FALSE;
|
||||
|
||||
c = jschar('}');
|
||||
|
||||
return scx->callback(&c, 1, scx->data);
|
||||
return scx->cb.append('}');
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -415,8 +402,7 @@ JA(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
{
|
||||
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||
|
||||
jschar c = jschar('[');
|
||||
if (!scx->callback(&c, 1, scx->data))
|
||||
if (!scx->cb.append('['))
|
||||
return JS_FALSE;
|
||||
|
||||
jsuint length;
|
||||
@ -438,13 +424,12 @@ JA(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
return JS_FALSE;
|
||||
|
||||
if (outputValue == JSVAL_VOID) {
|
||||
if (!scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data))
|
||||
if (!js_AppendLiteral(scx->cb, "null"))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (i < length - 1) {
|
||||
c = jschar(',');
|
||||
if (!scx->callback(&c, 1, scx->data))
|
||||
if (!scx->cb.append(','))
|
||||
return JS_FALSE;
|
||||
if (!WriteIndent(cx, scx, scx->depth))
|
||||
return JS_FALSE;
|
||||
@ -454,9 +439,7 @@ JA(JSContext *cx, jsval *vp, StringifyContext *scx)
|
||||
if (length != 0 && !WriteIndent(cx, scx, scx->depth - 1))
|
||||
return JS_FALSE;
|
||||
|
||||
c = jschar(']');
|
||||
|
||||
return scx->callback(&c, 1, scx->data);
|
||||
return scx->cb.append(']');
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -493,32 +476,26 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp,
|
||||
}
|
||||
|
||||
if (JSVAL_IS_STRING(*vp)) {
|
||||
JSString *s = JSVAL_TO_STRING(*vp);
|
||||
return write_string(cx, scx->callback, scx->data, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
JSVAL_TO_STRING(*vp)->getCharsAndLength(chars, length);
|
||||
return write_string(cx, scx->cb, chars, length);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_NULL(*vp)) {
|
||||
return scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data);
|
||||
return js_AppendLiteral(scx->cb, "null");
|
||||
}
|
||||
|
||||
if (JSVAL_IS_BOOLEAN(*vp)) {
|
||||
uint32 len = JS_ARRAY_LENGTH(true_ucstr);
|
||||
const jschar *chars = true_ucstr;
|
||||
JSBool b = JSVAL_TO_BOOLEAN(*vp);
|
||||
|
||||
if (!b) {
|
||||
chars = false_ucstr;
|
||||
len = JS_ARRAY_LENGTH(false_ucstr);
|
||||
}
|
||||
|
||||
return scx->callback(chars, len, scx->data);
|
||||
return JSVAL_TO_BOOLEAN(*vp) ? js_AppendLiteral(scx->cb, "true")
|
||||
: js_AppendLiteral(scx->cb, "false");
|
||||
}
|
||||
|
||||
if (JSVAL_IS_NUMBER(*vp)) {
|
||||
if (JSVAL_IS_DOUBLE(*vp)) {
|
||||
jsdouble d = *JSVAL_TO_DOUBLE(*vp);
|
||||
if (!JSDOUBLE_IS_FINITE(d))
|
||||
return scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data);
|
||||
return js_AppendLiteral(scx->cb, "null");
|
||||
}
|
||||
|
||||
char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
|
||||
@ -534,7 +511,7 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp,
|
||||
if (!js_InflateStringToBuffer(cx, numStr, strlen(numStr), dstr, &dbufSize))
|
||||
return JS_FALSE;
|
||||
|
||||
return scx->callback(dstr, dbufSize, scx->data);
|
||||
return scx->cb.append(dstr, dbufSize);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_OBJECT(*vp) && !VALUE_IS_FUNCTION(cx, *vp) && !VALUE_IS_XML(cx, *vp)) {
|
||||
@ -552,45 +529,24 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp,
|
||||
}
|
||||
|
||||
static JSBool
|
||||
WriteStringGap(JSContext *cx, jsval space, JSStringBuffer *sb)
|
||||
{
|
||||
JSString *s = js_ValueToString(cx, space);
|
||||
if (!s)
|
||||
return JS_FALSE;
|
||||
|
||||
js_AppendUCString(sb, JS_GetStringChars(s), JS_GetStringLength(s));
|
||||
if (!STRING_BUFFER_OK(sb)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InitializeGap(JSContext *cx, jsval space, JSStringBuffer *sb)
|
||||
InitializeGap(JSContext *cx, jsval space, JSCharBuffer &cb)
|
||||
{
|
||||
if (!JSVAL_IS_PRIMITIVE(space)) {
|
||||
JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(space));
|
||||
if (clasp == &js_StringClass || clasp == &js_NumberClass)
|
||||
return WriteStringGap(cx, space, sb);
|
||||
return js_ValueToCharBuffer(cx, space, cb);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_STRING(space))
|
||||
return WriteStringGap(cx, space, sb);
|
||||
return js_ValueToCharBuffer(cx, space, cb);
|
||||
|
||||
if (JSVAL_IS_NUMBER(space)) {
|
||||
jsdouble d = JSVAL_IS_INT(space)
|
||||
? JSVAL_TO_INT(space)
|
||||
: js_DoubleToInteger(*JSVAL_TO_DOUBLE(space));
|
||||
d = JS_MIN(10, d);
|
||||
if (d >= 1)
|
||||
js_RepeatChar(sb, jschar(' '), uint32(d));
|
||||
|
||||
if (!STRING_BUFFER_OK(sb)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
if (d >= 1 && !cb.appendN(' ', uint32(d)))
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
@ -598,15 +554,15 @@ InitializeGap(JSContext *cx, jsval space, JSStringBuffer *sb)
|
||||
|
||||
JSBool
|
||||
js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
|
||||
JSONWriteCallback callback, void *data)
|
||||
JSCharBuffer &cb)
|
||||
{
|
||||
// XXX stack
|
||||
JSObject *stack = JS_NewArrayObject(cx, 0, NULL);
|
||||
if (!stack)
|
||||
return JS_FALSE;
|
||||
|
||||
StringifyContext scx(callback, replacer, data);
|
||||
if (!InitializeGap(cx, space, &scx.gap))
|
||||
StringifyContext scx(cx, cb, replacer);
|
||||
if (!InitializeGap(cx, space, scx.gap))
|
||||
return JS_FALSE;
|
||||
|
||||
JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
|
||||
@ -640,7 +596,7 @@ static JSBool
|
||||
Walk(JSContext *cx, jsid id, JSObject *holder, jsval reviver, jsval *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
|
||||
if (!holder->getProperty(cx, id, vp))
|
||||
return JS_FALSE;
|
||||
|
||||
@ -726,8 +682,6 @@ Revive(JSContext *cx, jsval reviver, jsval *vp)
|
||||
return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp);
|
||||
}
|
||||
|
||||
#define JSON_INITIAL_BUFSIZE 256
|
||||
|
||||
JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, jsval *rootVal)
|
||||
{
|
||||
@ -738,7 +692,7 @@ js_BeginJSONParse(JSContext *cx, jsval *rootVal)
|
||||
if (!arr)
|
||||
return NULL;
|
||||
|
||||
JSONParser *jp = (JSONParser*) cx->calloc(sizeof(JSONParser));
|
||||
JSONParser *jp = JSONParser::create(cx);
|
||||
if (!jp)
|
||||
return NULL;
|
||||
|
||||
@ -750,14 +704,6 @@ js_BeginJSONParse(JSContext *cx, jsval *rootVal)
|
||||
*jp->statep = JSON_PARSE_STATE_INIT;
|
||||
jp->rootVal = rootVal;
|
||||
|
||||
js_InitStringBuffer(&jp->objectKey);
|
||||
js_InitStringBuffer(&jp->buffer);
|
||||
|
||||
if (!jp->buffer.grow(&jp->buffer, JSON_INITIAL_BUFSIZE)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return jp;
|
||||
|
||||
bad:
|
||||
@ -787,15 +733,13 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
||||
}
|
||||
}
|
||||
|
||||
js_FinishStringBuffer(&jp->objectKey);
|
||||
js_FinishStringBuffer(&jp->buffer);
|
||||
|
||||
/* This internal API is infallible, in spite of its JSBool return type. */
|
||||
js_RemoveRoot(cx->runtime, &jp->objectStack);
|
||||
|
||||
JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
|
||||
jsval *vp = jp->rootVal;
|
||||
cx->free(jp);
|
||||
|
||||
JSONParser::destroy(cx, jp);
|
||||
|
||||
if (!early_ok)
|
||||
return JS_FALSE;
|
||||
@ -862,10 +806,10 @@ PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
|
||||
ok = parent->defineProperty(cx, index, value, NULL, NULL, JSPROP_ENUMERATE, NULL);
|
||||
}
|
||||
} else {
|
||||
ok = JS_DefineUCProperty(cx, parent, jp->objectKey.base,
|
||||
STRING_BUFFER_OFFSET(&jp->objectKey), value,
|
||||
ok = JS_DefineUCProperty(cx, parent, jp->objectKey.begin(),
|
||||
jp->objectKey.length(), value,
|
||||
NULL, NULL, JSPROP_ENUMERATE);
|
||||
js_RewindStringBuffer(&jp->objectKey);
|
||||
jp->objectKey.clear();
|
||||
}
|
||||
|
||||
return ok;
|
||||
@ -1038,33 +982,25 @@ HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
|
||||
|
||||
switch (type) {
|
||||
case JSON_DATA_STRING:
|
||||
ok = HandleString(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
|
||||
ok = HandleString(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
break;
|
||||
|
||||
case JSON_DATA_KEYSTRING:
|
||||
js_AppendUCString(&jp->objectKey, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
|
||||
ok = STRING_BUFFER_OK(&jp->objectKey);
|
||||
if (!ok)
|
||||
JS_ReportOutOfMemory(cx);
|
||||
ok = jp->objectKey.append(jp->buffer.begin(), jp->buffer.end());
|
||||
break;
|
||||
|
||||
case JSON_DATA_NUMBER:
|
||||
ok = HandleNumber(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
|
||||
ok = HandleNumber(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_ASSERT(type == JSON_DATA_KEYWORD);
|
||||
ok = HandleKeyword(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
|
||||
ok = HandleKeyword(cx, jp, jp->buffer.begin(), jp->buffer.length());
|
||||
break;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
ok = STRING_BUFFER_OK(&jp->buffer);
|
||||
if (ok)
|
||||
js_RewindStringBuffer(&jp->buffer);
|
||||
else
|
||||
JS_ReportOutOfMemory(cx);
|
||||
}
|
||||
if (ok)
|
||||
jp->buffer.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -1110,13 +1046,15 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
|
||||
if (IsNumChar(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_NUMBER;
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (JS7_ISLET(c)) {
|
||||
*jp->statep = JSON_PARSE_STATE_KEYWORD;
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1202,7 +1140,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
} else if (c == '\\') {
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
|
||||
} else {
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1229,7 +1168,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
}
|
||||
}
|
||||
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
break;
|
||||
|
||||
@ -1246,7 +1186,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
}
|
||||
|
||||
if (++(jp->numHex) == 4) {
|
||||
js_FastAppendChar(&jp->buffer, jp->hexChar);
|
||||
if (!jp->buffer.append(jp->hexChar))
|
||||
return JS_FALSE;
|
||||
jp->hexChar = 0;
|
||||
jp->numHex = 0;
|
||||
*jp->statep = JSON_PARSE_STATE_STRING;
|
||||
@ -1255,7 +1196,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
|
||||
case JSON_PARSE_STATE_KEYWORD:
|
||||
if (JS7_ISLET(c)) {
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
// this character isn't part of the keyword, process it again
|
||||
i--;
|
||||
@ -1269,7 +1211,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
|
||||
case JSON_PARSE_STATE_NUMBER:
|
||||
if (IsNumChar(c)) {
|
||||
js_FastAppendChar(&jp->buffer, c);
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
// this character isn't part of the number, process it again
|
||||
i--;
|
||||
@ -1291,14 +1234,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
default:
|
||||
JS_NOT_REACHED("Invalid JSON parser state");
|
||||
}
|
||||
|
||||
if (!STRING_BUFFER_OK(&jp->buffer)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
*jp->buffer.ptr = 0;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ js_InitJSONClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSBool
|
||||
js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
|
||||
JSONWriteCallback callback, void *data);
|
||||
JSCharBuffer &cb);
|
||||
|
||||
extern JSBool js_TryJSON(JSContext *cx, jsval *vp);
|
||||
|
||||
@ -81,18 +81,7 @@ enum JSONDataType {
|
||||
JSON_DATA_KEYWORD
|
||||
};
|
||||
|
||||
struct JSONParser {
|
||||
/* Used while handling \uNNNN in strings */
|
||||
jschar hexChar;
|
||||
uint8 numHex;
|
||||
|
||||
JSONParserState *statep;
|
||||
JSONParserState stateStack[JSON_MAX_DEPTH];
|
||||
jsval *rootVal;
|
||||
JSStringBuffer objectKey;
|
||||
JSStringBuffer buffer;
|
||||
JSObject *objectStack;
|
||||
};
|
||||
struct JSONParser;
|
||||
|
||||
extern JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, jsval *rootVal);
|
||||
|
@ -2024,11 +2024,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
case JSOP_ENUMCONSTELEM:
|
||||
op = JSOP_GETELEM;
|
||||
break;
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case JSOP_SETCALL:
|
||||
op = JSOP_CALL;
|
||||
break;
|
||||
#endif
|
||||
case JSOP_GETTHISPROP:
|
||||
/*
|
||||
* NB: JSOP_GETTHISPROP can't fail due to |this|
|
||||
@ -3527,9 +3525,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
case JSOP_CALL:
|
||||
case JSOP_EVAL:
|
||||
case JSOP_APPLY:
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case JSOP_SETCALL:
|
||||
#endif
|
||||
argc = GET_ARGC(pc);
|
||||
argv = (char **)
|
||||
cx->malloc((size_t)(argc + 1) * sizeof *argv);
|
||||
@ -3594,13 +3590,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
cx->free(argv);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (op == JSOP_SETCALL) {
|
||||
if (!PushOff(ss, todo, op))
|
||||
return NULL;
|
||||
todo = Sprint(&ss->sprinter, "");
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case JSOP_DELNAME:
|
||||
|
@ -39,7 +39,7 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/* This file needs to be included in possibly multiple places. */
|
||||
|
||||
|
||||
#if JS_THREADED_INTERP
|
||||
interrupt:
|
||||
#else /* !JS_THREADED_INTERP */
|
||||
@ -260,7 +260,7 @@
|
||||
* calls eval unexpectedly (in a way that is hidden from the
|
||||
* compiler). See bug 325540.
|
||||
*/
|
||||
ok &= fp->putActivationObjects(cx);
|
||||
fp->putActivationObjects(cx);
|
||||
|
||||
#ifdef INCLUDE_MOZILLA_DTRACE
|
||||
/* DTrace function return, inlines */
|
||||
@ -2054,7 +2054,6 @@
|
||||
newifp->frame.argsobj = NULL;
|
||||
newifp->frame.varobj = NULL;
|
||||
newifp->frame.script = script;
|
||||
newifp->frame.callee = obj;
|
||||
newifp->frame.fun = fun;
|
||||
newifp->frame.argc = argc;
|
||||
newifp->frame.argv = vp + 2;
|
||||
@ -2205,63 +2204,15 @@
|
||||
TRACE_0(NativeCallComplete);
|
||||
|
||||
end_call:
|
||||
#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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
PUSH_OPND(cx->rval2);
|
||||
ELEMENT_OP(-1, obj->getProperty(cx, id, &rval));
|
||||
|
||||
regs.sp--;
|
||||
STORE_OPND(-1, rval);
|
||||
cx->rval2set = JS_FALSE;
|
||||
}
|
||||
#endif /* JS_HAS_LVALUE_RETURN */
|
||||
END_CASE(JSOP_CALL)
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
BEGIN_CASE(JSOP_SETCALL)
|
||||
argc = GET_ARGC(regs.pc);
|
||||
vp = regs.sp - argc - 2;
|
||||
ok = js_Invoke(cx, argc, vp, 0);
|
||||
regs.sp = vp + 1;
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
if (!ok)
|
||||
goto error;
|
||||
if (!cx->rval2set) {
|
||||
op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH);
|
||||
if (op2 != JSOP_DELELEM) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_LEFTSIDE_OF_ASS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store true as the result of the emulated delete of a
|
||||
* non-existent property. NB: We don't METER_OP_PAIR here;
|
||||
* it doesn't seem worth the code for this obscure case.
|
||||
*/
|
||||
*vp = JSVAL_TRUE;
|
||||
regs.pc += JSOP_SETCALL_LENGTH + JSOP_DELELEM_LENGTH;
|
||||
op = (JSOp) *regs.pc;
|
||||
DO_OP();
|
||||
}
|
||||
PUSH_OPND(cx->rval2);
|
||||
cx->rval2set = JS_FALSE;
|
||||
if (js_Invoke(cx, argc, vp, 0))
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
|
||||
goto error;
|
||||
END_CASE(JSOP_SETCALL)
|
||||
#endif
|
||||
|
||||
BEGIN_CASE(JSOP_NAME)
|
||||
BEGIN_CASE(JSOP_CALLNAME)
|
||||
@ -2422,7 +2373,7 @@
|
||||
* contains JS_SCRIPT_REGEXPS(script)->length reserved slots
|
||||
* for the cloned regexps; see fun_reserveSlots, jsfun.c.
|
||||
*/
|
||||
funobj = fp->callee;
|
||||
funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
|
||||
slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
|
||||
if (script->upvarsOffset != 0)
|
||||
slot += JS_SCRIPT_UPVARS(script)->length;
|
||||
@ -2791,7 +2742,8 @@
|
||||
|
||||
BEGIN_CASE(JSOP_GETDSLOT)
|
||||
BEGIN_CASE(JSOP_CALLDSLOT)
|
||||
obj = fp->callee;
|
||||
JS_ASSERT(fp->argv);
|
||||
obj = JSVAL_TO_OBJECT(fp->argv[-2]);
|
||||
JS_ASSERT(obj);
|
||||
JS_ASSERT(obj->dslots);
|
||||
|
||||
@ -3235,7 +3187,7 @@
|
||||
END_CASE(JSOP_LAMBDA_DBGFC)
|
||||
|
||||
BEGIN_CASE(JSOP_CALLEE)
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
|
||||
PUSH_OPND(fp->argv[-2]);
|
||||
END_CASE(JSOP_CALLEE)
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
@ -3544,7 +3496,7 @@
|
||||
if (rval == JSVAL_HOLE) {
|
||||
JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
|
||||
JS_ASSERT(JSID_IS_INT(id));
|
||||
JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT);
|
||||
JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
|
||||
if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
|
||||
!js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
|
||||
goto error;
|
||||
|
@ -78,6 +78,7 @@
|
||||
#include "jsstr.h"
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jslibmath.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
@ -211,7 +212,7 @@ JSCompiler::init(const jschar *base, size_t length,
|
||||
JSContext *cx = context;
|
||||
|
||||
tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
|
||||
if (!js_InitTokenStream(cx, TS(this), base, length, fp, filename, lineno)) {
|
||||
if (!tokenStream.init(cx, base, length, fp, filename, lineno)) {
|
||||
JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
|
||||
return false;
|
||||
}
|
||||
@ -231,7 +232,7 @@ JSCompiler::~JSCompiler()
|
||||
JS_ASSERT(tempRoot.u.compiler == this);
|
||||
JS_POP_TEMP_ROOT(cx, &tempRoot);
|
||||
JS_UNKEEP_ATOMS(cx->runtime);
|
||||
js_CloseTokenStream(cx, TS(this));
|
||||
tokenStream.close(cx);
|
||||
JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
|
||||
}
|
||||
|
||||
@ -3471,12 +3472,10 @@ BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
|
||||
pn->pn_op = JSOP_SETNAME;
|
||||
break;
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
@ -4673,9 +4672,7 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
? (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
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
(pn1->pn_type != TOK_UNARYOP ||
|
||||
pn1->pn_op != JSOP_XMLNAME) &&
|
||||
@ -4780,12 +4777,10 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
if (!pn2) {
|
||||
pn2 = pn1;
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (pn2->pn_type == TOK_LP &&
|
||||
!MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn2->pn_type == TOK_UNARYOP)
|
||||
pn2->pn_op = JSOP_BINDXMLNAME;
|
||||
@ -5773,12 +5768,10 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
return NULL;
|
||||
return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
|
||||
#endif
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
return NULL;
|
||||
break;
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
if (pn->pn_op == JSOP_XMLNAME) {
|
||||
@ -6012,10 +6005,8 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
|
||||
{
|
||||
if (kid->pn_type != TOK_NAME &&
|
||||
kid->pn_type != TOK_DOT &&
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
(kid->pn_type != TOK_LP ||
|
||||
(kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
(kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
|
||||
#endif
|
||||
@ -6054,12 +6045,10 @@ SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
: (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
|
||||
break;
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
case TOK_LP:
|
||||
if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
|
||||
return JS_FALSE;
|
||||
/* FALL THROUGH */
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_UNARYOP:
|
||||
if (kid->pn_op == JSOP_XMLNAME)
|
||||
@ -7738,7 +7727,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
if (!matched) {
|
||||
for (index = 0; ; index++) {
|
||||
if (index == ARRAY_INIT_LIMIT) {
|
||||
if (index == JS_ARGS_LENGTH_MAX) {
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
return NULL;
|
||||
@ -8218,11 +8207,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
/* Token stream ensures that tokenbuf is NUL-terminated. */
|
||||
JS_ASSERT(*ts->tokenbuf.ptr == (jschar) 0);
|
||||
obj = js_NewRegExpObject(cx, ts,
|
||||
ts->tokenbuf.base,
|
||||
ts->tokenbuf.ptr - ts->tokenbuf.base,
|
||||
ts->tokenbuf.begin(),
|
||||
ts->tokenbuf.length(),
|
||||
CURRENT_TOKEN(ts).t_reflags);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
@ -822,8 +822,8 @@ struct JSCompiler {
|
||||
JSTempValueRooter tempRoot; /* root to trace traceListHead */
|
||||
|
||||
JSCompiler(JSContext *cx, JSPrincipals *prin = NULL, JSStackFrame *cfp = NULL)
|
||||
: context(cx), aleFreeList(NULL), principals(NULL), callerFrame(cfp),
|
||||
nodeList(NULL), functionCount(0), traceListHead(NULL)
|
||||
: context(cx), aleFreeList(NULL), tokenStream(cx), principals(NULL),
|
||||
callerFrame(cfp), nodeList(NULL), functionCount(0), traceListHead(NULL)
|
||||
{
|
||||
memset(tempFreeList, 0, sizeof tempFreeList);
|
||||
setPrincipals(prin);
|
||||
@ -833,7 +833,7 @@ struct JSCompiler {
|
||||
~JSCompiler();
|
||||
|
||||
/*
|
||||
* Initialize a compiler. Parameters are passed on to js_InitTokenStream.
|
||||
* Initialize a compiler. Parameters are passed on to init tokenStream.
|
||||
* The compiler owns the arena pool "tops-of-stack" space above the current
|
||||
* JSContext.tempPool mark. This means you cannot allocate from tempPool
|
||||
* and save the pointer beyond the next JSCompiler destructor invocation.
|
||||
|
@ -127,7 +127,6 @@ typedef struct JSScope JSScope;
|
||||
typedef struct JSScopeOps JSScopeOps;
|
||||
typedef struct JSScopeProperty JSScopeProperty;
|
||||
typedef struct JSStackHeader JSStackHeader;
|
||||
typedef struct JSStringBuffer JSStringBuffer;
|
||||
typedef struct JSSubString JSSubString;
|
||||
typedef struct JSTraceableNative JSTraceableNative;
|
||||
typedef struct JSXML JSXML;
|
||||
@ -147,7 +146,7 @@ extern "C++" {
|
||||
template <class T, size_t MinInlineCapacity = 0> class JSTempVector;
|
||||
|
||||
/* Common JSTempVector instantiations: */
|
||||
typedef JSTempVector<jschar, 32> JSCharVector;
|
||||
typedef JSTempVector<jschar, 32> JSCharBuffer;
|
||||
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
@ -381,4 +380,12 @@ typedef JSBool
|
||||
extern JSBool js_CStringsAreUTF8;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Maximum supported value of Arguments.length. It bounds the maximum number
|
||||
* of arguments that can be supplied to the function call using
|
||||
* Function.prototype.apply. This value also gives the maximum number of
|
||||
* elements in the array initializer.
|
||||
*/
|
||||
#define JS_ARGS_LENGTH_MAX (JS_BIT(24) - 1)
|
||||
|
||||
#endif /* jsprvtd_h___ */
|
||||
|
@ -2005,42 +2005,35 @@ typedef JSTempVector<LIns *> LInsList;
|
||||
|
||||
/* Dummy GC for nanojit placement new. */
|
||||
static GC gc;
|
||||
static avmplus::AvmCore s_core = avmplus::AvmCore();
|
||||
static avmplus::AvmCore* core = &s_core;
|
||||
|
||||
static void *
|
||||
HashRegExp(uint16 flags, const jschar *s, size_t n)
|
||||
{
|
||||
uint32 h;
|
||||
|
||||
for (h = 0; n; s++, n--)
|
||||
h = JS_ROTATE_LEFT32(h, 4) ^ *s;
|
||||
return (void *)(h + flags);
|
||||
}
|
||||
|
||||
struct RESideExit : public SideExit {
|
||||
size_t re_length;
|
||||
uint16 re_flags;
|
||||
jschar re_chars[1];
|
||||
};
|
||||
|
||||
/* Return the cached fragment for the given regexp, or NULL. */
|
||||
/* Return the cached fragment for the given regexp, or create one. */
|
||||
static Fragment*
|
||||
LookupNativeRegExp(JSContext* cx, void* hash, uint16 re_flags,
|
||||
LookupNativeRegExp(JSContext* cx, uint16 re_flags,
|
||||
const jschar* re_chars, size_t re_length)
|
||||
{
|
||||
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento;
|
||||
Fragment* fragment = fragmento->getLoop(hash);
|
||||
while (fragment) {
|
||||
if (fragment->lastIns) {
|
||||
RESideExit *exit = (RESideExit*)fragment->lastIns->record()->exit;
|
||||
if (exit->re_flags == re_flags &&
|
||||
exit->re_length == re_length &&
|
||||
!memcmp(exit->re_chars, re_chars, re_length * sizeof(jschar))) {
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
fragment = fragment->peer;
|
||||
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
|
||||
VMAllocator &alloc = *tm->reAllocator;
|
||||
REHashMap &table = *tm->reFragments;
|
||||
|
||||
REHashKey k(re_length, re_flags, re_chars);
|
||||
Fragment *frag = table.get(k);
|
||||
|
||||
if (!frag) {
|
||||
frag = new (alloc) Fragment(0);
|
||||
frag->lirbuf = tm->reLirBuf;
|
||||
frag->root = frag;
|
||||
/*
|
||||
* Copy the re_chars portion of the hash key into the Allocator, so
|
||||
* its lifecycle is disconnected from the lifecycle of the
|
||||
* underlying regexp.
|
||||
*/
|
||||
k.re_chars = (const jschar*) new (alloc) jschar[re_length];
|
||||
memcpy((void*) k.re_chars, re_chars, re_length * sizeof(jschar));
|
||||
table.put(k, frag);
|
||||
}
|
||||
return NULL;
|
||||
return frag;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -2298,7 +2291,7 @@ class RegExpNativeCompiler {
|
||||
void targetCurrentPoint(LInsList &fails)
|
||||
{
|
||||
LIns *fail = lir->ins0(LIR_label);
|
||||
for (size_t i = 0; i < fails.size(); ++i) {
|
||||
for (size_t i = 0; i < fails.length(); ++i) {
|
||||
fails[i]->setTarget(fail);
|
||||
}
|
||||
fails.clear();
|
||||
@ -3065,16 +3058,13 @@ class RegExpNativeCompiler {
|
||||
GuardRecord* insertGuard(const jschar* re_chars, size_t re_length)
|
||||
{
|
||||
LIns* skip = lirBufWriter->insSkip(sizeof(GuardRecord) +
|
||||
sizeof(RESideExit) +
|
||||
sizeof(SideExit) +
|
||||
(re_length-1) * sizeof(jschar));
|
||||
GuardRecord* guard = (GuardRecord *) skip->payload();
|
||||
memset(guard, 0, sizeof(*guard));
|
||||
RESideExit* exit = (RESideExit*)(guard+1);
|
||||
SideExit* exit = (SideExit*)(guard+1);
|
||||
guard->exit = exit;
|
||||
guard->exit->target = fragment;
|
||||
exit->re_flags = re->flags;
|
||||
exit->re_length = re_length;
|
||||
memcpy(exit->re_chars, re_chars, re_length * sizeof(jschar));
|
||||
fragment->lastIns = lir->insGuard(LIR_loop, NULL, skip);
|
||||
return guard;
|
||||
}
|
||||
@ -3092,7 +3082,6 @@ class RegExpNativeCompiler {
|
||||
size_t re_length;
|
||||
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
|
||||
Assembler *assm = tm->reAssembler;
|
||||
Fragmento* fragmento = tm->reFragmento;
|
||||
VMAllocator& alloc = *tm->reAllocator;
|
||||
|
||||
re->source->getCharsAndLength(re_chars, re_length);
|
||||
@ -3148,7 +3137,7 @@ class RegExpNativeCompiler {
|
||||
|
||||
if (alloc.outOfMemory())
|
||||
goto fail;
|
||||
::compile(assm, fragment, alloc verbose_only(, fragmento->labels));
|
||||
::compile(assm, fragment, alloc verbose_only(, tm->reLabels));
|
||||
if (assm->error() != nanojit::None) {
|
||||
oom = assm->error() == nanojit::OutOMem;
|
||||
goto fail;
|
||||
@ -3162,20 +3151,26 @@ class RegExpNativeCompiler {
|
||||
return JS_TRUE;
|
||||
fail:
|
||||
if (alloc.outOfMemory() || oom ||
|
||||
js_OverfullFragmento(tm, fragmento)) {
|
||||
fragmento->clearFrags();
|
||||
tm->reCodeAlloc->sweep();
|
||||
js_OverfullJITCache(tm, true)) {
|
||||
delete lirBufWriter;
|
||||
delete tm->reCodeAlloc;
|
||||
tm->reCodeAlloc = new CodeAlloc();
|
||||
alloc.reset();
|
||||
tm->reFragments = new (alloc) REHashMap(alloc);
|
||||
tm->reLirBuf = new (alloc) LirBuffer(alloc);
|
||||
#ifdef DEBUG
|
||||
fragmento->labels = new (alloc) LabelMap(alloc, &js_LogController);
|
||||
lirbuf->names = new (alloc) LirNameMap(alloc, fragmento->labels);
|
||||
tm->reLabels = new (alloc) LabelMap(alloc, &js_LogController);
|
||||
tm->reLirBuf->names = new (alloc) LirNameMap(alloc, tm->reLabels);
|
||||
tm->reAssembler = new (alloc) Assembler(*tm->reCodeAlloc, alloc, core,
|
||||
&js_LogController);
|
||||
#else
|
||||
tm->reAssembler = new (alloc) Assembler(*tm->reCodeAlloc, alloc, core, NULL);
|
||||
#endif
|
||||
lirbuf->clear();
|
||||
} else {
|
||||
if (!guard) insertGuard(re_chars, re_length);
|
||||
re->flags |= JSREG_NOCOMPILE;
|
||||
delete lirBufWriter;
|
||||
}
|
||||
delete lirBufWriter;
|
||||
#ifdef NJ_VERBOSE
|
||||
debug_only_stmt( if (js_LogController.lcbits & LC_TMRegexp)
|
||||
delete lir; )
|
||||
@ -3216,19 +3211,11 @@ typedef void *(FASTCALL *NativeRegExp)(REGlobalData*, const jschar *);
|
||||
static NativeRegExp
|
||||
GetNativeRegExp(JSContext* cx, JSRegExp* re)
|
||||
{
|
||||
Fragment *fragment;
|
||||
const jschar *re_chars;
|
||||
size_t re_length;
|
||||
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento;
|
||||
|
||||
re->source->getCharsAndLength(re_chars, re_length);
|
||||
void* hash = HashRegExp(re->flags, re_chars, re_length);
|
||||
fragment = LookupNativeRegExp(cx, hash, re->flags, re_chars, re_length);
|
||||
if (!fragment) {
|
||||
fragment = fragmento->getAnchor(hash);
|
||||
fragment->lirbuf = JS_TRACE_MONITOR(cx).reLirBuf;
|
||||
fragment->root = fragment;
|
||||
}
|
||||
Fragment *fragment = LookupNativeRegExp(cx, re->flags, re_chars, re_length);
|
||||
JS_ASSERT(fragment);
|
||||
if (!fragment->code()) {
|
||||
if (!CompileRegExpToNative(cx, re, fragment))
|
||||
return NULL;
|
||||
@ -4995,6 +4982,34 @@ out:
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static jsdouble
|
||||
GetRegExpLastIndex(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == &js_RegExpClass);
|
||||
|
||||
jsval v = obj->fslots[JSSLOT_REGEXP_LAST_INDEX];
|
||||
if (JSVAL_IS_INT(v))
|
||||
return JSVAL_TO_INT(v);
|
||||
JS_ASSERT(JSVAL_IS_DOUBLE(v));
|
||||
return *JSVAL_TO_DOUBLE(v);
|
||||
}
|
||||
|
||||
static jsval
|
||||
GetRegExpLastIndexValue(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == &js_RegExpClass);
|
||||
return obj->fslots[JSSLOT_REGEXP_LAST_INDEX];
|
||||
}
|
||||
|
||||
static JSBool
|
||||
SetRegExpLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == &js_RegExpClass);
|
||||
|
||||
return JS_NewNumberValue(cx, lastIndex,
|
||||
&obj->fslots[JSSLOT_REGEXP_LAST_INDEX]);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
@ -5009,8 +5024,10 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
slot = JSVAL_TO_INT(id);
|
||||
if (slot == REGEXP_LAST_INDEX)
|
||||
return JS_GetReservedSlot(cx, obj, 0, vp);
|
||||
if (slot == REGEXP_LAST_INDEX) {
|
||||
*vp = GetRegExpLastIndexValue(obj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
re = (JSRegExp *) obj->getPrivate();
|
||||
@ -5057,8 +5074,7 @@ regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
if (!JS_ValueToNumber(cx, *vp, &lastIndex))
|
||||
return JS_FALSE;
|
||||
lastIndex = js_DoubleToInteger(lastIndex);
|
||||
ok = JS_NewNumberValue(cx, lastIndex, vp) &&
|
||||
JS_SetReservedSlot(cx, obj, 0, *vp);
|
||||
ok = SetRegExpLastIndex(cx, obj, lastIndex);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -5328,8 +5344,7 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
|
||||
if (!re)
|
||||
return JS_FALSE;
|
||||
obj->setPrivate(re);
|
||||
if (!js_SetLastIndex(xdr->cx, obj, 0))
|
||||
return JS_FALSE;
|
||||
js_ClearRegExpLastIndex(obj);
|
||||
*objp = obj;
|
||||
}
|
||||
return JS_TRUE;
|
||||
@ -5351,7 +5366,8 @@ regexp_trace(JSTracer *trc, JSObject *obj)
|
||||
|
||||
JSClass js_RegExpClass = {
|
||||
js_RegExp_str,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(REGEXP_CLASS_FIXED_RESERVED_SLOTS) |
|
||||
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
@ -5540,13 +5556,12 @@ created:
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
oldre = (JSRegExp *) obj->getPrivate();
|
||||
obj->setPrivate(re);
|
||||
|
||||
JSBool ok = js_SetLastIndex(cx, obj, 0);
|
||||
js_ClearRegExpLastIndex(obj);
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
if (oldre)
|
||||
js_DestroyRegExp(cx, oldre);
|
||||
*rval = OBJECT_TO_JSVAL(obj);
|
||||
return ok;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -5581,14 +5596,10 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
/* NB: we must reach out: after this paragraph, in order to drop re. */
|
||||
HOLD_REGEXP(cx, re);
|
||||
sticky = (re->flags & JSREG_STICKY) != 0;
|
||||
if (re->flags & (JSREG_GLOB | JSREG_STICKY)) {
|
||||
ok = js_GetLastIndex(cx, obj, &lastIndex);
|
||||
} else {
|
||||
lastIndex = 0;
|
||||
}
|
||||
lastIndex = (re->flags & (JSREG_GLOB | JSREG_STICKY))
|
||||
? GetRegExpLastIndex(obj)
|
||||
: 0;
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
/* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
|
||||
if (argc == 0) {
|
||||
@ -5618,14 +5629,17 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
}
|
||||
|
||||
if (lastIndex < 0 || str->length() < lastIndex) {
|
||||
ok = js_SetLastIndex(cx, obj, 0);
|
||||
js_ClearRegExpLastIndex(obj);
|
||||
*rval = JSVAL_NULL;
|
||||
} else {
|
||||
i = (size_t) lastIndex;
|
||||
ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
|
||||
if (ok &&
|
||||
((re->flags & JSREG_GLOB) || (*rval != JSVAL_NULL && sticky))) {
|
||||
ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i);
|
||||
if (*rval == JSVAL_NULL)
|
||||
js_ClearRegExpLastIndex(obj);
|
||||
else
|
||||
ok = SetRegExpLastIndex(cx, obj, i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5741,8 +5755,7 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
|
||||
return NULL;
|
||||
}
|
||||
obj->setPrivate(re);
|
||||
if (!js_SetLastIndex(cx, obj, 0))
|
||||
return NULL;
|
||||
js_ClearRegExpLastIndex(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -5759,26 +5772,9 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent)
|
||||
re = (JSRegExp *) obj->getPrivate();
|
||||
if (re) {
|
||||
clone->setPrivate(re);
|
||||
js_ClearRegExpLastIndex(clone);
|
||||
HOLD_REGEXP(cx, re);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex)
|
||||
{
|
||||
jsval v;
|
||||
|
||||
return JS_GetReservedSlot(cx, obj, 0, &v) &&
|
||||
JS_ValueToNumber(cx, v, lastIndex);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
|
||||
{
|
||||
jsval v;
|
||||
|
||||
return JS_NewNumberValue(cx, lastIndex, &v) &&
|
||||
JS_SetReservedSlot(cx, obj, 0, v);
|
||||
}
|
||||
|
||||
|
@ -185,14 +185,15 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
|
||||
extern JSObject *
|
||||
js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent);
|
||||
|
||||
/*
|
||||
* Get and set the per-object (clone or clone-parent) lastIndex slot.
|
||||
*/
|
||||
extern JSBool
|
||||
js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex);
|
||||
const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
|
||||
const uint32 REGEXP_CLASS_FIXED_RESERVED_SLOTS = 1;
|
||||
|
||||
extern JSBool
|
||||
js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex);
|
||||
static inline void
|
||||
js_ClearRegExpLastIndex(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == &js_RegExpClass);
|
||||
obj->fslots[JSSLOT_REGEXP_LAST_INDEX] = JSVAL_ZERO;
|
||||
}
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
|
@ -71,6 +71,7 @@
|
||||
#include "jsscan.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
@ -175,50 +176,15 @@ js_IsIdentifier(JSString *str)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#define TBMIN 64
|
||||
/* Initialize members that aren't initialized in |init|. */
|
||||
JSTokenStream::JSTokenStream(JSContext *cx)
|
||||
: tokens(), cursor(), lookahead(), ungetpos(), ungetbuf(), flags(), linelen(),
|
||||
linepos(), file(), listenerTSData(), saveEOL(), tokenbuf(cx)
|
||||
{}
|
||||
|
||||
static JSBool
|
||||
GrowTokenBuf(JSStringBuffer *sb, size_t newlength)
|
||||
{
|
||||
JSContext *cx;
|
||||
jschar *base;
|
||||
ptrdiff_t offset, length;
|
||||
size_t tbsize;
|
||||
JSArenaPool *pool;
|
||||
|
||||
cx = (JSContext*) sb->data;
|
||||
base = sb->base;
|
||||
offset = sb->ptr - base;
|
||||
pool = &cx->tempPool;
|
||||
if (!base) {
|
||||
tbsize = TBMIN * sizeof(jschar);
|
||||
length = TBMIN - 1;
|
||||
JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize);
|
||||
} else {
|
||||
length = sb->limit - base;
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!base) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
sb->base = STRING_BUFFER_ERROR_BASE;
|
||||
return JS_FALSE;
|
||||
}
|
||||
sb->base = base;
|
||||
sb->limit = base + length;
|
||||
sb->ptr = base + offset;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_InitTokenStream(JSContext *cx, JSTokenStream *ts,
|
||||
const jschar *base, size_t length,
|
||||
FILE *fp, const char *filename, uintN lineno)
|
||||
bool
|
||||
JSTokenStream::init(JSContext *cx, const jschar *base, size_t length,
|
||||
FILE *fp, const char *fn, uintN ln)
|
||||
{
|
||||
jschar *buf;
|
||||
size_t nb;
|
||||
@ -231,34 +197,33 @@ js_InitTokenStream(JSContext *cx, JSTokenStream *ts,
|
||||
JS_ARENA_ALLOCATE_CAST(buf, jschar *, &cx->tempPool, nb);
|
||||
if (!buf) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
memset(buf, 0, nb);
|
||||
memset(ts, 0, sizeof(*ts));
|
||||
ts->filename = filename;
|
||||
ts->lineno = lineno;
|
||||
ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = buf;
|
||||
|
||||
/* Initialize members. */
|
||||
filename = fn;
|
||||
lineno = ln;
|
||||
linebuf.base = linebuf.limit = linebuf.ptr = buf;
|
||||
if (fp) {
|
||||
ts->file = fp;
|
||||
ts->userbuf.base = buf + JS_LINE_LIMIT;
|
||||
ts->userbuf.ptr = ts->userbuf.limit = ts->userbuf.base + JS_LINE_LIMIT;
|
||||
file = fp;
|
||||
userbuf.base = buf + JS_LINE_LIMIT;
|
||||
userbuf.ptr = userbuf.limit = userbuf.base + JS_LINE_LIMIT;
|
||||
} else {
|
||||
ts->userbuf.base = (jschar *)base;
|
||||
ts->userbuf.limit = (jschar *)base + length;
|
||||
ts->userbuf.ptr = (jschar *)base;
|
||||
userbuf.base = (jschar *)base;
|
||||
userbuf.limit = (jschar *)base + length;
|
||||
userbuf.ptr = (jschar *)base;
|
||||
}
|
||||
ts->tokenbuf.grow = GrowTokenBuf;
|
||||
ts->tokenbuf.data = cx;
|
||||
ts->listener = cx->debugHooks->sourceHandler;
|
||||
ts->listenerData = cx->debugHooks->sourceHandlerData;
|
||||
return JS_TRUE;
|
||||
listener = cx->debugHooks->sourceHandler;
|
||||
listenerData = cx->debugHooks->sourceHandlerData;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
|
||||
JSTokenStream::close(JSContext *cx)
|
||||
{
|
||||
if (ts->flags & TSF_OWNFILENAME)
|
||||
cx->free((void *) ts->filename);
|
||||
if (flags & TSF_OWNFILENAME)
|
||||
cx->free((void *) filename);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
@ -684,138 +649,8 @@ js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
|
||||
return warning;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GrowStringBuffer(JSStringBuffer *sb, size_t amount)
|
||||
{
|
||||
ptrdiff_t offset = sb->ptr - sb->base;
|
||||
JS_ASSERT(offset >= 0);
|
||||
|
||||
/*
|
||||
* This addition needs an overflow check, but we can defer bounding against
|
||||
* ~size_t(0) / sizeof(jschar) till later to consolidate that test.
|
||||
*/
|
||||
size_t newlength = offset + amount + 1;
|
||||
if (size_t(offset) < newlength) {
|
||||
/* Grow by powers of two until 16MB, then grow by that chunk size. */
|
||||
const size_t CHUNK_SIZE_MASK = JS_BITMASK(24);
|
||||
|
||||
if (newlength <= CHUNK_SIZE_MASK)
|
||||
newlength = JS_BIT(JS_CeilingLog2(newlength));
|
||||
else if (newlength & CHUNK_SIZE_MASK)
|
||||
newlength = (newlength | CHUNK_SIZE_MASK) + 1;
|
||||
|
||||
/* Now do the full overflow check. */
|
||||
if (size_t(offset) < newlength && newlength < ~size_t(0) / sizeof(jschar)) {
|
||||
jschar *bp = (jschar *) js_realloc(sb->base, newlength * sizeof(jschar));
|
||||
if (bp) {
|
||||
sb->base = bp;
|
||||
sb->ptr = bp + offset;
|
||||
sb->limit = bp + newlength - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Either newlength overflow or realloc failure: poison the well. */
|
||||
js_free(sb->base);
|
||||
sb->base = STRING_BUFFER_ERROR_BASE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeStringBuffer(JSStringBuffer *sb)
|
||||
{
|
||||
JS_ASSERT(STRING_BUFFER_OK(sb));
|
||||
if (sb->base)
|
||||
js_free(sb->base);
|
||||
}
|
||||
|
||||
void
|
||||
js_InitStringBuffer(JSStringBuffer *sb)
|
||||
{
|
||||
sb->base = sb->limit = sb->ptr = NULL;
|
||||
sb->data = NULL;
|
||||
sb->grow = GrowStringBuffer;
|
||||
sb->free = FreeStringBuffer;
|
||||
}
|
||||
|
||||
void
|
||||
js_FinishStringBuffer(JSStringBuffer *sb)
|
||||
{
|
||||
sb->free(sb);
|
||||
}
|
||||
|
||||
void
|
||||
js_AppendChar(JSStringBuffer *sb, jschar c)
|
||||
{
|
||||
jschar *bp;
|
||||
|
||||
if (!STRING_BUFFER_OK(sb))
|
||||
return;
|
||||
if (!ENSURE_STRING_BUFFER(sb, 1))
|
||||
return;
|
||||
bp = sb->ptr;
|
||||
*bp++ = c;
|
||||
*bp = 0;
|
||||
sb->ptr = bp;
|
||||
}
|
||||
|
||||
void
|
||||
js_AppendUCString(JSStringBuffer *sb, const jschar *buf, uintN len)
|
||||
{
|
||||
jschar *bp;
|
||||
|
||||
if (!STRING_BUFFER_OK(sb))
|
||||
return;
|
||||
if (len == 0 || !ENSURE_STRING_BUFFER(sb, len))
|
||||
return;
|
||||
bp = sb->ptr;
|
||||
js_strncpy(bp, buf, len);
|
||||
bp += len;
|
||||
*bp = 0;
|
||||
sb->ptr = bp;
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
|
||||
void
|
||||
js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count)
|
||||
{
|
||||
jschar *bp;
|
||||
|
||||
if (!STRING_BUFFER_OK(sb) || count == 0)
|
||||
return;
|
||||
if (!ENSURE_STRING_BUFFER(sb, count))
|
||||
return;
|
||||
for (bp = sb->ptr; count; --count)
|
||||
*bp++ = c;
|
||||
*bp = 0;
|
||||
sb->ptr = bp;
|
||||
}
|
||||
|
||||
void
|
||||
js_AppendCString(JSStringBuffer *sb, const char *asciiz)
|
||||
{
|
||||
size_t length;
|
||||
jschar *bp;
|
||||
|
||||
if (!STRING_BUFFER_OK(sb) || *asciiz == '\0')
|
||||
return;
|
||||
length = strlen(asciiz);
|
||||
if (!ENSURE_STRING_BUFFER(sb, length))
|
||||
return;
|
||||
for (bp = sb->ptr; length; --length)
|
||||
*bp++ = (jschar) *asciiz++;
|
||||
*bp = 0;
|
||||
sb->ptr = bp;
|
||||
}
|
||||
|
||||
void
|
||||
js_AppendJSString(JSStringBuffer *sb, JSString *str)
|
||||
{
|
||||
js_AppendUCString(sb, str->chars(), str->length());
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GetXMLEntity(JSContext *cx, JSTokenStream *ts)
|
||||
{
|
||||
@ -826,10 +661,11 @@ GetXMLEntity(JSContext *cx, JSTokenStream *ts)
|
||||
char *bytes;
|
||||
JSErrNum msg;
|
||||
|
||||
JSCharBuffer &tb = ts->tokenbuf;
|
||||
|
||||
/* Put the entity, including the '&' already scanned, in ts->tokenbuf. */
|
||||
offset = ts->tokenbuf.ptr - ts->tokenbuf.base;
|
||||
js_FastAppendChar(&ts->tokenbuf, '&');
|
||||
if (!STRING_BUFFER_OK(&ts->tokenbuf))
|
||||
offset = tb.length();
|
||||
if (!tb.append('&'))
|
||||
return JS_FALSE;
|
||||
while ((c = GetChar(ts)) != ';') {
|
||||
if (c == EOF || c == '\n') {
|
||||
@ -837,14 +673,13 @@ GetXMLEntity(JSContext *cx, JSTokenStream *ts)
|
||||
JSMSG_END_OF_XML_ENTITY);
|
||||
return JS_FALSE;
|
||||
}
|
||||
js_FastAppendChar(&ts->tokenbuf, (jschar) c);
|
||||
if (!STRING_BUFFER_OK(&ts->tokenbuf))
|
||||
if (!tb.append(c))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Let length be the number of jschars after the '&', including the ';'. */
|
||||
length = (ts->tokenbuf.ptr - ts->tokenbuf.base) - offset;
|
||||
bp = ts->tokenbuf.base + offset;
|
||||
length = tb.length() - offset;
|
||||
bp = tb.begin() + offset;
|
||||
c = d = 0;
|
||||
ispair = JS_FALSE;
|
||||
if (length > 2 && bp[1] == '#') {
|
||||
@ -917,18 +752,15 @@ GetXMLEntity(JSContext *cx, JSTokenStream *ts)
|
||||
*bp++ = (jschar) c;
|
||||
if (ispair)
|
||||
*bp++ = (jschar) d;
|
||||
*bp = 0;
|
||||
ts->tokenbuf.ptr = bp;
|
||||
tb.shrinkBy(tb.end() - bp);
|
||||
return JS_TRUE;
|
||||
|
||||
badncr:
|
||||
msg = JSMSG_BAD_XML_NCR;
|
||||
bad:
|
||||
/* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */
|
||||
JS_ASSERT(STRING_BUFFER_OK(&ts->tokenbuf));
|
||||
JS_ASSERT((ts->tokenbuf.ptr - bp) >= 1);
|
||||
bytes = js_DeflateString(cx, bp + 1,
|
||||
(ts->tokenbuf.ptr - bp) - 1);
|
||||
JS_ASSERT((tb.end() - bp) >= 1);
|
||||
bytes = js_DeflateString(cx, bp + 1, (tb.end() - bp) - 1);
|
||||
if (bytes) {
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
msg, bytes);
|
||||
@ -1015,6 +847,12 @@ ScanAsSpace(jschar c)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE JSAtom *
|
||||
atomize(JSContext *cx, JSCharBuffer &cb)
|
||||
{
|
||||
return js_AtomizeChars(cx, cb.begin(), cb.length(), 0);
|
||||
}
|
||||
|
||||
JSTokenType
|
||||
js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
{
|
||||
@ -1030,27 +868,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
ptrdiff_t contentIndex;
|
||||
#endif
|
||||
|
||||
#define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base)
|
||||
#define TOKENBUF_LENGTH() (ts->tokenbuf.ptr - ts->tokenbuf.base)
|
||||
#define TOKENBUF_OK() STRING_BUFFER_OK(&ts->tokenbuf)
|
||||
#define TOKENBUF_TO_ATOM() (TOKENBUF_OK() \
|
||||
? js_AtomizeChars(cx, \
|
||||
TOKENBUF_BASE(), \
|
||||
TOKENBUF_LENGTH(), \
|
||||
0) \
|
||||
: NULL)
|
||||
#define ADD_TO_TOKENBUF(c) JS_BEGIN_MACRO \
|
||||
js_FastAppendChar(&ts->tokenbuf, jschar(c)); \
|
||||
if (!TOKENBUF_OK()) \
|
||||
goto error; \
|
||||
JS_END_MACRO
|
||||
|
||||
/* The following 4 macros should only be used when TOKENBUF_OK() is true. */
|
||||
#define TOKENBUF_BASE() (ts->tokenbuf.base)
|
||||
#define TOKENBUF_END() (ts->tokenbuf.ptr)
|
||||
#define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i])
|
||||
#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i)
|
||||
#define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0)
|
||||
JSCharBuffer &tb = ts->tokenbuf;
|
||||
|
||||
/* Check for a pushed-back token resulting from mismatching lookahead. */
|
||||
while (ts->lookahead != 0) {
|
||||
@ -1070,7 +888,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
if (ts->flags & TSF_XMLTEXTMODE) {
|
||||
tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */
|
||||
tp = NewToken(ts, 0);
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{';
|
||||
|
||||
while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) {
|
||||
@ -1083,14 +901,15 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
|
||||
if (!JS_ISXMLSPACE(c))
|
||||
tt = TOK_XMLTEXT;
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
UngetChar(ts, c);
|
||||
|
||||
if (TOKENBUF_LENGTH() == 0) {
|
||||
if (tb.empty()) {
|
||||
atom = NULL;
|
||||
} else {
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
}
|
||||
@ -1117,11 +936,12 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
goto out;
|
||||
}
|
||||
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
if (JS_ISXMLNSSTART(c)) {
|
||||
JSBool sawColon = JS_FALSE;
|
||||
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) {
|
||||
if (c == ':') {
|
||||
int nextc;
|
||||
@ -1138,11 +958,12 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
sawColon = JS_TRUE;
|
||||
}
|
||||
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
|
||||
UngetChar(ts, c);
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
tp->t_op = JSOP_STRING;
|
||||
@ -1179,7 +1000,9 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
*/
|
||||
if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) {
|
||||
JS_ASSERT(qc == '\'');
|
||||
js_AppendCString(&ts->tokenbuf, js_quot_entity_str);
|
||||
if (!tb.append(js_quot_entity_str,
|
||||
strlen(js_quot_entity_str)))
|
||||
goto error;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1189,9 +1012,10 @@ js_GetToken(JSContext *cx, JSTokenStream *ts)
|
||||
continue;
|
||||
}
|
||||
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
tp->pos.end.lineno = (uint16)ts->lineno;
|
||||
@ -1244,9 +1068,10 @@ retry:
|
||||
hadUnicodeEscape = JS_ISIDSTART(qc)))) {
|
||||
if (hadUnicodeEscape)
|
||||
c = qc;
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
for (;;) {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
if (c == '\\') {
|
||||
qc = GetUnicodeEscape(ts);
|
||||
@ -1267,8 +1092,7 @@ retry:
|
||||
*/
|
||||
if (!hadUnicodeEscape &&
|
||||
!(ts->flags & TSF_KEYWORD_IS_NAME) &&
|
||||
TOKENBUF_OK() &&
|
||||
(kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) {
|
||||
(kw = FindKeyword(tb.begin(), tb.length()))) {
|
||||
if (kw->tokentype == TOK_RESERVED) {
|
||||
if (!js_ReportCompileErrorNumber(cx, ts, NULL,
|
||||
JSREPORT_WARNING |
|
||||
@ -1284,7 +1108,7 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
tp->t_op = JSOP_NAME;
|
||||
@ -1299,13 +1123,15 @@ retry:
|
||||
jsdouble dval;
|
||||
|
||||
radix = 10;
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
|
||||
if (c == '0') {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
if (JS_TOLOWER(c) == 'x') {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
radix = 16;
|
||||
} else if (JS7_ISDEC(c)) {
|
||||
@ -1333,22 +1159,26 @@ retry:
|
||||
radix = 10;
|
||||
}
|
||||
}
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
}
|
||||
|
||||
if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
|
||||
if (c == '.') {
|
||||
do {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
} while (JS7_ISDEC(c));
|
||||
}
|
||||
if (JS_TOLOWER(c) == 'e') {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
if (c == '+' || c == '-') {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
}
|
||||
if (!JS7_ISDEC(c)) {
|
||||
@ -1357,7 +1187,8 @@ retry:
|
||||
goto error;
|
||||
}
|
||||
do {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
} while (JS7_ISDEC(c));
|
||||
}
|
||||
@ -1365,19 +1196,17 @@ retry:
|
||||
|
||||
/* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
|
||||
UngetChar(ts, c);
|
||||
ADD_TO_TOKENBUF(0);
|
||||
|
||||
if (!TOKENBUF_OK())
|
||||
if (!tb.append(0))
|
||||
goto error;
|
||||
|
||||
if (radix == 10) {
|
||||
if (!js_strtod(cx, TOKENBUF_BASE(), TOKENBUF_END(),
|
||||
&endptr, &dval)) {
|
||||
if (!js_strtod(cx, tb.begin(), tb.end(), &endptr, &dval)) {
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
JSMSG_OUT_OF_MEMORY);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (!js_strtointeger(cx, TOKENBUF_BASE(), TOKENBUF_END(),
|
||||
if (!js_strtointeger(cx, tb.begin(), tb.end(),
|
||||
&endptr, radix, &dval)) {
|
||||
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
|
||||
JSMSG_OUT_OF_MEMORY);
|
||||
@ -1391,7 +1220,7 @@ retry:
|
||||
|
||||
if (c == '"' || c == '\'') {
|
||||
qc = c;
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
while ((c = GetChar(ts)) != qc) {
|
||||
if (c == '\n' || c == EOF) {
|
||||
UngetChar(ts, c);
|
||||
@ -1453,9 +1282,10 @@ retry:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
tp->pos.end.lineno = (uint16)ts->lineno;
|
||||
@ -1593,7 +1423,7 @@ retry:
|
||||
(JS_HAS_XML_OPTION(cx) || PeekChar(ts) != '!')) {
|
||||
/* Check for XML comment or CDATA section. */
|
||||
if (MatchChar(ts, '!')) {
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
|
||||
/* Scan XML comment. */
|
||||
if (MatchChar(ts, '-')) {
|
||||
@ -1602,7 +1432,8 @@ retry:
|
||||
while ((c = GetChar(ts)) != '-' || !MatchChar(ts, '-')) {
|
||||
if (c == EOF)
|
||||
goto bad_xml_markup;
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
tt = TOK_XMLCOMMENT;
|
||||
tp->t_op = JSOP_XMLCOMMENT;
|
||||
@ -1626,7 +1457,8 @@ retry:
|
||||
cp[1] != '>') {
|
||||
if (c == EOF)
|
||||
goto bad_xml_markup;
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
GetChar(ts); /* discard ] but not > */
|
||||
tt = TOK_XMLCDATA;
|
||||
@ -1643,17 +1475,17 @@ retry:
|
||||
targetLength = 0;
|
||||
contentIndex = -1;
|
||||
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
while ((c = GetChar(ts)) != '?' || PeekChar(ts) != '>') {
|
||||
if (c == EOF)
|
||||
goto bad_xml_markup;
|
||||
if (inTarget) {
|
||||
if (JS_ISXMLSPACE(c)) {
|
||||
if (TOKENBUF_LENGTH() == 0)
|
||||
if (tb.empty())
|
||||
goto bad_xml_markup;
|
||||
inTarget = JS_FALSE;
|
||||
} else {
|
||||
if (!((TOKENBUF_LENGTH() == 0)
|
||||
if (!(tb.empty()
|
||||
? JS_ISXMLNSSTART(c)
|
||||
: JS_ISXMLNS(c))) {
|
||||
goto bad_xml_markup;
|
||||
@ -1662,32 +1494,31 @@ retry:
|
||||
}
|
||||
} else {
|
||||
if (contentIndex < 0 && !JS_ISXMLSPACE(c))
|
||||
contentIndex = TOKENBUF_LENGTH();
|
||||
contentIndex = tb.length();
|
||||
}
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
if (targetLength == 0)
|
||||
goto bad_xml_markup;
|
||||
if (!TOKENBUF_OK())
|
||||
goto error;
|
||||
if (contentIndex < 0) {
|
||||
atom = cx->runtime->atomState.emptyAtom;
|
||||
} else {
|
||||
atom = js_AtomizeChars(cx,
|
||||
&TOKENBUF_CHAR(contentIndex),
|
||||
TOKENBUF_LENGTH() - contentIndex,
|
||||
tb.begin() + contentIndex,
|
||||
tb.length() - contentIndex,
|
||||
0);
|
||||
if (!atom)
|
||||
goto error;
|
||||
}
|
||||
TRIM_TOKENBUF(targetLength);
|
||||
tb.shrinkBy(tb.length() - targetLength);
|
||||
tp->t_atom2 = atom;
|
||||
tt = TOK_XMLPI;
|
||||
|
||||
finish_xml_markup:
|
||||
if (!MatchChar(ts, '>'))
|
||||
goto bad_xml_markup;
|
||||
atom = TOKENBUF_TO_ATOM();
|
||||
atom = atomize(cx, tb);
|
||||
if (!atom)
|
||||
goto error;
|
||||
tp->t_atom = atom;
|
||||
@ -1849,7 +1680,7 @@ skipline:
|
||||
uintN flags, length;
|
||||
JSBool inCharClass = JS_FALSE;
|
||||
|
||||
INIT_TOKENBUF();
|
||||
tb.clear();
|
||||
for (;;) {
|
||||
c = GetChar(ts);
|
||||
if (c == '\n' || c == EOF) {
|
||||
@ -1859,7 +1690,8 @@ skipline:
|
||||
goto error;
|
||||
}
|
||||
if (c == '\\') {
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
c = GetChar(ts);
|
||||
} else if (c == '[') {
|
||||
inCharClass = JS_TRUE;
|
||||
@ -1869,9 +1701,10 @@ skipline:
|
||||
/* For compat with IE, allow unescaped / in char classes. */
|
||||
break;
|
||||
}
|
||||
ADD_TO_TOKENBUF(c);
|
||||
if (!tb.append(c))
|
||||
goto error;
|
||||
}
|
||||
for (flags = 0, length = TOKENBUF_LENGTH() + 1; ; length++) {
|
||||
for (flags = 0, length = tb.length() + 1; ; length++) {
|
||||
c = PeekChar(ts);
|
||||
if (c == 'g' && !(flags & JSREG_GLOB))
|
||||
flags |= JSREG_GLOB;
|
||||
@ -1895,10 +1728,6 @@ skipline:
|
||||
(void) GetChar(ts);
|
||||
goto error;
|
||||
}
|
||||
/* XXXbe fix jsregexp.c so it doesn't depend on NUL termination */
|
||||
if (!TOKENBUF_OK())
|
||||
goto error;
|
||||
NUL_TERM_TOKENBUF();
|
||||
tp->t_reflags = flags;
|
||||
tt = TOK_REGEXP;
|
||||
break;
|
||||
@ -2006,8 +1835,6 @@ out:
|
||||
ts->flags |= TSF_DIRTYLINE;
|
||||
|
||||
eol_out:
|
||||
if (!STRING_BUFFER_OK(&ts->tokenbuf))
|
||||
tt = TOK_ERROR;
|
||||
JS_ASSERT(tt < TOK_LIMIT);
|
||||
tp->pos.end.index = ts->linepos +
|
||||
(ts->linebuf.ptr - ts->linebuf.base) -
|
||||
@ -2019,16 +1846,6 @@ error:
|
||||
tt = TOK_ERROR;
|
||||
ts->flags |= TSF_ERROR;
|
||||
goto out;
|
||||
|
||||
#undef INIT_TOKENBUF
|
||||
#undef TOKENBUF_LENGTH
|
||||
#undef TOKENBUF_OK
|
||||
#undef TOKENBUF_TO_ATOM
|
||||
#undef ADD_TO_TOKENBUF
|
||||
#undef TOKENBUF_BASE
|
||||
#undef TOKENBUF_CHAR
|
||||
#undef TRIM_TOKENBUF
|
||||
#undef NUL_TERM_TOKENBUF
|
||||
}
|
||||
|
||||
void
|
||||
|
105
js/src/jsscan.h
105
js/src/jsscan.h
@ -48,6 +48,7 @@
|
||||
#include "jsopcode.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
@ -161,73 +162,6 @@ typedef enum JSTokenType {
|
||||
# define TOKEN_TYPE_IS_DECL(tt) ((tt) == TOK_VAR)
|
||||
#endif
|
||||
|
||||
struct JSStringBuffer {
|
||||
jschar *base;
|
||||
jschar *limit; /* length limit for quick bounds check */
|
||||
jschar *ptr; /* slot for next non-NUL char to store */
|
||||
void *data;
|
||||
JSBool (*grow)(JSStringBuffer *sb, size_t newlength);
|
||||
void (*free)(JSStringBuffer *sb);
|
||||
};
|
||||
|
||||
#define STRING_BUFFER_ERROR_BASE ((jschar *) 1)
|
||||
#define STRING_BUFFER_OK(sb) ((sb)->base != STRING_BUFFER_ERROR_BASE)
|
||||
#define STRING_BUFFER_OFFSET(sb) ((sb)->ptr -(sb)->base)
|
||||
|
||||
extern void
|
||||
js_InitStringBuffer(JSStringBuffer *sb);
|
||||
|
||||
extern void
|
||||
js_FinishStringBuffer(JSStringBuffer *sb);
|
||||
|
||||
static inline void
|
||||
js_RewindStringBuffer(JSStringBuffer *sb)
|
||||
{
|
||||
JS_ASSERT(STRING_BUFFER_OK(sb));
|
||||
sb->ptr = sb->base;
|
||||
}
|
||||
|
||||
#define ENSURE_STRING_BUFFER(sb,n) \
|
||||
((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n))
|
||||
|
||||
/*
|
||||
* NB: callers are obligated to test STRING_BUFFER_OK(sb) after this returns,
|
||||
* before calling it again -- but not necessarily before calling other sb ops
|
||||
* declared in this header file.
|
||||
*
|
||||
* Thus multiple calls, to ops other than this one that check STRING_BUFFER_OK
|
||||
* and suppress updating sb if true, can consolidate the final STRING_BUFFER_OK
|
||||
* test that conditions a JS_ReportOutOfMemory (if necessary -- the grow hook
|
||||
* can report OOM early, obviating the need for the callers to report).
|
||||
*
|
||||
* This style of error checking is not obviously better, and it could be worse
|
||||
* in efficiency, than the propagated failure return code style used elsewhere
|
||||
* in the engine. I view it as a failed experiment. /be
|
||||
*/
|
||||
static inline void
|
||||
js_FastAppendChar(JSStringBuffer *sb, jschar c)
|
||||
{
|
||||
JS_ASSERT(STRING_BUFFER_OK(sb));
|
||||
if (!ENSURE_STRING_BUFFER(sb, 1))
|
||||
return;
|
||||
*sb->ptr++ = c;
|
||||
}
|
||||
|
||||
extern void
|
||||
js_AppendChar(JSStringBuffer *sb, jschar c);
|
||||
|
||||
extern void
|
||||
js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count);
|
||||
|
||||
extern void
|
||||
js_AppendCString(JSStringBuffer *sb, const char *asciiz);
|
||||
|
||||
extern void
|
||||
js_AppendUCString(JSStringBuffer *sb, const jschar *buf, uintN len);
|
||||
|
||||
extern void
|
||||
js_AppendJSString(JSStringBuffer *sb, JSString *str);
|
||||
|
||||
struct JSTokenPtr {
|
||||
uint32 index; /* index of char in physical line */
|
||||
uint32 lineno; /* physical line number */
|
||||
@ -320,7 +254,6 @@ struct JSTokenStream {
|
||||
uint32 linepos; /* linebuf offset in physical line */
|
||||
JSTokenBuf linebuf; /* line buffer for diagnostics */
|
||||
JSTokenBuf userbuf; /* user input buffer if !file */
|
||||
JSStringBuffer tokenbuf; /* current token string buffer */
|
||||
const char *filename; /* input filename or null */
|
||||
FILE *file; /* stdio stream if reading from file */
|
||||
JSSourceHandler listener; /* callback for source; eg debugger */
|
||||
@ -328,6 +261,29 @@ struct JSTokenStream {
|
||||
void *listenerTSData;/* listener data for this TokenStream */
|
||||
jschar *saveEOL; /* save next end of line in userbuf, to
|
||||
optimize for very long lines */
|
||||
JSCharBuffer tokenbuf; /* current token string buffer */
|
||||
|
||||
/*
|
||||
* To construct a JSTokenStream, first call the constructor, which is
|
||||
* infallible, then call |init|, which can fail. To destroy a JSTokenStream,
|
||||
* first call |close| then call the destructor. If |init| fails, do not call
|
||||
* |close|.
|
||||
*
|
||||
* This class uses JSContext.tempPool to allocate internal buffers. The
|
||||
* caller should JS_ARENA_MARK before calling |init| and JS_ARENA_RELEASE
|
||||
* after calling |close|.
|
||||
*/
|
||||
JSTokenStream(JSContext *);
|
||||
|
||||
/*
|
||||
* Create a new token stream, either from an input buffer or from a file.
|
||||
* Return false on file-open or memory-allocation failure.
|
||||
*/
|
||||
bool init(JSContext *, const jschar *base, size_t length,
|
||||
FILE *fp, const char *filename, uintN lineno);
|
||||
|
||||
void close(JSContext *);
|
||||
~JSTokenStream() {}
|
||||
};
|
||||
|
||||
#define CURRENT_TOKEN(ts) ((ts)->tokens[(ts)->cursor])
|
||||
@ -380,19 +336,6 @@ struct JSTokenStream {
|
||||
#define LINE_SEPARATOR 0x2028
|
||||
#define PARA_SEPARATOR 0x2029
|
||||
|
||||
/*
|
||||
* Create a new token stream, either from an input buffer or from a file.
|
||||
* Return null on file-open or memory-allocation failure.
|
||||
*
|
||||
* The function uses JSContext.tempPool to allocate internal buffers. The
|
||||
* caller should release them using JS_ARENA_RELEASE after it has finished
|
||||
* with the token stream and has called js_CloseTokenStream.
|
||||
*/
|
||||
extern JSBool
|
||||
js_InitTokenStream(JSContext *cx, JSTokenStream *ts,
|
||||
const jschar *base, size_t length,
|
||||
FILE *fp, const char *filename, uintN lineno);
|
||||
|
||||
extern void
|
||||
js_CloseTokenStream(JSContext *cx, JSTokenStream *ts);
|
||||
|
||||
|
@ -1634,13 +1634,14 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
JS_ASSERT(script->owner == cx->thread);
|
||||
#endif
|
||||
#ifdef JS_TRACER
|
||||
js_PurgeScriptFragments(cx, script);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
js_PurgeScriptFragments(cx, script);
|
||||
#endif
|
||||
|
||||
cx->free(script);
|
||||
}
|
||||
|
||||
|
317
js/src/jsstr.cpp
317
js/src/jsstr.cpp
@ -1263,13 +1263,13 @@ str_trimRight(JSContext *cx, uintN argc, jsval *vp)
|
||||
/*
|
||||
* Perl-inspired string functions.
|
||||
*/
|
||||
typedef struct GlobData {
|
||||
struct GlobData {
|
||||
jsbytecode *pc; /* in: program counter resulting in us matching */
|
||||
uintN flags; /* inout: mode and flag bits, see below */
|
||||
uintN optarg; /* in: index of optional flags argument */
|
||||
JSString *str; /* out: 'this' parameter object as string */
|
||||
JSRegExp *regexp; /* out: regexp parameter object private data */
|
||||
} GlobData;
|
||||
};
|
||||
|
||||
/*
|
||||
* Mode and flag bit definitions for match_or_replace's GlobData.flags field.
|
||||
@ -1343,26 +1343,21 @@ match_or_replace(JSContext *cx,
|
||||
: INT_TO_JSVAL(-1);
|
||||
}
|
||||
} else if (data->flags & GLOBAL_REGEXP) {
|
||||
if (reobj) {
|
||||
/* Set the lastIndex property's reserved slot to 0. */
|
||||
ok = js_SetLastIndex(cx, reobj, 0);
|
||||
} else {
|
||||
ok = JS_TRUE;
|
||||
}
|
||||
if (ok) {
|
||||
length = str->length();
|
||||
for (count = 0; index <= length; count++) {
|
||||
ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);
|
||||
if (!ok || *vp != JSVAL_TRUE)
|
||||
if (reobj)
|
||||
js_ClearRegExpLastIndex(reobj);
|
||||
length = str->length();
|
||||
ok = true;
|
||||
for (count = 0; index <= length; count++) {
|
||||
ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);
|
||||
if (!ok || *vp != JSVAL_TRUE)
|
||||
break;
|
||||
ok = glob(cx, count, data);
|
||||
if (!ok)
|
||||
break;
|
||||
if (cx->regExpStatics.lastMatch.length == 0) {
|
||||
if (index == length)
|
||||
break;
|
||||
ok = glob(cx, count, data);
|
||||
if (!ok)
|
||||
break;
|
||||
if (cx->regExpStatics.lastMatch.length == 0) {
|
||||
if (index == length)
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1408,34 +1403,34 @@ match_or_replace(JSContext *cx,
|
||||
return ok;
|
||||
}
|
||||
|
||||
typedef struct MatchData {
|
||||
struct MatchData {
|
||||
GlobData base;
|
||||
jsval *arrayval; /* NB: local root pointer */
|
||||
} MatchData;
|
||||
};
|
||||
|
||||
static JSBool
|
||||
match_glob(JSContext *cx, jsint count, GlobData *data)
|
||||
{
|
||||
MatchData *mdata;
|
||||
JSObject *arrayobj;
|
||||
JSSubString *matchsub;
|
||||
JSString *matchstr;
|
||||
jsval v;
|
||||
JS_ASSERT(count <= JSVAL_INT_MAX);
|
||||
|
||||
mdata = (MatchData *)data;
|
||||
arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval);
|
||||
MatchData *mdata = (MatchData *)data;
|
||||
JSObject *arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval);
|
||||
if (!arrayobj) {
|
||||
arrayobj = js_NewArrayObject(cx, 0, NULL);
|
||||
if (!arrayobj)
|
||||
return JS_FALSE;
|
||||
*mdata->arrayval = OBJECT_TO_JSVAL(arrayobj);
|
||||
}
|
||||
matchsub = &cx->regExpStatics.lastMatch;
|
||||
matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length);
|
||||
|
||||
JSString *str = cx->regExpStatics.input;
|
||||
JSSubString &match = cx->regExpStatics.lastMatch;
|
||||
ptrdiff_t off = match.chars - str->chars();
|
||||
JS_ASSERT(off >= 0 && size_t(off) <= str->length());
|
||||
JSString *matchstr = js_NewDependentString(cx, str, off, match.length);
|
||||
if (!matchstr)
|
||||
return JS_FALSE;
|
||||
v = STRING_TO_JSVAL(matchstr);
|
||||
JS_ASSERT(count <= JSVAL_INT_MAX);
|
||||
|
||||
jsval v = STRING_TO_JSVAL(matchstr);
|
||||
|
||||
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
|
||||
return arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
|
||||
@ -1476,7 +1471,7 @@ str_search(JSContext *cx, uintN argc, jsval *vp)
|
||||
return match_or_replace(cx, NULL, &data, argc, vp);
|
||||
}
|
||||
|
||||
typedef struct ReplaceData {
|
||||
struct ReplaceData {
|
||||
ReplaceData(JSContext *cx) : cb(cx) {}
|
||||
GlobData base; /* base struct state */
|
||||
JSObject *lambda; /* replacement function object or null */
|
||||
@ -1487,8 +1482,8 @@ typedef struct ReplaceData {
|
||||
jsint leftIndex; /* left context index in base.str->chars */
|
||||
JSSubString dollarStr; /* for "$$" interpret_dollar result */
|
||||
bool globCalled; /* record whether replace_glob has been called */
|
||||
JSCharVector cb; /* buffer built during match_or_replace */
|
||||
} ReplaceData;
|
||||
JSCharBuffer cb; /* buffer built during match_or_replace */
|
||||
};
|
||||
|
||||
static JSSubString *
|
||||
interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata,
|
||||
@ -1548,7 +1543,19 @@ interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
static JS_ALWAYS_INLINE bool
|
||||
PushRegExpSubstr(JSContext *cx, const JSSubString &sub, jsval *&sp)
|
||||
{
|
||||
JSString *whole = cx->regExpStatics.input;
|
||||
size_t off = sub.chars - whole->chars();
|
||||
JSString *str = js_NewDependentString(cx, whole, off, sub.length);
|
||||
if (!str)
|
||||
return false;
|
||||
*sp++ = STRING_TO_JSVAL(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
|
||||
{
|
||||
JSString *repstr;
|
||||
@ -1559,22 +1566,10 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
|
||||
|
||||
lambda = rdata->lambda;
|
||||
if (lambda) {
|
||||
uintN argc, i, j, m, n, p;
|
||||
jsval *invokevp, *sp;
|
||||
void *mark;
|
||||
JSBool ok;
|
||||
uintN i, m, n;
|
||||
|
||||
js_LeaveTrace(cx);
|
||||
|
||||
/*
|
||||
* Save the regExpStatics from the current regexp, since they may be
|
||||
* clobbered by a RegExp usage in the lambda function. Note that all
|
||||
* members of JSRegExpStatics are JSSubStrings, so not GC roots, save
|
||||
* input, which is rooted otherwise via vp[1] in str_replace.
|
||||
*/
|
||||
JSRegExpStatics save = cx->regExpStatics;
|
||||
JSBool freeMoreParens = JS_FALSE;
|
||||
|
||||
/*
|
||||
* In the lambda case, not only do we find the replacement string's
|
||||
* length, we compute repstr and return it via rdata for use within
|
||||
@ -1583,48 +1578,53 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
|
||||
* For $&, etc., we must create string jsvals from cx->regExpStatics.
|
||||
* We grab up stack space to keep the newborn strings GC-rooted.
|
||||
*/
|
||||
p = rdata->base.regexp->parenCount;
|
||||
argc = 1 + p + 2;
|
||||
invokevp = js_AllocStack(cx, 2 + argc, &mark);
|
||||
uintN p = rdata->base.regexp->parenCount;
|
||||
uintN argc = 1 + p + 2;
|
||||
void *mark;
|
||||
jsval *invokevp = js_AllocStack(cx, 2 + argc, &mark);
|
||||
if (!invokevp)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
MUST_FLOW_THROUGH("lambda_out");
|
||||
bool ok = false;
|
||||
bool freeMoreParens = false;
|
||||
|
||||
/*
|
||||
* Save the regExpStatics from the current regexp, since they may be
|
||||
* clobbered by a RegExp usage in the lambda function. Note that all
|
||||
* members of JSRegExpStatics are JSSubStrings, so not GC roots, save
|
||||
* input, which is rooted otherwise via vp[1] in str_replace.
|
||||
*/
|
||||
JSRegExpStatics save = cx->regExpStatics;
|
||||
|
||||
/* Push lambda and its 'this' parameter. */
|
||||
sp = invokevp;
|
||||
jsval *sp = invokevp;
|
||||
*sp++ = OBJECT_TO_JSVAL(lambda);
|
||||
*sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));
|
||||
|
||||
#define PUSH_REGEXP_STATIC(sub) \
|
||||
JS_BEGIN_MACRO \
|
||||
JSString *str = js_NewStringCopyN(cx, \
|
||||
cx->regExpStatics.sub.chars, \
|
||||
cx->regExpStatics.sub.length); \
|
||||
if (!str) { \
|
||||
ok = JS_FALSE; \
|
||||
goto lambda_out; \
|
||||
} \
|
||||
*sp++ = STRING_TO_JSVAL(str); \
|
||||
JS_END_MACRO
|
||||
|
||||
/* Push $&, $1, $2, ... */
|
||||
PUSH_REGEXP_STATIC(lastMatch);
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
|
||||
goto lambda_out;
|
||||
|
||||
i = 0;
|
||||
m = cx->regExpStatics.parenCount;
|
||||
n = JS_MIN(m, 9);
|
||||
for (j = 0; i < n; i++, j++)
|
||||
PUSH_REGEXP_STATIC(parens[j]);
|
||||
for (j = 0; i < m; i++, j++)
|
||||
PUSH_REGEXP_STATIC(moreParens[j]);
|
||||
for (uintN j = 0; i < n; i++, j++) {
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
|
||||
goto lambda_out;
|
||||
}
|
||||
for (uintN j = 0; i < m; i++, j++) {
|
||||
if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
|
||||
goto lambda_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to clear moreParens in the top-of-stack cx->regExpStatics
|
||||
* to it won't be possibly realloc'ed, leaving the bottom-of-stack
|
||||
* so it won't be possibly realloc'ed, leaving the bottom-of-stack
|
||||
* moreParens pointing to freed memory.
|
||||
*/
|
||||
cx->regExpStatics.moreParens = NULL;
|
||||
freeMoreParens = JS_TRUE;
|
||||
|
||||
#undef PUSH_REGEXP_STATIC
|
||||
freeMoreParens = true;
|
||||
|
||||
/* Make sure to push undefined for any unmatched parens. */
|
||||
for (; i < p; i++)
|
||||
@ -1634,21 +1634,22 @@ find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
|
||||
*sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
|
||||
*sp++ = STRING_TO_JSVAL(rdata->base.str);
|
||||
|
||||
ok = js_Invoke(cx, argc, invokevp, 0);
|
||||
if (ok) {
|
||||
/*
|
||||
* NB: we count on the newborn string root to hold any string
|
||||
* created by this js_ValueToString that would otherwise be GC-
|
||||
* able, until we use rdata->repstr in do_replace.
|
||||
*/
|
||||
repstr = js_ValueToString(cx, *invokevp);
|
||||
if (!repstr) {
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
rdata->repstr = repstr;
|
||||
*sizep = repstr->length();
|
||||
}
|
||||
}
|
||||
if (!js_Invoke(cx, argc, invokevp, 0))
|
||||
goto lambda_out;
|
||||
|
||||
/*
|
||||
* NB: we count on the newborn string root to hold any string
|
||||
* created by this js_ValueToString that would otherwise be GC-
|
||||
* able, until we use rdata->repstr in do_replace.
|
||||
*/
|
||||
repstr = js_ValueToString(cx, *invokevp);
|
||||
if (!repstr)
|
||||
goto lambda_out;
|
||||
|
||||
rdata->repstr = repstr;
|
||||
*sizep = repstr->length();
|
||||
|
||||
ok = true;
|
||||
|
||||
lambda_out:
|
||||
js_FreeStack(cx, mark);
|
||||
@ -2481,7 +2482,7 @@ str_fromCharCode(JSContext *cx, uintN argc, jsval *vp)
|
||||
JSString *str;
|
||||
|
||||
argv = vp + 2;
|
||||
JS_ASSERT(argc < ARRAY_INIT_LIMIT);
|
||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||
if (argc == 1 &&
|
||||
(code = js_ValueToUint16(cx, &argv[0])) < UNIT_STRING_LIMIT) {
|
||||
str = js_GetUnitStringForChar(cx, code);
|
||||
@ -2608,8 +2609,10 @@ js_GetUnitStringForChar(JSContext *cx, jschar c)
|
||||
if (!str)
|
||||
return NULL;
|
||||
JS_LOCK_GC(rt);
|
||||
if (!rt->unitStrings[c])
|
||||
if (!rt->unitStrings[c]) {
|
||||
str->flatSetAtomized();
|
||||
rt->unitStrings[c] = str;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
str->initFlat(NULL, 0); /* avoid later assertion (bug 479381) */
|
||||
@ -2724,12 +2727,12 @@ js_NewString(JSContext *cx, jschar *chars, size_t length)
|
||||
static const size_t sMinWasteSize = 16;
|
||||
|
||||
JSString *
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb)
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb)
|
||||
{
|
||||
if (cb.empty())
|
||||
return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom);
|
||||
|
||||
size_t length = cb.size();
|
||||
size_t length = cb.length();
|
||||
if (!cb.append('\0'))
|
||||
return NULL;
|
||||
|
||||
@ -2915,18 +2918,18 @@ js_ValueToString(JSContext *cx, jsval v)
|
||||
}
|
||||
|
||||
static inline JSBool
|
||||
pushAtom(JSAtom *atom, JSCharVector &buf)
|
||||
pushAtom(JSAtom *atom, JSCharBuffer &cb)
|
||||
{
|
||||
JSString *str = ATOM_TO_STRING(atom);
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
str->getCharsAndLength(chars, length);
|
||||
return buf.append(chars, length);
|
||||
return cb.append(chars, length);
|
||||
}
|
||||
|
||||
/* This function implements E-262-3 section 9.8, toString. */
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb)
|
||||
{
|
||||
if (!JSVAL_IS_PRIMITIVE(v) && !JSVAL_TO_OBJECT(v)->defaultValue(cx, JSTYPE_STRING, &v))
|
||||
return JS_FALSE;
|
||||
@ -2936,16 +2939,16 @@ js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
str->getCharsAndLength(chars, length);
|
||||
return buf.append(chars, length);
|
||||
return cb.append(chars, length);
|
||||
}
|
||||
if (JSVAL_IS_NUMBER(v))
|
||||
return js_NumberValueToCharBuffer(cx, v, buf);
|
||||
return js_NumberValueToCharBuffer(cx, v, cb);
|
||||
if (JSVAL_IS_BOOLEAN(v))
|
||||
return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), buf);
|
||||
return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), cb);
|
||||
if (JSVAL_IS_NULL(v))
|
||||
return pushAtom(cx->runtime->atomState.nullAtom, buf);
|
||||
return pushAtom(cx->runtime->atomState.nullAtom, cb);
|
||||
JS_ASSERT(JSVAL_IS_VOID(v));
|
||||
return pushAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], buf);
|
||||
return pushAtom(cx->runtime->atomState.typeAtoms[JSTYPE_VOID], cb);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSString *)
|
||||
@ -4814,56 +4817,14 @@ const bool js_alnum[] = {
|
||||
|
||||
#define URI_CHUNK 64U
|
||||
|
||||
/* Concatenate jschars onto the buffer */
|
||||
static JSBool
|
||||
AddCharsToURI(JSContext *cx, JSCharBuffer *buf,
|
||||
const jschar *chars, size_t length)
|
||||
static inline bool
|
||||
TransferBufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval)
|
||||
{
|
||||
size_t total;
|
||||
jschar *newchars;
|
||||
|
||||
total = buf->length + length + 1;
|
||||
if (!buf->chars ||
|
||||
JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(buf->length + 1, URI_CHUNK)) {
|
||||
total = JS_ROUNDUP(total, URI_CHUNK);
|
||||
newchars = (jschar *) cx->realloc(buf->chars,
|
||||
total * sizeof(jschar));
|
||||
if (!newchars)
|
||||
return JS_FALSE;
|
||||
buf->chars = newchars;
|
||||
}
|
||||
js_strncpy(buf->chars + buf->length, chars, length);
|
||||
buf->length += length;
|
||||
buf->chars[buf->length] = 0;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
TransferBufferToString(JSContext *cx, JSCharBuffer *cb, jsval *rval)
|
||||
{
|
||||
jschar *chars;
|
||||
size_t n;
|
||||
JSString *str;
|
||||
|
||||
/*
|
||||
* Shrinking realloc can fail (e.g., with a BSD-style allocator), but we
|
||||
* don't worry about that case here.
|
||||
*/
|
||||
n = cb->length;
|
||||
chars = (jschar *) cx->realloc(cb->chars, (n + 1) * sizeof(jschar));
|
||||
if (!chars)
|
||||
chars = cb->chars;
|
||||
str = js_NewString(cx, chars, n);
|
||||
JSString *str = js_NewStringFromCharBuffer(cx, cb);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Successful allocation transfer ownership of cb->chars to the string. */
|
||||
#ifdef DEBUG
|
||||
memset(cb, JS_FREE_PATTERN, sizeof *cb);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
*rval = STRING_TO_JSVAL(str);
|
||||
return JS_TRUE;
|
||||
return true;;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4878,7 +4839,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
|
||||
const jschar *unescapedSet2, jsval *rval)
|
||||
{
|
||||
size_t length, j, k, L;
|
||||
JSCharBuffer cb;
|
||||
JSCharBuffer cb(cx);
|
||||
const jschar *chars;
|
||||
jschar c, c2;
|
||||
uint32 v;
|
||||
@ -4892,9 +4853,6 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
cb.length = 0;
|
||||
cb.chars = NULL;
|
||||
|
||||
/* From this point the control must goto bad on failures. */
|
||||
hexBuf[0] = '%';
|
||||
hexBuf[3] = 0;
|
||||
@ -4902,13 +4860,13 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
|
||||
c = chars[k];
|
||||
if (js_strchr(unescapedSet, c) ||
|
||||
(unescapedSet2 && js_strchr(unescapedSet2, c))) {
|
||||
if (!AddCharsToURI(cx, &cb, &c, 1))
|
||||
goto bad;
|
||||
if (!cb.append(c))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
if ((c >= 0xDC00) && (c <= 0xDFFF)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_URI, NULL);
|
||||
goto bad;
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (c < 0xD800 || c > 0xDBFF) {
|
||||
v = c;
|
||||
@ -4917,13 +4875,13 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
|
||||
if (k == length) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_URI, NULL);
|
||||
goto bad;
|
||||
return JS_FALSE;
|
||||
}
|
||||
c2 = chars[k];
|
||||
if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_URI, NULL);
|
||||
goto bad;
|
||||
return JS_FALSE;
|
||||
}
|
||||
v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
|
||||
}
|
||||
@ -4931,27 +4889,20 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
|
||||
for (j = 0; j < L; j++) {
|
||||
hexBuf[1] = HexDigits[utf8buf[j] >> 4];
|
||||
hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
|
||||
if (!AddCharsToURI(cx, &cb, hexBuf, 3))
|
||||
goto bad;
|
||||
if (!cb.append(hexBuf, 3))
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!TransferBufferToString(cx, &cb, rval))
|
||||
goto bad;
|
||||
|
||||
return JS_TRUE;
|
||||
|
||||
bad:
|
||||
cx->free(cb.chars);
|
||||
return JS_FALSE;
|
||||
return TransferBufferToString(cx, cb, rval);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
|
||||
{
|
||||
size_t length, start, k;
|
||||
JSCharBuffer cb;
|
||||
JSCharBuffer cb(cx);
|
||||
const jschar *chars;
|
||||
jschar c, H;
|
||||
uint32 v;
|
||||
@ -4965,9 +4916,6 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
cb.length = 0;
|
||||
cb.chars = NULL;
|
||||
|
||||
/* From this point the control must goto bad on failures. */
|
||||
for (k = 0; k < length; k++) {
|
||||
c = chars[k];
|
||||
@ -5009,36 +4957,31 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
|
||||
goto report_bad_uri;
|
||||
c = (jschar)((v & 0x3FF) + 0xDC00);
|
||||
H = (jschar)((v >> 10) + 0xD800);
|
||||
if (!AddCharsToURI(cx, &cb, &H, 1))
|
||||
goto bad;
|
||||
if (!cb.append(H))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
c = (jschar)v;
|
||||
}
|
||||
}
|
||||
if (js_strchr(reservedSet, c)) {
|
||||
if (!AddCharsToURI(cx, &cb, &chars[start], (k - start + 1)))
|
||||
goto bad;
|
||||
if (!cb.append(chars + start, k - start + 1))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
if (!AddCharsToURI(cx, &cb, &c, 1))
|
||||
goto bad;
|
||||
if (!cb.append(c))
|
||||
return JS_FALSE;
|
||||
}
|
||||
} else {
|
||||
if (!AddCharsToURI(cx, &cb, &c, 1))
|
||||
if (!cb.append(c))
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TransferBufferToString(cx, &cb, rval))
|
||||
goto bad;
|
||||
|
||||
return JS_TRUE;
|
||||
return TransferBufferToString(cx, cb, rval);
|
||||
|
||||
report_bad_uri:
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
|
||||
/* FALL THROUGH */
|
||||
|
||||
bad:
|
||||
cx->free(cb.chars);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,11 @@ private:
|
||||
*
|
||||
* ATOMIZED is used only with flat, immutable strings.
|
||||
*/
|
||||
enum {
|
||||
enum
|
||||
#if defined(_MSC_VER) && defined(_WIN64)
|
||||
: size_t /* VC++ 64-bit incorrectly defaults this enum's size to int. */
|
||||
#endif
|
||||
{
|
||||
DEPENDENT = JSSTRING_BIT(JS_BITS_PER_WORD - 1),
|
||||
PREFIX = JSSTRING_BIT(JS_BITS_PER_WORD - 2),
|
||||
MUTABLE = PREFIX,
|
||||
@ -145,7 +149,12 @@ private:
|
||||
LENGTH_BITS = JS_BITS_PER_WORD - 4,
|
||||
LENGTH_MASK = JSSTRING_BITMASK(LENGTH_BITS),
|
||||
|
||||
DEPENDENT_LENGTH_BITS = LENGTH_BITS / 2,
|
||||
/*
|
||||
* VC++ 64-bit incorrectly produces the compiler error "Conversion to
|
||||
* enumeration type requires an explicit cast" unless we cast to size_t
|
||||
* here.
|
||||
*/
|
||||
DEPENDENT_LENGTH_BITS = size_t(LENGTH_BITS) / 2,
|
||||
DEPENDENT_LENGTH_MASK = JSSTRING_BITMASK(DEPENDENT_LENGTH_BITS),
|
||||
DEPENDENT_START_BITS = LENGTH_BITS - DEPENDENT_LENGTH_BITS,
|
||||
DEPENDENT_START_SHIFT = DEPENDENT_LENGTH_BITS,
|
||||
@ -157,7 +166,11 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
enum {
|
||||
enum
|
||||
#if defined(_MSC_VER) && defined(_WIN64)
|
||||
: size_t /* VC++ 64-bit incorrectly defaults this enum's size to int. */
|
||||
#endif
|
||||
{
|
||||
MAX_LENGTH = LENGTH_MASK,
|
||||
MAX_DEPENDENT_START = DEPENDENT_START_MASK,
|
||||
MAX_DEPENDENT_LENGTH = DEPENDENT_LENGTH_MASK
|
||||
@ -371,11 +384,6 @@ js_toLowerCase(JSContext *cx, JSString *str);
|
||||
extern JSString * JS_FASTCALL
|
||||
js_toUpperCase(JSContext *cx, JSString *str);
|
||||
|
||||
typedef struct JSCharBuffer {
|
||||
size_t length;
|
||||
jschar *chars;
|
||||
} JSCharBuffer;
|
||||
|
||||
struct JSSubString {
|
||||
size_t length;
|
||||
const jschar *chars;
|
||||
@ -580,7 +588,7 @@ js_NewString(JSContext *cx, jschar *chars, size_t length);
|
||||
* by js_NewString.
|
||||
*/
|
||||
extern JSString *
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb);
|
||||
js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb);
|
||||
|
||||
extern JSString *
|
||||
js_NewDependentString(JSContext *cx, JSString *base, size_t start,
|
||||
@ -621,7 +629,7 @@ js_ValueToString(JSContext *cx, jsval v);
|
||||
* passed buffer may have partial results appended.
|
||||
*/
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
|
||||
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb);
|
||||
|
||||
/*
|
||||
* Convert a value to its source expression, returning null after reporting
|
||||
|
1591
js/src/jstracer.cpp
1591
js/src/jstracer.cpp
File diff suppressed because it is too large
Load Diff
@ -56,33 +56,46 @@
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
class Queue : public avmplus::GCObject {
|
||||
class Queue {
|
||||
T* _data;
|
||||
unsigned _len;
|
||||
unsigned _max;
|
||||
nanojit::Allocator* alloc;
|
||||
|
||||
public:
|
||||
void ensure(unsigned size) {
|
||||
if (!_max)
|
||||
_max = 16;
|
||||
while (_max < size)
|
||||
_max <<= 1;
|
||||
_data = (T*)realloc(_data, _max * sizeof(T));
|
||||
if (alloc) {
|
||||
T* tmp = new (*alloc) T[_max];
|
||||
memcpy(tmp, _data, _len * sizeof(T));
|
||||
_data = tmp;
|
||||
} else {
|
||||
_data = (T*)realloc(_data, _max * sizeof(T));
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
memset(&_data[_len], 0xcd, _max - _len);
|
||||
#endif
|
||||
}
|
||||
public:
|
||||
Queue(unsigned max = 16) {
|
||||
|
||||
Queue(nanojit::Allocator* alloc, unsigned max = 16)
|
||||
: alloc(alloc)
|
||||
{
|
||||
this->_max = max;
|
||||
this->_len = 0;
|
||||
if (max)
|
||||
this->_data = (T*)malloc(max * sizeof(T));
|
||||
this->_data = (alloc ?
|
||||
new (*alloc) T[max] :
|
||||
(T*)malloc(max * sizeof(T)));
|
||||
else
|
||||
this->_data = NULL;
|
||||
}
|
||||
|
||||
~Queue() {
|
||||
free(_data);
|
||||
if (!alloc)
|
||||
free(_data);
|
||||
}
|
||||
|
||||
bool contains(T a) {
|
||||
@ -185,7 +198,8 @@ enum LC_TMBits {
|
||||
LC_TMPatcher = 1<<19,
|
||||
LC_TMAbort = 1<<20,
|
||||
LC_TMStats = 1<<21,
|
||||
LC_TMRegexp = 1<<22
|
||||
LC_TMRegexp = 1<<22,
|
||||
LC_TMTreeVis = 1<<23
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -309,6 +323,7 @@ typedef Queue<uint16> SlotList;
|
||||
|
||||
class TypeMap : public Queue<JSTraceType> {
|
||||
public:
|
||||
TypeMap(nanojit::Allocator* alloc) : Queue<JSTraceType>(alloc) {}
|
||||
JS_REQUIRES_STACK void captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth);
|
||||
JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots,
|
||||
unsigned stackSlots);
|
||||
@ -409,7 +424,7 @@ struct VMSideExit : public nanojit::SideExit
|
||||
}
|
||||
};
|
||||
|
||||
struct VMAllocator : public nanojit::Allocator
|
||||
class VMAllocator : public nanojit::Allocator
|
||||
{
|
||||
|
||||
public:
|
||||
@ -426,6 +441,7 @@ public:
|
||||
|
||||
bool mOutOfMemory;
|
||||
size_t mSize;
|
||||
|
||||
/*
|
||||
* FIXME: Area the LIR spills into if we encounter an OOM mid-way
|
||||
* through compilation; we must check mOutOfMemory before we run out
|
||||
@ -436,37 +452,68 @@ public:
|
||||
uintptr_t mReserve[0x10000];
|
||||
};
|
||||
|
||||
|
||||
struct REHashKey {
|
||||
size_t re_length;
|
||||
uint16 re_flags;
|
||||
const jschar* re_chars;
|
||||
|
||||
REHashKey(size_t re_length, uint16 re_flags, const jschar *re_chars)
|
||||
: re_length(re_length)
|
||||
, re_flags(re_flags)
|
||||
, re_chars(re_chars)
|
||||
{}
|
||||
|
||||
bool operator==(const REHashKey& other) const
|
||||
{
|
||||
return ((this->re_length == other.re_length) &&
|
||||
(this->re_flags == other.re_flags) &&
|
||||
!memcmp(this->re_chars, other.re_chars,
|
||||
this->re_length * sizeof(jschar)));
|
||||
}
|
||||
};
|
||||
|
||||
struct REHashFn {
|
||||
static size_t hash(const REHashKey& k) {
|
||||
return
|
||||
k.re_length +
|
||||
k.re_flags +
|
||||
nanojit::murmurhash(k.re_chars, k.re_length * sizeof(jschar));
|
||||
}
|
||||
};
|
||||
|
||||
struct FrameInfo {
|
||||
JSObject* callee; // callee function object
|
||||
JSObject* block; // caller block chain head
|
||||
jsbytecode* pc; // caller fp->regs->pc
|
||||
jsbytecode* imacpc; // caller fp->imacpc
|
||||
uint16 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL
|
||||
uint32 spdist; // distance from fp->slots to fp->regs->sp at JSOP_CALL
|
||||
|
||||
/*
|
||||
* Bit 15 (0x8000) is a flag that is set if constructing (called through new).
|
||||
* Bits 0-14 are the actual argument count. This may be less than fun->nargs.
|
||||
* NB: This is argc for the callee, not the caller.
|
||||
*/
|
||||
uint16 argc;
|
||||
uint32 argc;
|
||||
|
||||
/*
|
||||
* Stack pointer adjustment needed for navigation of native stack in
|
||||
* js_GetUpvarOnTrace. spoffset is the number of slots in the native
|
||||
* stack frame for the caller *before* the slots covered by spdist.
|
||||
* This may be negative if the caller is the top level script.
|
||||
* The key fact is that if we let 'cpos' be the start of the caller's
|
||||
* native stack frame, then (cpos + spoffset) points to the first
|
||||
* non-argument slot in the callee's native stack frame.
|
||||
* Number of stack slots in the caller, not counting slots pushed when
|
||||
* invoking the callee. That is, slots after JSOP_CALL completes but
|
||||
* without the return value. This is also equal to the number of slots
|
||||
* between fp->down->argv[-2] (calleR fp->callee) and fp->argv[-2]
|
||||
* (calleE fp->callee).
|
||||
*/
|
||||
int32 spoffset;
|
||||
uint32 callerHeight;
|
||||
|
||||
/* argc of the caller */
|
||||
uint32 callerArgc;
|
||||
|
||||
// Safer accessors for argc.
|
||||
enum { CONSTRUCTING_MASK = 0x8000 };
|
||||
enum { CONSTRUCTING_FLAG = 0x10000 };
|
||||
void set_argc(uint16 argc, bool constructing) {
|
||||
this->argc = argc | (constructing ? CONSTRUCTING_MASK : 0);
|
||||
this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0);
|
||||
}
|
||||
uint16 get_argc() const { return argc & ~CONSTRUCTING_MASK; }
|
||||
bool is_constructing() const { return (argc & CONSTRUCTING_MASK) != 0; }
|
||||
uint16 get_argc() const { return argc & ~CONSTRUCTING_FLAG; }
|
||||
bool is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; }
|
||||
|
||||
// The typemap just before the callee is called.
|
||||
JSTraceType* get_typemap() { return (JSTraceType*) (this+1); }
|
||||
@ -479,7 +526,7 @@ struct UnstableExit
|
||||
UnstableExit* next;
|
||||
};
|
||||
|
||||
class TreeInfo MMGC_SUBCLASS_DECL {
|
||||
class TreeInfo {
|
||||
public:
|
||||
nanojit::Fragment* const fragment;
|
||||
JSScript* script;
|
||||
@ -496,25 +543,34 @@ public:
|
||||
unsigned branchCount;
|
||||
Queue<VMSideExit*> sideExits;
|
||||
UnstableExit* unstableExits;
|
||||
/* All embedded GC things are registered here so the GC can scan them. */
|
||||
Queue<jsval> gcthings;
|
||||
Queue<JSScopeProperty*> sprops;
|
||||
#ifdef DEBUG
|
||||
const char* treeFileName;
|
||||
uintN treeLineNumber;
|
||||
uintN treePCOffset;
|
||||
#endif
|
||||
|
||||
TreeInfo(nanojit::Fragment* _fragment,
|
||||
TreeInfo(nanojit::Allocator* alloc,
|
||||
nanojit::Fragment* _fragment,
|
||||
SlotList* _globalSlots)
|
||||
: fragment(_fragment),
|
||||
script(NULL),
|
||||
maxNativeStackSlots(0),
|
||||
nativeStackBase(0),
|
||||
maxCallDepth(0),
|
||||
nStackTypes(0),
|
||||
globalSlots(_globalSlots),
|
||||
branchCount(0),
|
||||
unstableExits(NULL)
|
||||
{}
|
||||
~TreeInfo();
|
||||
: fragment(_fragment),
|
||||
script(NULL),
|
||||
maxNativeStackSlots(0),
|
||||
nativeStackBase(0),
|
||||
maxCallDepth(0),
|
||||
typeMap(alloc),
|
||||
nStackTypes(0),
|
||||
globalSlots(_globalSlots),
|
||||
dependentTrees(alloc),
|
||||
linkedTrees(alloc),
|
||||
branchCount(0),
|
||||
sideExits(alloc),
|
||||
unstableExits(NULL),
|
||||
gcthings(alloc),
|
||||
sprops(alloc)
|
||||
{}
|
||||
|
||||
inline unsigned nGlobalTypes() {
|
||||
return typeMap.length() - nStackTypes;
|
||||
@ -659,6 +715,11 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
uint32 outerArgc; /* outer trace deepest frame argc */
|
||||
bool loop;
|
||||
|
||||
nanojit::LIns* insImmObj(JSObject* obj);
|
||||
nanojit::LIns* insImmFun(JSFunction* fun);
|
||||
nanojit::LIns* insImmStr(JSString* str);
|
||||
nanojit::LIns* insImmSprop(JSScopeProperty* sprop);
|
||||
|
||||
bool isGlobal(jsval* p) const;
|
||||
ptrdiff_t nativeGlobalOffset(jsval* p) const;
|
||||
JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(jsval* p) const;
|
||||
@ -730,7 +791,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK JSRecordingStatus inc(jsval& v, jsint incr, bool pre = true);
|
||||
JS_REQUIRES_STACK JSRecordingStatus inc(jsval v, nanojit::LIns*& v_ins, jsint incr,
|
||||
bool pre = true);
|
||||
JS_REQUIRES_STACK JSRecordingStatus incHelper(jsval v, nanojit::LIns* v_ins,
|
||||
JS_REQUIRES_STACK JSRecordingStatus incHelper(jsval v, nanojit::LIns* v_ins,
|
||||
nanojit::LIns*& v_after, jsint incr);
|
||||
JS_REQUIRES_STACK JSRecordingStatus incProp(jsint incr, bool pre = true);
|
||||
JS_REQUIRES_STACK JSRecordingStatus incElem(jsint incr, bool pre = true);
|
||||
@ -767,9 +828,9 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
jsuword& pcval);
|
||||
|
||||
void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot,
|
||||
nanojit::LIns* v_ins, const char *name);
|
||||
nanojit::LIns* v_ins);
|
||||
void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, nanojit::LIns*& dslots_ins,
|
||||
nanojit::LIns* v_ins, const char *name);
|
||||
nanojit::LIns* v_ins);
|
||||
void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins,
|
||||
nanojit::LIns* v_ins);
|
||||
|
||||
@ -808,6 +869,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK void enterDeepBailCall();
|
||||
JS_REQUIRES_STACK void leaveDeepBailCall();
|
||||
|
||||
JS_REQUIRES_STACK JSRecordingStatus primitiveToStringInPlace(jsval* vp);
|
||||
JS_REQUIRES_STACK void finishGetProp(nanojit::LIns* obj_ins, nanojit::LIns* vp_ins,
|
||||
nanojit::LIns* ok_ins, jsval* outp);
|
||||
JS_REQUIRES_STACK JSRecordingStatus getPropertyByName(nanojit::LIns* obj_ins, jsval* idvalp,
|
||||
@ -824,9 +886,15 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK JSRecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
|
||||
JSScopeProperty *sprop, nanojit::LIns *v_ins,
|
||||
jsval v);
|
||||
JS_REQUIRES_STACK JSRecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
|
||||
jsval* idvalp, jsval* rvalp,
|
||||
bool init);
|
||||
JS_REQUIRES_STACK JSRecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
|
||||
nanojit::LIns* index_ins,
|
||||
jsval* rvalp, bool init);
|
||||
|
||||
JS_REQUIRES_STACK void box_jsval(jsval v, nanojit::LIns*& v_ins);
|
||||
JS_REQUIRES_STACK void unbox_jsval(jsval v, nanojit::LIns*& v_ins, VMSideExit* exit);
|
||||
JS_REQUIRES_STACK nanojit::LIns* box_jsval(jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK nanojit::LIns* unbox_jsval(jsval v, nanojit::LIns* v_ins, VMSideExit* exit);
|
||||
JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
|
||||
VMSideExit* exit);
|
||||
JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
@ -840,6 +908,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
JS_REQUIRES_STACK JSRecordingStatus guardNotGlobalObject(JSObject* obj,
|
||||
nanojit::LIns* obj_ins);
|
||||
void clearFrameSlotsFromCache();
|
||||
JS_REQUIRES_STACK void putArguments();
|
||||
JS_REQUIRES_STACK JSRecordingStatus guardCallee(jsval& callee);
|
||||
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
|
||||
unsigned *depthp);
|
||||
@ -917,9 +986,7 @@ public:
|
||||
JS_REQUIRES_STACK bool closeLoop(SlotMap& slotMap, VMSideExit* exit, TypeConsensus &consensus);
|
||||
JS_REQUIRES_STACK void endLoop();
|
||||
JS_REQUIRES_STACK void endLoop(VMSideExit* exit);
|
||||
JS_REQUIRES_STACK void joinEdgesToEntry(nanojit::Fragmento* fragmento,
|
||||
VMFragment* peer_root);
|
||||
void blacklist() { fragment->blacklist(); }
|
||||
JS_REQUIRES_STACK void joinEdgesToEntry(VMFragment* peer_root);
|
||||
JS_REQUIRES_STACK void adjustCallerTypes(nanojit::Fragment* f);
|
||||
JS_REQUIRES_STACK nanojit::Fragment* findNestedCompatiblePeer(nanojit::Fragment* f);
|
||||
JS_REQUIRES_STACK void prepareTreeCall(nanojit::Fragment* inner);
|
||||
@ -927,7 +994,7 @@ public:
|
||||
unsigned getCallDepth() const;
|
||||
void pushAbortStack();
|
||||
void popAbortStack();
|
||||
void removeFragmentoReferences();
|
||||
void removeFragmentReferences();
|
||||
void deepAbort();
|
||||
|
||||
JS_REQUIRES_STACK JSRecordingStatus record_EnterFrame();
|
||||
@ -1004,7 +1071,7 @@ extern void
|
||||
js_PurgeScriptFragments(JSContext* cx, JSScript* script);
|
||||
|
||||
extern bool
|
||||
js_OverfullFragmento(JSTraceMonitor* tm, nanojit::Fragmento *frago);
|
||||
js_OverfullJITCache(JSTraceMonitor* tm, bool reCache);
|
||||
|
||||
extern void
|
||||
js_PurgeJITOracle();
|
||||
|
@ -73,17 +73,58 @@ template <class T> struct BitSize {
|
||||
static const size_t result = sizeof(T) * JS_BITS_PER_BYTE;
|
||||
};
|
||||
|
||||
/* Allow Assertions by only including the 'result' typedef if 'true'. */
|
||||
template <bool> struct StaticAssert {};
|
||||
template <> struct StaticAssert<true> { typedef int result; };
|
||||
|
||||
/*
|
||||
* Produce an N-bit mask, where N <= BitSize<size_t>::result. Handle the
|
||||
* language-undefined edge case when N = BitSize<size_t>::result.
|
||||
*/
|
||||
template <size_t N> struct NBitMask {
|
||||
typedef typename StaticAssert<N < BitSize<size_t>::result>::result _;
|
||||
static const size_t result = ~((size_t(1) << N) - 1);
|
||||
};
|
||||
template <> struct NBitMask<BitSize<size_t>::result> {
|
||||
static const size_t result = size_t(-1);
|
||||
};
|
||||
|
||||
/*
|
||||
* For the unsigned integral type size_t, compute a mask M for N such that
|
||||
* for all X, !(X & M) implies X * N will not overflow (w.r.t size_t)
|
||||
*/
|
||||
template <size_t N> struct MulOverflowMask {
|
||||
static const size_t result =
|
||||
~((1u << (BitSize<size_t>::result - CeilingLog2<N>::result)) - 1);
|
||||
NBitMask<BitSize<size_t>::result - CeilingLog2<N>::result>::result;
|
||||
};
|
||||
template <> struct MulOverflowMask<0> { /* Error */ };
|
||||
template <> struct MulOverflowMask<1> { static const size_t result = 0; };
|
||||
|
||||
/*
|
||||
* Safely subtract two pointers when it is known that end > begin. This avoids
|
||||
* the common compiler bug that if (size_t(end) - size_t(begin)) has the MSB
|
||||
* set, the unsigned subtraction followed by right shift will produce -1, or
|
||||
* size_t(-1), instead of the real difference.
|
||||
*/
|
||||
template <class T>
|
||||
size_t JS_ALWAYS_INLINE
|
||||
PointerRangeSize(T *begin, T *end) {
|
||||
return (size_t(end) - size_t(begin)) / sizeof(T);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a mask for T such that if (X & sUnsafeRangeSizeMask), an X-sized
|
||||
* array of T's is big enough to cause a ptrdiff_t overflow when subtracting
|
||||
* a pointer to the end of the array from the beginning.
|
||||
*/
|
||||
template <class T> struct UnsafeRangeSizeMask {
|
||||
/*
|
||||
* The '2' factor means the top bit is clear, sizeof(T) converts from
|
||||
* units of elements to bytes.
|
||||
*/
|
||||
static const size_t result = MulOverflowMask<2 * sizeof(T)>::result;
|
||||
};
|
||||
|
||||
/*
|
||||
* Traits class for identifying POD types. Until C++0x, there is no automatic
|
||||
* way to detect PODs, so for the moment it is done manually.
|
||||
@ -148,7 +189,8 @@ struct JSTempVectorImpl
|
||||
* newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will
|
||||
* not overflow.
|
||||
*/
|
||||
static inline bool growTo(JSTempVector<T> &v, size_t newcap) {
|
||||
static inline bool growTo(JSTempVector<T,N> &v, size_t newcap) {
|
||||
JS_ASSERT(!v.usingInlineStorage());
|
||||
T *newbuf = reinterpret_cast<T *>(v.mCx->malloc(newcap * sizeof(T)));
|
||||
if (!newbuf)
|
||||
return false;
|
||||
@ -156,7 +198,7 @@ struct JSTempVectorImpl
|
||||
new(dst) T(*src);
|
||||
JSTempVectorImpl::destroy(v.heapBegin(), v.heapEnd());
|
||||
v.mCx->free(v.heapBegin());
|
||||
v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin());
|
||||
v.heapEnd() = newbuf + v.heapLength();
|
||||
v.heapBegin() = newbuf;
|
||||
v.heapCapacity() = newcap;
|
||||
return true;
|
||||
@ -210,7 +252,7 @@ struct JSTempVectorImpl<T, N, true>
|
||||
T *newbuf = reinterpret_cast<T *>(v.mCx->realloc(v.heapBegin(), bytes));
|
||||
if (!newbuf)
|
||||
return false;
|
||||
v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin());
|
||||
v.heapEnd() = newbuf + v.heapLength();
|
||||
v.heapBegin() = newbuf;
|
||||
v.heapCapacity() = newcap;
|
||||
return true;
|
||||
@ -240,8 +282,9 @@ class JSTempVector
|
||||
typedef JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::result> Impl;
|
||||
friend struct JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::result>;
|
||||
|
||||
bool growHeapCapacityTo(size_t minCapacity);
|
||||
bool convertToHeapStorage(size_t minCapacity);
|
||||
bool calculateNewCapacity(size_t curLength, size_t lengthInc, size_t &newCap);
|
||||
bool growHeapStorageBy(size_t lengthInc);
|
||||
bool convertToHeapStorage(size_t lengthInc);
|
||||
|
||||
/* magic constants */
|
||||
|
||||
@ -260,10 +303,10 @@ class JSTempVector
|
||||
|
||||
/*
|
||||
* Since a vector either stores elements inline or in a heap-allocated
|
||||
* buffer, reuse the storage. mSizeOrCapacity serves as the union
|
||||
* buffer, reuse the storage. mLengthOrCapacity serves as the union
|
||||
* discriminator. In inline mode (when elements are stored in u.mBuf),
|
||||
* mSizeOrCapacity holds the vector's size. In heap mode (when elements
|
||||
* are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mSizeOrCapacity holds the
|
||||
* mLengthOrCapacity holds the vector's length. In heap mode (when elements
|
||||
* are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mLengthOrCapacity holds the
|
||||
* vector's capacity.
|
||||
*/
|
||||
static const size_t sInlineCapacity =
|
||||
@ -274,8 +317,8 @@ class JSTempVector
|
||||
|
||||
JSContext *mCx;
|
||||
|
||||
size_t mSizeOrCapacity;
|
||||
bool usingInlineStorage() const { return mSizeOrCapacity <= sInlineCapacity; }
|
||||
size_t mLengthOrCapacity;
|
||||
bool usingInlineStorage() const { return mLengthOrCapacity <= sInlineCapacity; }
|
||||
|
||||
union {
|
||||
BufferPtrs ptrs;
|
||||
@ -283,14 +326,14 @@ class JSTempVector
|
||||
} u;
|
||||
|
||||
/* Only valid when usingInlineStorage() */
|
||||
size_t &inlineSize() {
|
||||
size_t &inlineLength() {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
return mLengthOrCapacity;
|
||||
}
|
||||
|
||||
size_t inlineSize() const {
|
||||
size_t inlineLength() const {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
return mLengthOrCapacity;
|
||||
}
|
||||
|
||||
T *inlineBegin() const {
|
||||
@ -300,18 +343,21 @@ class JSTempVector
|
||||
|
||||
T *inlineEnd() const {
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return ((T *)u.mBuf) + mSizeOrCapacity;
|
||||
return ((T *)u.mBuf) + mLengthOrCapacity;
|
||||
}
|
||||
|
||||
/* Only valid when !usingInlineStorage() */
|
||||
size_t heapSize() {
|
||||
size_t heapLength() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
/* Guaranteed by calculateNewCapacity. */
|
||||
JS_ASSERT(size_t(u.ptrs.mEnd - u.ptrs.mBegin) ==
|
||||
((size_t(u.ptrs.mEnd) - size_t(u.ptrs.mBegin)) / sizeof(T)));
|
||||
return u.ptrs.mEnd - u.ptrs.mBegin;
|
||||
}
|
||||
|
||||
size_t &heapCapacity() {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
return mLengthOrCapacity;
|
||||
}
|
||||
|
||||
T *&heapBegin() {
|
||||
@ -326,15 +372,15 @@ class JSTempVector
|
||||
|
||||
size_t heapCapacity() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return mSizeOrCapacity;
|
||||
return mLengthOrCapacity;
|
||||
}
|
||||
|
||||
T *const &heapBegin() const {
|
||||
T *heapBegin() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mBegin;
|
||||
}
|
||||
|
||||
T *const &heapEnd() const {
|
||||
T *heapEnd() const {
|
||||
JS_ASSERT(!usingInlineStorage());
|
||||
return u.ptrs.mEnd;
|
||||
}
|
||||
@ -367,7 +413,7 @@ class JSTempVector
|
||||
|
||||
public:
|
||||
JSTempVector(JSContext *cx)
|
||||
: mCx(cx), mSizeOrCapacity(0)
|
||||
: mCx(cx), mLengthOrCapacity(0)
|
||||
#ifdef DEBUG
|
||||
, mInProgress(false)
|
||||
#endif
|
||||
@ -376,12 +422,12 @@ class JSTempVector
|
||||
|
||||
/* accessors */
|
||||
|
||||
size_t size() const {
|
||||
return usingInlineStorage() ? inlineSize() : (heapEnd() - heapBegin());
|
||||
size_t length() const {
|
||||
return usingInlineStorage() ? inlineLength() : heapLength();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return usingInlineStorage() ? inlineSize() == 0 : (heapBegin() == heapEnd());
|
||||
return usingInlineStorage() ? inlineLength() == 0 : heapBegin() == heapEnd();
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
@ -409,12 +455,12 @@ class JSTempVector
|
||||
}
|
||||
|
||||
T &operator[](size_t i) {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
JS_ASSERT(!mInProgress && i < length());
|
||||
return begin()[i];
|
||||
}
|
||||
|
||||
const T &operator[](size_t i) const {
|
||||
JS_ASSERT(!mInProgress && i < size());
|
||||
JS_ASSERT(!mInProgress && i < length());
|
||||
return begin()[i];
|
||||
}
|
||||
|
||||
@ -443,8 +489,8 @@ class JSTempVector
|
||||
*/
|
||||
bool growBy(size_t incr);
|
||||
|
||||
/* Call shrinkBy or growBy based on whether newSize > size(). */
|
||||
bool resize(size_t newSize);
|
||||
/* Call shrinkBy or growBy based on whether newSize > length(). */
|
||||
bool resize(size_t newLength);
|
||||
|
||||
void clear();
|
||||
|
||||
@ -461,7 +507,7 @@ class JSTempVector
|
||||
* buffer may need to be allocated (if the elements are currently
|
||||
* stored in-place), the call can fail, returning NULL.
|
||||
*
|
||||
* N.B. Although a T*, only the range [0, size()) is constructed.
|
||||
* N.B. Although a T*, only the range [0, length()) is constructed.
|
||||
*/
|
||||
T *extractRawBuffer();
|
||||
|
||||
@ -480,11 +526,11 @@ class JSTempVector
|
||||
* literal to a vector. This could not be done generically since one must take
|
||||
* care not to append the terminating '\0'.
|
||||
*/
|
||||
template <class T, size_t N, size_t ArraySize>
|
||||
template <class T, size_t N, size_t ArrayLength>
|
||||
bool
|
||||
js_AppendLiteral(JSTempVector<T,N> &v, const char (&array)[ArraySize])
|
||||
js_AppendLiteral(JSTempVector<T,N> &v, const char (&array)[ArrayLength])
|
||||
{
|
||||
return v.append(array, array + ArraySize - 1);
|
||||
return v.append(array, array + ArrayLength - 1);
|
||||
}
|
||||
|
||||
|
||||
@ -503,59 +549,84 @@ JSTempVector<T,N>::~JSTempVector()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate a new capacity that is at least lengthInc greater than
|
||||
* curLength and check for overflow.
|
||||
*/
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::growHeapCapacityTo(size_t mincap)
|
||||
JSTempVector<T,N>::calculateNewCapacity(size_t curLength, size_t lengthInc,
|
||||
size_t &newCap)
|
||||
{
|
||||
JS_ASSERT(mincap > heapCapacity());
|
||||
size_t newMinCap = curLength + lengthInc;
|
||||
|
||||
/* Check for overflow in both CEILING_LOG2 and growTo. */
|
||||
if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) {
|
||||
/*
|
||||
* Check for overflow in the above addition, below CEILING_LOG2, and later
|
||||
* multiplication by sizeof(T).
|
||||
*/
|
||||
if (newMinCap < curLength ||
|
||||
newMinCap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) {
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Round up to next power of 2. */
|
||||
size_t newcap;
|
||||
JS_CEILING_LOG2(newcap, mincap);
|
||||
JS_ASSERT(newcap < JSUtils::BitSize<size_t>::result);
|
||||
newcap = size_t(1) << newcap;
|
||||
size_t newCapLog2;
|
||||
JS_CEILING_LOG2(newCapLog2, newMinCap);
|
||||
JS_ASSERT(newCapLog2 < JSUtils::BitSize<size_t>::result);
|
||||
newCap = size_t(1) << newCapLog2;
|
||||
|
||||
return Impl::growTo(*this, newcap);
|
||||
/*
|
||||
* Do not allow a buffer large enough that the expression ((char *)end() -
|
||||
* (char *)begin()) overflows ptrdiff_t. See Bug 510319.
|
||||
*/
|
||||
if (newCap & JSUtils::UnsafeRangeSizeMask<T>::result) {
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will grow the current heap capacity to have capacity
|
||||
* (heapLength() + lengthInc) and fail on OOM or integer overflow.
|
||||
*/
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::convertToHeapStorage(size_t mincap)
|
||||
JSTempVector<T,N>::growHeapStorageBy(size_t lengthInc)
|
||||
{
|
||||
JS_ASSERT(mincap > sInlineCapacity);
|
||||
size_t newCap;
|
||||
return calculateNewCapacity(heapLength(), lengthInc, newCap) &&
|
||||
Impl::growTo(*this, newCap);
|
||||
}
|
||||
|
||||
/* Check for overflow in both CEILING_LOG2 and malloc. */
|
||||
if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) {
|
||||
js_ReportAllocationOverflow(mCx);
|
||||
/*
|
||||
* This function will create a new heap buffer with capacity (inlineLength() +
|
||||
* lengthInc()), move all elements in the inline buffer to this new buffer,
|
||||
* and fail on OOM or integer overflow.
|
||||
*/
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::convertToHeapStorage(size_t lengthInc)
|
||||
{
|
||||
size_t newCap;
|
||||
if (!calculateNewCapacity(inlineLength(), lengthInc, newCap))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Round up to next power of 2. */
|
||||
size_t newcap;
|
||||
JS_CEILING_LOG2(newcap, mincap);
|
||||
JS_ASSERT(newcap < 32);
|
||||
newcap = 1u << newcap;
|
||||
|
||||
/* Allocate buffer. */
|
||||
T *newbuf = reinterpret_cast<T *>(mCx->malloc(newcap * sizeof(T)));
|
||||
if (!newbuf)
|
||||
T *newBuf = reinterpret_cast<T *>(mCx->malloc(newCap * sizeof(T)));
|
||||
if (!newBuf)
|
||||
return false;
|
||||
|
||||
/* Copy inline elements into heap buffer. */
|
||||
size_t size = inlineEnd() - inlineBegin();
|
||||
Impl::copyConstruct(newbuf, inlineBegin(), inlineEnd());
|
||||
size_t length = inlineLength();
|
||||
Impl::copyConstruct(newBuf, inlineBegin(), inlineEnd());
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
|
||||
/* Switch in heap buffer. */
|
||||
mSizeOrCapacity = newcap; /* marks us as !usingInlineStorage() */
|
||||
heapBegin() = newbuf;
|
||||
heapEnd() = newbuf + size;
|
||||
mLengthOrCapacity = newCap; /* marks us as !usingInlineStorage() */
|
||||
heapBegin() = newBuf;
|
||||
heapEnd() = newBuf + length;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -566,10 +637,10 @@ JSTempVector<T,N>::reserve(size_t request)
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
if (request > sInlineCapacity)
|
||||
return convertToHeapStorage(request);
|
||||
return convertToHeapStorage(request - inlineLength());
|
||||
} else {
|
||||
if (request > heapCapacity())
|
||||
return growHeapCapacityTo(request);
|
||||
return growHeapStorageBy(request - heapLength());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -579,10 +650,10 @@ inline void
|
||||
JSTempVector<T,N>::shrinkBy(size_t incr)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
JS_ASSERT(incr <= size());
|
||||
JS_ASSERT(incr <= length());
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineEnd() - incr, inlineEnd());
|
||||
inlineSize() -= incr;
|
||||
inlineLength() -= incr;
|
||||
} else {
|
||||
Impl::destroy(heapEnd() - incr, heapEnd());
|
||||
heapEnd() -= incr;
|
||||
@ -595,29 +666,29 @@ JSTempVector<T,N>::growBy(size_t incr)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
size_t freespace = sInlineCapacity - inlineLength();
|
||||
if (incr <= freespace) {
|
||||
T *newend = inlineEnd() + incr;
|
||||
if (!JSUtils::IsPodType<T>::result)
|
||||
Impl::initialize(inlineEnd(), newend);
|
||||
inlineSize() += incr;
|
||||
inlineLength() += incr;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + incr))
|
||||
if (!convertToHeapStorage(incr))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* grow if needed */
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
size_t freespace = heapCapacity() - heapLength();
|
||||
if (incr > freespace) {
|
||||
if (!growHeapCapacityTo(heapSize() + incr))
|
||||
if (!growHeapStorageBy(incr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapCapacity() - heapSize() >= incr);
|
||||
JS_ASSERT(heapCapacity() - heapLength() >= incr);
|
||||
T *newend = heapEnd() + incr;
|
||||
if (!JSUtils::IsPodType<T>::result)
|
||||
Impl::initialize(heapEnd(), newend);
|
||||
@ -627,12 +698,12 @@ JSTempVector<T,N>::growBy(size_t incr)
|
||||
|
||||
template <class T, size_t N>
|
||||
inline bool
|
||||
JSTempVector<T,N>::resize(size_t newsize)
|
||||
JSTempVector<T,N>::resize(size_t newLength)
|
||||
{
|
||||
size_t cursize = size();
|
||||
if (newsize > cursize)
|
||||
return growBy(newsize - cursize);
|
||||
shrinkBy(cursize - newsize);
|
||||
size_t curLength = length();
|
||||
if (newLength > curLength)
|
||||
return growBy(newLength - curLength);
|
||||
shrinkBy(curLength - newLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -643,7 +714,7 @@ JSTempVector<T,N>::clear()
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
inlineLength() = 0;
|
||||
}
|
||||
else {
|
||||
Impl::destroy(heapBegin(), heapEnd());
|
||||
@ -657,21 +728,21 @@ JSTempVector<T,N>::append(const T &t)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
if (inlineSize() < sInlineCapacity) {
|
||||
if (inlineLength() < sInlineCapacity) {
|
||||
new(inlineEnd()) T(t);
|
||||
++inlineSize();
|
||||
++inlineLength();
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + 1))
|
||||
if (!convertToHeapStorage(1))
|
||||
return false;
|
||||
} else {
|
||||
if (heapSize() == heapCapacity() && !growHeapCapacityTo(heapSize() + 1))
|
||||
if (heapLength() == heapCapacity() && !growHeapStorageBy(1))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= 1);
|
||||
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= 1);
|
||||
new(heapEnd()++) T(t);
|
||||
return true;
|
||||
}
|
||||
@ -682,23 +753,23 @@ JSTempVector<T,N>::appendN(const T &t, size_t needed)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
size_t freespace = sInlineCapacity - inlineLength();
|
||||
if (needed <= freespace) {
|
||||
Impl::copyConstructN(inlineEnd(), needed, t);
|
||||
inlineSize() += needed;
|
||||
inlineLength() += needed;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + needed))
|
||||
if (!convertToHeapStorage(needed))
|
||||
return false;
|
||||
} else {
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
if (needed > freespace && !growHeapCapacityTo(heapSize() + needed))
|
||||
size_t freespace = heapCapacity() - heapLength();
|
||||
if (needed > freespace && !growHeapStorageBy(needed))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed);
|
||||
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed);
|
||||
Impl::copyConstructN(heapEnd(), needed, t);
|
||||
heapEnd() += needed;
|
||||
return true;
|
||||
@ -710,25 +781,25 @@ inline bool
|
||||
JSTempVector<T,N>::append(const U *insBegin, const U *insEnd)
|
||||
{
|
||||
ReentrancyGuard g(*this);
|
||||
size_t needed = insEnd - insBegin;
|
||||
size_t needed = JSUtils::PointerRangeSize(insBegin, insEnd);
|
||||
if (usingInlineStorage()) {
|
||||
size_t freespace = sInlineCapacity - inlineSize();
|
||||
size_t freespace = sInlineCapacity - inlineLength();
|
||||
if (needed <= freespace) {
|
||||
Impl::copyConstruct(inlineEnd(), insBegin, insEnd);
|
||||
inlineSize() += needed;
|
||||
inlineLength() += needed;
|
||||
JS_ASSERT(usingInlineStorage());
|
||||
return true;
|
||||
}
|
||||
if (!convertToHeapStorage(inlineSize() + needed))
|
||||
if (!convertToHeapStorage(needed))
|
||||
return false;
|
||||
} else {
|
||||
size_t freespace = heapCapacity() - heapSize();
|
||||
if (needed > freespace && !growHeapCapacityTo(heapSize() + needed))
|
||||
size_t freespace = heapCapacity() - heapLength();
|
||||
if (needed > freespace && !growHeapStorageBy(needed))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We are !usingInlineStorage(). Initialize new elements. */
|
||||
JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed);
|
||||
JS_ASSERT(heapLength() <= heapCapacity() && heapCapacity() - heapLength() >= needed);
|
||||
Impl::copyConstruct(heapEnd(), insBegin, insEnd);
|
||||
heapEnd() += needed;
|
||||
return true;
|
||||
@ -749,7 +820,7 @@ JSTempVector<T,N>::popBack()
|
||||
ReentrancyGuard g(*this);
|
||||
JS_ASSERT(!empty());
|
||||
if (usingInlineStorage()) {
|
||||
--inlineSize();
|
||||
--inlineLength();
|
||||
inlineEnd()->~T();
|
||||
} else {
|
||||
--heapEnd();
|
||||
@ -762,17 +833,17 @@ inline T *
|
||||
JSTempVector<T,N>::extractRawBuffer()
|
||||
{
|
||||
if (usingInlineStorage()) {
|
||||
T *ret = reinterpret_cast<T *>(mCx->malloc(inlineSize() * sizeof(T)));
|
||||
T *ret = reinterpret_cast<T *>(mCx->malloc(inlineLength() * sizeof(T)));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
Impl::copyConstruct(ret, inlineBegin(), inlineEnd());
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
inlineLength() = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
T *ret = heapBegin();
|
||||
mSizeOrCapacity = 0; /* marks us as !usingInlineStorage() */
|
||||
mLengthOrCapacity = 0; /* marks us as !usingInlineStorage() */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -785,7 +856,7 @@ JSTempVector<T,N>::replaceRawBuffer(T *p, size_t length)
|
||||
/* Destroy what we have. */
|
||||
if (usingInlineStorage()) {
|
||||
Impl::destroy(inlineBegin(), inlineEnd());
|
||||
inlineSize() = 0;
|
||||
inlineLength() = 0;
|
||||
} else {
|
||||
Impl::destroy(heapBegin(), heapEnd());
|
||||
mCx->free(heapBegin());
|
||||
@ -794,15 +865,15 @@ JSTempVector<T,N>::replaceRawBuffer(T *p, size_t length)
|
||||
/* Take in the new buffer. */
|
||||
if (length <= sInlineCapacity) {
|
||||
/*
|
||||
* (mSizeOrCapacity <= sInlineCapacity) means inline storage, so we MUST
|
||||
* use inline storage, even though p might otherwise be acceptable.
|
||||
* (mLengthOrCapacity <= sInlineCapacity) means inline storage, so we
|
||||
* MUST use inline storage, even though p might otherwise be acceptable.
|
||||
*/
|
||||
mSizeOrCapacity = length; /* marks us as usingInlineStorage() */
|
||||
mLengthOrCapacity = length; /* marks us as usingInlineStorage() */
|
||||
Impl::copyConstruct(inlineBegin(), p, p + length);
|
||||
Impl::destroy(p, p + length);
|
||||
mCx->free(p);
|
||||
} else {
|
||||
mSizeOrCapacity = length; /* marks us as !usingInlineStorage() */
|
||||
mLengthOrCapacity = length; /* marks us as !usingInlineStorage() */
|
||||
heapBegin() = p;
|
||||
heapEnd() = heapBegin() + length;
|
||||
}
|
||||
|
@ -99,7 +99,6 @@
|
||||
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
|
||||
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
|
||||
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
|
||||
#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */
|
||||
#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */
|
||||
@ -132,7 +131,6 @@
|
||||
#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 0 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */
|
||||
@ -161,7 +159,6 @@
|
||||
#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 */
|
||||
@ -190,7 +187,6 @@
|
||||
#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 */
|
||||
@ -219,7 +215,6 @@
|
||||
#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 */
|
||||
|
@ -204,7 +204,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
||||
* 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 - 49)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 51)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
866
js/src/jsxml.cpp
866
js/src/jsxml.cpp
File diff suppressed because it is too large
Load Diff
@ -67,14 +67,6 @@ struct JSXMLArray {
|
||||
#define JSXML_CAPACITY_MASK JS_BITMASK(31)
|
||||
#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK)
|
||||
|
||||
struct JSXMLArrayCursor {
|
||||
JSXMLArray *array;
|
||||
uint32 index;
|
||||
JSXMLArrayCursor *next;
|
||||
JSXMLArrayCursor **prevp;
|
||||
void *root;
|
||||
};
|
||||
|
||||
/*
|
||||
* NB: don't reorder this enum without changing all array initializers that
|
||||
* depend on it in jsxml.c.
|
||||
|
@ -48,56 +48,6 @@
|
||||
namespace nanojit
|
||||
{
|
||||
#ifdef NJ_VERBOSE
|
||||
class VerboseBlockReader: public LirFilter
|
||||
{
|
||||
Assembler *assm;
|
||||
LirNameMap *names;
|
||||
InsList block;
|
||||
bool flushnext;
|
||||
public:
|
||||
VerboseBlockReader(Allocator& alloc, LirFilter *in, Assembler *a, LirNameMap *n)
|
||||
: LirFilter(in), assm(a), names(n), block(alloc), flushnext(false)
|
||||
{}
|
||||
|
||||
void flush() {
|
||||
flushnext = false;
|
||||
if (!block.isEmpty()) {
|
||||
for (Seq<LIns*>* p = block.get(); p != NULL; p = p->tail)
|
||||
assm->outputf(" %s", names->formatIns(p->head));
|
||||
block.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void flush_add(LInsp i) {
|
||||
flush();
|
||||
block.add(i);
|
||||
}
|
||||
|
||||
LInsp read() {
|
||||
LInsp i = in->read();
|
||||
if (i->isop(LIR_start)) {
|
||||
flush();
|
||||
return i;
|
||||
}
|
||||
if (i->isGuard()) {
|
||||
flush_add(i);
|
||||
if (i->oprnd1())
|
||||
block.add(i->oprnd1());
|
||||
}
|
||||
else if (i->isRet() || i->isBranch()) {
|
||||
flush_add(i);
|
||||
}
|
||||
else {
|
||||
if (flushnext)
|
||||
flush();
|
||||
block.add(i);//flush_add(i);
|
||||
if (i->isop(LIR_label))
|
||||
flushnext = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
/* A listing filter for LIR, going through backwards. It merely
|
||||
passes its input to its output, but notes it down too. When
|
||||
destructed, prints out what went through. Is intended to be
|
||||
@ -187,34 +137,22 @@ namespace nanojit
|
||||
Register Assembler::registerAlloc(RegisterMask allow)
|
||||
{
|
||||
RegAlloc ®s = _allocator;
|
||||
// RegisterMask prefer = livePastCall(_ins) ? saved : scratch;
|
||||
RegisterMask prefer = SavedRegs & allow;
|
||||
RegisterMask free = regs.free & allow;
|
||||
RegisterMask allowedAndFree = allow & regs.free;
|
||||
|
||||
RegisterMask set = prefer;
|
||||
if (set == 0) set = allow;
|
||||
|
||||
if (free)
|
||||
if (allowedAndFree)
|
||||
{
|
||||
// at least one is free
|
||||
set &= free;
|
||||
|
||||
// ok we have at least 1 free register so let's try to pick
|
||||
// the best one given the profile of the instruction
|
||||
if (!set)
|
||||
{
|
||||
// desired register class is not free so pick first of any class
|
||||
set = free;
|
||||
}
|
||||
NanoAssert((set & allow) != 0);
|
||||
// At least one usable register is free -- no need to steal.
|
||||
// Pick a preferred one if possible.
|
||||
RegisterMask preferredAndFree = allowedAndFree & SavedRegs;
|
||||
RegisterMask set = ( preferredAndFree ? preferredAndFree : allowedAndFree );
|
||||
Register r = nRegisterAllocFromSet(set);
|
||||
regs.used |= rmask(r);
|
||||
return r;
|
||||
}
|
||||
counter_increment(steals);
|
||||
|
||||
// nothing free, steal one
|
||||
// LSRA says pick the one with the furthest use
|
||||
counter_increment(steals);
|
||||
LIns* vic = findVictim(regs, allow);
|
||||
NanoAssert(vic != NULL);
|
||||
|
||||
@ -363,22 +301,16 @@ namespace nanojit
|
||||
}
|
||||
else
|
||||
{
|
||||
Register rb = UnknownReg;
|
||||
resvb = getresv(ib);
|
||||
if (resvb && (rb = resvb->reg) != UnknownReg) {
|
||||
if (allow & rmask(rb)) {
|
||||
// ib already assigned to an allowable reg, keep that one
|
||||
allow &= ~rmask(rb);
|
||||
} else {
|
||||
// ib assigned to unusable reg, pick a different one below
|
||||
rb = UnknownReg;
|
||||
}
|
||||
bool rbDone = (resvb && resvb->reg != UnknownReg && (allow & rmask(resvb->reg)));
|
||||
if (rbDone) {
|
||||
// ib already assigned to an allowable reg, keep that one
|
||||
allow &= ~rmask(resvb->reg);
|
||||
}
|
||||
Register ra = findRegFor(ia, allow);
|
||||
resva = getresv(ia);
|
||||
NanoAssert(error() || (resva != 0 && ra != UnknownReg));
|
||||
if (rb == UnknownReg)
|
||||
{
|
||||
if (!rbDone) {
|
||||
allow &= ~rmask(ra);
|
||||
findRegFor(ib, allow);
|
||||
resvb = getresv(ib);
|
||||
@ -728,14 +660,6 @@ namespace nanojit
|
||||
prev = pp_after_sf2;
|
||||
})
|
||||
|
||||
// end of pipeline
|
||||
verbose_only(
|
||||
VerboseBlockReader vbr(alloc, prev, this, frag->lirbuf->names);
|
||||
if (_logc->lcbits & LC_Assembly)
|
||||
prev = &vbr;
|
||||
)
|
||||
|
||||
verbose_only(_thisfrag->compileNbr++; )
|
||||
_inExit = false;
|
||||
|
||||
LabelStateMap labels(alloc);
|
||||
@ -1364,6 +1288,31 @@ namespace nanojit
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
// We have to do final LIR printing inside this loop. If we do it
|
||||
// before this loop, we we end up printing a lot of dead LIR
|
||||
// instructions.
|
||||
//
|
||||
// We print the LIns after generating the code. This ensures that
|
||||
// the LIns will appear in debug output *before* the generated
|
||||
// code, because Assembler::outputf() prints everything in reverse.
|
||||
//
|
||||
// Note that some live LIR instructions won't be printed. Eg. an
|
||||
// immediate won't be printed unless it is explicitly loaded into
|
||||
// a register (as opposed to being incorporated into an immediate
|
||||
// field in another machine instruction).
|
||||
//
|
||||
if (_logc->lcbits & LC_Assembly) {
|
||||
outputf(" %s", _thisfrag->lirbuf->names->formatIns(ins));
|
||||
// Special case: a guard condition won't get printed next time
|
||||
// around the loop, so do it now.
|
||||
if (ins->isGuard() && ins->oprnd1()) {
|
||||
outputf(" %s # handled by the guard",
|
||||
_thisfrag->lirbuf->names->formatIns(ins->oprnd1()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (error())
|
||||
return;
|
||||
|
||||
|
@ -149,7 +149,7 @@ namespace nanojit
|
||||
* value. Temporary values can be placed into the AR as method calls
|
||||
* are issued. Also LIR_alloc instructions will consume space.
|
||||
*/
|
||||
class Assembler MMGC_SUBCLASS_DECL
|
||||
class Assembler
|
||||
{
|
||||
friend class VerboseBlockReader;
|
||||
public:
|
||||
|
@ -159,16 +159,104 @@ namespace nanojit
|
||||
Seq<T>* last;
|
||||
};
|
||||
|
||||
#ifdef NANOJIT_64BIT
|
||||
static inline size_t murmurhash(const void *key, size_t len) {
|
||||
const uint64_t m = 0xc6a4a7935bd1e995;
|
||||
const int r = 47;
|
||||
|
||||
uint64_t h = seed ^ (len * m);
|
||||
|
||||
const uint64_t *data = (const uint64_t*)key;
|
||||
const uint64_t *end = data + (len/8);
|
||||
|
||||
while(data != end)
|
||||
{
|
||||
uint64_t k = *data++;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
const unsigned char *data2 = (const unsigned char*)data;
|
||||
|
||||
switch(len & 7) {
|
||||
case 7: h ^= uint64_t(data2[6]) << 48;
|
||||
case 6: h ^= uint64_t(data2[5]) << 40;
|
||||
case 5: h ^= uint64_t(data2[4]) << 32;
|
||||
case 4: h ^= uint64_t(data2[3]) << 24;
|
||||
case 3: h ^= uint64_t(data2[2]) << 16;
|
||||
case 2: h ^= uint64_t(data2[1]) << 8;
|
||||
case 1: h ^= uint64_t(data2[0]);
|
||||
h *= m;
|
||||
};
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
return (size_t)h;
|
||||
}
|
||||
#else
|
||||
static inline size_t murmurhash(const void * key, size_t len) {
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
uint32_t h = 0;
|
||||
|
||||
const unsigned char * data = (const unsigned char *)key;
|
||||
while(len >= 4) {
|
||||
uint32_t k = *(size_t *)data;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return (size_t)h;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class K> struct DefaultHash {
|
||||
static size_t hash(const K &k) {
|
||||
return murmurhash(&k, sizeof(K));
|
||||
}
|
||||
};
|
||||
|
||||
template<class K> struct DefaultHash<K*> {
|
||||
static size_t hash(K* k) {
|
||||
uintptr_t h = (uintptr_t) k;
|
||||
// move the low 3 bits higher up since they're often 0
|
||||
h = (h>>3) ^ (h<<((sizeof(uintptr_t) * 8) - 3));
|
||||
return (size_t) h;
|
||||
}
|
||||
};
|
||||
|
||||
/** Bucket hashtable with a fixed # of buckets (never rehash)
|
||||
* Intended for use when a reasonable # of buckets can be estimated ahead of time.
|
||||
*
|
||||
* K is hashed by casting to intptr_t then to int. All users of this
|
||||
* class currently hash pointers and pointer-identity is okay. When this
|
||||
* changes we can parameterize the hash function and equality function,
|
||||
* or the caller can create a K type with custom cast(intptr_t) and == operators. */
|
||||
template<class K, class T> class HashMap {
|
||||
*/
|
||||
template<class K, class T, class H=DefaultHash<K> > class HashMap {
|
||||
Allocator& allocator;
|
||||
uint32_t nbuckets;
|
||||
size_t nbuckets;
|
||||
class Node {
|
||||
public:
|
||||
K key;
|
||||
@ -177,16 +265,9 @@ namespace nanojit
|
||||
};
|
||||
Seq<Node>** buckets;
|
||||
|
||||
/** optimized for the assumption that k is a 4-byte aligned pointer. */
|
||||
uint32_t hash(K k) {
|
||||
uintptr_t h = (uintptr_t) k;
|
||||
h = (h>>3) ^ (h<<29); // move the low 3 bits higher up since they're often 0
|
||||
return uint32_t(h) % nbuckets;
|
||||
}
|
||||
|
||||
/** return the node containing K, and the bucket index, or NULL if not found */
|
||||
Node* find(K k, uint32_t &i) {
|
||||
i = hash(k);
|
||||
Node* find(K k, size_t &i) {
|
||||
i = H::hash(k) % nbuckets;
|
||||
for (Seq<Node>* p = buckets[i]; p != NULL; p = p->tail) {
|
||||
if (p->head.key == k)
|
||||
return &p->head;
|
||||
@ -194,7 +275,7 @@ namespace nanojit
|
||||
return NULL;
|
||||
}
|
||||
public:
|
||||
HashMap(Allocator& a, uint32_t nbuckets = 16)
|
||||
HashMap(Allocator& a, size_t nbuckets = 16)
|
||||
: allocator(a)
|
||||
, nbuckets(nbuckets)
|
||||
, buckets(new (a) Seq<Node>*[nbuckets])
|
||||
@ -209,8 +290,8 @@ namespace nanojit
|
||||
}
|
||||
|
||||
/** add (k,v) to the map. If k is already in the map, replace the value */
|
||||
void put(K k, T v) {
|
||||
uint32_t i;
|
||||
void put(const K& k, const T& v) {
|
||||
size_t i;
|
||||
Node* n = find(k, i);
|
||||
if (n) {
|
||||
n->value = v;
|
||||
@ -220,22 +301,22 @@ namespace nanojit
|
||||
}
|
||||
|
||||
/** return v for element k, or T(0) if k is not present */
|
||||
T get(K k) {
|
||||
uint32_t i;
|
||||
T get(const K& k) {
|
||||
size_t i;
|
||||
Node* n = find(k, i);
|
||||
return n ? n->value : 0;
|
||||
}
|
||||
|
||||
/** returns true if k is in the map. */
|
||||
bool containsKey(K k) {
|
||||
uint32_t i;
|
||||
bool containsKey(const K& k) {
|
||||
size_t i;
|
||||
return find(k, i) != 0;
|
||||
}
|
||||
|
||||
/** remove k from the map, if it is present. if not, remove()
|
||||
* silently returns */
|
||||
void remove(K k) {
|
||||
uint32_t i = hash(k);
|
||||
void remove(const K& k) {
|
||||
size_t i = H::hash(k) % nbuckets;
|
||||
Seq<Node>** prev = &buckets[i];
|
||||
for (Seq<Node>* p = buckets[i]; p != NULL; p = p->tail) {
|
||||
if (p->head.key == k) {
|
||||
@ -260,12 +341,12 @@ namespace nanojit
|
||||
*/
|
||||
class Iter {
|
||||
friend class HashMap;
|
||||
const HashMap<K,T> ↦
|
||||
const HashMap<K,T,H> ↦
|
||||
int bucket;
|
||||
const Seq<Node>* current;
|
||||
|
||||
public:
|
||||
Iter(HashMap<K,T>& map) : map(map), bucket(map.nbuckets-1), current(NULL)
|
||||
Iter(HashMap<K,T,H>& map) : map(map), bucket(map.nbuckets-1), current(NULL)
|
||||
{ }
|
||||
|
||||
/** return true if more (k,v) remain to be visited */
|
||||
@ -278,13 +359,13 @@ namespace nanojit
|
||||
}
|
||||
|
||||
/** return the current key */
|
||||
K key() const {
|
||||
const K& key() const {
|
||||
NanoAssert(current != NULL);
|
||||
return current->head.key;
|
||||
}
|
||||
|
||||
/** return the current value */
|
||||
T value() const {
|
||||
/** return the current value */
|
||||
const T& value() const {
|
||||
NanoAssert(current != NULL);
|
||||
return current->head.value;
|
||||
}
|
||||
|
@ -48,171 +48,11 @@ namespace nanojit
|
||||
|
||||
using namespace avmplus;
|
||||
|
||||
static uint32_t calcSaneCacheSize(uint32_t in)
|
||||
{
|
||||
if (in < uint32_t(NJ_LOG2_PAGE_SIZE)) return NJ_LOG2_PAGE_SIZE; // at least 1 page
|
||||
if (in > 32) return 32; // 4GB should be enough for anyone
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main control center for creating and managing fragments.
|
||||
*/
|
||||
Fragmento::Fragmento(AvmCore* core, LogControl* logc, uint32_t cacheSizeLog2, CodeAlloc* codeAlloc)
|
||||
:
|
||||
#ifdef NJ_VERBOSE
|
||||
enterCounts(NULL),
|
||||
mergeCounts(NULL),
|
||||
labels(NULL),
|
||||
#endif
|
||||
_core(core),
|
||||
_codeAlloc(codeAlloc),
|
||||
_frags(core->GetGC()),
|
||||
_max_pages(1 << (calcSaneCacheSize(cacheSizeLog2) - NJ_LOG2_PAGE_SIZE)),
|
||||
_pagesGrowth(1)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
// XXX These belong somewhere else, but I can't find the
|
||||
// right location right now.
|
||||
NanoStaticAssert((LIR_lt ^ 3) == LIR_ge);
|
||||
NanoStaticAssert((LIR_le ^ 3) == LIR_gt);
|
||||
NanoStaticAssert((LIR_ult ^ 3) == LIR_uge);
|
||||
NanoStaticAssert((LIR_ule ^ 3) == LIR_ugt);
|
||||
NanoStaticAssert((LIR_flt ^ 3) == LIR_fge);
|
||||
NanoStaticAssert((LIR_fle ^ 3) == LIR_fgt);
|
||||
|
||||
/* Opcodes must be strictly increasing without holes. */
|
||||
uint32_t count = 0;
|
||||
#define OPDEF(op, number, operands, repkind) \
|
||||
NanoAssertMsg(LIR_##op == count++, "misnumbered opcode");
|
||||
#define OPDEF64(op, number, operands, repkind) \
|
||||
OPDEF(op, number, operands, repkind)
|
||||
#include "LIRopcode.tbl"
|
||||
#undef OPDEF
|
||||
#undef OPDEF64
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MEMORY_INFO
|
||||
_allocList.set_meminfo_name("Fragmento._allocList");
|
||||
#endif
|
||||
NanoAssert(_max_pages > _pagesGrowth); // shrink growth if needed
|
||||
verbose_only( enterCounts = NJ_NEW(core->gc, BlockHist)(core->gc); )
|
||||
verbose_only( mergeCounts = NJ_NEW(core->gc, BlockHist)(core->gc); )
|
||||
|
||||
memset(&_stats, 0, sizeof(_stats));
|
||||
}
|
||||
|
||||
Fragmento::~Fragmento()
|
||||
{
|
||||
clearFrags();
|
||||
#if defined(NJ_VERBOSE)
|
||||
NJ_DELETE(enterCounts);
|
||||
NJ_DELETE(mergeCounts);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Clear the fragment. This *does not* remove the fragment from the
|
||||
// map--the caller must take care of this.
|
||||
void Fragmento::clearFragment(Fragment* f)
|
||||
{
|
||||
Fragment *peer = f->peer;
|
||||
while (peer) {
|
||||
Fragment *next = peer->peer;
|
||||
peer->releaseTreeMem(_codeAlloc);
|
||||
NJ_DELETE(peer);
|
||||
peer = next;
|
||||
}
|
||||
f->releaseTreeMem(_codeAlloc);
|
||||
NJ_DELETE(f);
|
||||
}
|
||||
|
||||
void Fragmento::clearFrags()
|
||||
{
|
||||
while (!_frags.isEmpty()) {
|
||||
clearFragment(_frags.removeLast());
|
||||
}
|
||||
|
||||
verbose_only( enterCounts->clear();)
|
||||
verbose_only( mergeCounts->clear();)
|
||||
verbose_only( _stats.flushes++ );
|
||||
verbose_only( _stats.compiles = 0 );
|
||||
//nj_dprintf("Fragmento.clearFrags %d free pages of %d\n", _stats.freePages, _stats.pages);
|
||||
}
|
||||
|
||||
AvmCore* Fragmento::core()
|
||||
{
|
||||
return _core;
|
||||
}
|
||||
|
||||
Fragment* Fragmento::getAnchor(const void* ip)
|
||||
{
|
||||
Fragment *f = newFrag(ip);
|
||||
Fragment *p = _frags.get(ip);
|
||||
if (p) {
|
||||
f->first = p;
|
||||
/* append at the end of the peer list */
|
||||
Fragment* next;
|
||||
while ((next = p->peer) != NULL)
|
||||
p = next;
|
||||
p->peer = f;
|
||||
} else {
|
||||
f->first = f;
|
||||
_frags.put(ip, f); /* this is the first fragment */
|
||||
}
|
||||
f->anchor = f;
|
||||
f->root = f;
|
||||
f->kind = LoopTrace;
|
||||
verbose_only( addLabel(f, "T", _frags.size()); )
|
||||
return f;
|
||||
}
|
||||
|
||||
Fragment* Fragmento::getLoop(const void* ip)
|
||||
{
|
||||
return _frags.get(ip);
|
||||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
void Fragmento::addLabel(Fragment *f, const char *prefix, int id)
|
||||
{
|
||||
char fragname[20];
|
||||
sprintf(fragname,"%s%d", prefix, id);
|
||||
labels->add(f, sizeof(Fragment), 0, fragname);
|
||||
}
|
||||
#endif
|
||||
|
||||
Fragment *Fragmento::createBranch(SideExit* exit, const void* ip)
|
||||
{
|
||||
Fragment *f = newBranch(exit->from, ip);
|
||||
f->kind = BranchTrace;
|
||||
f->treeBranches = f->root->treeBranches;
|
||||
f->root->treeBranches = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
//
|
||||
// Fragment
|
||||
//
|
||||
Fragment::Fragment(const void* _ip)
|
||||
:
|
||||
#ifdef NJ_VERBOSE
|
||||
_called(0),
|
||||
_native(0),
|
||||
_exitNative(0),
|
||||
_lir(0),
|
||||
_lirbytes(0),
|
||||
_token(NULL),
|
||||
traceTicks(0),
|
||||
interpTicks(0),
|
||||
eot_target(NULL),
|
||||
sid(0),
|
||||
compileNbr(0),
|
||||
#endif
|
||||
treeBranches(NULL),
|
||||
branches(NULL),
|
||||
nextbranch(NULL),
|
||||
anchor(NULL),
|
||||
root(NULL),
|
||||
parent(NULL),
|
||||
@ -220,88 +60,16 @@ namespace nanojit
|
||||
peer(NULL),
|
||||
lirbuf(NULL),
|
||||
lastIns(NULL),
|
||||
spawnedFrom(NULL),
|
||||
kind(LoopTrace),
|
||||
ip(_ip),
|
||||
guardCount(0),
|
||||
xjumpCount(0),
|
||||
recordAttempts(0),
|
||||
blacklistLevel(0),
|
||||
fragEntry(NULL),
|
||||
loopEntry(NULL),
|
||||
vmprivate(NULL),
|
||||
codeList(0),
|
||||
_code(NULL),
|
||||
_hits(0)
|
||||
{
|
||||
}
|
||||
|
||||
Fragment::~Fragment()
|
||||
{
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
void Fragment::blacklist()
|
||||
{
|
||||
blacklistLevel++;
|
||||
_hits = -(1<<blacklistLevel);
|
||||
}
|
||||
|
||||
Fragment *Fragmento::newFrag(const void* ip)
|
||||
{
|
||||
GC *gc = _core->gc;
|
||||
Fragment *f = NJ_NEW(gc, Fragment)(ip);
|
||||
f->blacklistLevel = 5;
|
||||
return f;
|
||||
}
|
||||
|
||||
Fragment *Fragmento::newBranch(Fragment *from, const void* ip)
|
||||
{
|
||||
Fragment *f = newFrag(ip);
|
||||
f->anchor = from->anchor;
|
||||
f->root = from->root;
|
||||
f->xjumpCount = from->xjumpCount;
|
||||
/*// prepend
|
||||
f->nextbranch = from->branches;
|
||||
from->branches = f;*/
|
||||
// append
|
||||
if (!from->branches) {
|
||||
from->branches = f;
|
||||
} else {
|
||||
Fragment *p = from->branches;
|
||||
while (p->nextbranch != 0)
|
||||
p = p->nextbranch;
|
||||
p->nextbranch = f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
void Fragment::releaseLirBuffer()
|
||||
{
|
||||
lastIns = 0;
|
||||
}
|
||||
|
||||
void Fragment::releaseCode(CodeAlloc *codeAlloc)
|
||||
{
|
||||
_code = 0;
|
||||
codeAlloc->freeAll(codeList);
|
||||
}
|
||||
|
||||
void Fragment::releaseTreeMem(CodeAlloc *codeAlloc)
|
||||
{
|
||||
releaseLirBuffer();
|
||||
releaseCode(codeAlloc);
|
||||
|
||||
// now do it for all branches
|
||||
Fragment* branch = branches;
|
||||
while(branch)
|
||||
{
|
||||
Fragment* next = branch->nextbranch;
|
||||
branch->releaseTreeMem(codeAlloc); // @todo safer here to recurse in case we support nested trees
|
||||
NJ_DELETE(branch);
|
||||
branch = next;
|
||||
}
|
||||
}
|
||||
#endif /* FEATURE_NANOJIT */
|
||||
}
|
||||
|
||||
|
@ -46,74 +46,6 @@
|
||||
namespace nanojit
|
||||
{
|
||||
struct GuardRecord;
|
||||
class Assembler;
|
||||
|
||||
typedef avmplus::GCSortedMap<const void*, uint32_t, avmplus::LIST_NonGCObjects> BlockSortedMap;
|
||||
class BlockHist: public BlockSortedMap
|
||||
{
|
||||
public:
|
||||
BlockHist(avmplus::GC*gc) : BlockSortedMap(gc)
|
||||
{
|
||||
}
|
||||
uint32_t count(const void *p) {
|
||||
uint32_t c = 1+get(p);
|
||||
put(p, c);
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
struct fragstats;
|
||||
/*
|
||||
*
|
||||
* This is the main control center for creating and managing fragments.
|
||||
*/
|
||||
class Fragmento : public avmplus::GCFinalizedObject
|
||||
{
|
||||
public:
|
||||
Fragmento(AvmCore* core, LogControl* logc, uint32_t cacheSizeLog2, CodeAlloc *codeAlloc);
|
||||
~Fragmento();
|
||||
|
||||
AvmCore* core();
|
||||
|
||||
Fragment* getLoop(const void* ip);
|
||||
Fragment* getAnchor(const void* ip);
|
||||
// Remove one fragment. The caller is responsible for making sure
|
||||
// that this does not destroy any resources shared with other
|
||||
// fragments (such as a LirBuffer or this fragment itself as a
|
||||
// jump target).
|
||||
void clearFrags(); // clear all fragments from the cache
|
||||
Fragment* createBranch(SideExit *exit, const void* ip);
|
||||
Fragment* newFrag(const void* ip);
|
||||
Fragment* newBranch(Fragment *from, const void* ip);
|
||||
|
||||
verbose_only ( uint32_t pageCount(); )
|
||||
verbose_only( void addLabel(Fragment* f, const char *prefix, int id); )
|
||||
|
||||
// stats
|
||||
struct
|
||||
{
|
||||
uint32_t pages; // pages consumed
|
||||
uint32_t flushes, ilsize, abcsize, compiles, totalCompiles;
|
||||
}
|
||||
_stats;
|
||||
|
||||
verbose_only( DWB(BlockHist*) enterCounts; )
|
||||
verbose_only( DWB(BlockHist*) mergeCounts; )
|
||||
verbose_only( LabelMap* labels; )
|
||||
|
||||
#ifdef AVMPLUS_VERBOSE
|
||||
void drawTrees(char *fileName);
|
||||
#endif
|
||||
|
||||
void clearFragment(Fragment *f);
|
||||
private:
|
||||
AvmCore* _core;
|
||||
CodeAlloc* _codeAlloc;
|
||||
FragmentMap _frags; /* map from ip -> Fragment ptr */
|
||||
|
||||
const uint32_t _max_pages;
|
||||
uint32_t _pagesGrowth;
|
||||
};
|
||||
|
||||
enum TraceKind {
|
||||
LoopTrace,
|
||||
@ -128,59 +60,31 @@ namespace nanojit
|
||||
* It may turn out that that this arrangement causes too much traffic
|
||||
* between d and i-caches and that we need to carve up the structure differently.
|
||||
*/
|
||||
class Fragment : public avmplus::GCFinalizedObject
|
||||
class Fragment
|
||||
{
|
||||
public:
|
||||
Fragment(const void*);
|
||||
~Fragment();
|
||||
|
||||
NIns* code() { return _code; }
|
||||
void setCode(NIns* codee) { _code = codee; }
|
||||
int32_t& hits() { return _hits; }
|
||||
void blacklist();
|
||||
bool isBlacklisted() { return _hits < 0; }
|
||||
void releaseLirBuffer();
|
||||
void releaseCode(CodeAlloc *alloc);
|
||||
void releaseTreeMem(CodeAlloc *alloc);
|
||||
NIns* code() { return _code; }
|
||||
void setCode(NIns* codee) { _code = codee; }
|
||||
int32_t& hits() { return _hits; }
|
||||
bool isAnchor() { return anchor == this; }
|
||||
bool isRoot() { return root == this; }
|
||||
void onDestroy();
|
||||
|
||||
verbose_only( uint32_t _called; )
|
||||
verbose_only( uint32_t _native; )
|
||||
verbose_only( uint32_t _exitNative; )
|
||||
verbose_only( uint32_t _lir; )
|
||||
verbose_only( uint32_t _lirbytes; )
|
||||
verbose_only( const char* _token; )
|
||||
verbose_only( uint64_t traceTicks; )
|
||||
verbose_only( uint64_t interpTicks; )
|
||||
verbose_only( DWB(Fragment*) eot_target; )
|
||||
verbose_only( uint32_t sid;)
|
||||
verbose_only( uint32_t compileNbr;)
|
||||
|
||||
DWB(Fragment*) treeBranches;
|
||||
DWB(Fragment*) branches;
|
||||
DWB(Fragment*) nextbranch;
|
||||
DWB(Fragment*) anchor;
|
||||
DWB(Fragment*) root;
|
||||
DWB(Fragment*) parent;
|
||||
DWB(Fragment*) first;
|
||||
DWB(Fragment*) peer;
|
||||
Fragment* anchor;
|
||||
Fragment* root;
|
||||
Fragment* parent;
|
||||
Fragment* first;
|
||||
Fragment* peer;
|
||||
LirBuffer* lirbuf;
|
||||
LIns* lastIns;
|
||||
SideExit* spawnedFrom;
|
||||
GuardRecord* outbound;
|
||||
|
||||
TraceKind kind;
|
||||
const void* ip;
|
||||
uint32_t guardCount;
|
||||
uint32_t xjumpCount;
|
||||
uint32_t recordAttempts;
|
||||
int32_t blacklistLevel;
|
||||
NIns* fragEntry;
|
||||
NIns* loopEntry;
|
||||
void* vmprivate;
|
||||
CodeList* codeList;
|
||||
|
||||
private:
|
||||
NIns* _code; // ptr to start of code
|
||||
|
@ -109,8 +109,7 @@ namespace nanojit
|
||||
#endif /* NJ_PROFILE */
|
||||
|
||||
// LCompressedBuffer
|
||||
LirBuffer::LirBuffer(Allocator& alloc)
|
||||
:
|
||||
LirBuffer::LirBuffer(Allocator& alloc) :
|
||||
#ifdef NJ_VERBOSE
|
||||
names(NULL),
|
||||
#endif
|
||||
@ -1110,19 +1109,11 @@ namespace nanojit
|
||||
return hash;
|
||||
}
|
||||
|
||||
LInsHashSet::LInsHashSet(GC* gc) :
|
||||
m_used(0), m_cap(kInitialCap), m_gc(gc)
|
||||
LInsHashSet::LInsHashSet(Allocator& alloc) :
|
||||
m_cap(kInitialCap), alloc(alloc)
|
||||
{
|
||||
#ifdef MEMORY_INFO
|
||||
// m_list.set_meminfo_name("LInsHashSet.list");
|
||||
#endif
|
||||
LInsp *list = (LInsp*) gc->Alloc(sizeof(LInsp)*m_cap, GC::kZero);
|
||||
WB(gc, this, &m_list, list);
|
||||
}
|
||||
|
||||
LInsHashSet::~LInsHashSet()
|
||||
{
|
||||
m_gc->Free(m_list);
|
||||
m_list = new (alloc) LInsp[m_cap];
|
||||
clear();
|
||||
}
|
||||
|
||||
void LInsHashSet::clear() {
|
||||
@ -1230,11 +1221,9 @@ namespace nanojit
|
||||
void FASTCALL LInsHashSet::grow()
|
||||
{
|
||||
const uint32_t newcap = m_cap << 1;
|
||||
LInsp *newlist = (LInsp*) m_gc->Alloc(newcap * sizeof(LInsp), GC::kZero);
|
||||
LInsp *newlist = new (alloc) LInsp[newcap];
|
||||
VMPI_memset(newlist, 0, newcap * sizeof(LInsp));
|
||||
LInsp *list = m_list;
|
||||
#ifdef MEMORY_INFO
|
||||
// newlist.set_meminfo_name("LInsHashSet.list");
|
||||
#endif
|
||||
for (uint32_t i=0, n=m_cap; i < n; i++) {
|
||||
LInsp name = list[i];
|
||||
if (!name) continue;
|
||||
@ -1242,8 +1231,7 @@ namespace nanojit
|
||||
newlist[j] = name;
|
||||
}
|
||||
m_cap = newcap;
|
||||
m_gc->Free(list);
|
||||
WB(m_gc, this, &m_list, newlist);
|
||||
m_list = newlist;
|
||||
}
|
||||
|
||||
uint32_t FASTCALL LInsHashSet::find(LInsp name, uint32_t hash, const LInsp *list, uint32_t cap)
|
||||
@ -1863,8 +1851,8 @@ namespace nanojit
|
||||
|
||||
|
||||
#endif
|
||||
CseFilter::CseFilter(LirWriter *out, GC *gc)
|
||||
: LirWriter(out), exprs(gc) {}
|
||||
CseFilter::CseFilter(LirWriter *out, Allocator& alloc)
|
||||
: LirWriter(out), exprs(alloc) {}
|
||||
|
||||
LIns* CseFilter::insImm(int32_t imm)
|
||||
{
|
||||
@ -2074,10 +2062,6 @@ namespace nanojit
|
||||
frag->fragEntry = 0;
|
||||
frag->loopEntry = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CodeAlloc::moveAll(frag->codeList, assm->codeList);
|
||||
}
|
||||
|
||||
/* BEGIN decorative postamble */
|
||||
verbose_only( if (anyVerb) {
|
||||
@ -2133,7 +2117,7 @@ namespace nanojit
|
||||
#endif /* FEATURE_NANOJIT */
|
||||
|
||||
#if defined(NJ_VERBOSE)
|
||||
LabelMap::LabelMap(nanojit::Allocator& a, LogControl *logc)
|
||||
LabelMap::LabelMap(Allocator& a, LogControl *logc)
|
||||
: allocator(a), names(a), logc(logc), end(buf)
|
||||
{}
|
||||
|
||||
|
@ -781,8 +781,6 @@ namespace nanojit
|
||||
LIns* FASTCALL callArgN(LInsp i, uint32_t n);
|
||||
extern const uint8_t operandCount[];
|
||||
|
||||
class Fragmento; // @todo remove this ; needed for minbuild for some reason?!? Should not be compiling this code at all
|
||||
|
||||
// make it a GCObject so we can explicitly delete it early
|
||||
class LirWriter : public GCObject
|
||||
{
|
||||
@ -1036,7 +1034,7 @@ namespace nanojit
|
||||
|
||||
LInsp *m_list;
|
||||
uint32_t m_used, m_cap;
|
||||
GC* m_gc;
|
||||
Allocator& alloc;
|
||||
|
||||
static uint32_t FASTCALL hashcode(LInsp i);
|
||||
uint32_t FASTCALL find(LInsp name, uint32_t hash, const LInsp *list, uint32_t cap);
|
||||
@ -1044,8 +1042,8 @@ namespace nanojit
|
||||
void FASTCALL grow();
|
||||
|
||||
public:
|
||||
LInsHashSet(GC* gc);
|
||||
~LInsHashSet();
|
||||
|
||||
LInsHashSet(Allocator&);
|
||||
LInsp find32(int32_t a, uint32_t &i);
|
||||
LInsp find64(uint64_t a, uint32_t &i);
|
||||
LInsp find1(LOpcode v, LInsp a, uint32_t &i);
|
||||
@ -1069,7 +1067,7 @@ namespace nanojit
|
||||
{
|
||||
public:
|
||||
LInsHashSet exprs;
|
||||
CseFilter(LirWriter *out, GC *gc);
|
||||
CseFilter(LirWriter *out, Allocator&);
|
||||
LIns* insImm(int32_t imm);
|
||||
LIns* insImmq(uint64_t q);
|
||||
LIns* ins0(LOpcode v);
|
||||
@ -1084,7 +1082,7 @@ namespace nanojit
|
||||
class LirBuffer
|
||||
{
|
||||
public:
|
||||
LirBuffer(Allocator&);
|
||||
LirBuffer(Allocator& alloc);
|
||||
void clear();
|
||||
uintptr_t makeRoom(size_t szB); // make room for an instruction
|
||||
|
||||
@ -1144,7 +1142,7 @@ namespace nanojit
|
||||
}
|
||||
|
||||
// LirWriter interface
|
||||
LInsp insLoad(LOpcode op, LInsp base, int32_t disp);
|
||||
LInsp insLoad(LOpcode op, LInsp base, int32_t disp);
|
||||
LInsp insStorei(LInsp o1, LInsp o2, int32_t disp);
|
||||
LInsp ins0(LOpcode op);
|
||||
LInsp ins1(LOpcode op, LInsp o1);
|
||||
@ -1181,7 +1179,9 @@ namespace nanojit
|
||||
LInsp _i; // current instruction that this decoder is operating on.
|
||||
|
||||
public:
|
||||
LirReader(LInsp i) : LirFilter(0), _i(i) { }
|
||||
LirReader(LInsp i) : LirFilter(0), _i(i) {
|
||||
NanoAssert(_i);
|
||||
}
|
||||
virtual ~LirReader() {}
|
||||
|
||||
// LirReader i/f
|
||||
@ -1220,8 +1220,9 @@ namespace nanojit
|
||||
|
||||
void clear(LInsp p);
|
||||
public:
|
||||
LoadFilter(LirWriter *out, GC *gc)
|
||||
: LirWriter(out), exprs(gc) { }
|
||||
LoadFilter(LirWriter *out, Allocator& alloc)
|
||||
: LirWriter(out), sp(NULL), rp(NULL), exprs(alloc)
|
||||
{ }
|
||||
|
||||
LInsp ins0(LOpcode);
|
||||
LInsp insLoad(LOpcode, LInsp base, int32_t disp);
|
||||
|
1302
js/src/nanojit/NativeX64.cpp
Normal file
1302
js/src/nanojit/NativeX64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
360
js/src/nanojit/NativeX64.h
Normal file
360
js/src/nanojit/NativeX64.h
Normal file
@ -0,0 +1,360 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
||||
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is [Open Source Virtual Machine].
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Adobe System Incorporated.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Adobe AS3 Team
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __nanojit_NativeX64__
|
||||
#define __nanojit_NativeX64__
|
||||
|
||||
#ifndef NANOJIT_64BIT
|
||||
#error "NANOJIT_64BIT must be defined for X64 backend"
|
||||
#endif
|
||||
|
||||
#ifdef PERFM
|
||||
#define DOPROF
|
||||
#include "../vprof/vprof.h"
|
||||
#define count_instr() _nvprof("x64",1)
|
||||
#define count_prolog() _nvprof("x64-prolog",1); count_instr();
|
||||
#define count_imt() _nvprof("x64-imt",1) count_instr()
|
||||
#else
|
||||
#define count_instr()
|
||||
#define count_prolog()
|
||||
#define count_imt()
|
||||
#endif
|
||||
|
||||
namespace nanojit
|
||||
{
|
||||
#define NJ_MAX_STACK_ENTRY 256
|
||||
#define NJ_ALIGN_STACK 16
|
||||
|
||||
enum Register {
|
||||
RAX = 0, // 1st int return, # of sse varargs
|
||||
RCX = 1, // 4th int arg
|
||||
RDX = 2, // 3rd int arg 2nd return
|
||||
RBX = 3, // saved
|
||||
RSP = 4, // stack ptr
|
||||
RBP = 5, // frame ptr, saved, sib reqd
|
||||
RSI = 6, // 2nd int arg
|
||||
RDI = 7, // 1st int arg
|
||||
R8 = 8, // 5th int arg
|
||||
R9 = 9, // 6th int arg
|
||||
R10 = 10, // scratch
|
||||
R11 = 11, // scratch
|
||||
R12 = 12, // saved
|
||||
R13 = 13, // saved, sib reqd like rbp
|
||||
R14 = 14, // saved
|
||||
R15 = 15, // saved
|
||||
|
||||
XMM0 = 16, // 1st double arg, return
|
||||
XMM1 = 17, // 2nd double arg, return
|
||||
XMM2 = 18, // 3rd double arg
|
||||
XMM3 = 19, // 4th double arg
|
||||
XMM4 = 20, // 5th double arg
|
||||
XMM5 = 21, // 6th double arg
|
||||
XMM6 = 22, // 7th double arg
|
||||
XMM7 = 23, // 8th double arg
|
||||
XMM8 = 24, // scratch
|
||||
XMM9 = 25, // scratch
|
||||
XMM10 = 26, // scratch
|
||||
XMM11 = 27, // scratch
|
||||
XMM12 = 28, // scratch
|
||||
XMM13 = 29, // scratch
|
||||
XMM14 = 30, // scratch
|
||||
XMM15 = 31, // scratch
|
||||
|
||||
FP = RBP,
|
||||
UnknownReg = 32,
|
||||
FirstReg = RAX,
|
||||
LastReg = XMM15
|
||||
};
|
||||
|
||||
/*
|
||||
* Micro-templating variable-length opcodes, idea first
|
||||
* describe by Mike Pall of Luajit.
|
||||
*
|
||||
* X86-64 opcode encodings: LSB encodes the length of the
|
||||
* opcode in bytes, remaining bytes are encoded as 1-7 bytes
|
||||
* in a single uint64_t value. The value is written as a single
|
||||
* store into the code stream, and the code pointer is decremented
|
||||
* by the length. each successive instruction partially overlaps
|
||||
* the previous one.
|
||||
*
|
||||
* emit methods below are able to encode mod/rm, sib, rex, and
|
||||
* register and small immediate values into these opcode values
|
||||
* without much branchy code.
|
||||
*
|
||||
* these opcodes encapsulate all the const parts of the instruction.
|
||||
* for example, the alu-immediate opcodes (add, sub, etc) encode
|
||||
* part of their opcode in the R field of the mod/rm byte; this
|
||||
* hardcoded value is in the constant below, and the R argument
|
||||
* to emitrr() is 0. In a few cases, a whole instruction is encoded
|
||||
* this way (eg callrax).
|
||||
*
|
||||
* when a disp32, imm32, or imm64 suffix can't fit in an 8-byte
|
||||
* opcode, then it is written into the code separately and not counted
|
||||
* in the opcode length.
|
||||
*/
|
||||
|
||||
enum X64Opcode
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum
|
||||
: uint64_t
|
||||
#endif
|
||||
{
|
||||
// 64bit opcode constants
|
||||
// msb lsb len
|
||||
X64_addqrr = 0xC003480000000003LL, // 64bit add r += b
|
||||
X64_addqri = 0xC081480000000003LL, // 64bit add r += int64(imm32)
|
||||
X64_addqr8 = 0x00C0834800000004LL, // 64bit add r += int64(imm8)
|
||||
X64_andqri = 0xE081480000000003LL, // 64bit and r &= int64(imm32)
|
||||
X64_andqr8 = 0x00E0834800000004LL, // 64bit and r &= int64(imm8)
|
||||
X64_orqri = 0xC881480000000003LL, // 64bit or r |= int64(imm32)
|
||||
X64_orqr8 = 0x00C8834800000004LL, // 64bit or r |= int64(imm8)
|
||||
X64_xorqri = 0xF081480000000003LL, // 64bit xor r ^= int64(imm32)
|
||||
X64_xorqr8 = 0x00F0834800000004LL, // 64bit xor r ^= int64(imm8)
|
||||
X64_addlri = 0xC081400000000003LL, // 32bit add r += imm32
|
||||
X64_addlr8 = 0x00C0834000000004LL, // 32bit add r += imm8
|
||||
X64_andlri = 0xE081400000000003LL, // 32bit and r &= imm32
|
||||
X64_andlr8 = 0x00E0834000000004LL, // 32bit and r &= imm8
|
||||
X64_orlri = 0xC881400000000003LL, // 32bit or r |= imm32
|
||||
X64_orlr8 = 0x00C8834000000004LL, // 32bit or r |= imm8
|
||||
X64_sublri = 0xE881400000000003LL, // 32bit sub r -= imm32
|
||||
X64_sublr8 = 0x00E8834000000004LL, // 32bit sub r -= imm8
|
||||
X64_xorlri = 0xF081400000000003LL, // 32bit xor r ^= imm32
|
||||
X64_xorlr8 = 0x00F0834000000004LL, // 32bit xor r ^= imm8
|
||||
X64_addrr = 0xC003400000000003LL, // 32bit add r += b
|
||||
X64_andqrr = 0xC023480000000003LL, // 64bit and r &= b
|
||||
X64_andrr = 0xC023400000000003LL, // 32bit and r &= b
|
||||
X64_call = 0x00000000E8000005LL, // near call
|
||||
X64_callrax = 0xD0FF000000000002LL, // indirect call to addr in rax (no REX)
|
||||
X64_cmovqne = 0xC0450F4800000004LL, // 64bit conditional mov if (c) r = b
|
||||
X64_cmplr = 0xC03B400000000003LL, // 32bit compare r,b
|
||||
X64_cmpqr = 0xC03B480000000003LL, // 64bit compare r,b
|
||||
X64_cmplri = 0xF881400000000003LL, // 32bit compare r,imm32
|
||||
X64_cmpqri = 0xF881480000000003LL, // 64bit compare r,int64(imm32)
|
||||
X64_cmplr8 = 0x00F8834000000004LL, // 32bit compare r,imm8
|
||||
X64_cmpqr8 = 0x00F8834800000004LL, // 64bit compare r,int64(imm8)
|
||||
X64_cvtsi2sd= 0xC02A0F40F2000005LL, // convert int32 to double r = (double) b
|
||||
X64_cvtsq2sd= 0xC02A0F48F2000005LL, // convert int64 to double r = (double) b
|
||||
X64_divsd = 0xC05E0F40F2000005LL, // divide scalar double r /= b
|
||||
X64_mulsd = 0xC0590F40F2000005LL, // multiply scalar double r *= b
|
||||
X64_addsd = 0xC0580F40F2000005LL, // add scalar double r += b
|
||||
X64_imul = 0xC0AF0F4000000004LL, // 32bit signed mul r *= b
|
||||
X64_imuli = 0xC069400000000003LL, // 32bit signed mul r = b * imm32
|
||||
X64_imul8 = 0x00C06B4000000004LL, // 32bit signed mul r = b * imm8
|
||||
X64_jmp = 0x00000000E9000005LL, // jump near rel32
|
||||
X64_jmp8 = 0x00EB000000000002LL, // jump near rel8
|
||||
X64_jb = 0x00000000820F0006LL, // jump near if below (uint <)
|
||||
X64_jae = 0x00000000830F0006LL, // jump near if above or equal (uint >=)
|
||||
X64_ja = 0x00000000870F0006LL, // jump near if above (uint >)
|
||||
X64_jbe = 0x00000000860F0006LL, // jump near if below or equal (uint <=)
|
||||
X64_je = 0x00000000840F0006LL, // near jump if equal
|
||||
X64_jne = 0x00000000850F0006LL, // jump near if not equal
|
||||
X64_jl = 0x000000008C0F0006LL, // jump near if less (int <)
|
||||
X64_jge = 0x000000008D0F0006LL, // jump near if greater or equal (int >=)
|
||||
X64_jg = 0x000000008F0F0006LL, // jump near if greater (int >)
|
||||
X64_jle = 0x000000008E0F0006LL, // jump near if less or equal (int <=)
|
||||
X64_jp = 0x000000008A0F0006LL, // jump near if parity (PF == 1)
|
||||
X64_jnp = 0x000000008B0F0006LL, // jump near if not parity (PF == 0)
|
||||
X64_jneg = 0x0000000001000000LL, // xor with this mask to negate the condition
|
||||
X64_jb8 = 0x0072000000000002LL, // jump near if below (uint <)
|
||||
X64_jae8 = 0x0073000000000002LL, // jump near if above or equal (uint >=)
|
||||
X64_ja8 = 0x0077000000000002LL, // jump near if above (uint >)
|
||||
X64_jbe8 = 0x0076000000000002LL, // jump near if below or equal (uint <=)
|
||||
X64_je8 = 0x0074000000000002LL, // near jump if equal
|
||||
X64_jne8 = 0x0075000000000002LL, // jump near if not equal
|
||||
X64_jl8 = 0x007C000000000002LL, // jump near if less (int <)
|
||||
X64_jge8 = 0x007D000000000002LL, // jump near if greater or equal (int >=)
|
||||
X64_jg8 = 0x007F000000000002LL, // jump near if greater (int >)
|
||||
X64_jle8 = 0x007E000000000002LL, // jump near if less or equal (int <=)
|
||||
X64_jp8 = 0x007A000000000002LL, // jump near if parity (PF == 1)
|
||||
X64_jnp8 = 0x007B000000000002LL, // jump near if not parity (PF == 0)
|
||||
X64_jneg8 = 0x0001000000000000LL, // xor with this mask to negate the condition
|
||||
X64_leaqrm = 0x00000000808D4807LL, // 64bit load effective addr reg <- disp32+base
|
||||
X64_learm = 0x00000000808D4007LL, // 32bit load effective addr reg <- disp32+base
|
||||
X64_movlr = 0xC08B400000000003LL, // 32bit mov r <- b
|
||||
X64_movlmr = 0x0000000080894007LL, // 32bit store r -> [b+d32]
|
||||
X64_movlrm = 0x00000000808B4007LL, // 32bit load r <- [b+d32]
|
||||
X64_movqmr = 0x0000000080894807LL, // 64bit store gpr -> [b+d32]
|
||||
X64_movqspr = 0x0024448948000005LL, // 64bit store gpr -> [rsp+d32] (sib required)
|
||||
X64_movqr = 0xC08B480000000003LL, // 64bit mov r <- b
|
||||
X64_movqi = 0xB848000000000002LL, // 64bit mov r <- imm64
|
||||
X64_movi = 0xB840000000000002LL, // 32bit mov r <- imm32
|
||||
X64_movqi32 = 0xC0C7480000000003LL, // 64bit mov r <- int64(imm32)
|
||||
X64_movapsr = 0xC0280F4000000004LL, // 128bit mov xmm <- xmm
|
||||
X64_movqrx = 0xC07E0F4866000005LL, // 64bit mov b <- xmm-r
|
||||
X64_movqxr = 0xC06E0F4866000005LL, // 64bit mov b -> xmm-r
|
||||
X64_movqrm = 0x00000000808B4807LL, // 64bit load r <- [b+d32]
|
||||
X64_movsdrr = 0xC0100F40F2000005LL, // 64bit mov xmm-r <- xmm-b (upper 64bits unchanged)
|
||||
X64_movsdrm = 0x80100F40F2000005LL, // 64bit load xmm-r <- [b+d32] (upper 64 cleared)
|
||||
X64_movsdmr = 0x80110F40F2000005LL, // 64bit store xmm-r -> [b+d32]
|
||||
X64_movsxdr = 0xC063480000000003LL, // sign extend i32 to i64 r = (int64)(int32) b
|
||||
X64_movzx8 = 0xC0B60F4000000004LL, // zero extend i8 to i64 r = (uint64)(uint8) b
|
||||
X64_neg = 0xD8F7400000000003LL, // 32bit two's compliment b = -b
|
||||
X64_nop1 = 0x9000000000000001LL, // one byte NOP
|
||||
X64_nop2 = 0x9066000000000002LL, // two byte NOP
|
||||
X64_nop3 = 0x001F0F0000000003LL, // three byte NOP
|
||||
X64_nop4 = 0x00401F0F00000004LL, // four byte NOP
|
||||
X64_nop5 = 0x0000441F0F000005LL, // five byte NOP
|
||||
X64_nop6 = 0x0000441F0F660006LL, // six byte NOP
|
||||
X64_nop7 = 0x00000000801F0F07LL, // seven byte NOP
|
||||
X64_not = 0xD0F7400000000003LL, // 32bit ones compliment b = ~b
|
||||
X64_orlrr = 0xC00B400000000003LL, // 32bit or r |= b
|
||||
X64_orqrr = 0xC00B480000000003LL, // 64bit or r |= b
|
||||
X64_popr = 0x5840000000000002LL, // 64bit pop r <- [rsp++]
|
||||
X64_pushr = 0x5040000000000002LL, // 64bit push r -> [--rsp]
|
||||
X64_pxor = 0xC0EF0F4066000005LL, // 128bit xor xmm-r ^= xmm-b
|
||||
X64_ret = 0xC300000000000001LL, // near return from called procedure
|
||||
X64_sete = 0xC0940F4000000004LL, // set byte if equal (ZF == 1)
|
||||
X64_seto = 0xC0900F4000000004LL, // set byte if overflow (OF == 1)
|
||||
X64_setc = 0xC0920F4000000004LL, // set byte if carry (CF == 1)
|
||||
X64_setl = 0xC09C0F4000000004LL, // set byte if less (int <) (SF != OF)
|
||||
X64_setle = 0xC09E0F4000000004LL, // set byte if less or equal (int <=) (ZF == 1 || SF != OF)
|
||||
X64_setg = 0xC09F0F4000000004LL, // set byte if greater (int >) (ZF == 0 && SF == OF)
|
||||
X64_setge = 0xC09D0F4000000004LL, // set byte if greater or equal (int >=) (SF == OF)
|
||||
X64_seta = 0xC0970F4000000004LL, // set byte if above (uint >) (CF == 0 && ZF == 0)
|
||||
X64_setae = 0xC0930F4000000004LL, // set byte if above or equal (uint >=) (CF == 0)
|
||||
X64_setb = 0xC0920F4000000004LL, // set byte if below (uint <) (CF == 1)
|
||||
X64_setbe = 0xC0960F4000000004LL, // set byte if below or equal (uint <=) (ZF == 1 || CF == 1)
|
||||
X64_subsd = 0xC05C0F40F2000005LL, // subtract scalar double r -= b
|
||||
X64_shl = 0xE0D3400000000003LL, // 32bit left shift r <<= rcx
|
||||
X64_shlq = 0xE0D3480000000003LL, // 64bit left shift r <<= rcx
|
||||
X64_shr = 0xE8D3400000000003LL, // 32bit uint right shift r >>= rcx
|
||||
X64_shrq = 0xE8D3480000000003LL, // 64bit uint right shift r >>= rcx
|
||||
X64_sar = 0xF8D3400000000003LL, // 32bit int right shift r >>= rcx
|
||||
X64_sarq = 0xF8D3480000000003LL, // 64bit int right shift r >>= rcx
|
||||
X64_shli = 0x00E0C14000000004LL, // 32bit left shift r <<= imm8
|
||||
X64_shlqi = 0x00E0C14800000004LL, // 64bit left shift r <<= imm8
|
||||
X64_sari = 0x00F8C14000000004LL, // 32bit int right shift r >>= imm8
|
||||
X64_sarqi = 0x00F8C14800000004LL, // 64bit int right shift r >>= imm8
|
||||
X64_shri = 0x00E8C14000000004LL, // 32bit uint right shift r >>= imm8
|
||||
X64_shrqi = 0x00E8C14800000004LL, // 64bit uint right shift r >>= imm8
|
||||
X64_subqrr = 0xC02B480000000003LL, // 64bit sub r -= b
|
||||
X64_subrr = 0xC02B400000000003LL, // 32bit sub r -= b
|
||||
X64_subqri = 0xE881480000000003LL, // 64bit sub r -= int64(imm32)
|
||||
X64_subqr8 = 0x00E8834800000004LL, // 64bit sub r -= int64(imm8)
|
||||
X64_ucomisd = 0xC02E0F4066000005LL, // unordered compare scalar double
|
||||
X64_xorqrr = 0xC033480000000003LL, // 64bit xor r &= b
|
||||
X64_xorrr = 0xC033400000000003LL, // 32bit xor r &= b
|
||||
X64_xorpd = 0xC0570F4066000005LL, // 128bit xor xmm (two packed doubles)
|
||||
X64_xorps = 0xC0570F4000000004LL, // 128bit xor xmm (four packed singles), one byte shorter
|
||||
X64_xorpsm = 0x05570F4000000004LL, // 128bit xor xmm, [rip+disp32]
|
||||
X64_xorpsa = 0x2504570F40000005LL, // 128bit xor xmm, [disp32]
|
||||
|
||||
X86_and8r = 0xC022000000000002LL, // and rl,rh
|
||||
X86_sete = 0xC0940F0000000003LL, // no-rex version of X64_sete
|
||||
X86_setnp = 0xC09B0F0000000003LL // no-rex set byte if odd parity (ordered fcmp result) (PF == 0)
|
||||
};
|
||||
|
||||
typedef uint32_t RegisterMask;
|
||||
|
||||
static const RegisterMask GpRegs = 0xffff;
|
||||
static const RegisterMask FpRegs = 0xffff0000;
|
||||
static const bool CalleeRegsNeedExplicitSaving = true;
|
||||
#ifdef _MSC_VER
|
||||
static const RegisterMask SavedRegs = 1<<RBX | 1<<RSI | 1<<RDI | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15;
|
||||
static const int NumSavedRegs = 7; // rbx, rsi, rdi, r12-15
|
||||
static const int NumArgRegs = 4;
|
||||
#else
|
||||
static const RegisterMask SavedRegs = 1<<RBX | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15;
|
||||
static const int NumSavedRegs = 5; // rbx, r12-15
|
||||
static const int NumArgRegs = 6;
|
||||
#endif
|
||||
|
||||
static inline bool IsFpReg(Register r) {
|
||||
return ((1<<r) & FpRegs) != 0;
|
||||
}
|
||||
static inline bool IsGpReg(Register r) {
|
||||
return ((1<<r) & GpRegs) != 0;
|
||||
}
|
||||
|
||||
verbose_only( extern const char* regNames[]; )
|
||||
|
||||
#define DECLARE_PLATFORM_STATS()
|
||||
#define DECLARE_PLATFORM_REGALLOC()
|
||||
|
||||
#define DECLARE_PLATFORM_ASSEMBLER() \
|
||||
const static Register argRegs[NumArgRegs], retRegs[1]; \
|
||||
void underrunProtect(ptrdiff_t bytes); \
|
||||
void nativePageReset(); \
|
||||
void nativePageSetup(); \
|
||||
void asm_qbinop(LIns*); \
|
||||
void MR(Register, Register);\
|
||||
void JMP(NIns*);\
|
||||
void emit(uint64_t op);\
|
||||
void emit8(uint64_t op, int64_t val);\
|
||||
void emit32(uint64_t op, int64_t val);\
|
||||
void emitrr(uint64_t op, Register r, Register b);\
|
||||
void emitrr8(uint64_t op, Register r, Register b);\
|
||||
void emitr(uint64_t op, Register b) { emitrr(op, (Register)0, b); }\
|
||||
void emitr8(uint64_t op, Register b) { emitrr8(op, (Register)0, b); }\
|
||||
void emitprr(uint64_t op, Register r, Register b);\
|
||||
void emitrm(uint64_t op, Register r, int32_t d, Register b);\
|
||||
void emitprm(uint64_t op, Register r, int32_t d, Register b);\
|
||||
void emitrr_imm(uint64_t op, Register r, Register b, int32_t imm);\
|
||||
void emitr_imm(uint64_t op, Register r, int32_t imm) { emitrr_imm(op, (Register)0, r, imm); }\
|
||||
void emitr_imm8(uint64_t op, Register b, int32_t imm8);\
|
||||
void emit_int(Register r, int32_t v);\
|
||||
void emit_quad(Register r, uint64_t v);\
|
||||
void asm_regarg(ArgSize, LIns*, Register);\
|
||||
void asm_stkarg(ArgSize, LIns*, int);\
|
||||
void asm_shift(LIns*);\
|
||||
void asm_shift_imm(LIns*);\
|
||||
void asm_arith_imm(LIns*);\
|
||||
void regalloc_unary(LIns *ins, RegisterMask allow, Register &rr, Register &ra);\
|
||||
void regalloc_binary(LIns *ins, RegisterMask allow, Register &rr, Register &ra, Register &rb);\
|
||||
void regalloc_load(LIns *ins, Register &rr, int32_t &d, Register &rb);\
|
||||
void dis(NIns *p, int bytes);\
|
||||
void asm_cmp(LIns*);\
|
||||
void asm_cmp_imm(LIns*);\
|
||||
void fcmp(LIns*, LIns*);\
|
||||
NIns* asm_fbranch(bool, LIns*, NIns*);\
|
||||
int max_stk_used;
|
||||
|
||||
#define swapptrs() { NIns* _tins = _nIns; _nIns=_nExitIns; _nExitIns=_tins; }
|
||||
|
||||
const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect
|
||||
|
||||
typedef uint8_t NIns;
|
||||
|
||||
inline Register nextreg(Register r) {
|
||||
return Register(r+1);
|
||||
}
|
||||
|
||||
} // namespace nanojit
|
||||
|
||||
#endif // __nanojit_NativeX64__
|
@ -3982,15 +3982,6 @@ static JSPropertySpec its_props[] = {
|
||||
{NULL,0,0,NULL,NULL}
|
||||
};
|
||||
|
||||
static JSBool
|
||||
its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
*rval = OBJECT_TO_JSVAL(obj);
|
||||
if (argc != 0)
|
||||
JS_SetCallReturnValue2(cx, argv[0]);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
@ -4019,7 +4010,6 @@ 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}
|
||||
};
|
||||
|
6021
js/src/trace-test.js
6021
js/src/trace-test.js
File diff suppressed because it is too large
Load Diff
41
js/src/trace-test/README
Normal file
41
js/src/trace-test/README
Normal file
@ -0,0 +1,41 @@
|
||||
JS Trace Test Suite
|
||||
|
||||
* PURPOSE
|
||||
|
||||
This is a test suite for testing TraceMonkey. All tests are run in the JS shell
|
||||
with tracing enabled (-j).
|
||||
|
||||
* REQUIREMENTS
|
||||
|
||||
Python 3.1: http://www.python.org
|
||||
|
||||
* RUNNING THE TESTS
|
||||
|
||||
Basic usage:
|
||||
|
||||
python3.1 trace-test.py <path-to-js-shell>
|
||||
|
||||
The progress bar shows [#tests passed, #tests failed, #tests run] at the left.
|
||||
If all tests pass, the output is 'PASSED ALL'. The test suite can be interrupted
|
||||
at any time with Ctrl+C and partial results will be printed.
|
||||
|
||||
To run only the basic tests, not including the slow tests:
|
||||
|
||||
python3.1 trace-test.py <path-to-js-shell> basic
|
||||
|
||||
For more options:
|
||||
|
||||
python3.1 trace-test.py -h
|
||||
|
||||
* CREATING NEW TESTS
|
||||
|
||||
Simply create a JS file under the 'tests/' directory. Most tests should go in
|
||||
'tests/basic/'. Tests that run a long time (many seconds) should go in
|
||||
'tests/slow/' so they can be easily excluded from test runs.
|
||||
|
||||
All tests are run with 'lib/prolog.js' included first on the command line. The
|
||||
command line also creates a global variable 'libdir' that is set to the path
|
||||
of the 'lib' directory. To include a file 'foo.js' from the lib directory in a
|
||||
test case:
|
||||
|
||||
load(libdir + 'foo.js')
|
9
js/src/trace-test/lib/andTestHelper.js
Normal file
9
js/src/trace-test/lib/andTestHelper.js
Normal file
@ -0,0 +1,9 @@
|
||||
function andTestHelper(a, b, n)
|
||||
{
|
||||
var k = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (a && b)
|
||||
k += i;
|
||||
}
|
||||
return k;
|
||||
}
|
9
js/src/trace-test/lib/orTestHelper.js
Normal file
9
js/src/trace-test/lib/orTestHelper.js
Normal file
@ -0,0 +1,9 @@
|
||||
function orTestHelper(a, b, n)
|
||||
{
|
||||
var k = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (a || b)
|
||||
k += i;
|
||||
}
|
||||
return k;
|
||||
}
|
37
js/src/trace-test/lib/prolog.js
Normal file
37
js/src/trace-test/lib/prolog.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
const HAVE_TM = 'tracemonkey' in this;
|
||||
|
||||
const HOTLOOP = HAVE_TM ? tracemonkey.HOTLOOP : 2;
|
||||
const RECORDLOOP = HOTLOOP;
|
||||
const RUNLOOP = HOTLOOP + 1;
|
||||
|
||||
var checkStats;
|
||||
if (HAVE_TM) {
|
||||
checkStats = function(stats)
|
||||
{
|
||||
function jit(on)
|
||||
{
|
||||
if (on && !options().match(/jit/))
|
||||
{
|
||||
options('jit');
|
||||
}
|
||||
else if (!on && options().match(/jit/))
|
||||
{
|
||||
options('jit');
|
||||
}
|
||||
}
|
||||
|
||||
jit(false);
|
||||
for (var name in stats) {
|
||||
var expected = stats[name];
|
||||
var actual = tracemonkey[name];
|
||||
if (expected != actual) {
|
||||
print('Trace stats check failed: got ' + actual + ', expected ' + expected + ' for ' + name);
|
||||
}
|
||||
}
|
||||
jit(true);
|
||||
};
|
||||
} else {
|
||||
checkStats = function() {};
|
||||
}
|
18
js/src/trace-test/lib/range.js
Normal file
18
js/src/trace-test/lib/range.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Library file for tests to load.
|
||||
|
||||
function Range(start, stop) {
|
||||
this.i = start;
|
||||
this.stop = stop;
|
||||
}
|
||||
Range.prototype = {
|
||||
__iterator__: function() this,
|
||||
next: function() {
|
||||
if (this.i >= this.stop)
|
||||
throw StopIteration;
|
||||
return this.i++;
|
||||
}
|
||||
};
|
||||
|
||||
function range(start, stop) {
|
||||
return new Range(start, stop);
|
||||
}
|
35
js/src/trace-test/progressbar.py
Normal file
35
js/src/trace-test/progressbar.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Text progress bar library, like curl or scp.
|
||||
|
||||
import sys, datetime
|
||||
|
||||
class ProgressBar:
|
||||
def __init__(self, label, limit, label_width=12):
|
||||
self.label = label
|
||||
self.limit = limit
|
||||
self.label_width = label_width
|
||||
self.cur = 0
|
||||
self.t0 = datetime.datetime.now()
|
||||
|
||||
self.barlen = 64 - self.label_width
|
||||
self.fmt = '\r%-' + str(label_width) + 's %3d%% %-' + str(self.barlen) + 's| %6.1fs'
|
||||
|
||||
def update(self, value):
|
||||
self.cur = value
|
||||
pct = int(100.0 * self.cur / self.limit)
|
||||
barlen = int(1.0 * self.barlen * self.cur / self.limit) - 1
|
||||
bar = '='*barlen + '>'
|
||||
dt = datetime.datetime.now() - self.t0
|
||||
dt = dt.seconds + dt.microseconds * 1e-6
|
||||
sys.stdout.write(self.fmt%(self.label[:self.label_width], pct, bar, dt))
|
||||
sys.stdout.flush()
|
||||
|
||||
def finish(self):
|
||||
self.update(self.limit)
|
||||
sys.stdout.write('\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pb = ProgressBar('test', 12)
|
||||
for i in range(12):
|
||||
pb.update(i)
|
||||
time.sleep(0.5)
|
||||
pb.finish()
|
13
js/src/trace-test/tests/allow_oom/testBug507425.js
Normal file
13
js/src/trace-test/tests/allow_oom/testBug507425.js
Normal file
@ -0,0 +1,13 @@
|
||||
function testBug507425() {
|
||||
var r = /x/;
|
||||
for (var i = 0; i < 3; i++)
|
||||
r.lastIndex = 0; // call a setter
|
||||
var s = ';';
|
||||
try {
|
||||
for (i = 0; i < 80; i++)
|
||||
s += s; // call js_CanLeaveTrace
|
||||
} catch (exc) {
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
assertEq(testBug507425(), "ok");
|
6
js/src/trace-test/tests/basic/FPQuadCmp.js
Normal file
6
js/src/trace-test/tests/basic/FPQuadCmp.js
Normal file
@ -0,0 +1,6 @@
|
||||
function FPQuadCmp()
|
||||
{
|
||||
for (let j = 0; j < 3; ++j) { true == 0; }
|
||||
return "ok";
|
||||
}
|
||||
assertEq(FPQuadCmp(), "ok");
|
9
js/src/trace-test/tests/basic/arith.js
Normal file
9
js/src/trace-test/tests/basic/arith.js
Normal file
@ -0,0 +1,9 @@
|
||||
function arith()
|
||||
{
|
||||
var accum = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
accum += (i * 2) - 1;
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
assertEq(arith(), 9800);
|
13
js/src/trace-test/tests/basic/arityMismatchExtraArg.js
Normal file
13
js/src/trace-test/tests/basic/arityMismatchExtraArg.js
Normal file
@ -0,0 +1,13 @@
|
||||
function arityMismatchMissingArg(arg)
|
||||
{
|
||||
for (var a = 0, i = 1; i < 10000; i *= 2) {
|
||||
a += i;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function arityMismatchExtraArg()
|
||||
{
|
||||
return arityMismatchMissingArg(1, 2);
|
||||
}
|
||||
assertEq(arityMismatchExtraArg(), 16383);
|
8
js/src/trace-test/tests/basic/arityMismatchMissingArg.js
Normal file
8
js/src/trace-test/tests/basic/arityMismatchMissingArg.js
Normal file
@ -0,0 +1,8 @@
|
||||
function arityMismatchMissingArg(arg)
|
||||
{
|
||||
for (var a = 0, i = 1; i < 10000; i *= 2) {
|
||||
a += i;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
assertEq(arityMismatchMissingArg(), 16383);
|
10
js/src/trace-test/tests/basic/bitwiseAnd.js
Normal file
10
js/src/trace-test/tests/basic/bitwiseAnd.js
Normal file
@ -0,0 +1,10 @@
|
||||
function bitwiseAnd_inner(bitwiseAndValue) {
|
||||
for (var i = 0; i < 60000; i++)
|
||||
bitwiseAndValue = bitwiseAndValue & i;
|
||||
return bitwiseAndValue;
|
||||
}
|
||||
function bitwiseAnd()
|
||||
{
|
||||
return bitwiseAnd_inner(12341234);
|
||||
}
|
||||
assertEq(bitwiseAnd(), 0);
|
8
js/src/trace-test/tests/basic/bitwiseGlobal.js
Normal file
8
js/src/trace-test/tests/basic/bitwiseGlobal.js
Normal file
@ -0,0 +1,8 @@
|
||||
bitwiseAndValue = Math.pow(2,32);
|
||||
for (var i = 0; i < 60000; i++)
|
||||
bitwiseAndValue = bitwiseAndValue & i;
|
||||
|
||||
assertEq(bitwiseAndValue, 0);
|
||||
|
||||
|
||||
|
7
js/src/trace-test/tests/basic/bug464403.js
Normal file
7
js/src/trace-test/tests/basic/bug464403.js
Normal file
@ -0,0 +1,7 @@
|
||||
function bug464403() {
|
||||
print(8);
|
||||
var u = [print, print, function(){}]
|
||||
for each (x in u) for (u.e in [1,1,1,1]);
|
||||
return "ok";
|
||||
}
|
||||
assertEq(bug464403(), "ok");
|
13
js/src/trace-test/tests/basic/bug504587-1.js
Normal file
13
js/src/trace-test/tests/basic/bug504587-1.js
Normal file
@ -0,0 +1,13 @@
|
||||
// This test case failed a WIP patch. See https://bugzilla.mozilla.org/show_bug.cgi?id=504587#c68
|
||||
|
||||
function B() {}
|
||||
B.prototype.x = 1;
|
||||
var d = new B;
|
||||
|
||||
var names = ['z', 'z', 'z', 'z', 'z', 'z', 'z', 'x'];
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
x = d.x; // guard on shapeOf(d)
|
||||
d[names[i]] = 2; // unpredicted shape change
|
||||
y = d.x; // guard here is elided
|
||||
}
|
||||
assertEq(y, 2); // Assertion failed: got 1, expected 2
|
69
js/src/trace-test/tests/basic/bug507180.js
Normal file
69
js/src/trace-test/tests/basic/bug507180.js
Normal file
@ -0,0 +1,69 @@
|
||||
var o = {
|
||||
valueOf : function() {
|
||||
return {
|
||||
toString : function() { return "fail" }
|
||||
}
|
||||
},
|
||||
toString : function() { return "good" }
|
||||
};
|
||||
|
||||
var p = {
|
||||
valueOf : function() {
|
||||
return {
|
||||
toString : function() { return "0" }
|
||||
}
|
||||
},
|
||||
toString : function() { return "7" }
|
||||
};
|
||||
|
||||
var q = {
|
||||
toString : function() {
|
||||
return {
|
||||
valueOf : function() { return "0" }
|
||||
}
|
||||
},
|
||||
valueOf : function() { return "7" }
|
||||
};
|
||||
|
||||
function assert(b, s) {
|
||||
if (b)
|
||||
return;
|
||||
assertEq("imacro produces incorrect result for " + s, "fail");
|
||||
}
|
||||
|
||||
function run() {
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
// equality / inequality
|
||||
assert(!(o == "fail"), "obj == any");
|
||||
assert(!("fail" == o), "any == obj");
|
||||
assert(!(o != "good"), "obj != any");
|
||||
assert(!("good" != o), "any != obj");
|
||||
|
||||
// binary
|
||||
assert(!((p | 3) != 7), "obj | any");
|
||||
assert(!((3 | p) != 7), "any | obj");
|
||||
assert(!((p | p) != 7), "obj | obj");
|
||||
assert(!((p & 3) != 3), "obj & any");
|
||||
assert(!((3 & p) != 3), "any & obj");
|
||||
assert(!((p & p) != 7), "obj & obj");
|
||||
assert(!((p * 3) != 21), "obj * any");
|
||||
assert(!((3 * p) != 21), "any * obj");
|
||||
assert(!((p * p) != 49), "obj * obj");
|
||||
|
||||
// addition
|
||||
assert(!(o + "" != "good"), "obj + any");
|
||||
assert(!("" + o != "good"), "any + obj");
|
||||
assert(!(o + o != "goodgood"), "any + any");
|
||||
|
||||
// sign
|
||||
assert(!(-p != -7), "-obj");
|
||||
assert(!(+p != 7), "+obj");
|
||||
|
||||
// String
|
||||
assert(!(String(q) != "7"), "String(obj)");
|
||||
assert(!(new String(q) != "7"), "new String(obj)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
run();
|
15
js/src/trace-test/tests/basic/bug509982.js
Normal file
15
js/src/trace-test/tests/basic/bug509982.js
Normal file
@ -0,0 +1,15 @@
|
||||
function g() {
|
||||
const e = 0;
|
||||
return function () {
|
||||
switch (7) {
|
||||
case e:
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
let f = g;
|
||||
f();
|
||||
}
|
||||
|
||||
// Just test that we don't crash.
|
2
js/src/trace-test/tests/basic/bug510434.js
Normal file
2
js/src/trace-test/tests/basic/bug510434.js
Normal file
@ -0,0 +1,2 @@
|
||||
var f = function(){ var arguments = 3; for (var j=0;j<4;++j) print(arguments); };
|
||||
f();
|
15
js/src/trace-test/tests/basic/bug510655.js
Normal file
15
js/src/trace-test/tests/basic/bug510655.js
Normal file
@ -0,0 +1,15 @@
|
||||
// This should not crash (or assert in debug builds).
|
||||
|
||||
(function () {
|
||||
for (b in [0, 0]) {
|
||||
(eval("\
|
||||
[this\
|
||||
for (b in [\
|
||||
[undefined],\
|
||||
arguments,\
|
||||
[undefined]\
|
||||
])\
|
||||
]\
|
||||
"))
|
||||
}
|
||||
})()
|
28
js/src/trace-test/tests/basic/call.js
Normal file
28
js/src/trace-test/tests/basic/call.js
Normal file
@ -0,0 +1,28 @@
|
||||
function glob_f1() {
|
||||
return 1;
|
||||
}
|
||||
function glob_f2() {
|
||||
return glob_f1();
|
||||
}
|
||||
function call()
|
||||
{
|
||||
var q1 = 0, q2 = 0, q3 = 0, q4 = 0, q5 = 0;
|
||||
var o = {};
|
||||
function f1() {
|
||||
return 1;
|
||||
}
|
||||
function f2(f) {
|
||||
return f();
|
||||
}
|
||||
o.f = f1;
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
q1 += f1();
|
||||
q2 += f2(f1);
|
||||
q3 += glob_f1();
|
||||
q4 += o.f();
|
||||
q5 += glob_f2();
|
||||
}
|
||||
var ret = String([q1, q2, q3, q4, q5]);
|
||||
return ret;
|
||||
}
|
||||
assertEq(call(), "100,100,100,100,100");
|
11
js/src/trace-test/tests/basic/deep2.js
Normal file
11
js/src/trace-test/tests/basic/deep2.js
Normal file
@ -0,0 +1,11 @@
|
||||
function deep1(x) {
|
||||
if (x > 90)
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
function deep2() {
|
||||
for (var i = 0; i < 100; ++i)
|
||||
deep1(i);
|
||||
return "ok";
|
||||
}
|
||||
assertEq(deep2(), "ok");
|
12
js/src/trace-test/tests/basic/deepForInLoop.js
Normal file
12
js/src/trace-test/tests/basic/deepForInLoop.js
Normal file
@ -0,0 +1,12 @@
|
||||
function deepForInLoop() {
|
||||
// NB: the number of props set in C is arefully tuned to match HOTLOOP = 2.
|
||||
function C(){this.p = 1, this.q = 2}
|
||||
C.prototype = {p:1, q:2, r:3, s:4, t:5};
|
||||
var o = new C;
|
||||
var j = 0;
|
||||
var a = [];
|
||||
for (var i in o)
|
||||
a[j++] = i;
|
||||
return a.join("");
|
||||
}
|
||||
assertEq(deepForInLoop(), "pqrst");
|
11
js/src/trace-test/tests/basic/dependentStrings.js
Normal file
11
js/src/trace-test/tests/basic/dependentStrings.js
Normal file
@ -0,0 +1,11 @@
|
||||
function dependentStrings()
|
||||
{
|
||||
var a = [];
|
||||
var t = "abcdefghijklmnopqrst";
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var s = t.substring(2*i, 2*i + 2);
|
||||
a[i] = s + s.length;
|
||||
}
|
||||
return a.join("");
|
||||
}
|
||||
assertEq(dependentStrings(), "ab2cd2ef2gh2ij2kl2mn2op2qr2st2");
|
60
js/src/trace-test/tests/basic/doMath.js
Normal file
60
js/src/trace-test/tests/basic/doMath.js
Normal file
@ -0,0 +1,60 @@
|
||||
function map_test(t, cases)
|
||||
{
|
||||
for (var i = 0; i < cases.length; i++) {
|
||||
function c() { return t(cases[i].input); }
|
||||
var expected = cases[i].expected;
|
||||
assertEq(c(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
function lsh_inner(n)
|
||||
{
|
||||
var r;
|
||||
for (var i = 0; i < 35; i++)
|
||||
r = 0x1 << n;
|
||||
return r;
|
||||
}
|
||||
map_test (lsh_inner,
|
||||
[{input: 15, expected: 32768},
|
||||
{input: 55, expected: 8388608},
|
||||
{input: 1, expected: 2},
|
||||
{input: 0, expected: 1}]);
|
||||
|
||||
function rsh_inner(n)
|
||||
{
|
||||
var r;
|
||||
for (var i = 0; i < 35; i++)
|
||||
r = 0x11010101 >> n;
|
||||
return r;
|
||||
}
|
||||
map_test (rsh_inner,
|
||||
[{input: 8, expected: 1114369},
|
||||
{input: 5, expected: 8914952},
|
||||
{input: 35, expected: 35659808},
|
||||
{input: -1, expected: 0}]);
|
||||
|
||||
function ursh_inner(n)
|
||||
{
|
||||
var r;
|
||||
for (var i = 0; i < 35; i++)
|
||||
r = -55 >>> n;
|
||||
return r;
|
||||
}
|
||||
map_test (ursh_inner,
|
||||
[{input: 8, expected: 16777215},
|
||||
{input: 33, expected: 2147483620},
|
||||
{input: 0, expected: 4294967241},
|
||||
{input: 1, expected: 2147483620}]);
|
||||
|
||||
function doMath_inner(cos)
|
||||
{
|
||||
var s = 0;
|
||||
var sin = Math.sin;
|
||||
for (var i = 0; i < 200; i++)
|
||||
s = -Math.pow(sin(i) + cos(i * 0.75), 4);
|
||||
return s;
|
||||
}
|
||||
function doMath() {
|
||||
return doMath_inner(Math.cos);
|
||||
}
|
||||
assertEq(doMath(), -0.5405549555611059);
|
29
js/src/trace-test/tests/basic/equalInt.js
Normal file
29
js/src/trace-test/tests/basic/equalInt.js
Normal file
@ -0,0 +1,29 @@
|
||||
function equalInt()
|
||||
{
|
||||
var i1 = 55, one = 1, zero = 0, undef;
|
||||
var o1 = { }, o2 = { };
|
||||
var s = "5";
|
||||
var hits = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||
for (var i = 0; i < 5000; i++) {
|
||||
if (i1 == 55) hits[0]++;
|
||||
if (i1 != 56) hits[1]++;
|
||||
if (i1 < 56) hits[2]++;
|
||||
if (i1 > 50) hits[3]++;
|
||||
if (i1 <= 60) hits[4]++;
|
||||
if (i1 >= 30) hits[5]++;
|
||||
if (i1 == 7) hits[6]++;
|
||||
if (i1 != 55) hits[7]++;
|
||||
if (i1 < 30) hits[8]++;
|
||||
if (i1 > 90) hits[9]++;
|
||||
if (i1 <= 40) hits[10]++;
|
||||
if (i1 >= 70) hits[11]++;
|
||||
if (o1 == o2) hits[12]++;
|
||||
if (o2 != null) hits[13]++;
|
||||
if (s < 10) hits[14]++;
|
||||
if (true < zero) hits[15]++;
|
||||
if (undef > one) hits[16]++;
|
||||
if (undef < zero) hits[17]++;
|
||||
}
|
||||
return hits.toString();
|
||||
}
|
||||
assertEq(equalInt(), "5000,5000,5000,5000,5000,5000,0,0,0,0,0,0,0,5000,5000,0,0,0");
|
17
js/src/trace-test/tests/basic/fannkuch.js
Normal file
17
js/src/trace-test/tests/basic/fannkuch.js
Normal file
@ -0,0 +1,17 @@
|
||||
function fannkuch() {
|
||||
var count = Array(8);
|
||||
var r = 8;
|
||||
var done = 0;
|
||||
while (done < 40) {
|
||||
// write-out the first 30 permutations
|
||||
done += r;
|
||||
while (r != 1) { count[r - 1] = r; r--; }
|
||||
while (true) {
|
||||
count[r] = count[r] - 1;
|
||||
if (count[r] > 0) break;
|
||||
r++;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
assertEq(fannkuch(), 41);
|
12
js/src/trace-test/tests/basic/forVarInWith.js
Normal file
12
js/src/trace-test/tests/basic/forVarInWith.js
Normal file
@ -0,0 +1,12 @@
|
||||
function forVarInWith() {
|
||||
function foo() ({notk:42});
|
||||
function bar() ({p:1, q:2, r:3, s:4, t:5});
|
||||
var o = foo();
|
||||
var a = [];
|
||||
with (o) {
|
||||
for (var k in bar())
|
||||
a[a.length] = k;
|
||||
}
|
||||
return a.join("");
|
||||
}
|
||||
assertEq(forVarInWith(), "pqrst");
|
30
js/src/trace-test/tests/basic/getelem.js
Normal file
30
js/src/trace-test/tests/basic/getelem.js
Normal file
@ -0,0 +1,30 @@
|
||||
var a;
|
||||
function setelem()
|
||||
{
|
||||
a = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||
a = a.concat(a, a, a);
|
||||
var l = a.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
return a.toString();
|
||||
}
|
||||
|
||||
setelem();
|
||||
|
||||
function getelem_inner(a)
|
||||
{
|
||||
var accum = 0;
|
||||
var l = a.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
accum += a[i];
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
function getelem()
|
||||
{
|
||||
return getelem_inner(a);
|
||||
}
|
||||
|
||||
assertEq(getelem(), 3486);
|
16
js/src/trace-test/tests/basic/getprop.js
Normal file
16
js/src/trace-test/tests/basic/getprop.js
Normal file
@ -0,0 +1,16 @@
|
||||
var a = 2;
|
||||
function getprop_inner(o2)
|
||||
{
|
||||
var o = {a:5};
|
||||
var t = this;
|
||||
var x = 0;
|
||||
for (var i = 0; i < 20; i++) {
|
||||
t = this;
|
||||
x += o.a + o2.a + this.a + t.a;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
function getprop() {
|
||||
return getprop_inner({a:9});
|
||||
}
|
||||
assertEq(getprop(), 360);
|
6
js/src/trace-test/tests/basic/globalGet.js
Normal file
6
js/src/trace-test/tests/basic/globalGet.js
Normal file
@ -0,0 +1,6 @@
|
||||
globalName = 907;
|
||||
var globalInt = 0;
|
||||
for (var i = 0; i < 500; i++)
|
||||
globalInt = globalName + i;
|
||||
|
||||
assertEq(globalInt, globalName + 499);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user