Merge tracemonkey to mozilla-central. a=blockers

--HG--
rename : js/src/trace-test/tests/basic/bug616762.js => js/src/jit-test/tests/basic/bug616762.js
This commit is contained in:
Robert Sayre 2010-12-15 12:21:50 -08:00
Родитель 8e7827bf42 e46d5e99d6
Коммит ecc4cfb7c4
75 изменённых файлов: 1286 добавлений и 1074 удалений

Просмотреть файл

@ -1911,13 +1911,13 @@ case "$host" in
esac esac
dnl We require version 2.5 or newer of Python to build. dnl We require version 2.5 or newer of Python to build.
AC_MSG_CHECKING([for minimum required Python version >= $PYTHON_VERSION]) AC_MSG_CHECKING([for Python version >= $PYTHON_VERSION but not 3.x])
changequote(,) changequote(,)
$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1])" $PYTHON_VERSION $PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1] or sys.version[:2] != '2.')" $PYTHON_VERSION
_python_res=$? _python_res=$?
changequote([,]) changequote([,])
if test "$_python_res" != 0; then if test "$_python_res" != 0; then
AC_MSG_ERROR([Python $PYTHON_VERSION or higher is required.]) AC_MSG_ERROR([Python $PYTHON_VERSION or higher (but not Python 3.x) is required.])
fi fi
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])

Просмотреть файл

@ -159,7 +159,6 @@ nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -208,7 +207,6 @@ nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -237,7 +235,6 @@ nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -313,7 +310,6 @@ nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -363,7 +359,6 @@ nsDOMWorkerFunctions::AtoB(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -372,39 +367,8 @@ nsDOMWorkerFunctions::AtoB(JSContext* aCx,
return JS_FALSE; return JS_FALSE;
} }
JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]); return nsXPConnect::Base64Decode(aCx, JS_ARGV(aCx, aVp)[0],
if (!str) { &JS_RVAL(aCx, aVp));
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
return JS_FALSE;
}
size_t len = JS_GetStringEncodingLength(aCx, str);
if (len == size_t(-1))
return JS_FALSE;
JSUint32 alloc_len = (len + 1) * sizeof(char);
char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
if (!buffer)
return JS_FALSE;
JS_EncodeStringToBuffer(str, buffer, len);
buffer[len] = '\0';
nsDependentCString string(buffer, len);
nsCAutoString result;
if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
JS_ReportError(aCx, "Failed to decode base64 string!");
return JS_FALSE;
}
str = JS_NewStringCopyN(aCx, result.get(), result.Length());
if (!str) {
return JS_FALSE;
}
JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
return JS_TRUE;
} }
JSBool JSBool
@ -416,7 +380,6 @@ nsDOMWorkerFunctions::BtoA(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -425,39 +388,8 @@ nsDOMWorkerFunctions::BtoA(JSContext* aCx,
return JS_FALSE; return JS_FALSE;
} }
JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]); return nsXPConnect::Base64Encode(aCx, JS_ARGV(aCx, aVp)[0],
if (!str) { &JS_RVAL(aCx, aVp));
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
return JS_FALSE;
}
size_t len = JS_GetStringEncodingLength(aCx, str);
if (len == size_t(-1))
return JS_FALSE;
JSUint32 alloc_len = (len + 1) * sizeof(char);
char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
if (!buffer)
return JS_FALSE;
JS_EncodeStringToBuffer(str, buffer, len);
buffer[len] = '\0';
nsDependentCString string(buffer, len);
nsCAutoString result;
if (NS_FAILED(nsXPConnect::Base64Encode(string, result))) {
JS_ReportError(aCx, "Failed to encode base64 data!");
return JS_FALSE;
}
str = JS_NewStringCopyN(aCx, result.get(), result.Length());
if (!str) {
return JS_FALSE;
}
JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
return JS_TRUE;
} }
JSBool JSBool
@ -491,7 +423,6 @@ nsDOMWorkerFunctions::MakeNewWorker(JSContext* aCx,
NS_ASSERTION(worker, "This should be set by the DOM thread service!"); NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }
@ -585,7 +516,6 @@ nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx,
NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!"); NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!");
if (worker->IsCanceled()) { if (worker->IsCanceled()) {
JS_ReportError(aCx, "Worker is canceled");
return JS_FALSE; return JS_FALSE;
} }

Просмотреть файл

@ -1,7 +1,5 @@
var data = [ -1, 0, 1, 1.5, undefined, true, false ]; var data = [ -1, 0, 1, 1.5, null, undefined, true, false, "foo",
"123456789012345", "1234567890123456", "12345678901234567"];
// XXXbent window.atob treats |null| as the empty string, whereas worker.atob
// and the js component loader treat it as the string 'null'. Meh.
var str = ""; var str = "";
for (var i = 0; i < 30; i++) { for (var i = 0; i < 30; i++) {

Просмотреть файл

@ -1168,6 +1168,19 @@ private:
); );
#endif #endif
#elif WTF_COMPILER_SUNPRO #elif WTF_COMPILER_SUNPRO
#if WTF_CPU_X86_64
asm (
"movl $0x1, %%eax;"
"pushq %%rbx;"
"cpuid;"
"popq %%rbx;"
"movl %%ecx, (%rsi);"
"movl %%edx, (%rdi);"
:
: "S" (&flags_ecx), "D" (&flags_edx)
: "%eax", "%ecx", "%edx"
);
#else
asm ( asm (
"movl $0x1, %eax;" "movl $0x1, %eax;"
"pushl %ebx;" "pushl %ebx;"
@ -1179,6 +1192,7 @@ private:
: "S" (&flags_ecx), "D" (&flags_edx) : "S" (&flags_ecx), "D" (&flags_edx)
: "%eax", "%ecx", "%edx" : "%eax", "%ecx", "%edx"
); );
#endif
#endif #endif
static const int SSEFeatureBit = 1 << 25; static const int SSEFeatureBit = 1 << 25;
static const int SSE2FeatureBit = 1 << 26; static const int SSE2FeatureBit = 1 << 26;

Просмотреть файл

@ -986,7 +986,7 @@ else
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
fi fi
MOZ_PATH_PROGS(PYTHON, $PYTHON python2.6 python2.5 python2.4 python) MOZ_PATH_PROGS(PYTHON, $PYTHON python2.7 python2.6 python2.5 python)
if test -z "$PYTHON"; then if test -z "$PYTHON"; then
AC_MSG_ERROR([python was not found in \$PATH]) AC_MSG_ERROR([python was not found in \$PATH])
fi fi
@ -1900,13 +1900,13 @@ esac
dnl We require version 2.4 or newer of Python to build, dnl We require version 2.4 or newer of Python to build,
dnl and 2.5 or newer on Windows. dnl and 2.5 or newer on Windows.
AC_MSG_CHECKING([for minimum required Python version >= $PYTHON_VERSION]) AC_MSG_CHECKING([for Python version >= $PYTHON_VERSION but not 3.x])
changequote(,) changequote(,)
$PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1])" $PYTHON_VERSION $PYTHON -c "import sys; sys.exit(sys.version[:3] < sys.argv[1] or sys.version[:2] != '2.')" $PYTHON_VERSION
_python_res=$? _python_res=$?
changequote([,]) changequote([,])
if test "$_python_res" != 0; then if test "$_python_res" != 0; then
AC_MSG_ERROR([Python $PYTHON_VERSION or higher is required.]) AC_MSG_ERROR([Python $PYTHON_VERSION or higher (but not Python 3.x) is required.])
fi fi
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])

Просмотреть файл

@ -0,0 +1,12 @@
function s(f) { this._m = f; }
function C() {
Object.defineProperty(this, "m", {set: s});
this.m = function () {};
}
var last = {};
for (var i = 0; i < 20; i++) {
var a = new C;
assertEq(a._m === last._m, false);
last = a;
}

Просмотреть файл

@ -0,0 +1,9 @@
for(var i = 0; i < RUNLOOP; i++) {
x = ''.charCodeAt(NaN);
}
for(var i = 0; i < RUNLOOP; i++) {
x = ''.charAt(NaN);
}
// Don't assert

Просмотреть файл

@ -0,0 +1,5 @@
for (var i = 0; i < RUNLOOP; i++) {
Math.abs(-2147483648)
}
// Don't assert

Просмотреть файл

@ -0,0 +1,11 @@
{
function a() {}
}
Math.floor(Math.d)
function c() {}
c()
for each(let b in [0, 0, 0, 0, 0, 0, 0, -2147483648]) {
print(Math.abs(b))
}
// Don't assert

Просмотреть файл

@ -0,0 +1,5 @@
var x = Uint32Array();
for (var i = 0; i < 100; i++)
x[77] = x[77];
// Don't assert.

Просмотреть файл

@ -0,0 +1,10 @@
/* Bug 614653 - This test .2 seconds with the fix, 20 minutes without. */
for (var i = 0; i < 100; ++i) {
var arr = [];
var s = "abcdefghijklmnop";
for (var i = 0; i < 50000; ++i) {
s = "<" + s + ">";
arr.push(s);
}
gc();
}

Просмотреть файл

@ -95,36 +95,3 @@ BEGIN_TEST(testXDR_bug516827)
return true; return true;
} }
END_TEST(testXDR_bug516827) END_TEST(testXDR_bug516827)
BEGIN_TEST(testXDR_bug525481)
{
// get the empty script const singleton
JSScript *script = JSScript::emptyScript();
CHECK(script);
// freeze with junk after the empty script shorthand
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
const char s[] = "don't decode me; don't encode me";
char b[sizeof s - 1];
memcpy(b, s, sizeof b);
CHECK(JS_XDRBytes(w, b, sizeof b));
uint32 nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
void *frozen = JS_malloc(cx, nbytes);
CHECK(frozen);
memcpy(frozen, p, nbytes);
JS_XDRDestroy(w);
// thaw, reading junk if bug 525481 is not patched
script = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
JS_DestroyScript(cx, script);
JS_XDRDestroy(r); // this frees `frozen`
return true;
}
END_TEST(testXDR_bug525481)

Просмотреть файл

@ -4023,7 +4023,7 @@ JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
/* NB: jsuint cast does ToUint32. */ /* NB: jsuint cast does ToUint32. */
assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0)); assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0));
return js_NewArrayObject(cx, (jsuint)length, Valueify(vector)); return NewDenseCopiedArray(cx, (jsuint)length, Valueify(vector));
} }
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
@ -4672,7 +4672,6 @@ JS_NewScriptObject(JSContext *cx, JSScript *script)
* described in the comment for JSScript::u.object. * described in the comment for JSScript::u.object.
*/ */
JS_ASSERT(script->u.object); JS_ASSERT(script->u.object);
JS_ASSERT(script != JSScript::emptyScript());
return script->u.object; return script->u.object;
} }
@ -4889,7 +4888,7 @@ JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, script); assertSameCompartment(cx, obj, script);
/* This should receive only scripts handed out via the JSAPI. */ /* This should receive only scripts handed out via the JSAPI. */
JS_ASSERT(script == JSScript::emptyScript() || script->u.object); JS_ASSERT(script->u.object);
ok = Execute(cx, obj, script, NULL, 0, Valueify(rval)); ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
LAST_FRAME_CHECKS(cx, ok); LAST_FRAME_CHECKS(cx, ok);
return ok; return ok;
@ -5166,32 +5165,6 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
} }
/************************************************************************/ /************************************************************************/
JS_PUBLIC_API(JSString *)
JS_NewString(JSContext *cx, char *bytes, size_t nbytes)
{
size_t length = nbytes;
jschar *chars;
JSString *str;
CHECK_REQUEST(cx);
/* Make a UTF-16 vector from the 8-bit char codes in bytes. */
chars = js_InflateString(cx, bytes, &length);
if (!chars)
return NULL;
/* Free chars (but not bytes, which caller frees on error) if we fail. */
str = js_NewString(cx, chars, length);
if (!str) {
cx->free(chars);
return NULL;
}
js_free(bytes);
return str;
}
JS_PUBLIC_API(JSString *) JS_PUBLIC_API(JSString *)
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
{ {

Просмотреть файл

@ -1335,63 +1335,23 @@ namespace js {
* than that, we have avoided all garbage collection hazards. * than that, we have avoided all garbage collection hazards.
*/ */
template<typename T> class AnchorPermitted; template<typename T> class AnchorPermitted;
template<> class AnchorPermitted<JSObject *> { };
template<> class AnchorPermitted<const JSObject *> { };
template<> class AnchorPermitted<JSFunction *> { };
template<> class AnchorPermitted<const JSFunction *> { };
template<> class AnchorPermitted<JSString *> { };
template<> class AnchorPermitted<const JSString *> { };
template<> class AnchorPermitted<jsval> { };
template<typename T> template<typename T>
class Anchor: AnchorPermitted<T> { class Anchor: AnchorPermitted<T> {
public: public:
Anchor() { } Anchor() { }
explicit Anchor(T t) { hold = t; } explicit Anchor(T t) { hold = t; }
~Anchor() { inline ~Anchor();
#ifdef __GNUC__ T &get() { return hold; }
/* void set(const T &t) { hold = t; }
* No code is generated for this. But because this is marked 'volatile', G++ will void clear() { hold = 0; }
* assume it has important side-effects, and won't delete it. (G++ never looks at
* the actual text and notices it's empty.) And because we have passed |hold| to
* it, GCC will keep |hold| alive until this point.
*
* The "memory" clobber operand ensures that G++ will not move prior memory
* accesses after the asm --- it's a barrier. Unfortunately, it also means that
* G++ will assume that all memory has changed after the asm, as it would for a
* call to an unknown function. I don't know of a way to avoid that consequence.
*/
asm volatile("":: "g" (hold) : "memory");
#else
/*
* An adequate portable substitute.
*
* The compiler promises that, by the end of an expression statement, the
* last-stored value to a volatile object is the same as it would be in an
* unoptimized, direct implementation (the "abstract machine" whose behavior the
* language spec describes). However, the compiler is still free to reorder
* non-volatile accesses across this store --- which is what we must prevent. So
* assigning the held value to a volatile variable, as we do here, is not enough.
*
* In our case, however, garbage collection only occurs at function calls, so it
* is sufficient to ensure that the destructor's store isn't moved earlier across
* any function calls that could collect. It is hard to imagine the compiler
* analyzing the program so thoroughly that it could prove that such motion was
* safe. In practice, compilers treat calls to the collector as opaque operations
* --- in particular, as operations which could access volatile variables, across
* which this destructor must not be moved.
*
* ("Objection, your honor! *Alleged* killer whale!")
*
* The disadvantage of this approach is that it does generate code for the store.
* We do need to use Anchors in some cases where cycles are tight.
*/
volatile T sink;
#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
/*
* Can't just do a simple assignment here.
*/
doAssignment(sink, hold);
#else
sink = hold;
#endif
#endif
}
T &get() { return hold; }
void set(T t) { hold = t; }
void clear() { hold = 0; }
private: private:
T hold; T hold;
/* Anchors should not be assigned or passed to functions. */ /* Anchors should not be assigned or passed to functions. */
@ -1399,44 +1359,71 @@ class Anchor: AnchorPermitted<T> {
const Anchor &operator=(const Anchor &); const Anchor &operator=(const Anchor &);
}; };
#ifdef __GNUC__
template<typename T>
inline Anchor<T>::~Anchor() {
/*
* No code is generated for this. But because this is marked 'volatile', G++ will
* assume it has important side-effects, and won't delete it. (G++ never looks at
* the actual text and notices it's empty.) And because we have passed |hold| to
* it, GCC will keep |hold| alive until this point.
*
* The "memory" clobber operand ensures that G++ will not move prior memory
* accesses after the asm --- it's a barrier. Unfortunately, it also means that
* G++ will assume that all memory has changed after the asm, as it would for a
* call to an unknown function. I don't know of a way to avoid that consequence.
*/
asm volatile("":: "g" (hold) : "memory");
}
#else
template<typename T>
inline Anchor<T>::~Anchor() {
/*
* An adequate portable substitute, for non-structure types.
*
* The compiler promises that, by the end of an expression statement, the
* last-stored value to a volatile object is the same as it would be in an
* unoptimized, direct implementation (the "abstract machine" whose behavior the
* language spec describes). However, the compiler is still free to reorder
* non-volatile accesses across this store --- which is what we must prevent. So
* assigning the held value to a volatile variable, as we do here, is not enough.
*
* In our case, however, garbage collection only occurs at function calls, so it
* is sufficient to ensure that the destructor's store isn't moved earlier across
* any function calls that could collect. It is hard to imagine the compiler
* analyzing the program so thoroughly that it could prove that such motion was
* safe. In practice, compilers treat calls to the collector as opaque operations
* --- in particular, as operations which could access volatile variables, across
* which this destructor must not be moved.
*
* ("Objection, your honor! *Alleged* killer whale!")
*
* The disadvantage of this approach is that it does generate code for the store.
* We do need to use Anchors in some cases where cycles are tight.
*/
volatile T sink;
sink = hold;
}
#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
/* /*
* Ensure that attempts to create Anchors for types the garbage collector's conservative * The default assignment operator for |struct C| has the signature:
* scanner doesn't actually recgonize fail. Such anchors would have no effect. *
* C& C::operator=(const C&)
*
* And in particular requires implicit conversion of |this| to type |C| for the return
* value. But |volatile C| cannot thus be converted to |C|, so just doing |sink = hold| as
* in the non-specialized version would fail to compile. Do the assignment on asBits
* instead, since I don't think we want to give jsval_layout an assignment operator
* returning |volatile jsval_layout|.
*/ */
class Anchor_base { template<>
protected: inline Anchor<jsval>::~Anchor() {
#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES volatile jsval sink;
template<typename T> void doAssignment(volatile T &lhs, const T &rhs) { sink.asBits = hold.asBits;
lhs = rhs; }
}
#endif #endif
};
template<> class AnchorPermitted<JSObject *> : protected Anchor_base { };
template<> class AnchorPermitted<const JSObject *> : protected Anchor_base { };
template<> class AnchorPermitted<JSFunction *> : protected Anchor_base { };
template<> class AnchorPermitted<const JSFunction *> : protected Anchor_base { };
template<> class AnchorPermitted<JSString *> : protected Anchor_base { };
template<> class AnchorPermitted<const JSString *> : protected Anchor_base { };
template<> class AnchorPermitted<jsval> : protected Anchor_base {
protected:
#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
void doAssignment(volatile jsval &lhs, const jsval &rhs) {
/*
* The default assignment operator for |struct C| has the signature:
*
* C& C::operator=(const C&)
*
* And in particular requires implicit conversion of |this| to
* type |C| for the return value. But |volatile C| cannot
* thus be converted to |C|, so just doing |sink = hold| here
* would fail to compile. Do the assignment on asBits
* instead, since I don't think we want to give jsval_layout
* an assignment operator returning |volatile jsval_layout|.
*/
lhs.asBits = rhs.asBits;
}
#endif #endif
};
} /* namespace js */ } /* namespace js */
@ -2932,15 +2919,12 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
/* /*
* Strings. * Strings.
* *
* NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
* on error (signified by null return), it leaves bytes owned by the caller. * but on error (signified by null return), it leaves chars owned by the
* So the caller must free bytes in the error case, if it has no use for them. * caller. So the caller must free bytes in the error case, if it has no use
* In contrast, all the JS_New*StringCopy* functions do not take ownership of * for them. In contrast, all the JS_New*StringCopy* functions do not take
* the character memory passed to them -- they copy it. * ownership of the character memory passed to them -- they copy it.
*/ */
extern JS_PUBLIC_API(JSString *)
JS_NewString(JSContext *cx, char *bytes, size_t length);
extern JS_PUBLIC_API(JSString *) extern JS_PUBLIC_API(JSString *)
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); JS_NewStringCopyN(JSContext *cx, const char *s, size_t n);

Просмотреть файл

@ -52,13 +52,15 @@
* - The number of element slots (capacity), gettable with * - The number of element slots (capacity), gettable with
* getDenseArrayCapacity(). * getDenseArrayCapacity().
* *
* In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid * In dense mode, holes in the array are represented by
* values. The final slot in fslots is unused. * MagicValue(JS_ARRAY_HOLE) invalid values.
* *
* NB: the capacity and length of a dense array are entirely unrelated! The * NB: the capacity and length of a dense array are entirely unrelated! The
* length may be greater than, less than, or equal to the capacity. See * length may be greater than, less than, or equal to the capacity. The first
* array_length_setter for an explanation of how the first, most surprising * case may occur when the user writes "new Array(100), in which case the
* case may occur. * length is 100 while the capacity remains 0 (indices below length and above
* capaicty must be treated as holes). See array_length_setter for another
* explanation of how the first case may occur.
* *
* Arrays are converted to use js_SlowArrayClass when any of these conditions * Arrays are converted to use js_SlowArrayClass when any of these conditions
* are met: * are met:
@ -170,14 +172,16 @@ js_StringIsIndex(JSString *str, jsuint *indexp)
return JS_FALSE; return JS_FALSE;
} }
static jsuint static bool
ValueIsLength(JSContext *cx, Value* vp) ValueToLength(JSContext *cx, Value* vp, jsuint* plength)
{ {
if (vp->isInt32()) { if (vp->isInt32()) {
int32_t i = vp->toInt32(); int32_t i = vp->toInt32();
if (i < 0) if (i < 0)
goto error; goto error;
return (jsuint) i;
*plength = (jsuint)(i);
return true;
} }
jsdouble d; jsdouble d;
@ -186,18 +190,20 @@ ValueIsLength(JSContext *cx, Value* vp)
if (JSDOUBLE_IS_NaN(d)) if (JSDOUBLE_IS_NaN(d))
goto error; goto error;
jsuint length; jsuint length;
length = (jsuint) d; length = (jsuint) d;
if (d != (jsdouble) length) if (d != (jsdouble) length)
goto error; goto error;
vp->setNumber(length);
return length;
error:
*plength = length;
return true;
error:
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_ARRAY_LENGTH); JSMSG_BAD_ARRAY_LENGTH);
vp->setNull(); return false;
return 0;
} }
JSBool JSBool
@ -557,8 +563,10 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
if (!ok) if (!ok)
return false; return false;
*lengthp = ValueIsLength(cx, tvr.addr()); if (!ValueToLength(cx, tvr.addr(), lengthp))
return !tvr.value().isNull(); return false;
return true;
} }
/* /*
@ -581,27 +589,20 @@ array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
} }
static JSBool static JSBool
array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict) array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
jsuint newlen, oldlen, gap, index; jsuint newlen, oldlen, gap, index;
Value junk; Value junk;
/* Check for a sealed object first. */
if (!obj->isExtensible()) {
return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_READ_ONLY,
JSDVG_IGNORE_STACK, IdToValue(id), NULL,
NULL, NULL);
}
if (!obj->isArray()) { if (!obj->isArray()) {
jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE); return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE);
} }
newlen = ValueIsLength(cx, vp); if (!ValueToLength(cx, vp, &newlen))
if (vp->isNull())
return false; return false;
oldlen = obj->getArrayLength(); oldlen = obj->getArrayLength();
if (oldlen == newlen) if (oldlen == newlen)
@ -633,8 +634,6 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str
} }
if (!DeleteArrayElement(cx, obj, oldlen, true)) { if (!DeleteArrayElement(cx, obj, oldlen, true)) {
obj->setArrayLength(oldlen + 1); obj->setArrayLength(oldlen + 1);
if (strict)
return false;
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
return true; return true;
} }
@ -798,7 +797,7 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric
uint32 i; uint32 i;
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
return array_length_setter(cx, obj, id, vp, strict); return array_length_setter(cx, obj, id, vp);
if (!obj->isDenseArray()) if (!obj->isDenseArray())
return js_SetProperty(cx, obj, id, vp, strict); return js_SetProperty(cx, obj, id, vp, strict);
@ -828,17 +827,6 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric
return js_SetProperty(cx, obj, id, vp, strict); return js_SetProperty(cx, obj, id, vp, strict);
} }
static JSBool
slowarray_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
{
JS_ASSERT(obj->isSlowArray());
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
return array_length_setter(cx, obj, id, vp, strict);
return js_SetProperty(cx, obj, id, vp, strict);
}
JSBool JSBool
js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj) js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
{ {
@ -996,34 +984,7 @@ Class js_SlowArrayClass = {
PropertyStub, /* setProperty */ PropertyStub, /* setProperty */
EnumerateStub, EnumerateStub,
ResolveStub, ResolveStub,
js_TryValueOf, js_TryValueOf
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
NULL, /* mark */
JS_NULL_CLASS_EXT,
{
NULL, /* lookupProperty */
NULL, /* defineProperty */
NULL, /* getProperty */
/*
* For assignments to 'length', we need to know the setter's strictness. A property's
* setter isn't passed that, but the ObjectOps member is, so use that.
*/
slowarray_setProperty,
NULL, /* getAttributes */
NULL, /* setAttributes */
NULL, /* deleteProperty */
NULL, /* enumerate */
NULL, /* typeOf */
NULL, /* trace */
NULL, /* thisObject */
NULL, /* clear */
}
}; };
/* /*
@ -1053,7 +1014,7 @@ JSObject::makeDenseArraySlow(JSContext *cx)
* The getter/setter here will directly access the object's private value. * The getter/setter here will directly access the object's private value.
*/ */
if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
array_length_getter, NULL, array_length_getter, array_length_setter,
SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) { SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) {
setMap(oldMap); setMap(oldMap);
return false; return false;
@ -2285,7 +2246,7 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
* arguments. We think this is best because it eliminates the need * arguments. We think this is best because it eliminates the need
* for callers to do an extra test to handle the empty splice case. * for callers to do an extra test to handle the empty splice case.
*/ */
JSObject *obj2 = js_NewArrayObject(cx, 0, NULL); JSObject *obj2 = NewDenseEmptyArray(cx);
if (!obj2) if (!obj2)
return JS_FALSE; return JS_FALSE;
vp->setObject(*obj2); vp->setObject(*obj2);
@ -2339,7 +2300,6 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
/* If there are elements to remove, put them into the return value. */ /* If there are elements to remove, put them into the return value. */
if (count > 0) { if (count > 0) {
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) && if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
!js_PrototypeHasIndexedProperties(cx, obj2) &&
end <= obj->getDenseArrayCapacity()) { end <= obj->getDenseArrayCapacity()) {
if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin)) if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
return JS_FALSE; return JS_FALSE;
@ -2459,7 +2419,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
*/ */
length = aobj->getArrayLength(); length = aobj->getArrayLength();
jsuint capacity = aobj->getDenseArrayCapacity(); jsuint capacity = aobj->getDenseArrayCapacity();
nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements()); nobj = NewDenseCopiedArray(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
nobj->setArrayLength(length); nobj->setArrayLength(length);
@ -2469,7 +2429,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
argc--; argc--;
p++; p++;
} else { } else {
nobj = js_NewArrayObject(cx, 0, NULL); nobj = NewDenseEmptyArray(cx);
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
vp->setObject(*nobj); vp->setObject(*nobj);
@ -2490,8 +2450,8 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
if (!aobj->getProperty(cx, id, tvr.addr())) if (!aobj->getProperty(cx, id, tvr.addr()))
return false; return false;
jsuint alength = ValueIsLength(cx, tvr.addr()); jsuint alength;
if (tvr.value().isNull()) if (!ValueToLength(cx, tvr.addr(), &alength))
return false; return false;
for (jsuint slot = 0; slot < alength; slot++) { for (jsuint slot = 0; slot < alength; slot++) {
JSBool hole; JSBool hole;
@ -2572,7 +2532,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() && if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
!js_PrototypeHasIndexedProperties(cx, obj)) { !js_PrototypeHasIndexedProperties(cx, obj)) {
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin); nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
vp->setObject(*nobj); vp->setObject(*nobj);
@ -2580,7 +2540,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
} }
/* Create a new Array object and root it using *vp. */ /* Create a new Array object and root it using *vp. */
nobj = js_NewArrayObject(cx, 0, NULL); nobj = NewDenseAllocatedArray(cx, end - begin);
if (!nobj) if (!nobj)
return JS_FALSE; return JS_FALSE;
vp->setObject(*nobj); vp->setObject(*nobj);
@ -2595,7 +2555,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE; return JS_FALSE;
} }
return js_SetLengthProperty(cx, nobj, end - begin); return JS_TRUE;
} }
#if JS_HAS_ARRAY_EXTRAS #if JS_HAS_ARRAY_EXTRAS
@ -2757,7 +2717,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
case MAP: case MAP:
case FILTER: case FILTER:
newlen = (mode == MAP) ? length : 0; newlen = (mode == MAP) ? length : 0;
newarr = js_NewArrayObject(cx, newlen, NULL); newarr = NewDenseAllocatedArray(cx, newlen);
if (!newarr) if (!newarr)
return JS_FALSE; return JS_FALSE;
vp->setObject(*newarr); vp->setObject(*newarr);
@ -2964,86 +2924,31 @@ static JSFunctionSpec array_static_methods[] = {
JS_FS_END JS_FS_END
}; };
/* The count here is a guess for the final capacity. */
static inline JSObject *
NewDenseArrayObject(JSContext *cx, jsuint count)
{
gc::FinalizeKind kind = GuessObjectGCKind(count, true);
return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
}
JSBool JSBool
js_Array(JSContext *cx, uintN argc, Value *vp) js_Array(JSContext *cx, uintN argc, Value *vp)
{ {
jsuint length; JSObject *obj;
const Value *vector;
if (argc == 0) { if (argc == 0) {
length = 0; obj = NewDenseEmptyArray(cx);
vector = NULL;
} else if (argc > 1) { } else if (argc > 1) {
length = (jsuint) argc; obj = NewDenseCopiedArray(cx, argc, vp + 2);
vector = vp + 2;
} else if (!vp[2].isNumber()) { } else if (!vp[2].isNumber()) {
length = 1; obj = NewDenseCopiedArray(cx, 1, vp + 2);
vector = vp + 2;
} else { } else {
length = ValueIsLength(cx, vp + 2); jsuint length;
if (vp[2].isNull()) if (!ValueToLength(cx, vp + 2, &length))
return JS_FALSE; return JS_FALSE;
vector = NULL; obj = NewDenseUnallocatedArray(cx, length);
} }
/* Whether called with 'new' or not, use a new Array object. */
JSObject *obj = NewDenseArrayObject(cx, length);
if (!obj) if (!obj)
return JS_FALSE; return JS_FALSE;
vp->setObject(*obj); vp->setObject(*obj);
return InitArrayObject(cx, obj, length, vector); return JS_TRUE;
} }
JSObject* JS_FASTCALL
js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len)
{
if (len < 0)
return NULL;
JS_ASSERT(proto->isArray());
gc::FinalizeKind kind = GuessObjectGCKind(len, true);
JSObject* obj = js_NewGCObject(cx, kind);
if (!obj)
return NULL;
/* Initialize all fields of JSObject. */
obj->init(cx, &js_ArrayClass, proto, proto->getParent(),
(void*) len, true);
obj->setSharedNonNativeMap();
return obj;
}
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, INT32, 0,
nanojit::ACCSET_STORE_ANY)
#endif
JSObject* JS_FASTCALL
js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len)
{
JSObject *obj = js_NewEmptyArray(cx, proto, len);
if (!obj)
return NULL;
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
if (!obj->ensureSlots(cx, len))
return NULL;
return obj;
}
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, INT32,
0, nanojit::ACCSET_STORE_ANY)
#endif
JSObject * JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj) js_InitArrayClass(JSContext *cx, JSObject *obj)
{ {
@ -3062,31 +2967,83 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
return proto; return proto;
} }
JSObject * /*
js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector) * Array allocation functions.
*/
namespace js {
template<bool allocateCapacity>
static JS_ALWAYS_INLINE JSObject *
NewArray(JSContext *cx, jsuint length, JSObject *proto)
{ {
JSObject *obj = NewDenseArrayObject(cx, length); JS_ASSERT_IF(proto, proto->isArray());
gc::FinalizeKind kind = GuessObjectGCKind(length, true);
JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
obj->setArrayLength(length);
if (allocateCapacity && !obj->ensureSlots(cx, length))
return NULL;
return obj;
}
JSObject * JS_FASTCALL
NewDenseEmptyArray(JSContext *cx, JSObject *proto)
{
return NewArray<false>(cx, 0, proto);
}
JSObject * JS_FASTCALL
NewDenseAllocatedArray(JSContext *cx, uint32 length, JSObject *proto)
{
return NewArray<true>(cx, length, proto);
}
JSObject * JS_FASTCALL
NewDenseUnallocatedArray(JSContext *cx, uint32 length, JSObject *proto)
{
return NewArray<false>(cx, length, proto);
}
JSObject *
NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
{
JSObject* obj = NewArray<true>(cx, length, proto);
JS_ASSERT(obj->getDenseArrayCapacity() >= length);
if (vp)
memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value));
return obj;
}
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_2(extern, OBJECT, NewDenseEmptyArray, CONTEXT, OBJECT, 0,
nanojit::ACCSET_STORE_ANY)
JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseAllocatedArray, CONTEXT, UINT32, OBJECT, 0,
nanojit::ACCSET_STORE_ANY)
JS_DEFINE_CALLINFO_3(extern, OBJECT, NewDenseUnallocatedArray, CONTEXT, UINT32, OBJECT, 0,
nanojit::ACCSET_STORE_ANY)
#endif
JSObject *
NewSlowEmptyArray(JSContext *cx)
{
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL);
if (!obj) if (!obj)
return NULL; return NULL;
/* obj->setArrayLength(0);
* If this fails, the global object was not initialized and its class does
* not have JSCLASS_IS_GLOBAL.
*/
JS_ASSERT(obj->getProto());
return InitArrayObject(cx, obj, length, vector) ? obj : NULL;
}
JSObject *
js_NewSlowArrayObject(JSContext *cx)
{
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_SlowArrayClass, NULL, NULL);
if (obj)
obj->setArrayLength(0);
return obj; return obj;
} }
}
#ifdef DEBUG #ifdef DEBUG
JSBool JSBool
js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp) js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
@ -3241,10 +3198,11 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
vector.append(val); vector.append(val);
} }
*clone = js_NewArrayObject(cx, jsvalCount, vector.begin()); *clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin());
if (!*clone) if (!*clone)
return JS_FALSE; return JS_FALSE;
(*clone)->setArrayLength(length);
/* The length will be set to the JS_MIN, above, but length might be larger. */
(*clone)->setArrayLength(length);
return JS_TRUE; return JS_TRUE;
} }

Просмотреть файл

@ -177,12 +177,33 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
extern bool extern bool
js_InitContextBusyArrayTable(JSContext *cx); js_InitContextBusyArrayTable(JSContext *cx);
extern JSObject * namespace js
js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector); {
/* Create an array object that starts out already made slow/sparse. */ /* Create a dense array with no capacity allocated, length set to 0. */
extern JSObject * JS_FASTCALL
NewDenseEmptyArray(JSContext *cx, JSObject *proto=NULL);
/* Create a dense array with length and capacity == 'length'. */
extern JSObject * JS_FASTCALL
NewDenseAllocatedArray(JSContext *cx, uint length, JSObject *proto=NULL);
/*
* Create a dense array with a set length, but without allocating space for the
* contents. This is useful, e.g., when accepting length from the user.
*/
extern JSObject * JS_FASTCALL
NewDenseUnallocatedArray(JSContext *cx, uint length, JSObject *proto=NULL);
/* Create a dense array with a copy of vp. */
extern JSObject * extern JSObject *
js_NewSlowArrayObject(JSContext *cx); NewDenseCopiedArray(JSContext *cx, uint length, Value *vp, JSObject *proto=NULL);
/* Create a sparse array. */
extern JSObject *
NewSlowEmptyArray(JSContext *cx);
}
extern JSBool extern JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
@ -283,25 +304,6 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
JSBool JSBool
js_Array(JSContext *cx, uintN argc, js::Value *vp); js_Array(JSContext *cx, uintN argc, js::Value *vp);
/*
* Friend api function that allows direct creation of an array object with a
* given capacity. Non-null return value means allocation of the internal
* buffer for a capacity of at least |capacity| succeeded. A pointer to the
* first element of this internal buffer is returned in the |vector| out
* parameter. The caller promises to fill in the first |capacity| values
* starting from that pointer immediately after this function returns and
* without triggering GC (so this method is allowed to leave those
* uninitialized) and to set them to non-JS_ARRAY_HOLE-magic-why values, so
* that the resulting array has length and count both equal to |capacity|.
*
* FIXME: for some strange reason, when this file is included from
* dom/ipc/TabParent.cpp in MSVC, jsuint resolves to a slightly different
* builtin than when mozjs.dll is built, resulting in a link error in xul.dll.
* It would be useful to find out what is causing this insanity.
*/
JS_FRIEND_API(JSObject *)
js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector);
/* /*
* Makes a fast clone of a dense array as long as the array only contains * Makes a fast clone of a dense array as long as the array only contains
* primitive values. * primitive values.

Просмотреть файл

@ -575,8 +575,11 @@ js_dmod(jsdouble a, jsdouble b);
#endif /* !JS_TRACER */ #endif /* !JS_TRACER */
/* Defined in jsarray.cpp. */ /* Defined in jsarray.cpp. */
JS_DECLARE_CALLINFO(js_NewEmptyArray) namespace js {
JS_DECLARE_CALLINFO(js_NewPreallocatedArray) JS_DECLARE_CALLINFO(NewDenseEmptyArray)
JS_DECLARE_CALLINFO(NewDenseAllocatedArray)
JS_DECLARE_CALLINFO(NewDenseUnallocatedArray)
}
JS_DECLARE_CALLINFO(js_ArrayCompPush_tn) JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity) JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity)

Просмотреть файл

@ -763,7 +763,7 @@ JSStructuredCloneReader::startRead(Value *vp)
case SCTAG_ARRAY_OBJECT: case SCTAG_ARRAY_OBJECT:
case SCTAG_OBJECT_OBJECT: { case SCTAG_OBJECT_OBJECT: {
JSObject *obj = (tag == SCTAG_ARRAY_OBJECT) JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
? js_NewArrayObject(context(), 0, NULL) ? NewDenseEmptyArray(context())
: NewBuiltinClassInstance(context(), &js_ObjectClass); : NewBuiltinClassInstance(context(), &js_ObjectClass);
if (!obj || !objs.append(ObjectValue(*obj))) if (!obj || !objs.append(ObjectValue(*obj)))
return false; return false;

Просмотреть файл

@ -591,7 +591,7 @@ class CompartmentChecker
} }
void check(JSScript *script) { void check(JSScript *script) {
if (script && script != JSScript::emptyScript()) { if (script) {
check(script->compartment); check(script->compartment);
if (script->u.object) if (script->u.object)
check(script->u.object); check(script->u.object);

Просмотреть файл

@ -265,12 +265,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
if (!CheckDebugMode(cx)) if (!CheckDebugMode(cx))
return JS_FALSE; return JS_FALSE;
if (script == JSScript::emptyScript()) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
NULL, JSMSG_READ_ONLY, "empty script");
return JS_FALSE;
}
JS_ASSERT((JSOp) *pc != JSOP_TRAP); JS_ASSERT((JSOp) *pc != JSOP_TRAP);
junk = NULL; junk = NULL;
rt = cx->runtime; rt = cx->runtime;
@ -1761,7 +1755,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
continue; continue;
nbytes += (sn - notes + 1) * sizeof *sn; nbytes += (sn - notes + 1) * sizeof *sn;
if (script->objectsOffset != 0) { if (JSScript::isValidOffset(script->objectsOffset)) {
objarray = script->objects(); objarray = script->objects();
i = objarray->length; i = objarray->length;
nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
@ -1770,7 +1764,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
} while (i != 0); } while (i != 0);
} }
if (script->regexpsOffset != 0) { if (JSScript::isValidOffset(script->regexpsOffset)) {
objarray = script->regexps(); objarray = script->regexps();
i = objarray->length; i = objarray->length;
nbytes += sizeof *objarray + i * sizeof objarray->vector[0]; nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
@ -1779,7 +1773,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
} while (i != 0); } while (i != 0);
} }
if (script->trynotesOffset != 0) { if (JSScript::isValidOffset(script->trynotesOffset)) {
nbytes += sizeof(JSTryNoteArray) + nbytes += sizeof(JSTryNoteArray) +
script->trynotes()->length * sizeof(JSTryNote); script->trynotes()->length * sizeof(JSTryNote);
} }

Просмотреть файл

@ -1737,7 +1737,7 @@ LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
break; break;
} }
} }
} while ((cg = (JSCodeGenerator *) cg->parent) != NULL); } while (cg->parent && (cg = cg->parent->asCodeGenerator()));
return JS_TRUE; return JS_TRUE;
} }
@ -2161,13 +2161,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* *
* Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
* bindings visible to the compiler are permanent in JS unless the * bindings visible to the compiler are permanent in JS unless the
* declaration originates in eval code. We detect eval code by testing * declaration originates at top level in eval code.
* cg->parser->callerFrame, which is set only by eval or a debugger
* equivalent.
*
* Note that this callerFrame non-null test must be qualified by testing
* !cg->funbox to exclude function code nested in eval code, which is not
* subject to the deletable binding exception.
*/ */
switch (op) { switch (op) {
case JSOP_NAME: case JSOP_NAME:
@ -2175,7 +2169,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
break; break;
case JSOP_DELNAME: case JSOP_DELNAME:
if (dn_kind != JSDefinition::UNKNOWN) { if (dn_kind != JSDefinition::UNKNOWN) {
if (cg->parser->callerFrame && !cg->funbox) if (cg->parser->callerFrame && dn->isTopLevel())
JS_ASSERT(cg->compileAndGo()); JS_ASSERT(cg->compileAndGo());
else else
pn->pn_op = JSOP_FALSE; pn->pn_op = JSOP_FALSE;
@ -2353,9 +2347,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JSTreeContext *tc = cg; JSTreeContext *tc = cg;
while (tc->staticLevel != level) while (tc->staticLevel != level)
tc = tc->parent; tc = tc->parent;
JS_ASSERT(tc->compiling());
JSCodeGenerator *evalcg = (JSCodeGenerator *) tc; JSCodeGenerator *evalcg = tc->asCodeGenerator();
JS_ASSERT(evalcg->compileAndGo()); JS_ASSERT(evalcg->compileAndGo());
JS_ASSERT(caller->isFunctionFrame()); JS_ASSERT(caller->isFunctionFrame());
JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain()); JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
@ -2433,10 +2426,9 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(index == cg->upvarList.count - 1); JS_ASSERT(index == cg->upvarList.count - 1);
UpvarCookie *vector = cg->upvarMap.vector; UpvarCookie *vector = cg->upvarMap.vector;
if (!vector) { uint32 length = cg->lexdeps.count;
uint32 length = cg->lexdeps.count; if (!vector || cg->upvarMap.length != length) {
vector = (UpvarCookie *) js_realloc(vector, length * sizeof *vector);
vector = (UpvarCookie *) js_calloc(length * sizeof *vector);
if (!vector) { if (!vector) {
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
return JS_FALSE; return JS_FALSE;
@ -2455,6 +2447,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
slot += tc->fun()->nargs; slot += tc->fun()->nargs;
} }
JS_ASSERT(index < cg->upvarMap.length);
vector[index].set(skip, slot); vector[index].set(skip, slot);
} }
@ -4601,11 +4594,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
break; break;
} }
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
pn->pn_defn ||
(!pn->pn_used && !pn->isTopLevel()) ||
(fun->flags & JSFUN_LAMBDA));
JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT, JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
FUN_KIND(fun) == JSFUN_INTERPRETED); FUN_KIND(fun) == JSFUN_INTERPRETED);
@ -4625,7 +4613,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!cg2->init()) if (!cg2->init())
return JS_FALSE; return JS_FALSE;
cg2->flags = pn->pn_funbox->tcflags | TCF_IN_FUNCTION; cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION;
#if JS_HAS_SHARP_VARS #if JS_HAS_SHARP_VARS
if (cg2->flags & TCF_HAS_SHARPS) { if (cg2->flags & TCF_HAS_SHARPS) {
cg2->sharpSlotBase = fun->sharpSlotBase(cx); cg2->sharpSlotBase = fun->sharpSlotBase(cx);

Просмотреть файл

@ -364,8 +364,16 @@ struct JSTreeContext { /* tree context for semantic checks */
JSObject *blockChain() { JSObject *blockChain() {
return blockChainBox ? blockChainBox->object : NULL; return blockChainBox ? blockChainBox->object : NULL;
} }
bool atTopLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); } /*
* True if we are at the topmost level of a entire script or function body.
* For example, while parsing this code we would encounter f1 and f2 at
* body level, but we would not encounter f3 or f4 at body level:
*
* function f1() { function f2() { } }
* if (cond) { function f3() { if (cond) { function f4() { } } } }
*/
bool atBodyLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); }
/* Test whether we're in a statement of given type. */ /* Test whether we're in a statement of given type. */
bool inStatement(JSStmtType type); bool inStatement(JSStmtType type);
@ -392,7 +400,9 @@ struct JSTreeContext { /* tree context for semantic checks */
bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; } bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; }
bool inFunction() const { return flags & TCF_IN_FUNCTION; } bool inFunction() const { return flags & TCF_IN_FUNCTION; }
bool compiling() const { return flags & TCF_COMPILING; } bool compiling() const { return flags & TCF_COMPILING; }
inline JSCodeGenerator *asCodeGenerator();
bool usesArguments() const { bool usesArguments() const {
return flags & TCF_FUN_USES_ARGUMENTS; return flags & TCF_FUN_USES_ARGUMENTS;
@ -594,7 +604,7 @@ struct JSCodeGenerator : public JSTreeContext
SlotVector closedVars; SlotVector closedVars;
uint16 traceIndex; /* index for the next JSOP_TRACE instruction */ uint16 traceIndex; /* index for the next JSOP_TRACE instruction */
/* /*
* Initialize cg to allocate bytecode space from codePool, source note * Initialize cg to allocate bytecode space from codePool, source note
* space from notePool, and all other arena-allocated temporaries from * space from notePool, and all other arena-allocated temporaries from
@ -668,6 +678,13 @@ struct JSCodeGenerator : public JSTreeContext
#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) #define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main)
#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) #define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog)
inline JSCodeGenerator *
JSTreeContext::asCodeGenerator()
{
JS_ASSERT(compiling());
return static_cast<JSCodeGenerator *>(this);
}
/* /*
* Emit one bytecode. * Emit one bytecode.
*/ */

Просмотреть файл

@ -389,20 +389,20 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
/* NB: GC must not occur before wscript is homed in wfun->u.i.script. */ /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes, JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes,
script->atomMap.length, script->atomMap.length,
(script->objectsOffset != 0) JSScript::isValidOffset(script->objectsOffset)
? script->objects()->length ? script->objects()->length
: 0, : 0,
fun->u.i.nupvars, fun->u.i.nupvars,
(script->regexpsOffset != 0) JSScript::isValidOffset(script->regexpsOffset)
? script->regexps()->length ? script->regexps()->length
: 0, : 0,
(script->trynotesOffset != 0) JSScript::isValidOffset(script->trynotesOffset)
? script->trynotes()->length ? script->trynotes()->length
: 0, : 0,
(script->constOffset != 0) JSScript::isValidOffset(script->constOffset)
? script->consts()->length ? script->consts()->length
: 0, : 0,
(script->globalsOffset != 0) JSScript::isValidOffset(script->globalsOffset)
? script->globals()->length ? script->globals()->length
: 0, : 0,
script->nClosedArgs, script->nClosedArgs,
@ -416,19 +416,19 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote)); memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
memcpy(wscript->atomMap.vector, script->atomMap.vector, memcpy(wscript->atomMap.vector, script->atomMap.vector,
wscript->atomMap.length * sizeof(JSAtom *)); wscript->atomMap.length * sizeof(JSAtom *));
if (script->objectsOffset != 0) { if (JSScript::isValidOffset(script->objectsOffset)) {
memcpy(wscript->objects()->vector, script->objects()->vector, memcpy(wscript->objects()->vector, script->objects()->vector,
wscript->objects()->length * sizeof(JSObject *)); wscript->objects()->length * sizeof(JSObject *));
} }
if (script->regexpsOffset != 0) { if (JSScript::isValidOffset(script->regexpsOffset)) {
memcpy(wscript->regexps()->vector, script->regexps()->vector, memcpy(wscript->regexps()->vector, script->regexps()->vector,
wscript->regexps()->length * sizeof(JSObject *)); wscript->regexps()->length * sizeof(JSObject *));
} }
if (script->trynotesOffset != 0) { if (JSScript::isValidOffset(script->trynotesOffset)) {
memcpy(wscript->trynotes()->vector, script->trynotes()->vector, memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
wscript->trynotes()->length * sizeof(JSTryNote)); wscript->trynotes()->length * sizeof(JSTryNote));
} }
if (script->globalsOffset != 0) { if (JSScript::isValidOffset(script->globalsOffset)) {
memcpy(wscript->globals()->vector, script->globals()->vector, memcpy(wscript->globals()->vector, script->globals()->vector,
wscript->globals()->length * sizeof(GlobalSlotArray::Entry)); wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
} }
@ -1959,17 +1959,15 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
fun->freezeLocalNames(cx); fun->freezeLocalNames(cx);
} }
if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL)) if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
return false; return false;
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE) {
*objp = FUN_OBJECT(fun); *objp = FUN_OBJECT(fun);
if (fun->u.i.script != JSScript::emptyScript()) {
#ifdef CHECK_SCRIPT_OWNER #ifdef CHECK_SCRIPT_OWNER
fun->u.i.script->owner = NULL; fun->script()->owner = NULL;
#endif #endif
js_CallNewScriptHook(cx, fun->u.i.script, fun); js_CallNewScriptHook(cx, fun->script(), fun);
}
} }
return true; return true;
@ -2717,7 +2715,18 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
if (!fun) if (!fun)
return NULL; return NULL;
fun->flags |= JSFUN_PROTOTYPE; fun->flags |= JSFUN_PROTOTYPE;
fun->u.i.script = JSScript::emptyScript();
JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!script)
return NULL;
script->setVersion(JSVERSION_DEFAULT);
script->noScriptRval = true;
script->code[0] = JSOP_STOP;
script->code[1] = SRC_NULL;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
fun->u.i.script = script;
if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) { if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
/* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
@ -2821,18 +2830,16 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
if (cfun->isInterpreted()) { if (cfun->isInterpreted()) {
JSScript *script = cfun->u.i.script; JSScript *script = cfun->u.i.script;
JS_ASSERT(script); JS_ASSERT(script);
if (script != JSScript::emptyScript()) { JS_ASSERT(script->compartment == fun->compartment());
JS_ASSERT(script->compartment == fun->compartment()); JS_ASSERT(script->compartment != cx->compartment);
JS_ASSERT(script->compartment != cx->compartment);
cfun->u.i.script = js_CloneScript(cx, script); cfun->u.i.script = js_CloneScript(cx, script);
if (!cfun->u.i.script) if (!cfun->u.i.script)
return NULL; return NULL;
JS_ASSERT(cfun->u.i.script != JSScript::emptyScript());
#ifdef CHECK_SCRIPT_OWNER #ifdef CHECK_SCRIPT_OWNER
cfun->u.i.script->owner = NULL; cfun->script()->owner = NULL;
#endif #endif
js_CallNewScriptHook(cx, cfun->u.i.script, cfun); js_CallNewScriptHook(cx, cfun->script(), cfun);
}
} }
} }
return clone; return clone;
@ -2852,7 +2859,7 @@ JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
{ {
JS_ASSERT(FUN_FLAT_CLOSURE(fun)); JS_ASSERT(FUN_FLAT_CLOSURE(fun));
JS_ASSERT((fun->u.i.script->upvarsOffset JS_ASSERT((JSScript::isValidOffset(fun->u.i.script->upvarsOffset)
? fun->u.i.script->upvars()->length ? fun->u.i.script->upvars()->length
: 0) == fun->u.i.nupvars); : 0) == fun->u.i.nupvars);

Просмотреть файл

@ -351,18 +351,24 @@ TypedMarker(JSTracer *trc, JSShortString *thing)
} }
static JS_ALWAYS_INLINE void static JS_ALWAYS_INLINE void
TypedMarker(JSTracer *trc, JSString *thing) TypedMarker(JSTracer *trc, JSString *str)
{ {
/* /*
* Iterate through all nodes and leaves in the rope if this is part of a * When marking any node of a rope, mark the entire rope. This means if
* rope; otherwise, we only iterate once: on the string itself. * a given rope node is already marked, we are done.
*/ */
JSRopeNodeIterator iter(thing); JSRopeNodeIterator iter;
JSString *str = iter.init(); if (str->isRope()) {
if (str->asCell()->isMarked())
return;
str = iter.init(str);
goto not_static;
}
do { do {
for (;;) { for (;;) {
if (JSString::isStatic(str)) if (JSString::isStatic(str))
break; break;
not_static:
JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind)); JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind));
if (!str->asCell()->markIfUnmarked()) if (!str->asCell()->markIfUnmarked())
break; break;

Просмотреть файл

@ -616,7 +616,7 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION); args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
args.thisv() = vp[1]; args.thisv() = vp[1];
args[0] = obj->getSlot(JSSLOT_SAVED_ID); args[0] = obj->getSlot(JSSLOT_SAVED_ID);
JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2); JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
if (!argsobj) if (!argsobj)
return JS_FALSE; return JS_FALSE;
args[1].setObject(*argsobj); args[1].setObject(*argsobj);
@ -908,7 +908,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
if (script->isEmpty()) { if (script->isEmpty()) {
if (result) if (result)
result->setUndefined(); result->setUndefined();
return JS_TRUE; return true;
} }
LeaveTrace(cx); LeaveTrace(cx);
@ -2506,9 +2506,6 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
Value *argv = regs.fp->maybeFormalArgs(); Value *argv = regs.fp->maybeFormalArgs();
CHECK_INTERRUPT_HANDLER(); CHECK_INTERRUPT_HANDLER();
JS_ASSERT(!script->isEmpty());
JS_ASSERT(script->length >= 1);
#if defined(JS_TRACER) && defined(JS_METHODJIT) #if defined(JS_TRACER) && defined(JS_METHODJIT)
bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT); bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false)) # define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
@ -4078,8 +4075,8 @@ BEGIN_CASE(JSOP_UNBRANDTHIS)
Value &thisv = regs.fp->thisValue(); Value &thisv = regs.fp->thisValue();
if (thisv.isObject()) { if (thisv.isObject()) {
JSObject *obj = &thisv.toObject(); JSObject *obj = &thisv.toObject();
if (obj->isNative() && !obj->unbrand(cx)) if (obj->isNative())
goto error; obj->unbrand(cx);
} }
} }
END_CASE(JSOP_UNBRANDTHIS) END_CASE(JSOP_UNBRANDTHIS)
@ -4293,8 +4290,7 @@ END_CASE(JSOP_CALLPROP)
BEGIN_CASE(JSOP_UNBRAND) BEGIN_CASE(JSOP_UNBRAND)
JS_ASSERT(regs.sp - regs.fp->slots() >= 1); JS_ASSERT(regs.sp - regs.fp->slots() >= 1);
if (!regs.sp[-1].toObject().unbrand(cx)) regs.sp[-1].toObject().unbrand(cx);
goto error;
END_CASE(JSOP_UNBRAND) END_CASE(JSOP_UNBRAND)
BEGIN_CASE(JSOP_SETGNAME) BEGIN_CASE(JSOP_SETGNAME)
@ -4677,7 +4673,7 @@ BEGIN_CASE(JSOP_FUNCALL)
if (newfun->isInterpreted()) if (newfun->isInterpreted())
inline_call: inline_call:
{ {
JSScript *newscript = newfun->u.i.script; JSScript *newscript = newfun->script();
if (JS_UNLIKELY(newscript->isEmpty())) { if (JS_UNLIKELY(newscript->isEmpty())) {
vp->setUndefined(); vp->setUndefined();
regs.sp = vp + 1; regs.sp = vp + 1;
@ -5892,7 +5888,7 @@ BEGIN_CASE(JSOP_NEWINIT)
JSObject *obj; JSObject *obj;
if (i == JSProto_Array) { if (i == JSProto_Array) {
obj = js_NewArrayObject(cx, 0, NULL); obj = NewDenseEmptyArray(cx);
} else { } else {
gc::FinalizeKind kind = GuessObjectGCKind(0, false); gc::FinalizeKind kind = GuessObjectGCKind(0, false);
obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
@ -5909,10 +5905,8 @@ END_CASE(JSOP_NEWINIT)
BEGIN_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWARRAY)
{ {
unsigned count = GET_UINT24(regs.pc); unsigned count = GET_UINT24(regs.pc);
JSObject *obj = js_NewArrayObject(cx, count, NULL); JSObject *obj = NewDenseAllocatedArray(cx, count);
if (!obj)
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
if (!obj || !obj->ensureSlots(cx, count))
goto error; goto error;
PUSH_OBJECT(*obj); PUSH_OBJECT(*obj);
@ -6069,7 +6063,7 @@ BEGIN_CASE(JSOP_DEFSHARP)
obj = &lref.toObject(); obj = &lref.toObject();
} else { } else {
JS_ASSERT(lref.isUndefined()); JS_ASSERT(lref.isUndefined());
obj = js_NewArrayObject(cx, 0, NULL); obj = NewDenseEmptyArray(cx);
if (!obj) if (!obj)
goto error; goto error;
regs.fp->slots()[slot].setObject(*obj); regs.fp->slots()[slot].setObject(*obj);
@ -6832,7 +6826,7 @@ END_CASE(JSOP_ARRAYPUSH)
/* /*
* Look for a try block in script that can catch this exception. * Look for a try block in script that can catch this exception.
*/ */
if (script->trynotesOffset == 0) if (!JSScript::isValidOffset(script->trynotesOffset))
goto no_catch; goto no_catch;
offset = (uint32)(regs.pc - script->main); offset = (uint32)(regs.pc - script->main);

Просмотреть файл

@ -61,7 +61,7 @@ struct JSFrameRegs
/* Flags to toggle js::Interpret() execution. */ /* Flags to toggle js::Interpret() execution. */
enum JSInterpMode enum JSInterpMode
{ {
JSINTERP_NORMAL = 0, /* Interpreter is running normally. */ JSINTERP_NORMAL = 0, /* interpreter is running normally */
JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */ JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */
JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */ JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */
JSINTERP_PROFILE = 3 /* interpreter should profile a loop */ JSINTERP_PROFILE = 3 /* interpreter should profile a loop */
@ -76,8 +76,8 @@ enum JSFrameFlags
JSFRAME_DUMMY = 0x4, /* frame pushed for bookkeeping */ JSFRAME_DUMMY = 0x4, /* frame pushed for bookkeeping */
/* Frame subtypes */ /* Frame subtypes */
JSFRAME_EVAL = 0x8, /* frame pushed by js::Execute */ JSFRAME_EVAL = 0x8, /* frame pushed for eval() or debugger eval */
JSFRAME_DEBUGGER = 0x10, /* frame pushed by JS_EvaluateInStackFrame */ JSFRAME_DEBUGGER = 0x10, /* frame pushed for debugger eval */
JSFRAME_GENERATOR = 0x20, /* frame is associated with a generator */ JSFRAME_GENERATOR = 0x20, /* frame is associated with a generator */
JSFRAME_FLOATING_GENERATOR = 0x40, /* frame is is in generator obj, not on stack */ JSFRAME_FLOATING_GENERATOR = 0x40, /* frame is is in generator obj, not on stack */
JSFRAME_CONSTRUCTING = 0x80, /* frame is for a constructor invocation */ JSFRAME_CONSTRUCTING = 0x80, /* frame is for a constructor invocation */

Просмотреть файл

@ -165,7 +165,7 @@ NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
Value vec[2] = { IdToValue(id), val }; Value vec[2] = { IdToValue(id), val };
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec); AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
JSObject *aobj = js_NewArrayObject(cx, 2, vec); JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
if (!aobj) if (!aobj)
return false; return false;
rval->setObject(*aobj); rval->setObject(*aobj);
@ -615,6 +615,14 @@ EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVe
typedef Vector<uint32, 8> ShapeVector; typedef Vector<uint32, 8> ShapeVector;
static inline void
UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
{
// Update the object for which the native iterator is associated, so
// SuppressDeletedPropertyHelper will recognize the iterator as a match.
ni->obj = obj;
}
bool bool
GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp) GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
{ {
@ -654,6 +662,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
proto->shape() == lastni->shapes_array[1] && proto->shape() == lastni->shapes_array[1] &&
!proto->getProto()) { !proto->getProto()) {
vp->setObject(*last); vp->setObject(*last);
UpdateNativeIterator(lastni, obj);
RegisterEnumerator(cx, last, lastni); RegisterEnumerator(cx, last, lastni);
return true; return true;
} }
@ -691,6 +700,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) { Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
vp->setObject(*iterobj); vp->setObject(*iterobj);
UpdateNativeIterator(ni, obj);
RegisterEnumerator(cx, iterobj, ni); RegisterEnumerator(cx, iterobj, ni);
if (shapes.length() == 2) if (shapes.length() == 2)
JS_THREAD_DATA(cx)->lastNativeIterator = iterobj; JS_THREAD_DATA(cx)->lastNativeIterator = iterobj;
@ -967,6 +977,7 @@ public:
bool bool
js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id) js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
{ {
id = js_CheckForStringIndex(id);
return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id)); return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
} }

Просмотреть файл

@ -1055,7 +1055,7 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static
int i = 1; int i = 1;
if (objarray->length == 1) { if (objarray->length == 1) {
if (script->regexpsOffset != 0) { if (JSScript::isValidOffset(script->regexpsOffset)) {
objarray = script->regexps(); objarray = script->regexps();
i = 0; i = 0;
} else { } else {
@ -1806,7 +1806,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
} }
JS_ASSERT(props.length() <= UINT32_MAX); JS_ASSERT(props.length() <= UINT32_MAX);
JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin()); JSObject *aobj = NewDenseCopiedArray(cx, jsuint(vals.length()), vals.begin());
if (!aobj) if (!aobj)
return false; return false;
vp->setObject(*aobj); vp->setObject(*aobj);
@ -2516,7 +2516,7 @@ obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
} }
} }
JSObject *aobj = js_NewArrayObject(cx, vals.length(), vals.begin()); JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
if (!aobj) if (!aobj)
return false; return false;
@ -3538,9 +3538,9 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_ENCODE) { if (xdr->mode == JSXDR_ENCODE) {
obj = *objp; obj = *objp;
parent = obj->getParent(); parent = obj->getParent();
parentId = (xdr->script->objectsOffset == 0) parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
? NO_PARENT_INDEX ? FindObjectIndex(xdr->script->objects(), parent)
: FindObjectIndex(xdr->script->objects(), parent); : NO_PARENT_INDEX;
depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
count = (uint16)OBJ_BLOCK_COUNT(cx, obj); count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
depthAndCount = (uint32)(depth << 16) | count; depthAndCount = (uint32)(depth << 16) | count;
@ -3835,10 +3835,10 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
* Pre-brand the prototype and constructor if they have built-in methods. * Pre-brand the prototype and constructor if they have built-in methods.
* This avoids extra shape guard branch exits in the tracejitted code. * This avoids extra shape guard branch exits in the tracejitted code.
*/ */
if (fs && !proto->brand(cx)) if (fs)
goto bad; proto->brand(cx);
if (ctor != proto && static_fs && !ctor->brand(cx)) if (ctor != proto && static_fs)
goto bad; ctor->brand(cx);
/* /*
* Make sure proto's emptyShape is available to be shared by objects of * Make sure proto's emptyShape is available to be shared by objects of
@ -5515,7 +5515,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
return false; return false;
JS_ASSERT(IsFunctionObject(*vp)); JS_ASSERT(IsFunctionObject(*vp));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JSObject *funobj = &vp->toObject(); JSObject *funobj = &vp->toObject();
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);

Просмотреть файл

@ -337,16 +337,19 @@ struct JSObject : js::gc::Cell {
inline bool nativeContains(const js::Shape &shape); inline bool nativeContains(const js::Shape &shape);
enum { enum {
DELEGATE = 0x01, DELEGATE = 0x01,
SYSTEM = 0x02, SYSTEM = 0x02,
NOT_EXTENSIBLE = 0x04, NOT_EXTENSIBLE = 0x04,
BRANDED = 0x08, BRANDED = 0x08,
GENERIC = 0x10, GENERIC = 0x10,
METHOD_BARRIER = 0x20, METHOD_BARRIER = 0x20,
INDEXED = 0x40, INDEXED = 0x40,
OWN_SHAPE = 0x80, OWN_SHAPE = 0x80,
BOUND_FUNCTION = 0x100, BOUND_FUNCTION = 0x100,
HAS_EQUALITY = 0x200 HAS_EQUALITY = 0x200,
METHOD_THRASH_COUNT_MASK = 0xc00,
METHOD_THRASH_COUNT_SHIFT = 10,
METHOD_THRASH_COUNT_MAX = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT
}; };
/* /*
@ -423,12 +426,26 @@ struct JSObject : js::gc::Cell {
*/ */
bool branded() { return !!(flags & BRANDED); } bool branded() { return !!(flags & BRANDED); }
/*
* NB: these return false on shape overflow but do not report any error.
* Callers who depend on shape guarantees should therefore bail off trace,
* e.g., on false returns.
*/
bool brand(JSContext *cx); bool brand(JSContext *cx);
bool unbrand(JSContext *cx); bool unbrand(JSContext *cx);
bool generic() { return !!(flags & GENERIC); } bool generic() { return !!(flags & GENERIC); }
void setGeneric() { flags |= GENERIC; } void setGeneric() { flags |= GENERIC; }
uintN getMethodThrashCount() const {
return (flags & METHOD_THRASH_COUNT_MASK) >> METHOD_THRASH_COUNT_SHIFT;
}
void setMethodThrashCount(uintN count) {
JS_ASSERT(count <= METHOD_THRASH_COUNT_MAX);
flags = (flags & ~METHOD_THRASH_COUNT_MASK) | (count << METHOD_THRASH_COUNT_SHIFT);
}
bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); } bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); }
void assertSpecialEqualitySynced() const { void assertSpecialEqualitySynced() const {
JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality()); JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality());

Просмотреть файл

@ -105,8 +105,13 @@ inline bool
JSObject::unbrand(JSContext *cx) JSObject::unbrand(JSContext *cx)
{ {
JS_ASSERT(isNative()); JS_ASSERT(isNative());
if (!branded()) if (branded()) {
setGeneric(); generateOwnShape(cx);
if (js_IsPropertyCacheDisabled(cx)) // check for rt->shapeGen overflow
return false;
flags &= ~BRANDED;
}
setGeneric();
return true; return true;
} }
@ -189,14 +194,12 @@ ChangesMethodValue(const js::Value &prev, const js::Value &v)
inline bool inline bool
JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v) JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v)
{ {
if (flags & (BRANDED | METHOD_BARRIER)) { if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) {
if (shape.slot != SHAPE_INVALID_SLOT) { const js::Value &prev = nativeGetSlot(shape.slot);
const js::Value &prev = nativeGetSlot(shape.slot);
if (ChangesMethodValue(prev, v)) { if (ChangesMethodValue(prev, v)) {
JS_FUNCTION_METER(cx, mwritebarrier); JS_FUNCTION_METER(cx, mwritebarrier);
return methodShapeChange(cx, shape); return methodShapeChange(cx, shape);
}
} }
} }
return true; return true;
@ -205,7 +208,7 @@ JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Va
inline bool inline bool
JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v) JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
{ {
if (flags & (BRANDED | METHOD_BARRIER)) { if (brandedOrHasMethodBarrier()) {
const js::Value &prev = nativeGetSlot(slot); const js::Value &prev = nativeGetSlot(slot);
if (ChangesMethodValue(prev, v)) { if (ChangesMethodValue(prev, v)) {
@ -931,6 +934,18 @@ namespace WithProto {
* Note that as a template, there will be lots of instantiations, which means * Note that as a template, there will be lots of instantiations, which means
* the internals will be specialized based on the template parameters. * the internals will be specialized based on the template parameters.
*/ */
static JS_ALWAYS_INLINE bool
FindProto(JSContext *cx, js::Class *clasp, JSObject *parent, JSObject ** proto)
{
JSProtoKey protoKey = GetClassProtoKey(clasp);
if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp))
return false;
if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto))
return false;
return true;
}
namespace detail namespace detail
{ {
template <bool withProto, bool isFunction> template <bool withProto, bool isFunction>
@ -940,11 +955,8 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
{ {
/* Bootstrap the ur-object, and make it the default prototype object. */ /* Bootstrap the ur-object, and make it the default prototype object. */
if (withProto == WithProto::Class && !proto) { if (withProto == WithProto::Class && !proto) {
JSProtoKey protoKey = GetClassProtoKey(clasp); if (!FindProto (cx, clasp, parent, &proto))
if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp)) return NULL;
return NULL;
if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
} }
/* /*
@ -1025,13 +1037,6 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
return NewObject<withProto>(cx, clasp, proto, parent, kind); return NewObject<withProto>(cx, clasp, proto, parent, kind);
} }
/* Creates a new array with a zero length and the given finalize kind. */
static inline JSObject *
NewArrayWithKind(JSContext* cx, gc::FinalizeKind kind)
{
return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
}
/* /*
* As for js_GetGCObjectKind, where numSlots is a guess at the final size of * As for js_GetGCObjectKind, where numSlots is a guess at the final size of
* the object, zero if the final size is unknown. * the object, zero if the final size is unknown.

Просмотреть файл

@ -680,7 +680,7 @@ js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/
if (!cx) if (!cx)
return NULL; return NULL;
JSObject *arr = js_NewArrayObject(cx, 0, NULL); JSObject *arr = NewDenseEmptyArray(cx);
if (!arr) if (!arr)
return NULL; return NULL;
@ -856,7 +856,7 @@ static JSBool
OpenArray(JSContext *cx, JSONParser *jp) OpenArray(JSContext *cx, JSONParser *jp)
{ {
// Add an array to an existing array or object // Add an array to an existing array or object
JSObject *arr = js_NewArrayObject(cx, 0, NULL); JSObject *arr = NewDenseEmptyArray(cx);
if (!arr) if (!arr)
return JS_FALSE; return JS_FALSE;

Просмотреть файл

@ -1337,7 +1337,7 @@ GetLocal(SprintStack *ss, jsint i)
* not in a block. In either case, return GetStr(ss, i). * not in a block. In either case, return GetStr(ss, i).
*/ */
JSScript *script = ss->printer->script; JSScript *script = ss->printer->script;
if (script->objectsOffset == 0) if (!JSScript::isValidOffset(script->objectsOffset))
return GetStr(ss, i); return GetStr(ss, i);
for (jsatomid j = 0, n = script->objects()->length; j != n; j++) { for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
@ -2890,7 +2890,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
JS_ASSERT(fp->prev()->fun() == jp->fun); JS_ASSERT(fp->prev()->fun() == jp->fun);
JS_ASSERT(FUN_INTERPRETED(jp->fun)); JS_ASSERT(FUN_INTERPRETED(jp->fun));
JS_ASSERT(jp->script != jp->fun->u.i.script); JS_ASSERT(jp->script != jp->fun->u.i.script);
JS_ASSERT(jp->script->upvarsOffset != 0); JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
} }
#endif #endif
uva = jp->script->upvars(); uva = jp->script->upvars();

Просмотреть файл

@ -640,9 +640,9 @@ NameNode::initCommon(JSTreeContext *tc)
{ {
pn_expr = NULL; pn_expr = NULL;
pn_cookie.makeFree(); pn_cookie.makeFree();
pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0; pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK) ? PND_BLOCKCHILD
pn_dflags |= PND_BLOCKCHILD; : 0;
pn_blockid = tc->blockid(); pn_blockid = tc->blockid();
} }
@ -865,6 +865,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
#endif #endif
inDirectivePrologue = true; inDirectivePrologue = true;
tokenStream.setOctalCharacterEscape(false);
for (;;) { for (;;) {
tt = tokenStream.peekToken(TSF_OPERAND); tt = tokenStream.peekToken(TSF_OPERAND);
if (tt <= TOK_EOF) { if (tt <= TOK_EOF) {
@ -879,8 +880,8 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
goto out; goto out;
JS_ASSERT(!cg.blockNode); JS_ASSERT(!cg.blockNode);
if (inDirectivePrologue) if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
inDirectivePrologue = parser.recognizeDirectivePrologue(pn); goto out;
if (!js_FoldConstants(cx, pn, &cg)) if (!js_FoldConstants(cx, pn, &cg))
goto out; goto out;
@ -978,7 +979,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
JS_DumpArenaStats(stdout); JS_DumpArenaStats(stdout);
#endif #endif
script = JSScript::NewScriptFromCG(cx, &cg); script = JSScript::NewScriptFromCG(cx, &cg);
if (script && funbox && script != script->emptyScript()) if (script && funbox)
script->savedCallerFun = true; script->savedCallerFun = true;
#ifdef JS_SCOPE_DEPTH_METER #ifdef JS_SCOPE_DEPTH_METER
@ -1069,7 +1070,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
JSScript *inner = worklist.back(); JSScript *inner = worklist.back();
worklist.popBack(); worklist.popBack();
if (inner->objectsOffset != 0) { if (JSScript::isValidOffset(inner->objectsOffset)) {
JSObjectArray *arr = inner->objects(); JSObjectArray *arr = inner->objects();
for (size_t i = 0; i < arr->length; i++) { for (size_t i = 0; i < arr->length; i++) {
JSObject *obj = arr->vector[i]; JSObject *obj = arr->vector[i];
@ -1078,14 +1079,16 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
JSFunction *fun = obj->getFunctionPrivate(); JSFunction *fun = obj->getFunctionPrivate();
JS_ASSERT(fun->isInterpreted()); JS_ASSERT(fun->isInterpreted());
JSScript *inner = fun->u.i.script; JSScript *inner = fun->u.i.script;
if (inner->globalsOffset == 0 && inner->objectsOffset == 0) if (!JSScript::isValidOffset(inner->globalsOffset) &&
!JSScript::isValidOffset(inner->objectsOffset)) {
continue; continue;
}
if (!worklist.append(inner)) if (!worklist.append(inner))
return false; return false;
} }
} }
if (inner->globalsOffset == 0) if (!JSScript::isValidOffset(inner->globalsOffset))
continue; continue;
GlobalSlotArray *globalUses = inner->globals(); GlobalSlotArray *globalUses = inner->globals();
@ -1479,6 +1482,8 @@ Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
ALE_SET_DEFN(ale, pn); ALE_SET_DEFN(ale, pn);
pn->pn_defn = true; pn->pn_defn = true;
pn->pn_dflags &= ~PND_PLACEHOLDER; pn->pn_dflags &= ~PND_PLACEHOLDER;
if (!tc->parent)
pn->pn_dflags |= PND_TOPLEVEL;
return true; return true;
} }
@ -1560,7 +1565,6 @@ MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *t
dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME; dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
} else if (dn->kind() == JSDefinition::FUNCTION) { } else if (dn->kind() == JSDefinition::FUNCTION) {
JS_ASSERT(dn->isTopLevel());
JS_ASSERT(dn->pn_op == JSOP_NOP); JS_ASSERT(dn->pn_op == JSOP_NOP);
dn->pn_type = TOK_NAME; dn->pn_type = TOK_NAME;
dn->pn_arity = PN_NAME; dn->pn_arity = PN_NAME;
@ -2543,8 +2547,6 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR); funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
fn->pn_dflags |= PND_INITIALIZED; fn->pn_dflags |= PND_INITIALIZED;
JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
fn->pn_dflags & PND_TOPLEVEL);
if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK) if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
fn->pn_dflags |= PND_BLOCKCHILD; fn->pn_dflags |= PND_BLOCKCHILD;
@ -2864,12 +2866,12 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr). * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
* *
* Also treat function sub-statements (non-lambda, non-top-level functions) * Treat function sub-statements (non-lambda, non-body-level functions) as
* as escaping funargs, since we can't statically analyze their definitions * escaping funargs, since we can't statically analyze their definitions
* and uses. * and uses.
*/ */
bool topLevel = tc->atTopLevel(); bool bodyLevel = tc->atBodyLevel();
pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0; pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
/* /*
* Record names for function statements in tc->decls so we know when to * Record names for function statements in tc->decls so we know when to
@ -2897,7 +2899,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
} }
} }
if (topLevel) { if (bodyLevel) {
ALE_SET_DEFN(ale, pn); ALE_SET_DEFN(ale, pn);
pn->pn_defn = true; pn->pn_defn = true;
pn->dn_uses = dn; /* dn->dn_uses is now pn_link */ pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
@ -2905,7 +2907,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
if (!MakeDefIntoUse(dn, pn, funAtom, tc)) if (!MakeDefIntoUse(dn, pn, funAtom, tc))
return NULL; return NULL;
} }
} else if (topLevel) { } else if (bodyLevel) {
/* /*
* If this function was used before it was defined, claim the * If this function was used before it was defined, claim the
* pre-created definition node for this function that primaryExpr * pre-created definition node for this function that primaryExpr
@ -2934,43 +2936,36 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
} }
/* /*
* A function nested at top level inside another's body needs only a * A function directly inside another's body needs only a local
* local variable to bind its name to its value, and not an activation * variable to bind its name to its value, and not an activation object
* object property (it might also need the activation property, if the * property (it might also need the activation property, if the outer
* outer function contains with statements, e.g., but the stack slot * function contains with statements, e.g., but the stack slot wins
* wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a * when jsemit.cpp's BindNameToSlot can optimize a JSOP_NAME into a
* JSOP_GETLOCAL bytecode). * JSOP_GETLOCAL bytecode).
*/ */
if (topLevel) { if (bodyLevel && tc->inFunction()) {
pn->pn_dflags |= PND_TOPLEVEL; /*
* Define a local in the outer function so that BindNameToSlot
* can properly optimize accesses. Note that we need a local
* variable, not an argument, for the function statement. Thus
* we add a variable even if a parameter with the given name
* already exists.
*/
uintN index;
switch (tc->fun()->lookupLocal(context, funAtom, &index)) {
case JSLOCAL_NONE:
case JSLOCAL_ARG:
index = tc->fun()->u.i.nvars;
if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
return NULL;
/* FALL THROUGH */
if (tc->inFunction()) { case JSLOCAL_VAR:
JSLocalKind localKind; pn->pn_cookie.set(tc->staticLevel, index);
uintN index; pn->pn_dflags |= PND_BOUND;
break;
/* default:;
* Define a local in the outer function so that BindNameToSlot
* can properly optimize accesses. Note that we need a local
* variable, not an argument, for the function statement. Thus
* we add a variable even if a parameter with the given name
* already exists.
*/
localKind = tc->fun()->lookupLocal(context, funAtom, &index);
switch (localKind) {
case JSLOCAL_NONE:
case JSLOCAL_ARG:
index = tc->fun()->u.i.nvars;
if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
return NULL;
/* FALL THROUGH */
case JSLOCAL_VAR:
pn->pn_cookie.set(tc->staticLevel, index);
pn->pn_dflags |= PND_BOUND;
break;
default:;
}
} }
} }
} }
@ -3104,11 +3099,12 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
outertc->flags |= TCF_FUN_HEAVYWEIGHT; outertc->flags |= TCF_FUN_HEAVYWEIGHT;
} else { } else {
/* /*
* If this function is a named statement function not at top-level * If this function is not at body level of a program or function (i.e.
* (i.e. not a top-level function definiton or expression), then our * it is a function statement that is not a direct child of a program
* enclosing function, if any, must be heavyweight. * or function), then our enclosing function, if any, must be
* heavyweight.
*/ */
if (!topLevel && lambda == 0 && funAtom) if (!bodyLevel && lambda == 0 && funAtom)
outertc->flags |= TCF_FUN_HEAVYWEIGHT; outertc->flags |= TCF_FUN_HEAVYWEIGHT;
} }
@ -3134,7 +3130,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
result->pn_pos = pn->pn_pos; result->pn_pos = pn->pn_pos;
result->pn_kid = pn; result->pn_kid = pn;
op = JSOP_LAMBDA; op = JSOP_LAMBDA;
} else if (!topLevel) { } else if (!bodyLevel) {
/* /*
* ECMA ed. 3 extension: a function expression statement not at the * ECMA ed. 3 extension: a function expression statement not at the
* top level, e.g., in a compound statement such as the "then" part * top level, e.g., in a compound statement such as the "then" part
@ -3156,10 +3152,9 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
pn->pn_body = body; pn->pn_body = body;
} }
if (!outertc->inFunction() && topLevel && funAtom && !lambda && if (!outertc->inFunction() && bodyLevel && funAtom && !lambda && outertc->compiling()) {
outertc->compiling()) {
JS_ASSERT(pn->pn_cookie.isFree()); JS_ASSERT(pn->pn_cookie.isFree());
if (!DefineGlobal(pn, (JSCodeGenerator *)outertc, funAtom)) if (!DefineGlobal(pn, outertc->asCodeGenerator(), funAtom))
return false; return false;
} }
@ -3225,13 +3220,33 @@ Parser::functionExpr()
* if it can't possibly be a directive, now or in the future. * if it can't possibly be a directive, now or in the future.
*/ */
bool bool
Parser::recognizeDirectivePrologue(JSParseNode *pn) Parser::recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember)
{ {
if (!pn->isDirectivePrologueMember()) *isDirectivePrologueMember = pn->isDirectivePrologueMember();
return false; if (!*isDirectivePrologueMember)
return true;
if (pn->isDirective()) { if (pn->isDirective()) {
JSAtom *directive = pn->pn_kid->pn_atom; JSAtom *directive = pn->pn_kid->pn_atom;
if (directive == context->runtime->atomState.useStrictAtom) { if (directive == context->runtime->atomState.useStrictAtom) {
/*
* Unfortunately, Directive Prologue members in general may contain
* escapes, even while "use strict" directives may not. Therefore
* we must check whether an octal character escape has been seen in
* any previous directives whenever we encounter a "use strict"
* directive, so that the octal escape is properly treated as a
* syntax error. An example of this case:
*
* function error()
* {
* "\145"; // octal escape
* "use strict"; // retroactively makes "\145" a syntax error
* }
*/
if (tokenStream.hasOctalCharacterEscape()) {
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
return false;
}
tc->flags |= TCF_STRICT_MODE_CODE; tc->flags |= TCF_STRICT_MODE_CODE;
tokenStream.setStrictMode(); tokenStream.setStrictMode();
} }
@ -3249,7 +3264,6 @@ Parser::statements()
{ {
JSParseNode *pn, *pn2, *saveBlock; JSParseNode *pn, *pn2, *saveBlock;
TokenKind tt; TokenKind tt;
bool inDirectivePrologue = tc->atTopLevel();
JS_CHECK_RECURSION(context, return NULL); JS_CHECK_RECURSION(context, return NULL);
@ -3262,6 +3276,8 @@ Parser::statements()
saveBlock = tc->blockNode; saveBlock = tc->blockNode;
tc->blockNode = pn; tc->blockNode = pn;
bool inDirectivePrologue = tc->atBodyLevel();
tokenStream.setOctalCharacterEscape(false);
for (;;) { for (;;) {
tt = tokenStream.peekToken(TSF_OPERAND); tt = tokenStream.peekToken(TSF_OPERAND);
if (tt <= TOK_EOF || tt == TOK_RC) { if (tt <= TOK_EOF || tt == TOK_RC) {
@ -3279,20 +3295,20 @@ Parser::statements()
return NULL; return NULL;
} }
if (inDirectivePrologue) if (inDirectivePrologue && !recognizeDirectivePrologue(pn2, &inDirectivePrologue))
inDirectivePrologue = recognizeDirectivePrologue(pn2); return NULL;
if (pn2->pn_type == TOK_FUNCTION) { if (pn2->pn_type == TOK_FUNCTION) {
/* /*
* PNX_FUNCDEFS notifies the emitter that the block contains top- * PNX_FUNCDEFS notifies the emitter that the block contains body-
* level function definitions that should be processed before the * level function definitions that should be processed before the
* rest of nodes. * rest of nodes.
* *
* TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
* is relevant only for function definitions not at top-level, * is relevant only for function definitions not at body-level,
* which we call function statements. * which we call function statements.
*/ */
if (tc->atTopLevel()) if (tc->atBodyLevel())
pn->pn_xflags |= PNX_FUNCDEFS; pn->pn_xflags |= PNX_FUNCDEFS;
else else
tc->flags |= TCF_HAS_FUNCTION_STMT; tc->flags |= TCF_HAS_FUNCTION_STMT;
@ -3362,10 +3378,10 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
jsint n; jsint n;
/* /*
* Top-level 'let' is the same as 'var' currently -- this may change in a * Body-level 'let' is the same as 'var' currently -- this may change in a
* successor standard to ES3.1 that specifies 'let'. * successor standard to ES5 that specifies 'let'.
*/ */
JS_ASSERT(!tc->atTopLevel()); JS_ASSERT(!tc->atBodyLevel());
pn = data->pn; pn = data->pn;
if (!CheckStrictBinding(cx, tc, atom, pn)) if (!CheckStrictBinding(cx, tc, atom, pn))
@ -3583,7 +3599,7 @@ BindGvar(JSParseNode *pn, JSTreeContext *tc)
if (!tc->compiling() || tc->parser->callerFrame) if (!tc->compiling() || tc->parser->callerFrame)
return true; return true;
JSCodeGenerator *cg = (JSCodeGenerator *) tc; JSCodeGenerator *cg = tc->asCodeGenerator();
if (pn->pn_dflags & PND_CONST) if (pn->pn_dflags & PND_CONST)
return true; return true;

Просмотреть файл

@ -479,7 +479,7 @@ public:
#define PND_CONST 0x02 /* const binding (orthogonal to let) */ #define PND_CONST 0x02 /* const binding (orthogonal to let) */
#define PND_INITIALIZED 0x04 /* initialized declaration */ #define PND_INITIALIZED 0x04 /* initialized declaration */
#define PND_ASSIGNED 0x08 /* set if ever LHS of assignment */ #define PND_ASSIGNED 0x08 /* set if ever LHS of assignment */
#define PND_TOPLEVEL 0x10 /* function at top of body or prog */ #define PND_TOPLEVEL 0x10 /* see isTopLevel() below */
#define PND_BLOCKCHILD 0x20 /* use or def is direct block child */ #define PND_BLOCKCHILD 0x20 /* use or def is direct block child */
#define PND_GVAR 0x40 /* gvar binding, can't close over #define PND_GVAR 0x40 /* gvar binding, can't close over
because it could be deleted */ because it could be deleted */
@ -529,7 +529,6 @@ public:
bool isLet() const { return test(PND_LET); } bool isLet() const { return test(PND_LET); }
bool isConst() const { return test(PND_CONST); } bool isConst() const { return test(PND_CONST); }
bool isInitialized() const { return test(PND_INITIALIZED); } bool isInitialized() const { return test(PND_INITIALIZED); }
bool isTopLevel() const { return test(PND_TOPLEVEL); }
bool isBlockChild() const { return test(PND_BLOCKCHILD); } bool isBlockChild() const { return test(PND_BLOCKCHILD); }
bool isPlaceholder() const { return test(PND_PLACEHOLDER); } bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); } bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
@ -537,6 +536,20 @@ public:
bool isFunArg() const { return test(PND_FUNARG); } bool isFunArg() const { return test(PND_FUNARG); }
bool isClosed() const { return test(PND_CLOSED); } bool isClosed() const { return test(PND_CLOSED); }
/*
* True iff this definition creates a top-level binding in the overall
* script being compiled -- that is, it affects the whole program's
* bindings, not bindings for a specific function (unless this definition
* is in the outermost scope in eval code, executed within a function) or
* the properties of a specific object (through the with statement).
*
* NB: Function sub-statements found in overall program code and not nested
* within other functions are not currently top level, even though (if
* executed) they do create top-level bindings; there is no particular
* rationale for this behavior.
*/
bool isTopLevel() const { return test(PND_TOPLEVEL); }
/* Defined below, see after struct JSDefinition. */ /* Defined below, see after struct JSDefinition. */
void setFunArg(); void setFunArg();
@ -1118,7 +1131,7 @@ private:
/* /*
* Additional JS parsers. * Additional JS parsers.
*/ */
bool recognizeDirectivePrologue(JSParseNode *pn); bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
enum FunctionType { GETTER, SETTER, GENERAL }; enum FunctionType { GETTER, SETTER, GENERAL };
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun, bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,

Просмотреть файл

@ -335,8 +335,6 @@ GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
static bool static bool
Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval) Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval)
{ {
JS_CHECK_RECURSION(cx, return false);
return ExternalInvoke(cx, handler, fval, argc, argv, rval); return ExternalInvoke(cx, handler, fval, argc, argv, rval);
} }
@ -672,6 +670,7 @@ bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
PropertyDescriptor *desc) PropertyDescriptor *desc)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc); return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc);
} }
@ -679,6 +678,7 @@ JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set
bool bool
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp) JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
AutoPropertyDescriptorRooter desc(cx); AutoPropertyDescriptorRooter desc(cx);
return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) && return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) &&
@ -689,6 +689,7 @@ bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
PropertyDescriptor *desc) PropertyDescriptor *desc)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc); return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
} }
@ -696,6 +697,7 @@ JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool
bool bool
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp) JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
AutoPropertyDescriptorRooter desc(cx); AutoPropertyDescriptorRooter desc(cx);
return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) && return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) &&
@ -705,6 +707,7 @@ JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool
bool bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc) JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc); return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
} }
@ -712,6 +715,7 @@ JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescrip
bool bool
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v) JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
AutoPropertyDescriptorRooter desc(cx); AutoPropertyDescriptorRooter desc(cx);
return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) && return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
@ -721,6 +725,7 @@ JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
bool bool
JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props) JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props); return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
} }
@ -728,6 +733,7 @@ JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props
bool bool
JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->delete_(cx, proxy, id, bp); return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
} }
@ -735,6 +741,7 @@ JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
bool bool
JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->enumerate(cx, proxy, props); return proxy->getProxyHandler()->enumerate(cx, proxy, props);
} }
@ -742,6 +749,7 @@ JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
bool bool
JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp) JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->fix(cx, proxy, vp); return proxy->getProxyHandler()->fix(cx, proxy, vp);
} }
@ -749,6 +757,7 @@ JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
bool bool
JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->has(cx, proxy, id, bp); return proxy->getProxyHandler()->has(cx, proxy, id, bp);
} }
@ -756,6 +765,7 @@ JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
bool bool
JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp); return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
} }
@ -763,6 +773,7 @@ JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
bool bool
JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp) JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp); return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp);
} }
@ -770,6 +781,7 @@ JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value
bool bool
JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp) JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->set(cx, proxy, receiver, id, vp); return proxy->getProxyHandler()->set(cx, proxy, receiver, id, vp);
} }
@ -777,6 +789,7 @@ JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value
bool bool
JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props) JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props); return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
} }
@ -784,6 +797,7 @@ JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
bool bool
JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp) JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp); return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
} }
@ -791,6 +805,7 @@ JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
bool bool
JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp) JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->call(cx, proxy, argc, vp); return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
} }
@ -798,6 +813,7 @@ JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
bool bool
JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval) JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->construct(cx, proxy, argc, argv, rval); return proxy->getProxyHandler()->construct(cx, proxy, argc, argv, rval);
} }
@ -805,6 +821,7 @@ JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Valu
bool bool
JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp) JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
{ {
JS_CHECK_RECURSION(cx, return false);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp); return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp);
} }
@ -812,6 +829,8 @@ JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *
JSType JSType
JSProxy::typeOf(JSContext *cx, JSObject *proxy) JSProxy::typeOf(JSContext *cx, JSObject *proxy)
{ {
// FIXME: API doesn't allow us to report error (bug 618906).
JS_CHECK_RECURSION(cx, return JSTYPE_OBJECT);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->typeOf(cx, proxy); return proxy->getProxyHandler()->typeOf(cx, proxy);
} }
@ -819,6 +838,7 @@ JSProxy::typeOf(JSContext *cx, JSObject *proxy)
JSString * JSString *
JSProxy::obj_toString(JSContext *cx, JSObject *proxy) JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
{ {
JS_CHECK_RECURSION(cx, return NULL);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->obj_toString(cx, proxy); return proxy->getProxyHandler()->obj_toString(cx, proxy);
} }
@ -826,6 +846,7 @@ JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
JSString * JSString *
JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent) JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
{ {
JS_CHECK_RECURSION(cx, return NULL);
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return proxy->getProxyHandler()->fun_toString(cx, proxy, indent); return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
} }

Просмотреть файл

@ -510,7 +510,7 @@ NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst)
bool bool
NodeBuilder::newArray(NodeVector &elts, Value *dst) NodeBuilder::newArray(NodeVector &elts, Value *dst)
{ {
JSObject *array = js_NewArrayObject(cx, 0, NULL); JSObject *array = NewDenseEmptyArray(cx);
if (!array) if (!array)
return false; return false;

Просмотреть файл

@ -246,7 +246,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC
* 0: matched string * 0: matched string
* 1..pairCount-1: paren matches * 1..pairCount-1: paren matches
*/ */
JSObject *array = js_NewSlowArrayObject(cx); JSObject *array = NewSlowEmptyArray(cx);
if (!array) if (!array)
return NULL; return NULL;

Просмотреть файл

@ -1192,6 +1192,7 @@ TokenStream::getTokenInternal()
JSMSG_DEPRECATED_OCTAL)) { JSMSG_DEPRECATED_OCTAL)) {
goto error; goto error;
} }
setOctalCharacterEscape();
} }
if ('0' <= c && c < '8') { if ('0' <= c && c < '8') {
val = 8 * val + JS7_UNDEC(c); val = 8 * val + JS7_UNDEC(c);
@ -1586,7 +1587,7 @@ TokenStream::getTokenInternal()
} }
} }
filenameBuf[i] = '\0'; filenameBuf[i] = '\0';
if (c == '\n') { if (c == EOF || c == '\n') {
if (i > 0) { if (i > 0) {
if (flags & TSF_OWNFILENAME) if (flags & TSF_OWNFILENAME)
cx->free((void *) filename); cx->free((void *) filename);

Просмотреть файл

@ -263,6 +263,7 @@ enum TokenStreamFlags
TSF_XMLTAGMODE = 0x200, /* scanning within an XML tag in E4X */ TSF_XMLTAGMODE = 0x200, /* scanning within an XML tag in E4X */
TSF_XMLTEXTMODE = 0x400, /* scanning XMLText terminal from E4X */ TSF_XMLTEXTMODE = 0x400, /* scanning XMLText terminal from E4X */
TSF_XMLONLYMODE = 0x800, /* don't scan {expr} within text/tag */ TSF_XMLONLYMODE = 0x800, /* don't scan {expr} within text/tag */
TSF_OCTAL_CHAR = 0x1000, /* observed a octal character escape */
/* /*
* To handle the hard case of contiguous HTML comments, we want to clear the * To handle the hard case of contiguous HTML comments, we want to clear the
@ -333,12 +334,15 @@ class TokenStream
void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); } void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); }
void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); } void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); }
void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); } void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); }
void setOctalCharacterEscape(bool enabled = true) { setFlag(enabled, TSF_OCTAL_CHAR); }
bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); } bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); }
bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); } bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); }
bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); } bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); }
bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); } bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); }
bool isEOF() const { return !!(flags & TSF_EOF); } bool isEOF() const { return !!(flags & TSF_EOF); }
bool isError() const { return !!(flags & TSF_ERROR); } bool isError() const { return !!(flags & TSF_ERROR); }
bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; }
/* Mutators. */ /* Mutators. */
bool reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN errorNumber, va_list ap); bool reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN errorNumber, va_list ap);

Просмотреть файл

@ -1385,6 +1385,18 @@ JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
} }
} }
if (branded()) {
uintN thrashCount = getMethodThrashCount();
if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) {
++thrashCount;
setMethodThrashCount(thrashCount);
if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) {
unbrand(cx);
return true;
}
}
}
generateOwnShape(cx); generateOwnShape(cx);
return true; return true;
} }

Просмотреть файл

@ -73,28 +73,6 @@
using namespace js; using namespace js;
using namespace js::gc; using namespace js::gc;
static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
/* static */ const JSScript JSScript::emptyScriptConst = {
JS_INIT_STATIC_CLIST(NULL),
const_cast<jsbytecode*>(emptyScriptCode),
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, 0, true, false, false, false, false,
false, /* usesEval */
false, /* usesArguments */
true, /* warnedAboutTwoArgumentEval */
#ifdef JS_METHODJIT
false, /* debugMode */
#endif
const_cast<jsbytecode*>(emptyScriptCode),
{0, jsatomid(0)}, NULL, NULL, 0, 0, 0,
0, /* nClosedArgs */
0, /* nClosedVars */
NULL, {NULL},
#ifdef CHECK_SCRIPT_OWNER
reinterpret_cast<JSThread*>(1)
#endif
};
#if JS_HAS_XDR #if JS_HAS_XDR
enum ScriptBits { enum ScriptBits {
@ -108,8 +86,7 @@ enum ScriptBits {
}; };
JSBool JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
JSBool *hasMagic)
{ {
JSContext *cx; JSContext *cx;
JSScript *script, *oldscript; JSScript *script, *oldscript;
@ -133,7 +110,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
notes = NULL; notes = NULL;
/* Should not XDR scripts optimized for a single global object. */ /* Should not XDR scripts optimized for a single global object. */
JS_ASSERT_IF(script, !script->globalsOffset); JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset));
if (xdr->mode == JSXDR_ENCODE) if (xdr->mode == JSXDR_ENCODE)
magic = JSXDR_MAGIC_SCRIPT_CURRENT; magic = JSXDR_MAGIC_SCRIPT_CURRENT;
@ -152,45 +129,10 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
if (hasMagic) if (hasMagic)
*hasMagic = JS_TRUE; *hasMagic = JS_TRUE;
/*
* Since the shortest possible script has JSOP_STOP as its only bytecode,
* encode only the length 0 for the emptyScript singleton, and return the
* emptyScript instead of a new script when decoding a script of length 0.
*/
if (xdr->mode == JSXDR_ENCODE) if (xdr->mode == JSXDR_ENCODE)
length = (script == JSScript::emptyScript()) ? 0 : script->length; length = script->length;
if (!JS_XDRUint32(xdr, &length)) if (!JS_XDRUint32(xdr, &length))
return JS_FALSE; return JS_FALSE;
if (length == 0) {
if (xdr->mode == JSXDR_ENCODE) {
JS_ASSERT(*scriptp == JSScript::emptyScript());
return JS_TRUE;
}
/* Decoding: check whether we need a mutable empty script. */
if (cx->debugHooks->newScriptHook)
needMutableScript = true;
if (needMutableScript) {
/*
* We need a mutable empty script but the encoder serialized only
* the shorthand (0 length word) for us. Make a new mutable empty
* script here and return it immediately.
*/
script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!script)
return JS_FALSE;
script->setVersion(JSVERSION_DEFAULT);
script->noScriptRval = true;
script->code[0] = JSOP_STOP;
script->code[1] = SRC_NULL;
*scriptp = script;
return JS_TRUE;
}
*scriptp = JSScript::emptyScript();
return JS_TRUE;
}
if (xdr->mode == JSXDR_ENCODE) { if (xdr->mode == JSXDR_ENCODE) {
prologLength = script->main - script->code; prologLength = script->main - script->code;
@ -208,15 +150,15 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
nsrcnotes = sn - notes; nsrcnotes = sn - notes;
nsrcnotes++; /* room for the terminator */ nsrcnotes++; /* room for the terminator */
if (script->objectsOffset != 0) if (JSScript::isValidOffset(script->objectsOffset))
nobjects = script->objects()->length; nobjects = script->objects()->length;
if (script->upvarsOffset != 0) if (JSScript::isValidOffset(script->upvarsOffset))
nupvars = script->upvars()->length; nupvars = script->upvars()->length;
if (script->regexpsOffset != 0) if (JSScript::isValidOffset(script->regexpsOffset))
nregexps = script->regexps()->length; nregexps = script->regexps()->length;
if (script->trynotesOffset != 0) if (JSScript::isValidOffset(script->trynotesOffset))
ntrynotes = script->trynotes()->length; ntrynotes = script->trynotes()->length;
if (script->constOffset != 0) if (JSScript::isValidOffset(script->constOffset))
nconsts = script->consts()->length; nconsts = script->consts()->length;
nClosedArgs = script->nClosedArgs; nClosedArgs = script->nClosedArgs;
@ -956,39 +898,52 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
script->length = length; script->length = length;
script->setVersion(cx->findVersion()); script->setVersion(cx->findVersion());
uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
cursor = (uint8 *)script + sizeof(JSScript); cursor = (uint8 *)script + sizeof(JSScript);
if (nobjects != 0) { if (nobjects != 0) {
script->objectsOffset = (uint8)(cursor - (uint8 *)script); script->objectsOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSObjectArray); cursor += sizeof(JSObjectArray);
} else {
script->objectsOffset = JSScript::INVALID_OFFSET;
} }
if (nupvars != 0) { if (nupvars != 0) {
script->upvarsOffset = (uint8)(cursor - (uint8 *)script); script->upvarsOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSUpvarArray); cursor += sizeof(JSUpvarArray);
} else {
script->upvarsOffset = JSScript::INVALID_OFFSET;
} }
if (nregexps != 0) { if (nregexps != 0) {
script->regexpsOffset = (uint8)(cursor - (uint8 *)script); script->regexpsOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSObjectArray); cursor += sizeof(JSObjectArray);
} else {
script->regexpsOffset = JSScript::INVALID_OFFSET;
} }
if (ntrynotes != 0) { if (ntrynotes != 0) {
script->trynotesOffset = (uint8)(cursor - (uint8 *)script); script->trynotesOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSTryNoteArray); cursor += sizeof(JSTryNoteArray);
} else {
script->trynotesOffset = JSScript::INVALID_OFFSET;
} }
if (nglobals != 0) { if (nglobals != 0) {
script->globalsOffset = (uint8)(cursor - (uint8 *)script); script->globalsOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(GlobalSlotArray); cursor += sizeof(GlobalSlotArray);
} else {
script->globalsOffset = JSScript::INVALID_OFFSET;
} }
JS_ASSERT((cursor - (uint8 *)script) <= 0xFF); JS_ASSERT((cursor - (uint8 *)script) < 0xFF);
if (nconsts != 0) { if (nconsts != 0) {
script->constOffset = (uint8)(cursor - (uint8 *)script); script->constOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSConstArray); cursor += sizeof(JSConstArray);
} else {
script->constOffset = JSScript::INVALID_OFFSET;
} }
JS_STATIC_ASSERT(sizeof(JSScript) + JS_STATIC_ASSERT(sizeof(JSObjectArray) +
sizeof(JSObjectArray) +
sizeof(JSUpvarArray) + sizeof(JSUpvarArray) +
sizeof(JSObjectArray) + sizeof(JSObjectArray) +
sizeof(JSTryNoteArray) + sizeof(JSTryNoteArray) +
sizeof(GlobalSlotArray) <= 0xFF); sizeof(GlobalSlotArray) < 0xFF);
if (natoms != 0) { if (natoms != 0) {
script->atomMap.length = natoms; script->atomMap.length = natoms;
@ -1097,84 +1052,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
mainLength = CG_OFFSET(cg); mainLength = CG_OFFSET(cg);
prologLength = CG_PROLOG_OFFSET(cg); prologLength = CG_PROLOG_OFFSET(cg);
if (prologLength + mainLength <= 3 &&
!(cg->flags & TCF_IN_FUNCTION)) {
/*
* Check very short scripts to see whether they are "empty" and return
* the const empty-script singleton if so.
*/
jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg);
if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE)
++pc;
if (JSOp(*pc) == JSOP_STOP &&
!cx->debugHooks->newScriptHook &&
!(cg->flags & TCF_NEED_MUTABLE_SCRIPT))
{
/*
* We can probably use the immutable empty script singleton, just
* two hard cases (nupvars != 0, strict mode code) may stand in our
* way.
*/
JSScript *empty = JSScript::emptyScript();
if (cg->inFunction()) {
fun = cg->fun();
JS_ASSERT(fun->isInterpreted() && !FUN_SCRIPT(fun));
if (cg->flags & TCF_STRICT_MODE_CODE) {
/*
* We can't use a script singleton for empty strict mode
* functions because they have poison-pill caller and
* arguments properties:
*
* function strict() { "use strict"; }
* strict.caller; // calls [[ThrowTypeError]] function
*/
goto skip_empty;
}
if (fun->u.i.nupvars != 0) {
/*
* FIXME: upvar uses that were all optimized away may leave
* fun->u.i.nupvars non-zero, and since that count is added
* into fun->countLocalNames() in order to discriminate the
* fun->u.i.names union, we cannot force fun->u.i.nupvars
* to 0 to match JSScript::emptyScript()->upvars()->length.
* So we skip the empty script optimization.
*
* Fixing this requires the compiler to track upvar uses as
* it analyzes and optimizes closures, and subsequently as
* the emitter performs useless expression elimination.
*/
goto skip_empty;
}
fun->freezeLocalNames(cx);
fun->u.i.script = empty;
}
#ifdef DEBUG
{
jsrefcount newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
jsrefcount newLive = cx->runtime->liveScripts;
jsrefcount newTotal =
JS_RUNTIME_METER(cx->runtime, totalEmptyScripts) + cx->runtime->totalScripts;
jsrefcount oldHigh = cx->runtime->highWaterLiveScripts;
if (newEmptyLive + newLive > oldHigh) {
JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive);
if (getenv("JS_DUMP_LIVE_SCRIPTS")) {
fprintf(stderr, "high water script count: %d empty, %d not (total %d)\n",
newEmptyLive, newLive, newTotal);
}
}
}
#endif
return empty;
}
}
skip_empty:
CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
uint16 nClosedArgs = uint16(cg->closedArgs.length()); uint16 nClosedArgs = uint16(cg->closedArgs.length());
JS_ASSERT(nClosedArgs == cg->closedArgs.length()); JS_ASSERT(nClosedArgs == cg->closedArgs.length());
@ -1268,7 +1145,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
if (cg->inFunction()) { if (cg->inFunction()) {
fun = cg->fun(); fun = cg->fun();
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
if (script->upvarsOffset != 0) if (JSScript::isValidOffset(script->upvarsOffset))
JS_ASSERT(script->upvars()->length == fun->u.i.nupvars); JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
else else
fun->u.i.nupvars = 0; fun->u.i.nupvars = 0;
@ -1286,10 +1163,19 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
js_CallNewScriptHook(cx, script, fun); js_CallNewScriptHook(cx, script, fun);
#ifdef DEBUG #ifdef DEBUG
{ {
jsrefcount newLive = JS_RUNTIME_METER(cx->runtime, liveScripts); jsrefcount newEmptyLive, newLive, newTotal;
jsrefcount newEmptyLive = cx->runtime->liveEmptyScripts; if (script->isEmpty()) {
jsrefcount newTotal = newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
JS_RUNTIME_METER(cx->runtime, totalScripts) + cx->runtime->totalEmptyScripts; newLive = cx->runtime->liveScripts;
newTotal =
JS_RUNTIME_METER(cx->runtime, totalEmptyScripts) + cx->runtime->totalScripts;
} else {
newEmptyLive = cx->runtime->liveEmptyScripts;
newLive = JS_RUNTIME_METER(cx->runtime, liveScripts);
newTotal =
cx->runtime->totalEmptyScripts + JS_RUNTIME_METER(cx->runtime, totalScripts);
}
jsrefcount oldHigh = cx->runtime->highWaterLiveScripts; jsrefcount oldHigh = cx->runtime->highWaterLiveScripts;
if (newEmptyLive + newLive > oldHigh) { if (newEmptyLive + newLive > oldHigh) {
JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive); JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive);
@ -1311,8 +1197,6 @@ bad:
JS_FRIEND_API(void) JS_FRIEND_API(void)
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
{ {
JS_ASSERT(script != JSScript::emptyScript());
JSNewScriptHook hook; JSNewScriptHook hook;
hook = cx->debugHooks->newScriptHook; hook = cx->debugHooks->newScriptHook;
@ -1326,8 +1210,6 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
JS_FRIEND_API(void) JS_FRIEND_API(void)
js_CallDestroyScriptHook(JSContext *cx, JSScript *script) js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
{ {
JS_ASSERT(script != JSScript::emptyScript());
JSDestroyScriptHook hook; JSDestroyScriptHook hook;
hook = cx->debugHooks->destroyScriptHook; hook = cx->debugHooks->destroyScriptHook;
@ -1338,13 +1220,11 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
static void static void
DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data) DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
{ {
if (script == JSScript::emptyScript()) {
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
return;
}
#ifdef DEBUG #ifdef DEBUG
JS_RUNTIME_UNMETER(cx->runtime, liveScripts); if (script->isEmpty())
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
else
JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
#endif #endif
js_CallDestroyScriptHook(cx, script); js_CallDestroyScriptHook(cx, script);
@ -1437,7 +1317,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
JSAtomMap *map = &script->atomMap; JSAtomMap *map = &script->atomMap;
MarkAtomRange(trc, map->length, map->vector, "atomMap"); MarkAtomRange(trc, map->length, map->vector, "atomMap");
if (script->objectsOffset != 0) { if (JSScript::isValidOffset(script->objectsOffset)) {
JSObjectArray *objarray = script->objects(); JSObjectArray *objarray = script->objects();
uintN i = objarray->length; uintN i = objarray->length;
do { do {
@ -1449,7 +1329,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
} while (i != 0); } while (i != 0);
} }
if (script->regexpsOffset != 0) { if (JSScript::isValidOffset(script->regexpsOffset)) {
JSObjectArray *objarray = script->regexps(); JSObjectArray *objarray = script->regexps();
uintN i = objarray->length; uintN i = objarray->length;
do { do {
@ -1461,7 +1341,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
} while (i != 0); } while (i != 0);
} }
if (script->constOffset != 0) { if (JSScript::isValidOffset(script->constOffset)) {
JSConstArray *constarray = script->consts(); JSConstArray *constarray = script->consts();
MarkValueRange(trc, constarray->length, constarray->vector, "consts"); MarkValueRange(trc, constarray->length, constarray->vector, "consts");
} }
@ -1481,7 +1361,6 @@ js_NewScriptObject(JSContext *cx, JSScript *script)
AutoScriptRooter root(cx, script); AutoScriptRooter root(cx, script);
JS_ASSERT(!script->u.object); JS_ASSERT(!script->u.object);
JS_ASSERT(script != JSScript::emptyScript());
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL); JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
if (!obj) if (!obj)
@ -1733,7 +1612,6 @@ class DisablePrincipalsTranscoding {
JSScript * JSScript *
js_CloneScript(JSContext *cx, JSScript *script) js_CloneScript(JSContext *cx, JSScript *script)
{ {
JS_ASSERT(script != JSScript::emptyScript());
JS_ASSERT(cx->compartment != script->compartment); JS_ASSERT(cx->compartment != script->compartment);
JS_ASSERT(script->compartment); JS_ASSERT(script->compartment);
@ -1770,7 +1648,7 @@ js_CloneScript(JSContext *cx, JSScript *script)
JS_XDRMemSetData(w, NULL, 0); JS_XDRMemSetData(w, NULL, 0);
// We can't use the public API because it makes a script object. // We can't use the public API because it makes a script object.
if (!js_XDRScript(r, &script, true, NULL)) if (!js_XDRScript(r, &script, NULL))
return NULL; return NULL;
JS_XDRDestroy(r); JS_XDRDestroy(r);
@ -1789,4 +1667,3 @@ JSScript::copyClosedSlotsTo(JSScript *other)
{ {
memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars); memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
} }

Просмотреть файл

@ -198,12 +198,6 @@ struct JSScript {
* to the newly made script's function, if any -- so callers of NewScript * to the newly made script's function, if any -- so callers of NewScript
* are responsible for notifying the debugger after successfully creating any * are responsible for notifying the debugger after successfully creating any
* kind (function or other) of new JSScript. * kind (function or other) of new JSScript.
*
* NB: NewScript always creates a new script; it never returns the empty
* script singleton (JSScript::emptyScript()). Callers who know they can use
* that read-only singleton are responsible for choosing it instead of calling
* NewScript with length and nsrcnotes equal to 1 and other parameters save
* cx all zero.
*/ */
static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
uint32 nobjects, uint32 nupvars, uint32 nregexps, uint32 nobjects, uint32 nupvars, uint32 nregexps,
@ -219,19 +213,22 @@ struct JSScript {
uint16 version; /* JS version under which script was compiled */ uint16 version; /* JS version under which script was compiled */
uint16 nfixed; /* number of slots besides stack operands in uint16 nfixed; /* number of slots besides stack operands in
slot array */ slot array */
/*
* Offsets to various array structures from the end of this script, or
* JSScript::INVALID_OFFSET if the array has length 0.
*/
uint8 objectsOffset; /* offset to the array of nested function, uint8 objectsOffset; /* offset to the array of nested function,
block, scope, xml and one-time regexps block, scope, xml and one-time regexps
objects or 0 if none */ objects */
uint8 upvarsOffset; /* offset of the array of display ("up") uint8 upvarsOffset; /* offset of the array of display ("up")
closure vars or 0 if none */ closure vars */
uint8 regexpsOffset; /* offset to the array of to-be-cloned uint8 regexpsOffset; /* offset to the array of to-be-cloned
regexps or 0 if none. */ regexps */
uint8 trynotesOffset; /* offset to the array of try notes or uint8 trynotesOffset; /* offset to the array of try notes */
0 if none */ uint8 globalsOffset; /* offset to the array of global slots */
uint8 globalsOffset; /* offset to the array of global slots or uint8 constOffset; /* offset to the array of constants */
0 if none */
uint8 constOffset; /* offset to the array of constants or
0 if none */
bool noScriptRval:1; /* no need for result value of last bool noScriptRval:1; /* no need for result value of last
expression statement */ expression statement */
bool savedCallerFun:1; /* object 0 is caller function */ bool savedCallerFun:1; /* object 0 is caller function */
@ -322,34 +319,37 @@ struct JSScript {
/* Script notes are allocated right after the code. */ /* Script notes are allocated right after the code. */
jssrcnote *notes() { return (jssrcnote *)(code + length); } jssrcnote *notes() { return (jssrcnote *)(code + length); }
static const uint8 INVALID_OFFSET = 0xFF;
static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; }
JSObjectArray *objects() { JSObjectArray *objects() {
JS_ASSERT(objectsOffset != 0); JS_ASSERT(isValidOffset(objectsOffset));
return (JSObjectArray *)((uint8 *) this + objectsOffset); return (JSObjectArray *)((uint8 *) (this + 1) + objectsOffset);
} }
JSUpvarArray *upvars() { JSUpvarArray *upvars() {
JS_ASSERT(upvarsOffset != 0); JS_ASSERT(isValidOffset(upvarsOffset));
return (JSUpvarArray *) ((uint8 *) this + upvarsOffset); return (JSUpvarArray *) ((uint8 *) (this + 1) + upvarsOffset);
} }
JSObjectArray *regexps() { JSObjectArray *regexps() {
JS_ASSERT(regexpsOffset != 0); JS_ASSERT(isValidOffset(regexpsOffset));
return (JSObjectArray *) ((uint8 *) this + regexpsOffset); return (JSObjectArray *) ((uint8 *) (this + 1) + regexpsOffset);
} }
JSTryNoteArray *trynotes() { JSTryNoteArray *trynotes() {
JS_ASSERT(trynotesOffset != 0); JS_ASSERT(isValidOffset(trynotesOffset));
return (JSTryNoteArray *) ((uint8 *) this + trynotesOffset); return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset);
} }
js::GlobalSlotArray *globals() { js::GlobalSlotArray *globals() {
JS_ASSERT(globalsOffset != 0); JS_ASSERT(isValidOffset(globalsOffset));
return (js::GlobalSlotArray *) ((uint8 *)this + globalsOffset); return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset);
} }
JSConstArray *consts() { JSConstArray *consts() {
JS_ASSERT(constOffset != 0); JS_ASSERT(isValidOffset(constOffset));
return (JSConstArray *) ((uint8 *) this + constOffset); return (JSConstArray *) ((uint8 *) (this + 1) + constOffset);
} }
JSAtom *getAtom(size_t index) { JSAtom *getAtom(size_t index) {
@ -397,20 +397,10 @@ struct JSScript {
/* /*
* The isEmpty method tells whether this script has code that computes any * The isEmpty method tells whether this script has code that computes any
* result (not return value, result AKA normal completion value) other than * result (not return value, result AKA normal completion value) other than
* JSVAL_VOID, or any other effects. It has a fast path for the case where * JSVAL_VOID, or any other effects.
* |this| is the emptyScript singleton, but it also checks this->length and
* this->code, to handle debugger-generated mutable empty scripts.
*/ */
inline bool isEmpty() const; inline bool isEmpty() const;
/*
* Accessor for the emptyScriptConst singleton, to consolidate const_cast.
* See the private member declaration.
*/
static JSScript *emptyScript() {
return const_cast<JSScript *>(&emptyScriptConst);
}
uint32 getClosedArg(uint32 index) { uint32 getClosedArg(uint32 index) {
JS_ASSERT(index < nClosedArgs); JS_ASSERT(index < nClosedArgs);
return closedSlots[index]; return closedSlots[index];
@ -422,16 +412,6 @@ struct JSScript {
} }
void copyClosedSlotsTo(JSScript *other); void copyClosedSlotsTo(JSScript *other);
private:
/*
* Use const to put this in read-only memory if possible. We are stuck with
* non-const JSScript * and jsbytecode * by legacy code (back in the 1990s,
* const wasn't supported correctly on all target platforms). The debugger
* does mutate bytecode, and script->u.object may be set after construction
* in some cases, so making JSScript pointers const will be "hard".
*/
static const JSScript emptyScriptConst;
}; };
#define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script #define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script
@ -575,18 +555,11 @@ js_CloneScript(JSContext *cx, JSScript *script);
* If magic is null, js_XDRScript returns false on bad magic number errors, * If magic is null, js_XDRScript returns false on bad magic number errors,
* which it reports. * which it reports.
* *
* NB: after a successful JSXDR_DECODE, and provided that *scriptp is not the * NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any
* JSScript::emptyScript() immutable singleton, js_XDRScript callers must do * required subsequent set-up of owning function or script object and then call
* any required subsequent set-up of owning function or script object and then * js_CallNewScriptHook.
* call js_CallNewScriptHook.
*
* If the caller requires a mutable empty script (for debugging or u.object
* ownership setting), pass true for needMutableScript. Otherwise pass false.
* Call js_CallNewScriptHook only with a mutable script, i.e. never with the
* JSScript::emptyScript() singleton.
*/ */
extern JSBool extern JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript, js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic);
JSBool *hasMagic);
#endif /* jsscript_h___ */ #endif /* jsscript_h___ */

Просмотреть файл

@ -70,23 +70,13 @@ JSScript::getRegExp(size_t index)
inline bool inline bool
JSScript::isEmpty() const JSScript::isEmpty() const
{ {
return (this == emptyScript()); if (length > 3)
return false;
// See bug 603044 comment #21. jsbytecode *pc = code;
#if 0 if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
if (this == emptyScript()) ++pc;
return true; return JSOp(*pc) == JSOP_STOP;
if (length <= 3) {
jsbytecode *pc = code;
if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
++pc;
if (JSOp(*pc) == JSOP_STOP)
return true;
}
return false;
#endif
} }
#endif /* jsscriptinlines_h___ */ #endif /* jsscriptinlines_h___ */

Просмотреть файл

@ -1384,8 +1384,8 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen)
*/ */
size_t textstrlen = textstr->length(); size_t textstrlen = textstr->length();
size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2; size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
JSRopeLeafIterator iter(textstr); JSRopeLeafIterator iter;
for (JSString *str = iter.init(); str; str = iter.next()) { for (JSString *str = iter.init(textstr); str; str = iter.next()) {
if (threshold-- == 0 || !strs.append(str)) if (threshold-- == 0 || !strs.append(str))
return StringMatch(textstr->chars(), textstrlen, pat, patlen); return StringMatch(textstr->chars(), textstrlen, pat, patlen);
} }
@ -1871,7 +1871,7 @@ BuildFlatMatchArray(JSContext *cx, JSString *textstr, const FlatMatch &fm, Value
} }
/* For this non-global match, produce a RegExp.exec-style array. */ /* For this non-global match, produce a RegExp.exec-style array. */
JSObject *obj = js_NewSlowArrayObject(cx); JSObject *obj = NewSlowEmptyArray(cx);
if (!obj) if (!obj)
return false; return false;
vp->setObject(*obj); vp->setObject(*obj);
@ -1896,7 +1896,7 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
JSObject *&arrayobj = *static_cast<MatchArgType>(p); JSObject *&arrayobj = *static_cast<MatchArgType>(p);
if (!arrayobj) { if (!arrayobj) {
arrayobj = js_NewArrayObject(cx, 0, NULL); arrayobj = NewDenseEmptyArray(cx);
if (!arrayobj) if (!arrayobj)
return false; return false;
} }
@ -2242,9 +2242,9 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
* If we are replacing over a rope, avoid flattening it by iterating * If we are replacing over a rope, avoid flattening it by iterating
* through it, building a new rope. * through it, building a new rope.
*/ */
JSRopeLeafIterator iter(textstr); JSRopeLeafIterator iter;
size_t pos = 0; size_t pos = 0;
for (JSString *str = iter.init(); str; str = iter.next()) { for (JSString *str = iter.init(textstr); str; str = iter.next()) {
size_t len = str->length(); size_t len = str->length();
size_t strEnd = pos + len; size_t strEnd = pos + len;
if (pos < matchEnd && strEnd > match) { if (pos < matchEnd && strEnd > match) {
@ -2679,7 +2679,7 @@ str_split(JSContext *cx, uintN argc, Value *vp)
if (argc == 0) { if (argc == 0) {
Value v = StringValue(str); Value v = StringValue(str);
JSObject *aobj = js_NewArrayObject(cx, 1, &v); JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
if (!aobj) if (!aobj)
return false; return false;
vp->setObject(*aobj); vp->setObject(*aobj);
@ -2762,7 +2762,7 @@ str_split(JSContext *cx, uintN argc, Value *vp)
if (j == -2) if (j == -2)
return false; return false;
JSObject *aobj = js_NewArrayObject(cx, splits.length(), splits.begin()); JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
if (!aobj) if (!aobj)
return false; return false;
vp->setObject(*aobj); vp->setObject(*aobj);

Просмотреть файл

@ -630,20 +630,13 @@ class JSRopeNodeIterator {
static const size_t DONE_RIGHT = 0x2; static const size_t DONE_RIGHT = 0x2;
public: public:
JSRopeNodeIterator(JSString *str) JSRopeNodeIterator()
: mUsedFlags(0) : mStr(NULL), mUsedFlags(0)
{ {}
mStr = str;
} JSString *init(JSString *str) {
JSString *init() {
/* If we were constructed with a non-rope string, just return that. */
if (!mStr->isRope()) {
JSString *oldStr = mStr;
mStr = NULL;
return oldStr;
}
/* Move to the farthest-left leaf in the rope. */ /* Move to the farthest-left leaf in the rope. */
mStr = str;
while (mStr->isInteriorNode()) while (mStr->isInteriorNode())
mStr = mStr->interiorNodeParent(); mStr = mStr->interiorNodeParent();
while (mStr->ropeLeft()->isInteriorNode()) while (mStr->ropeLeft()->isInteriorNode())
@ -697,14 +690,9 @@ class JSRopeLeafIterator {
JSRopeNodeIterator mNodeIterator; JSRopeNodeIterator mNodeIterator;
public: public:
inline JSString *init(JSString *str) {
JSRopeLeafIterator(JSString *topNode) : JS_ASSERT(str->isTopNode());
mNodeIterator(topNode) { str = mNodeIterator.init(str);
JS_ASSERT(topNode->isTopNode());
}
inline JSString *init() {
JSString *str = mNodeIterator.init();
while (str->isRope()) { while (str->isRope()) {
str = mNodeIterator.next(); str = mNodeIterator.next();
JS_ASSERT(str); JS_ASSERT(str);

Просмотреть файл

@ -2221,7 +2221,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag
global_slots(NULL), global_slots(NULL),
callDepth(anchor ? anchor->calldepth : 0), callDepth(anchor ? anchor->calldepth : 0),
atoms(FrameAtomBase(cx, cx->fp())), atoms(FrameAtomBase(cx, cx->fp())),
consts(cx->fp()->script()->constOffset consts(JSScript::isValidOffset(cx->fp()->script()->constOffset)
? cx->fp()->script()->consts()->vector ? cx->fp()->script()->consts()->vector
: NULL), : NULL),
strictModeCode_ins(NULL), strictModeCode_ins(NULL),
@ -2446,7 +2446,7 @@ TraceRecorder::finishAbort(const char* reason)
AUDIT(recorderAborted); AUDIT(recorderAborted);
#ifdef DEBUG #ifdef DEBUG
debug_only_printf(LC_TMMinimal, debug_only_printf(LC_TMMinimal | LC_TMAbort,
"Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n", "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
tree->treeFileName, tree->treeFileName,
tree->treeLineNumber, tree->treeLineNumber,
@ -2788,6 +2788,13 @@ TraceMonitor::sweep()
JS_ASSERT(!ontrace()); JS_ASSERT(!ontrace());
debug_only_print0(LC_TMTracer, "Purging fragments with dead things"); debug_only_print0(LC_TMTracer, "Purging fragments with dead things");
bool shouldAbortRecording = false;
TreeFragment *recorderTree = NULL;
if (recorder) {
recorderTree = recorder->getTree();
shouldAbortRecording = HasUnreachableGCThings(recorderTree);
}
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
TreeFragment** fragp = &vmfragments[i]; TreeFragment** fragp = &vmfragments[i];
while (TreeFragment* frag = *fragp) { while (TreeFragment* frag = *fragp) {
@ -2805,7 +2812,9 @@ TraceMonitor::sweep()
JS_ASSERT(frag->root == frag); JS_ASSERT(frag->root == frag);
*fragp = frag->next; *fragp = frag->next;
do { do {
verbose_only( FragProfiling_FragFinalizer(frag, this); ) verbose_only( FragProfiling_FragFinalizer(frag, this); );
if (recorderTree == frag)
shouldAbortRecording = true;
TrashTree(frag); TrashTree(frag);
frag = frag->peer; frag = frag->peer;
} while (frag); } while (frag);
@ -2815,7 +2824,7 @@ TraceMonitor::sweep()
} }
} }
if (recorder && HasUnreachableGCThings(recorder->getTree())) if (shouldAbortRecording)
recorder->finishAbort("dead GC things"); recorder->finishAbort("dead GC things");
} }
@ -4289,7 +4298,7 @@ TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit,
RETURN_STOP("Constantly false guard detected"); RETURN_STOP("Constantly false guard detected");
} }
/* /*
* If this assertion fails, first decide if you want recording to * If you hit this assertion, first decide if you want recording to
* abort in the case where the guard always exits. If not, find a way * abort in the case where the guard always exits. If not, find a way
* to detect that case and avoid calling guard(). Otherwise, change * to detect that case and avoid calling guard(). Otherwise, change
* the invocation of guard() so it passes in abortIfAlwaysExits=true, * the invocation of guard() so it passes in abortIfAlwaysExits=true,
@ -4298,7 +4307,7 @@ TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit,
* insGuard() below and an always-exits guard will be inserted, which * insGuard() below and an always-exits guard will be inserted, which
* is correct but sub-optimal.) * is correct but sub-optimal.)
*/ */
JS_ASSERT(0); JS_NOT_REACHED("unexpected constantly false guard detected");
} }
/* /*
@ -7884,9 +7893,9 @@ TraceRecorder::updateAtoms()
{ {
JSScript *script = cx->fp()->script(); JSScript *script = cx->fp()->script();
atoms = FrameAtomBase(cx, cx->fp()); atoms = FrameAtomBase(cx, cx->fp());
consts = cx->fp()->hasImacropc() || script->constOffset == 0 consts = (cx->fp()->hasImacropc() || !JSScript::isValidOffset(script->constOffset))
? 0 ? 0
: script->consts()->vector; : script->consts()->vector;
strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict"); strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
} }
@ -7894,7 +7903,7 @@ JS_REQUIRES_STACK void
TraceRecorder::updateAtoms(JSScript *script) TraceRecorder::updateAtoms(JSScript *script)
{ {
atoms = script->atomMap.vector; atoms = script->atomMap.vector;
consts = script->constOffset == 0 ? 0 : script->consts()->vector; consts = JSScript::isValidOffset(script->constOffset) ? script->consts()->vector : 0;
strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict"); strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
} }
@ -10839,13 +10848,20 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval)
CHECK_STATUS(getClassPrototype(ctor, proto_ins)); CHECK_STATUS(getClassPrototype(ctor, proto_ins));
LIns *arr_ins; LIns *arr_ins;
if (argc == 0 || (argc == 1 && argv[0].isNumber())) { if (argc == 0) {
LIns *args[] = { argc == 0 ? w.immi(0) : d2i(get(argv)), proto_ins, cx_ins }; LIns *args[] = { proto_ins, cx_ins };
arr_ins = w.call(&js_NewEmptyArray_ci, args); arr_ins = w.call(&js::NewDenseEmptyArray_ci, args);
guard(false, w.eqp0(arr_ins), OOM_EXIT); guard(false, w.eqp0(arr_ins), OOM_EXIT);
} else if (argc == 1 && argv[0].isNumber()) {
/* Abort on RangeError if the double doesn't fit in a uint. */
LIns *args[] = { proto_ins, d2i(get(argv)), cx_ins };
arr_ins = w.call(&js::NewDenseUnallocatedArray_ci, args);
guard(false, w.eqp0(arr_ins), OOM_EXIT);
} else { } else {
LIns *args[] = { w.nameImmi(argc), proto_ins, cx_ins }; LIns *args[] = { proto_ins, w.nameImmi(argc), cx_ins };
arr_ins = w.call(&js_NewPreallocatedArray_ci, args); arr_ins = w.call(&js::NewDenseAllocatedArray_ci, args);
guard(false, w.eqp0(arr_ins), OOM_EXIT); guard(false, w.eqp0(arr_ins), OOM_EXIT);
// arr->slots[i] = box_jsval(vp[i]); for i in 0..argc // arr->slots[i] = box_jsval(vp[i]); for i in 0..argc
@ -11165,7 +11181,7 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
} }
} else if (native == js_math_abs) { } else if (native == js_math_abs) {
LIns* a = get(&vp[2]); LIns* a = get(&vp[2]);
if (IsPromoteInt(a)) { if (IsPromoteInt(a) && vp[2].toNumber() != INT_MIN) {
a = w.demote(a); a = w.demote(a);
/* abs(INT_MIN) can't be done using integers; exit if we see it. */ /* abs(INT_MIN) can't be done using integers; exit if we see it. */
LIns* intMin_ins = w.name(w.immi(0x80000000), "INT_MIN"); LIns* intMin_ins = w.name(w.immi(0x80000000), "INT_MIN");
@ -11183,6 +11199,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
JSString *str = vp[1].toString(); JSString *str = vp[1].toString();
if (native == js_str_charAt) { if (native == js_str_charAt) {
jsdouble i = vp[2].toNumber(); jsdouble i = vp[2].toNumber();
if (JSDOUBLE_IS_NaN(i))
i = 0;
if (i < 0 || i >= str->length()) if (i < 0 || i >= str->length())
RETURN_STOP("charAt out of bounds"); RETURN_STOP("charAt out of bounds");
LIns* str_ins = get(&vp[1]); LIns* str_ins = get(&vp[1]);
@ -11194,6 +11212,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
return RECORD_CONTINUE; return RECORD_CONTINUE;
} else if (native == js_str_charCodeAt) { } else if (native == js_str_charCodeAt) {
jsdouble i = vp[2].toNumber(); jsdouble i = vp[2].toNumber();
if (JSDOUBLE_IS_NaN(i))
i = 0;
if (i < 0 || i >= str->length()) if (i < 0 || i >= str->length())
RETURN_STOP("charCodeAt out of bounds"); RETURN_STOP("charCodeAt out of bounds");
LIns* str_ins = get(&vp[1]); LIns* str_ins = get(&vp[1]);
@ -11447,7 +11467,7 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
if (Probes::callTrackingActive(cx)) { if (Probes::callTrackingActive(cx)) {
JSScript *script = FUN_SCRIPT(fun); JSScript *script = FUN_SCRIPT(fun);
if (! script || ! script->isEmpty()) { if (!script || !script->isEmpty()) {
LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins }; LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins };
LIns* call_ins = w.call(&functionProbe_ci, args); LIns* call_ins = w.call(&functionProbe_ci, args);
guard(false, w.eqi0(call_ins), MISMATCH_EXIT); guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
@ -12773,9 +12793,10 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex)
CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins)); CHECK_STATUS_A(makeNumberInt32(idx_ins, &idx_ins));
// Ensure idx >= 0 && idx < length (by using uint32) // Ensure idx >= 0 && idx < length (by using uint32)
guard(true, CHECK_STATUS_A(guard(true,
w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
OVERFLOW_EXIT); "inRange"),
OVERFLOW_EXIT, /* abortIfAlwaysExits = */true));
// We're now ready to store // We're now ready to store
LIns* data_ins = w.ldpConstTypedArrayData(priv_ins); LIns* data_ins = w.ldpConstTypedArrayData(priv_ins);
@ -13198,13 +13219,11 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc,
{ {
/* /*
* The function's identity (JSFunction and therefore JSScript) is guarded, * The function's identity (JSFunction and therefore JSScript) is guarded,
* so we can optimize for the empty script singleton right away. No need to * so we can optimize away the function call if the corresponding script is
* worry about crossing globals or relocating argv, even, in this case! * empty. No need to worry about crossing globals or relocating argv, even,
* * in this case!
* Note that the interpreter shortcuts empty-script call and construct too,
* and does not call any TR::record_*CallComplete hook.
*/ */
if (fun->u.i.script->isEmpty()) { if (fun->script()->isEmpty()) {
LIns* rval_ins; LIns* rval_ins;
if (constructing) { if (constructing) {
LIns* args[] = { get(&fval), w.nameImmpNonGC(&js_ObjectClass), cx_ins }; LIns* args[] = { get(&fval), w.nameImmpNonGC(&js_ObjectClass), cx_ins };
@ -13829,7 +13848,7 @@ TraceRecorder::typedArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_
* length. * length.
*/ */
guard(true, guard(true,
w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), "inRange"),
BRANCH_EXIT); BRANCH_EXIT);
/* We are now ready to load. Do a different type of load /* We are now ready to load. Do a different type of load
@ -14075,8 +14094,8 @@ TraceRecorder::record_JSOP_NEWINIT()
LIns *v_ins; LIns *v_ins;
if (key == JSProto_Array) { if (key == JSProto_Array) {
LIns *args[] = { w.immi(0), proto_ins, cx_ins }; LIns *args[] = { proto_ins, cx_ins };
v_ins = w.call(&js_NewPreallocatedArray_ci, args); v_ins = w.call(&NewDenseEmptyArray_ci, args);
} else { } else {
LIns *args[] = { w.immpNull(), proto_ins, cx_ins }; LIns *args[] = { w.immpNull(), proto_ins, cx_ins };
v_ins = w.call(&js_InitializerObject_ci, args); v_ins = w.call(&js_InitializerObject_ci, args);
@ -14095,8 +14114,8 @@ TraceRecorder::record_JSOP_NEWARRAY()
CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins)); CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins));
unsigned count = GET_UINT24(cx->regs->pc); unsigned count = GET_UINT24(cx->regs->pc);
LIns *args[] = { w.immi(count), proto_ins, cx_ins }; LIns *args[] = { proto_ins, w.immi(count), cx_ins };
LIns *v_ins = w.call(&js_NewPreallocatedArray_ci, args); LIns *v_ins = w.call(&NewDenseAllocatedArray_ci, args);
guard(false, w.eqp0(v_ins), OOM_EXIT); guard(false, w.eqp0(v_ins), OOM_EXIT);
stack(0, v_ins); stack(0, v_ins);

Просмотреть файл

@ -1213,7 +1213,7 @@ class TypedArrayTemplate
bool bool
copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset = 0) copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset = 0)
{ {
JS_ASSERT(offset < length); JS_ASSERT(offset <= length);
NativeType *dest = static_cast<NativeType*>(data) + offset; NativeType *dest = static_cast<NativeType*>(data) + offset;

Просмотреть файл

@ -789,10 +789,10 @@ extern "C++"
# endif /* defined(__cplusplus) */ # endif /* defined(__cplusplus) */
/* Internal helper macros */ /* Internal helper macros */
#define JSVAL_BITS(v) (v.asBits) #define JSVAL_BITS(v) ((v).asBits)
#define JSVAL_FROM_LAYOUT(l) (l) #define JSVAL_FROM_LAYOUT(l) (l)
#define IMPL_TO_JSVAL(v) (v) #define IMPL_TO_JSVAL(v) (v)
#define JSID_BITS(id) (id.asBits) #define JSID_BITS(id) ((id).asBits)
#else /* defined(JS_USE_JSVAL_JSID_STRUCT_TYPES) */ #else /* defined(JS_USE_JSVAL_JSID_STRUCT_TYPES) */

Просмотреть файл

@ -526,12 +526,27 @@ CanReify(Value *vp)
(obj->getNativeIterator()->flags & JSITER_ENUMERATE); (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
} }
struct AutoCloseIterator
{
AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
~AutoCloseIterator() { if (obj) js_CloseIterator(cx, obj); }
void clear() { obj = NULL; }
private:
JSContext *cx;
JSObject *obj;
};
static bool static bool
Reify(JSContext *cx, JSCompartment *origin, Value *vp) Reify(JSContext *cx, JSCompartment *origin, Value *vp)
{ {
JSObject *iterObj = &vp->toObject(); JSObject *iterObj = &vp->toObject();
NativeIterator *ni = iterObj->getNativeIterator(); NativeIterator *ni = iterObj->getNativeIterator();
AutoCloseIterator close(cx, iterObj);
/* Wrap the iteratee. */ /* Wrap the iteratee. */
JSObject *obj = ni->obj; JSObject *obj = ni->obj;
if (!origin->wrap(cx, &obj)) if (!origin->wrap(cx, &obj))
@ -556,6 +571,7 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp)
} }
} }
close.clear();
return js_CloseIterator(cx, iterObj) && return js_CloseIterator(cx, iterObj) &&
VectorToKeyIterator(cx, obj, ni->flags, keys, vp); VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
} }
@ -573,6 +589,7 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp)
} }
close.clear();
return js_CloseIterator(cx, iterObj) && return js_CloseIterator(cx, iterObj) &&
VectorToValueIterator(cx, obj, ni->flags, vals, vp); VectorToValueIterator(cx, obj, ni->flags, vals, vp);
} }

Просмотреть файл

@ -669,13 +669,12 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
{ {
if (!js_XDRScript(xdr, scriptp, true, NULL)) if (!js_XDRScript(xdr, scriptp, NULL))
return JS_FALSE; return JS_FALSE;
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE) {
js_CallNewScriptHook(xdr->cx, *scriptp, NULL); js_CallNewScriptHook(xdr->cx, *scriptp, NULL);
if (*scriptp != JSScript::emptyScript() && if (!js_NewScriptObject(xdr->cx, *scriptp)) {
!js_NewScriptObject(xdr->cx, *scriptp)) {
js_DestroyScript(xdr->cx, *scriptp); js_DestroyScript(xdr->cx, *scriptp);
*scriptp = NULL; *scriptp = NULL;
return JS_FALSE; return JS_FALSE;

Просмотреть файл

@ -5774,7 +5774,7 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
static bool static bool
NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval) NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
{ {
JSObject *arrayobj = js_NewArrayObject(cx, 0, NULL); JSObject *arrayobj = NewDenseEmptyArray(cx);
if (!arrayobj) if (!arrayobj)
return false; return false;
*rval = OBJECT_TO_JSVAL(arrayobj); *rval = OBJECT_TO_JSVAL(arrayobj);
@ -7466,7 +7466,7 @@ GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
if (VALUE_IS_FUNCTION(cx, *vp)) if (VALUE_IS_FUNCTION(cx, *vp))
return true; return true;
target = target->getProto(); target = target->getProto();
if (target == NULL) if (target == NULL || !target->isNative())
break; break;
tvr.setObject(target); tvr.setObject(target);
} }

Просмотреть файл

@ -122,7 +122,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
CompileStatus CompileStatus
mjit::Compiler::compile() mjit::Compiler::compile()
{ {
JS_ASSERT(!script->isEmpty());
JS_ASSERT_IF(isConstructing, !script->jitCtor); JS_ASSERT_IF(isConstructing, !script->jitCtor);
JS_ASSERT_IF(!isConstructing, !script->jitNormal); JS_ASSERT_IF(!isConstructing, !script->jitNormal);

Просмотреть файл

@ -81,7 +81,7 @@ FindExceptionHandler(JSContext *cx)
JSScript *script = fp->script(); JSScript *script = fp->script();
top: top:
if (cx->throwing && script->trynotesOffset) { if (cx->throwing && JSScript::isValidOffset(script->trynotesOffset)) {
// The PC is updated before every stub call, so we can use it here. // The PC is updated before every stub call, so we can use it here.
unsigned offset = cx->regs->pc - script->main; unsigned offset = cx->regs->pc - script->main;
@ -308,18 +308,6 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
*/ */
fp->initCallFrameEarlyPrologue(fun, nactual); fp->initCallFrameEarlyPrologue(fun, nactual);
/* Empty script does nothing. */
bool callingNew = fp->isConstructing();
if (script->isEmpty()) {
RemovePartialFrame(cx, fp);
Value *vp = f.regs.sp - (nactual + 2);
if (callingNew)
vp[0] = vp[1];
else
vp[0].setUndefined();
return NULL;
}
if (nactual != fp->numFormalArgs()) { if (nactual != fp->numFormalArgs()) {
fp = (JSStackFrame *)FixupArity(f, nactual); fp = (JSStackFrame *)FixupArity(f, nactual);
if (!fp) if (!fp)
@ -339,7 +327,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
CompileStatus status = CanMethodJIT(cx, script, fp); CompileStatus status = CanMethodJIT(cx, script, fp);
if (status == Compile_Okay) if (status == Compile_Okay)
return script->getJIT(callingNew)->invokeEntry; return script->getJIT(fp->isConstructing())->invokeEntry;
/* Function did not compile... interpret it. */ /* Function did not compile... interpret it. */
JSBool ok = Interpret(cx, fp); JSBool ok = Interpret(cx, fp);
@ -422,9 +410,7 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
Value *vp = f.regs.sp - (argc + 2); Value *vp = f.regs.sp - (argc + 2);
/* Try to do a fast inline call before the general Invoke path. */ /* Try to do a fast inline call before the general Invoke path. */
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() && if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
!ucr->fun->script()->isEmpty())
{
ucr->callee = &vp->toObject(); ucr->callee = &vp->toObject();
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc)) if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc))
THROW(); THROW();
@ -476,12 +462,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee); ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
if (ucr->fun->isInterpreted()) { if (ucr->fun->isInterpreted()) {
if (ucr->fun->u.i.script->isEmpty()) {
vp->setUndefined();
f.regs.sp = vp + 1;
return;
}
if (!UncachedInlineCall(f, 0, &ucr->codeAddr, argc)) if (!UncachedInlineCall(f, 0, &ucr->codeAddr, argc))
THROW(); THROW();
return; return;

Просмотреть файл

@ -1368,14 +1368,10 @@ stubs::Neg(VMFrame &f)
JSObject * JS_FASTCALL JSObject * JS_FASTCALL
stubs::NewInitArray(VMFrame &f, uint32 count) stubs::NewInitArray(VMFrame &f, uint32 count)
{ {
JSContext *cx = f.cx; JSObject *obj = NewDenseAllocatedArray(f.cx, count);
gc::FinalizeKind kind = GuessObjectGCKind(count, true); if (!obj)
JSObject *obj = NewArrayWithKind(cx, kind);
if (!obj || !obj->ensureSlots(cx, count))
THROWV(NULL); THROWV(NULL);
obj->setArrayLength(count);
return obj; return obj;
} }
@ -2574,8 +2570,8 @@ stubs::Unbrand(VMFrame &f)
if (!thisv.isObject()) if (!thisv.isObject())
return; return;
JSObject *obj = &thisv.toObject(); JSObject *obj = &thisv.toObject();
if (obj->isNative() && !obj->unbrand(f.cx)) if (obj->isNative())
THROW(); obj->unbrand(f.cx);
} }
void JS_FASTCALL void JS_FASTCALL

Просмотреть файл

@ -1975,7 +1975,7 @@ TryNotes(JSContext *cx, JSScript *script)
{ {
JSTryNote *tn, *tnlimit; JSTryNote *tn, *tnlimit;
if (script->trynotesOffset == 0) if (!JSScript::isValidOffset(script->trynotesOffset))
return JS_TRUE; return JS_TRUE;
tn = script->trynotes()->vector; tn = script->trynotes()->vector;
@ -2053,7 +2053,7 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
SrcNotes(cx, script); SrcNotes(cx, script);
TryNotes(cx, script); TryNotes(cx, script);
if (recursive && script->objectsOffset != 0) { if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
JSObjectArray *objects = script->objects(); JSObjectArray *objects = script->objects();
for (uintN i = 0; i != objects->length; ++i) { for (uintN i = 0; i != objects->length; ++i) {
JSObject *obj = objects->vector[i]; JSObject *obj = objects->vector[i];
@ -2139,11 +2139,6 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp)
if (!script) if (!script)
return JS_FALSE; return JS_FALSE;
if (script->isEmpty()) {
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
JSObject *obj = JS_NewScriptObject(cx, script); JSObject *obj = JS_NewScriptObject(cx, script);
if (!obj) if (!obj)
return JS_FALSE; return JS_FALSE;
@ -4293,7 +4288,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("startTraceVis", StartTraceVisNative, 1,0), JS_FN("startTraceVis", StartTraceVisNative, 1,0),
JS_FN("stopTraceVis", StopTraceVisNative, 0,0), JS_FN("stopTraceVis", StopTraceVisNative, 0,0),
#endif #endif
#ifdef DEBUG_ARRAYS #ifdef DEBUG
JS_FN("arrayInfo", js_ArrayInfo, 1,0), JS_FN("arrayInfo", js_ArrayInfo, 1,0),
#endif #endif
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
@ -4420,7 +4415,7 @@ static const char *const shell_help_messages[] = {
"startTraceVis(filename) Start TraceVis recording (stops any current recording)", "startTraceVis(filename) Start TraceVis recording (stops any current recording)",
"stopTraceVis() Stop TraceVis recording", "stopTraceVis() Stop TraceVis recording",
#endif #endif
#ifdef DEBUG_ARRAYS #ifdef DEBUG
"arrayInfo(a1, a2, ...) Report statistics about arrays", "arrayInfo(a1, a2, ...) Report statistics about arrays",
#endif #endif
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE

Просмотреть файл

@ -2,4 +2,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Array/
script sort-01.js script sort-01.js
script toString-01.js script toString-01.js
script toLocaleString-01.js script toLocaleString-01.js
script regress-599159.js
script unshift-01.js script unshift-01.js

Просмотреть файл

@ -0,0 +1,10 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var b = Object.create(Array.prototype);
b.length = 12;
assertEq(b.length, 12);
reportCompare(true,true);

Просмотреть файл

@ -1,4 +1,5 @@
url-prefix ../../jsreftest.html?test=ecma_5/Expressions/ url-prefix ../../jsreftest.html?test=ecma_5/Expressions/
script 11.1.5-01.js script 11.1.5-01.js
script named-accessor-function.js script named-accessor-function.js
script nested-delete-name-in-evalcode.js
script object-literal-accessor-arguments.js script object-literal-accessor-arguments.js

Просмотреть файл

@ -0,0 +1,163 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
//-----------------------------------------------------------------------------
var BUGNUMBER = 616294;
var summary =
"|delete x| inside a function in eval code, where that eval code includes " +
"|var x| at top level, actually does delete the binding for x";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var f;
function testOuterVar()
{
return eval("var x; (function() { return delete x; })");
}
f = testOuterVar();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterFunction()
{
return eval("function x() { } (function() { return delete x; })");
}
f = testOuterFunction();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterForVar()
{
return eval("for (var x; false; ); (function() { return delete x; })");
}
f = testOuterForVar();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterForInVar()
{
return eval("for (var x in {}); (function() { return delete x; })");
}
f = testOuterForInVar();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterNestedVar()
{
return eval("for (var q = 0; q < 5; q++) { var x; } (function() { return delete x; })");
}
f = testOuterNestedVar();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterNestedConditionalVar()
{
return eval("for (var q = 0; q < 5; q++) { if (false) { var x; } } (function() { return delete x; })");
}
f = testOuterNestedConditionalVar();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testVarInWith()
{
return eval("with ({}) { var x; } (function() { return delete x; })");
}
f = testVarInWith();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testForVarInWith()
{
return eval("with ({}) { for (var x = 0; x < 5; x++); } (function() { return delete x; })");
}
f = testForVarInWith();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testForInVarInWith()
{
return eval("with ({}) { for (var x in {}); } (function() { return delete x; })");
}
f = testForInVarInWith();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testUnknown()
{
return eval("nameToDelete = 17; (function() { return delete nameToDelete; })");
}
f = testUnknown();
assertEq(f(), true); // configurable global property, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testArgumentShadow()
{
return eval("var x; (function(x) { return delete x; })");
}
f = testArgumentShadow();
assertEq(f(), false); // non-configurable argument => false
function testArgument()
{
return eval("(function(x) { return delete x; })");
}
f = testArgument();
assertEq(f(), false); // non-configurable argument => false
function testFunctionLocal()
{
return eval("(function() { var x; return delete x; })");
}
f = testFunctionLocal();
assertEq(f(), false); // defined by function code => not configurable => false
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

Просмотреть файл

@ -15,3 +15,4 @@ script watchpoint-deletes-JSPropertyOp-setter.js
script eval-native-callback-is-indirect.js script eval-native-callback-is-indirect.js
script regress-bug607284.js script regress-bug607284.js
script Object-keys-and-object-ids.js script Object-keys-and-object-ids.js
fails script nested-delete-name-in-evalcode.js # bug 604301, at a minimum

Просмотреть файл

@ -0,0 +1,98 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
//-----------------------------------------------------------------------------
var BUGNUMBER = 616294;
var summary =
"|delete x| inside a function in eval code, where that eval code includes " +
"|var x| at top level, actually does delete the binding for x";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var f;
function testOuterLet()
{
return eval("let x; (function() { return delete x; })");
}
f = testOuterLet();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterForLet()
{
return eval("for (let x; false; ); (function() { return delete x; })");
}
f = testOuterForLet();
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterForInLet()
{
return eval("for (let x in {}); (function() { return delete x; })");
}
f = testOuterForInLet();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // not there => true (only non-configurable => false)
function testOuterNestedVarInLetBlock()
{
return eval("let (x = 7) { var x = 9; } (function() { return delete x; })");
}
f = testOuterNestedVarInLetBlock();
assertEq(f(), true); // configurable var, so remove => true
assertEq(f(), true); // let still there, configurable => true
assertEq(f(), true); // nothing at all => true
function testOuterNestedVarInForLet()
{
return eval("for (let q = 0; q < 5; q++) { var x; } (function() { return delete x; })");
}
f = testOuterNestedVarInForLet();
assertEq(f(), true); // configurable, so remove => true
assertEq(f(), true); // there => true
function testArgumentShadowLet()
{
return eval("let x; (function(x) { return delete x; })");
}
f = testArgumentShadowLet();
assertEq(f(), false); // non-configurable argument => false
function testFunctionLocal()
{
return eval("(function() { let x; return delete x; })");
}
f = testFunctionLocal();
assertEq(f(), false); // defined by function code => not configurable => false
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

Просмотреть файл

@ -0,0 +1,78 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
//-----------------------------------------------------------------------------
var BUGNUMBER = 601262;
var summary =
"A string literal containing an octal escape before a strict mode " +
"directive should be a syntax error";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
try
{
eval(" '\\145'; 'use strict'; ");
throw new Error("no error thrown for eval");
}
catch (e)
{
assertEq(e instanceof SyntaxError, true,
"wrong error for octal-escape before strict directive in eval");
}
try
{
Function(" '\\145'; 'use strict'; ");
throw new Error("no error thrown for Function");
}
catch (e)
{
assertEq(e instanceof SyntaxError, true,
"wrong error for octal-escape before strict directive in Function");
}
try
{
eval(" function f(){ '\\145'; 'use strict'; } ");
throw new Error("no error thrown for eval of function");
}
catch (e)
{
assertEq(e instanceof SyntaxError, true,
"wrong error for octal-escape before strict directive in eval of " +
"function");
}
try
{
Function(" function f(){ '\\145'; 'use strict'; } ");
throw new Error("no error thrown for eval of function");
}
catch (e)
{
assertEq(e instanceof SyntaxError, true,
"wrong error for octal-escape before strict directive in eval of " +
"function");
}
eval("function notAnError1() { 5; '\\145'; function g() { 'use strict'; } }");
Function("function notAnError2() { 5; '\\145'; function g() { 'use strict'; } }");
function notAnError3()
{
5;
"\145";
function g() { "use strict"; }
}
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

Просмотреть файл

@ -25,7 +25,7 @@ script 15.4.4.8.js
script 15.4.4.9.js script 15.4.4.9.js
script 15.4.4.12.js script 15.4.4.12.js
script 15.4.4.13.js script 15.4.4.13.js
script 15.4.5.1.js skip script 15.4.5.1.js # Waiting for bug 537873 to be fully resolved.
script 15.5.5.1.js script 15.5.5.1.js
script 15.5.5.2.js script 15.5.5.2.js
script 15.10.7.js script 15.10.7.js
@ -37,3 +37,4 @@ script regress-532254.js
script regress-532041.js script regress-532041.js
script unbrand-this.js script unbrand-this.js
script this-for-function-expression-recursion.js script this-for-function-expression-recursion.js
script directive-prologue-01.js

Просмотреть файл

@ -236,6 +236,7 @@ function test()
empty.set([]); empty.set([]);
empty.set([], 0); empty.set([], 0);
empty.set(empty);
checkThrows(function() empty.set([1])); checkThrows(function() empty.set([1]));
checkThrows(function() empty.set([1], 0)); checkThrows(function() empty.set([1], 0));
@ -244,6 +245,7 @@ function test()
a.set([]); a.set([]);
a.set([], 3); a.set([], 3);
a.set([], 9); a.set([], 9);
a.set(a);
a.set(empty); a.set(empty);
a.set(empty, 3); a.set(empty, 3);

Просмотреть файл

@ -56,3 +56,5 @@ script regress-610026.js
script regress-609617.js script regress-609617.js
script regress-617405-1.js script regress-617405-1.js
script regress-617405-2.js script regress-617405-2.js
skip-if(!xulRuntime.shell) script regress-618576.js # uses evalcx
fails-if(!xulRuntime.shell) script regress-618652.js

Просмотреть файл

@ -0,0 +1,12 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributors: Gary Kwong and Jason Orendorff
*/
var x = <x/>;
x.function::__proto__ = evalcx('');
for (a in x) // don't assert
;
reportCompare(0, 0, 'ok');

Просмотреть файл

@ -0,0 +1,10 @@
options('atline')
var F, L;
eval('//@line 42 "foo"\n' +
'try { nosuchname; } catch (e) { F = e.fileName; L = e.lineNumber; }');
assertEq(F, "foo");
assertEq(L, 42);
reportCompare(0, 0, "ok");

Просмотреть файл

@ -221,36 +221,7 @@ Atob(JSContext *cx, uintN argc, jsval *vp)
if (!argc) if (!argc)
return JS_TRUE; return JS_TRUE;
JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); return nsXPConnect::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
if (!str)
return JS_FALSE;
size_t len = JS_GetStringEncodingLength(cx, str);
if (len == size_t(-1))
return JS_FALSE;
JSUint32 alloc_len = (len + 1) * sizeof(char);
char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
if (!buffer)
return JS_FALSE;
JS_EncodeStringToBuffer(str, buffer, len);
buffer[len] = '\0';
nsDependentCString string(buffer, JS_GetStringLength(str));
nsCAutoString result;
if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
JS_ReportError(cx, "Failed to decode base64 string!");
return JS_FALSE;
}
str = JS_NewStringCopyN(cx, result.get(), result.Length());
if (!str)
return JS_FALSE;
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
return JS_TRUE;
} }
static JSBool static JSBool
@ -259,36 +230,7 @@ Btoa(JSContext *cx, uintN argc, jsval *vp)
if (!argc) if (!argc)
return JS_TRUE; return JS_TRUE;
JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]); return nsXPConnect::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
if (!str)
return JS_FALSE;
size_t len = JS_GetStringEncodingLength(cx, str);
if (len == size_t(-1))
return JS_FALSE;
JSUint32 alloc_len = (len + 1) * sizeof(char);
char *buffer = static_cast<char *>(nsMemory::Alloc(alloc_len));
if (!buffer)
return JS_FALSE;
JS_EncodeStringToBuffer(str, buffer, len);
buffer[len] = '\0';
nsDependentCString data(buffer, len);
nsCAutoString result;
if (NS_FAILED(nsXPConnect::Base64Encode(data, result))) {
JS_ReportError(cx, "Failed to encode base64 data!");
return JS_FALSE;
}
str = JS_NewStringCopyN(cx, result.get(), result.Length());
if (!str)
return JS_FALSE;
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
return JS_TRUE;
} }
static JSFunctionSpec gGlobalFun[] = { static JSFunctionSpec gGlobalFun[] = {

Просмотреть файл

@ -64,6 +64,8 @@
#include "jsdIDebuggerService.h" #include "jsdIDebuggerService.h"
#include "xpcquickstubs.h"
NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect, NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect,
nsIXPConnect, nsIXPConnect,
nsISupportsWeakReference, nsISupportsWeakReference,
@ -2615,8 +2617,7 @@ nsXPConnect::GetCaller(JSContext **aJSContext, JSObject **aObj)
// static // static
nsresult nsresult
nsXPConnect::Base64Encode(const nsACString &aBinaryData, nsXPConnect::Base64Encode(const nsACString &aBinaryData, nsACString &aString)
nsACString &aString)
{ {
// Check for overflow. // Check for overflow.
if(aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3) if(aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3)
@ -2645,8 +2646,7 @@ nsXPConnect::Base64Encode(const nsACString &aBinaryData,
// static // static
nsresult nsresult
nsXPConnect::Base64Encode(const nsAString &aString, nsXPConnect::Base64Encode(const nsAString &aString, nsAString &aBinaryData)
nsAString &aBinaryData)
{ {
NS_LossyConvertUTF16toASCII string(aString); NS_LossyConvertUTF16toASCII string(aString);
nsCAutoString binaryData; nsCAutoString binaryData;
@ -2660,10 +2660,37 @@ nsXPConnect::Base64Encode(const nsAString &aString,
return rv; return rv;
} }
// static
JSBool
nsXPConnect::Base64Encode(JSContext *cx, jsval val, jsval *out)
{
NS_ASSERTION(cx, "Null context!");
NS_ASSERTION(out, "Null jsval pointer!");
jsval root = val;
xpc_qsACString encodedString(cx, root, &root, xpc_qsACString::eNull,
xpc_qsACString::eStringify);
if(!encodedString.IsValid())
return JS_FALSE;
nsCAutoString result;
if(NS_FAILED(nsXPConnect::Base64Encode(encodedString, result)))
{
JS_ReportError(cx, "Failed to encode base64 data!");
return JS_FALSE;
}
JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length());
if (!str)
return JS_FALSE;
*out = STRING_TO_JSVAL(str);
return JS_TRUE;
}
// static // static
nsresult nsresult
nsXPConnect::Base64Decode(const nsACString &aString, nsXPConnect::Base64Decode(const nsACString &aString, nsACString &aBinaryData)
nsACString &aBinaryData)
{ {
// Check for overflow. // Check for overflow.
if(aString.Length() > PR_UINT32_MAX / 3) if(aString.Length() > PR_UINT32_MAX / 3)
@ -2700,8 +2727,7 @@ nsXPConnect::Base64Decode(const nsACString &aString,
// static // static
nsresult nsresult
nsXPConnect::Base64Decode(const nsAString &aBinaryData, nsXPConnect::Base64Decode(const nsAString &aBinaryData, nsAString &aString)
nsAString &aString)
{ {
NS_LossyConvertUTF16toASCII binaryData(aBinaryData); NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
nsCAutoString string; nsCAutoString string;
@ -2715,6 +2741,34 @@ nsXPConnect::Base64Decode(const nsAString &aBinaryData,
return rv; return rv;
} }
// static
JSBool
nsXPConnect::Base64Decode(JSContext *cx, jsval val, jsval *out)
{
NS_ASSERTION(cx, "Null context!");
NS_ASSERTION(out, "Null jsval pointer!");
jsval root = val;
xpc_qsACString encodedString(cx, root, &root, xpc_qsACString::eNull,
xpc_qsACString::eNull);
if(!encodedString.IsValid())
return JS_FALSE;
nsCAutoString result;
if(NS_FAILED(nsXPConnect::Base64Decode(encodedString, result)))
{
JS_ReportError(cx, "Failed to decode base64 string!");
return JS_FALSE;
}
JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length());
if(!str)
return JS_FALSE;
*out = STRING_TO_JSVAL(str);
return JS_TRUE;
}
NS_IMETHODIMP NS_IMETHODIMP
nsXPConnect::SetDebugModeWhenPossible(PRBool mode) nsXPConnect::SetDebugModeWhenPossible(PRBool mode)
{ {

Просмотреть файл

@ -570,12 +570,18 @@ public:
static nsresult Base64Encode(const nsAString &aString, static nsresult Base64Encode(const nsAString &aString,
nsAString &aBinaryData); nsAString &aBinaryData);
// If this returns JS_FALSE then an exception will be set on cx.
static JSBool Base64Encode(JSContext *cx, jsval val, jsval *out);
static nsresult Base64Decode(const nsACString &aBinaryData, static nsresult Base64Decode(const nsACString &aBinaryData,
nsACString &aString); nsACString &aString);
static nsresult Base64Decode(const nsAString &aBinaryData, static nsresult Base64Decode(const nsAString &aBinaryData,
nsAString &aString); nsAString &aString);
// If this returns JS_FALSE then an exception will be set on cx.
static JSBool Base64Decode(JSContext *cx, jsval val, jsval *out);
// nsCycleCollectionParticipant // nsCycleCollectionParticipant
NS_IMETHOD RootAndUnlinkJSObjects(void *p); NS_IMETHOD RootAndUnlinkJSObjects(void *p);
NS_IMETHOD Unlink(void *p); NS_IMETHOD Unlink(void *p);

Просмотреть файл

@ -709,14 +709,24 @@ xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
mValid = JS_TRUE; mValid = JS_TRUE;
} }
xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval) xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
StringificationBehavior nullBehavior,
StringificationBehavior undefinedBehavior)
{ {
typedef implementation_type::char_traits traits; typedef implementation_type::char_traits traits;
// From the T_CSTRING case in XPCConvert::JSData2Native. // From the T_CSTRING case in XPCConvert::JSData2Native.
JSString *s = InitOrStringify<traits>(cx, v, pval, eNull, eNull); JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
undefinedBehavior);
if (!s) if (!s)
return; return;
size_t len = JS_GetStringEncodingLength(cx, s);
if(len == size_t(-1))
{
mValid = JS_FALSE;
return;
}
JSAutoByteString bytes(cx, s); JSAutoByteString bytes(cx, s);
if(!bytes) if(!bytes)
{ {
@ -724,7 +734,7 @@ xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval)
return; return;
} }
new(mBuf) implementation_type(bytes.ptr(), strlen(bytes.ptr())); new(mBuf) implementation_type(bytes.ptr(), len);
mValid = JS_TRUE; mValid = JS_TRUE;
} }

Просмотреть файл

@ -439,7 +439,9 @@ public:
class xpc_qsACString : public xpc_qsBasicString<nsACString, nsCString> class xpc_qsACString : public xpc_qsBasicString<nsACString, nsCString>
{ {
public: public:
xpc_qsACString(JSContext *cx, jsval v, jsval *pval); xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
StringificationBehavior nullBehavior = eNull,
StringificationBehavior undefinedBehavior = eNull);
}; };
/** /**