Merge tracemonkey to mozilla-central.

This commit is contained in:
Robert Sayre 2009-08-25 09:52:56 -07:00
commit b55965f33d
552 changed files with 14329 additions and 12229 deletions

View File

@ -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 \

View File

@ -5215,6 +5215,7 @@ MAKEFILES="
Makefile
shell/Makefile
lirasm/Makefile
jsapi-tests/Makefile
config/Makefile
config/autoconf.mk
config/mkdepend/Makefile

View File

@ -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;
}

View File

@ -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

View File

@ -54,3 +54,9 @@ JITSTAT(returnLoopExits)
JITSTAT(mergedLoopExits)
JITSTAT(noCompatInnerTrees)
JITSTAT(blacklisted)
JITSTAT(archIsIA32)
JITSTAT(archIsAMD64)
JITSTAT(archIs64BIT)
JITSTAT(archIsARM)
JITSTAT(archIsSPARC)
JITSTAT(archIsPPC)

View File

@ -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")

View 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)

View 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)

View 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)

View 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)

View 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
View 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;

View File

@ -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)

View File

@ -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.
*

View File

@ -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))

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)
{

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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. */

View File

@ -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),

View File

@ -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

View File

@ -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) ||

View File

@ -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

View File

@ -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)

View File

@ -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(&quote, 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(&quote, 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;
}

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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___ */

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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();

View File

@ -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;
}

View File

@ -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 */

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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 &regs = _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;

View File

@ -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:

View File

@ -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> &map;
const HashMap<K,T,H> &map;
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;
}

View File

@ -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 */
}

View File

@ -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

View File

@ -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)
{}

View File

@ -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

File diff suppressed because it is too large Load Diff

360
js/src/nanojit/NativeX64.h Normal file
View 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__

View File

@ -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}
};

File diff suppressed because it is too large Load Diff

41
js/src/trace-test/README Normal file
View 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')

View 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;
}

View 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;
}

View 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() {};
}

View 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);
}

View 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()

View 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");

View File

@ -0,0 +1,6 @@
function FPQuadCmp()
{
for (let j = 0; j < 3; ++j) { true == 0; }
return "ok";
}
assertEq(FPQuadCmp(), "ok");

View 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);

View 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);

View 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);

View 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);

View File

@ -0,0 +1,8 @@
bitwiseAndValue = Math.pow(2,32);
for (var i = 0; i < 60000; i++)
bitwiseAndValue = bitwiseAndValue & i;
assertEq(bitwiseAndValue, 0);

View 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");

View 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

View 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();

View 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.

View File

@ -0,0 +1,2 @@
var f = function(){ var arguments = 3; for (var j=0;j<4;++j) print(arguments); };
f();

View 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]\
])\
]\
"))
}
})()

View 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");

View 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");

View 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");

View 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");

View 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);

View 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");

View 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);

View 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");

View 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);

View 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);

View 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