зеркало из https://github.com/mozilla/pjs.git
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:
Коммит
ecc4cfb7c4
|
@ -1911,13 +1911,13 @@ case "$host" in
|
|||
esac
|
||||
|
||||
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(,)
|
||||
$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=$?
|
||||
changequote([,])
|
||||
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
|
||||
AC_MSG_RESULT([yes])
|
||||
|
||||
|
|
|
@ -159,7 +159,6 @@ nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -208,7 +207,6 @@ nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -237,7 +235,6 @@ nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -313,7 +310,6 @@ nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -363,7 +359,6 @@ nsDOMWorkerFunctions::AtoB(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -372,39 +367,8 @@ nsDOMWorkerFunctions::AtoB(JSContext* aCx,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
|
||||
if (!str) {
|
||||
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;
|
||||
return nsXPConnect::Base64Decode(aCx, JS_ARGV(aCx, aVp)[0],
|
||||
&JS_RVAL(aCx, aVp));
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -416,7 +380,6 @@ nsDOMWorkerFunctions::BtoA(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -425,39 +388,8 @@ nsDOMWorkerFunctions::BtoA(JSContext* aCx,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
|
||||
if (!str) {
|
||||
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;
|
||||
return nsXPConnect::Base64Encode(aCx, JS_ARGV(aCx, aVp)[0],
|
||||
&JS_RVAL(aCx, aVp));
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -491,7 +423,6 @@ nsDOMWorkerFunctions::MakeNewWorker(JSContext* aCx,
|
|||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -585,7 +516,6 @@ nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx,
|
|||
NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
JS_ReportError(aCx, "Worker is canceled");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
var data = [ -1, 0, 1, 1.5, undefined, true, false ];
|
||||
|
||||
// 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 data = [ -1, 0, 1, 1.5, null, undefined, true, false, "foo",
|
||||
"123456789012345", "1234567890123456", "12345678901234567"];
|
||||
|
||||
var str = "";
|
||||
for (var i = 0; i < 30; i++) {
|
||||
|
|
|
@ -1168,6 +1168,19 @@ private:
|
|||
);
|
||||
#endif
|
||||
#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 (
|
||||
"movl $0x1, %eax;"
|
||||
"pushl %ebx;"
|
||||
|
@ -1179,6 +1192,7 @@ private:
|
|||
: "S" (&flags_ecx), "D" (&flags_edx)
|
||||
: "%eax", "%ecx", "%edx"
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
static const int SSEFeatureBit = 1 << 25;
|
||||
static const int SSE2FeatureBit = 1 << 26;
|
||||
|
|
|
@ -986,7 +986,7 @@ else
|
|||
AC_MSG_RESULT([yes])
|
||||
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
|
||||
AC_MSG_ERROR([python was not found in \$PATH])
|
||||
fi
|
||||
|
@ -1900,13 +1900,13 @@ esac
|
|||
|
||||
dnl We require version 2.4 or newer of Python to build,
|
||||
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(,)
|
||||
$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=$?
|
||||
changequote([,])
|
||||
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
|
||||
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;
|
||||
}
|
||||
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);
|
||||
/* NB: jsuint cast does ToUint32. */
|
||||
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)
|
||||
|
@ -4672,7 +4672,6 @@ JS_NewScriptObject(JSContext *cx, JSScript *script)
|
|||
* described in the comment for JSScript::u.object.
|
||||
*/
|
||||
JS_ASSERT(script->u.object);
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
return script->u.object;
|
||||
}
|
||||
|
||||
|
@ -4889,7 +4888,7 @@ JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
|
|||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj, script);
|
||||
/* 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));
|
||||
LAST_FRAME_CHECKS(cx, 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_NewStringCopyN(JSContext *cx, const char *s, size_t n)
|
||||
{
|
||||
|
|
172
js/src/jsapi.h
172
js/src/jsapi.h
|
@ -1335,63 +1335,23 @@ namespace js {
|
|||
* than that, we have avoided all garbage collection hazards.
|
||||
*/
|
||||
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>
|
||||
class Anchor: AnchorPermitted<T> {
|
||||
public:
|
||||
Anchor() { }
|
||||
explicit Anchor(T t) { hold = t; }
|
||||
~Anchor() {
|
||||
#ifdef __GNUC__
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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; }
|
||||
inline ~Anchor();
|
||||
T &get() { return hold; }
|
||||
void set(const T &t) { hold = t; }
|
||||
void clear() { hold = 0; }
|
||||
private:
|
||||
T hold;
|
||||
/* Anchors should not be assigned or passed to functions. */
|
||||
|
@ -1399,44 +1359,71 @@ class Anchor: AnchorPermitted<T> {
|
|||
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
|
||||
* scanner doesn't actually recgonize fail. Such anchors would have no effect.
|
||||
* 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| 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 {
|
||||
protected:
|
||||
#ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES
|
||||
template<typename T> void doAssignment(volatile T &lhs, const T &rhs) {
|
||||
lhs = rhs;
|
||||
}
|
||||
template<>
|
||||
inline Anchor<jsval>::~Anchor() {
|
||||
volatile jsval sink;
|
||||
sink.asBits = hold.asBits;
|
||||
}
|
||||
#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
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
@ -2932,15 +2919,12 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp);
|
|||
/*
|
||||
* Strings.
|
||||
*
|
||||
* NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but
|
||||
* on error (signified by null return), it leaves bytes owned by the caller.
|
||||
* So the caller must free bytes in the error case, if it has no use for them.
|
||||
* In contrast, all the JS_New*StringCopy* functions do not take ownership of
|
||||
* the character memory passed to them -- they copy it.
|
||||
* NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
|
||||
* but on error (signified by null return), it leaves chars owned by the
|
||||
* caller. So the caller must free bytes in the error case, if it has no use
|
||||
* for them. In contrast, all the JS_New*StringCopy* functions do not take
|
||||
* 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 *)
|
||||
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n);
|
||||
|
||||
|
|
|
@ -52,13 +52,15 @@
|
|||
* - The number of element slots (capacity), gettable with
|
||||
* getDenseArrayCapacity().
|
||||
*
|
||||
* In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
|
||||
* values. The final slot in fslots is unused.
|
||||
* In dense mode, holes in the array are represented by
|
||||
* MagicValue(JS_ARRAY_HOLE) invalid values.
|
||||
*
|
||||
* 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
|
||||
* array_length_setter for an explanation of how the first, most surprising
|
||||
* case may occur.
|
||||
* length may be greater than, less than, or equal to the capacity. The first
|
||||
* case may occur when the user writes "new Array(100), in which case the
|
||||
* 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
|
||||
* are met:
|
||||
|
@ -170,14 +172,16 @@ js_StringIsIndex(JSString *str, jsuint *indexp)
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static jsuint
|
||||
ValueIsLength(JSContext *cx, Value* vp)
|
||||
static bool
|
||||
ValueToLength(JSContext *cx, Value* vp, jsuint* plength)
|
||||
{
|
||||
if (vp->isInt32()) {
|
||||
int32_t i = vp->toInt32();
|
||||
if (i < 0)
|
||||
goto error;
|
||||
return (jsuint) i;
|
||||
|
||||
*plength = (jsuint)(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
jsdouble d;
|
||||
|
@ -186,18 +190,20 @@ ValueIsLength(JSContext *cx, Value* vp)
|
|||
|
||||
if (JSDOUBLE_IS_NaN(d))
|
||||
goto error;
|
||||
|
||||
jsuint length;
|
||||
length = (jsuint) d;
|
||||
if (d != (jsdouble) length)
|
||||
goto error;
|
||||
vp->setNumber(length);
|
||||
return length;
|
||||
|
||||
error:
|
||||
|
||||
*plength = length;
|
||||
return true;
|
||||
|
||||
error:
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_ARRAY_LENGTH);
|
||||
vp->setNull();
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -557,8 +563,10 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
|
|||
if (!ok)
|
||||
return false;
|
||||
|
||||
*lengthp = ValueIsLength(cx, tvr.addr());
|
||||
return !tvr.value().isNull();
|
||||
if (!ValueToLength(cx, tvr.addr(), lengthp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -581,27 +589,20 @@ array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||
}
|
||||
|
||||
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;
|
||||
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()) {
|
||||
jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
|
||||
|
||||
return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
newlen = ValueIsLength(cx, vp);
|
||||
if (vp->isNull())
|
||||
if (!ValueToLength(cx, vp, &newlen))
|
||||
return false;
|
||||
|
||||
oldlen = obj->getArrayLength();
|
||||
|
||||
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)) {
|
||||
obj->setArrayLength(oldlen + 1);
|
||||
if (strict)
|
||||
return false;
|
||||
JS_ClearPendingException(cx);
|
||||
return true;
|
||||
}
|
||||
|
@ -798,7 +797,7 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric
|
|||
uint32 i;
|
||||
|
||||
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())
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -996,34 +984,7 @@ Class js_SlowArrayClass = {
|
|||
PropertyStub, /* setProperty */
|
||||
EnumerateStub,
|
||||
ResolveStub,
|
||||
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 */
|
||||
}
|
||||
js_TryValueOf
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1053,7 +1014,7 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
|||
* The getter/setter here will directly access the object's private value.
|
||||
*/
|
||||
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)) {
|
||||
setMap(oldMap);
|
||||
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
|
||||
* 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)
|
||||
return JS_FALSE;
|
||||
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 (count > 0) {
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj2) &&
|
||||
end <= obj->getDenseArrayCapacity()) {
|
||||
if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
|
||||
return JS_FALSE;
|
||||
|
@ -2459,7 +2419,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
|||
*/
|
||||
length = aobj->getArrayLength();
|
||||
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)
|
||||
return JS_FALSE;
|
||||
nobj->setArrayLength(length);
|
||||
|
@ -2469,7 +2429,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
|||
argc--;
|
||||
p++;
|
||||
} else {
|
||||
nobj = js_NewArrayObject(cx, 0, NULL);
|
||||
nobj = NewDenseEmptyArray(cx);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
|
@ -2490,8 +2450,8 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
|
|||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
|
||||
if (!aobj->getProperty(cx, id, tvr.addr()))
|
||||
return false;
|
||||
jsuint alength = ValueIsLength(cx, tvr.addr());
|
||||
if (tvr.value().isNull())
|
||||
jsuint alength;
|
||||
if (!ValueToLength(cx, tvr.addr(), &alength))
|
||||
return false;
|
||||
for (jsuint slot = 0; slot < alength; slot++) {
|
||||
JSBool hole;
|
||||
|
@ -2572,7 +2532,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
|||
|
||||
if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
|
||||
!js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
|
||||
nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
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. */
|
||||
nobj = js_NewArrayObject(cx, 0, NULL);
|
||||
nobj = NewDenseAllocatedArray(cx, end - begin);
|
||||
if (!nobj)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*nobj);
|
||||
|
@ -2595,7 +2555,7 @@ array_slice(JSContext *cx, uintN argc, Value *vp)
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return js_SetLengthProperty(cx, nobj, end - begin);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#if JS_HAS_ARRAY_EXTRAS
|
||||
|
@ -2757,7 +2717,7 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
|||
case MAP:
|
||||
case FILTER:
|
||||
newlen = (mode == MAP) ? length : 0;
|
||||
newarr = js_NewArrayObject(cx, newlen, NULL);
|
||||
newarr = NewDenseAllocatedArray(cx, newlen);
|
||||
if (!newarr)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*newarr);
|
||||
|
@ -2964,86 +2924,31 @@ static JSFunctionSpec array_static_methods[] = {
|
|||
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
|
||||
js_Array(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
jsuint length;
|
||||
const Value *vector;
|
||||
JSObject *obj;
|
||||
|
||||
if (argc == 0) {
|
||||
length = 0;
|
||||
vector = NULL;
|
||||
obj = NewDenseEmptyArray(cx);
|
||||
} else if (argc > 1) {
|
||||
length = (jsuint) argc;
|
||||
vector = vp + 2;
|
||||
obj = NewDenseCopiedArray(cx, argc, vp + 2);
|
||||
} else if (!vp[2].isNumber()) {
|
||||
length = 1;
|
||||
vector = vp + 2;
|
||||
obj = NewDenseCopiedArray(cx, 1, vp + 2);
|
||||
} else {
|
||||
length = ValueIsLength(cx, vp + 2);
|
||||
if (vp[2].isNull())
|
||||
jsuint length;
|
||||
if (!ValueToLength(cx, vp + 2, &length))
|
||||
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)
|
||||
return JS_FALSE;
|
||||
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 *
|
||||
js_InitArrayClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -3062,31 +2967,83 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
|
|||
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)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
obj->setArrayLength(0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
JSBool
|
||||
js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp)
|
||||
|
@ -3241,10 +3198,11 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
|
|||
vector.append(val);
|
||||
}
|
||||
|
||||
*clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
|
||||
*clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin());
|
||||
if (!*clone)
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -177,12 +177,33 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
|
|||
extern bool
|
||||
js_InitContextBusyArrayTable(JSContext *cx);
|
||||
|
||||
extern JSObject *
|
||||
js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
|
||||
namespace js
|
||||
{
|
||||
|
||||
/* 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 *
|
||||
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
|
||||
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
|
||||
|
@ -283,25 +304,6 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
|
|||
JSBool
|
||||
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
|
||||
* primitive values.
|
||||
|
|
|
@ -575,8 +575,11 @@ js_dmod(jsdouble a, jsdouble b);
|
|||
#endif /* !JS_TRACER */
|
||||
|
||||
/* Defined in jsarray.cpp. */
|
||||
JS_DECLARE_CALLINFO(js_NewEmptyArray)
|
||||
JS_DECLARE_CALLINFO(js_NewPreallocatedArray)
|
||||
namespace js {
|
||||
JS_DECLARE_CALLINFO(NewDenseEmptyArray)
|
||||
JS_DECLARE_CALLINFO(NewDenseAllocatedArray)
|
||||
JS_DECLARE_CALLINFO(NewDenseUnallocatedArray)
|
||||
}
|
||||
JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
|
||||
JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity)
|
||||
|
||||
|
|
|
@ -763,7 +763,7 @@ JSStructuredCloneReader::startRead(Value *vp)
|
|||
case SCTAG_ARRAY_OBJECT:
|
||||
case SCTAG_OBJECT_OBJECT: {
|
||||
JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
|
||||
? js_NewArrayObject(context(), 0, NULL)
|
||||
? NewDenseEmptyArray(context())
|
||||
: NewBuiltinClassInstance(context(), &js_ObjectClass);
|
||||
if (!obj || !objs.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
|
|
|
@ -591,7 +591,7 @@ class CompartmentChecker
|
|||
}
|
||||
|
||||
void check(JSScript *script) {
|
||||
if (script && script != JSScript::emptyScript()) {
|
||||
if (script) {
|
||||
check(script->compartment);
|
||||
if (script->u.object)
|
||||
check(script->u.object);
|
||||
|
|
|
@ -265,12 +265,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|||
if (!CheckDebugMode(cx))
|
||||
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);
|
||||
junk = NULL;
|
||||
rt = cx->runtime;
|
||||
|
@ -1761,7 +1755,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
|
|||
continue;
|
||||
nbytes += (sn - notes + 1) * sizeof *sn;
|
||||
|
||||
if (script->objectsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->objectsOffset)) {
|
||||
objarray = script->objects();
|
||||
i = objarray->length;
|
||||
nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
|
||||
|
@ -1770,7 +1764,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
|
|||
} while (i != 0);
|
||||
}
|
||||
|
||||
if (script->regexpsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->regexpsOffset)) {
|
||||
objarray = script->regexps();
|
||||
i = objarray->length;
|
||||
nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
|
||||
|
@ -1779,7 +1773,7 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
|
|||
} while (i != 0);
|
||||
}
|
||||
|
||||
if (script->trynotesOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->trynotesOffset)) {
|
||||
nbytes += sizeof(JSTryNoteArray) +
|
||||
script->trynotes()->length * sizeof(JSTryNote);
|
||||
}
|
||||
|
|
|
@ -1737,7 +1737,7 @@ LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
|||
break;
|
||||
}
|
||||
}
|
||||
} while ((cg = (JSCodeGenerator *) cg->parent) != NULL);
|
||||
} while (cg->parent && (cg = cg->parent->asCodeGenerator()));
|
||||
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
|
||||
* bindings visible to the compiler are permanent in JS unless the
|
||||
* declaration originates in eval code. We detect eval code by testing
|
||||
* 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.
|
||||
* declaration originates at top level in eval code.
|
||||
*/
|
||||
switch (op) {
|
||||
case JSOP_NAME:
|
||||
|
@ -2175,7 +2169,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
break;
|
||||
case JSOP_DELNAME:
|
||||
if (dn_kind != JSDefinition::UNKNOWN) {
|
||||
if (cg->parser->callerFrame && !cg->funbox)
|
||||
if (cg->parser->callerFrame && dn->isTopLevel())
|
||||
JS_ASSERT(cg->compileAndGo());
|
||||
else
|
||||
pn->pn_op = JSOP_FALSE;
|
||||
|
@ -2353,9 +2347,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
JSTreeContext *tc = cg;
|
||||
while (tc->staticLevel != level)
|
||||
tc = tc->parent;
|
||||
JS_ASSERT(tc->compiling());
|
||||
|
||||
JSCodeGenerator *evalcg = (JSCodeGenerator *) tc;
|
||||
JSCodeGenerator *evalcg = tc->asCodeGenerator();
|
||||
JS_ASSERT(evalcg->compileAndGo());
|
||||
JS_ASSERT(caller->isFunctionFrame());
|
||||
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);
|
||||
|
||||
UpvarCookie *vector = cg->upvarMap.vector;
|
||||
if (!vector) {
|
||||
uint32 length = cg->lexdeps.count;
|
||||
|
||||
vector = (UpvarCookie *) js_calloc(length * sizeof *vector);
|
||||
uint32 length = cg->lexdeps.count;
|
||||
if (!vector || cg->upvarMap.length != length) {
|
||||
vector = (UpvarCookie *) js_realloc(vector, length * sizeof *vector);
|
||||
if (!vector) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
|
@ -2455,6 +2447,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
slot += tc->fun()->nargs;
|
||||
}
|
||||
|
||||
JS_ASSERT(index < cg->upvarMap.length);
|
||||
vector[index].set(skip, slot);
|
||||
}
|
||||
|
||||
|
@ -4601,11 +4594,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
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,
|
||||
FUN_KIND(fun) == JSFUN_INTERPRETED);
|
||||
|
||||
|
@ -4625,7 +4613,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (!cg2->init())
|
||||
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 (cg2->flags & TCF_HAS_SHARPS) {
|
||||
cg2->sharpSlotBase = fun->sharpSlotBase(cx);
|
||||
|
|
|
@ -364,8 +364,16 @@ struct JSTreeContext { /* tree context for semantic checks */
|
|||
JSObject *blockChain() {
|
||||
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. */
|
||||
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 inFunction() const { return flags & TCF_IN_FUNCTION; }
|
||||
|
||||
bool compiling() const { return flags & TCF_COMPILING; }
|
||||
inline JSCodeGenerator *asCodeGenerator();
|
||||
|
||||
bool usesArguments() const {
|
||||
return flags & TCF_FUN_USES_ARGUMENTS;
|
||||
|
@ -594,7 +604,7 @@ struct JSCodeGenerator : public JSTreeContext
|
|||
SlotVector closedVars;
|
||||
|
||||
uint16 traceIndex; /* index for the next JSOP_TRACE instruction */
|
||||
|
||||
|
||||
/*
|
||||
* Initialize cg to allocate bytecode space from codePool, source note
|
||||
* 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_PROLOG(cg) ((cg)->current = &(cg)->prolog)
|
||||
|
||||
inline JSCodeGenerator *
|
||||
JSTreeContext::asCodeGenerator()
|
||||
{
|
||||
JS_ASSERT(compiling());
|
||||
return static_cast<JSCodeGenerator *>(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. */
|
||||
JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes,
|
||||
script->atomMap.length,
|
||||
(script->objectsOffset != 0)
|
||||
JSScript::isValidOffset(script->objectsOffset)
|
||||
? script->objects()->length
|
||||
: 0,
|
||||
fun->u.i.nupvars,
|
||||
(script->regexpsOffset != 0)
|
||||
JSScript::isValidOffset(script->regexpsOffset)
|
||||
? script->regexps()->length
|
||||
: 0,
|
||||
(script->trynotesOffset != 0)
|
||||
JSScript::isValidOffset(script->trynotesOffset)
|
||||
? script->trynotes()->length
|
||||
: 0,
|
||||
(script->constOffset != 0)
|
||||
JSScript::isValidOffset(script->constOffset)
|
||||
? script->consts()->length
|
||||
: 0,
|
||||
(script->globalsOffset != 0)
|
||||
JSScript::isValidOffset(script->globalsOffset)
|
||||
? script->globals()->length
|
||||
: 0,
|
||||
script->nClosedArgs,
|
||||
|
@ -416,19 +416,19 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
|||
memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
|
||||
memcpy(wscript->atomMap.vector, script->atomMap.vector,
|
||||
wscript->atomMap.length * sizeof(JSAtom *));
|
||||
if (script->objectsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->objectsOffset)) {
|
||||
memcpy(wscript->objects()->vector, script->objects()->vector,
|
||||
wscript->objects()->length * sizeof(JSObject *));
|
||||
}
|
||||
if (script->regexpsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->regexpsOffset)) {
|
||||
memcpy(wscript->regexps()->vector, script->regexps()->vector,
|
||||
wscript->regexps()->length * sizeof(JSObject *));
|
||||
}
|
||||
if (script->trynotesOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->trynotesOffset)) {
|
||||
memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
|
||||
wscript->trynotes()->length * sizeof(JSTryNote));
|
||||
}
|
||||
if (script->globalsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->globalsOffset)) {
|
||||
memcpy(wscript->globals()->vector, script->globals()->vector,
|
||||
wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
|
||||
}
|
||||
|
@ -1959,17 +1959,15 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
|||
fun->freezeLocalNames(cx);
|
||||
}
|
||||
|
||||
if (!js_XDRScript(xdr, &fun->u.i.script, false, NULL))
|
||||
if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
|
||||
return false;
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
*objp = FUN_OBJECT(fun);
|
||||
if (fun->u.i.script != JSScript::emptyScript()) {
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
fun->u.i.script->owner = NULL;
|
||||
fun->script()->owner = NULL;
|
||||
#endif
|
||||
js_CallNewScriptHook(cx, fun->u.i.script, fun);
|
||||
}
|
||||
js_CallNewScriptHook(cx, fun->script(), fun);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2717,7 +2715,18 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
|
|||
if (!fun)
|
||||
return NULL;
|
||||
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) {
|
||||
/* 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()) {
|
||||
JSScript *script = cfun->u.i.script;
|
||||
JS_ASSERT(script);
|
||||
if (script != JSScript::emptyScript()) {
|
||||
JS_ASSERT(script->compartment == fun->compartment());
|
||||
JS_ASSERT(script->compartment != cx->compartment);
|
||||
cfun->u.i.script = js_CloneScript(cx, script);
|
||||
if (!cfun->u.i.script)
|
||||
return NULL;
|
||||
JS_ASSERT(cfun->u.i.script != JSScript::emptyScript());
|
||||
JS_ASSERT(script->compartment == fun->compartment());
|
||||
JS_ASSERT(script->compartment != cx->compartment);
|
||||
|
||||
cfun->u.i.script = js_CloneScript(cx, script);
|
||||
if (!cfun->u.i.script)
|
||||
return NULL;
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
cfun->u.i.script->owner = NULL;
|
||||
cfun->script()->owner = NULL;
|
||||
#endif
|
||||
js_CallNewScriptHook(cx, cfun->u.i.script, cfun);
|
||||
}
|
||||
js_CallNewScriptHook(cx, cfun->script(), cfun);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
|
@ -2852,7 +2859,7 @@ JSObject * JS_FASTCALL
|
|||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
|
||||
{
|
||||
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
|
||||
: 0) == fun->u.i.nupvars);
|
||||
|
||||
|
|
|
@ -351,18 +351,24 @@ TypedMarker(JSTracer *trc, JSShortString *thing)
|
|||
}
|
||||
|
||||
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
|
||||
* rope; otherwise, we only iterate once: on the string itself.
|
||||
* When marking any node of a rope, mark the entire rope. This means if
|
||||
* a given rope node is already marked, we are done.
|
||||
*/
|
||||
JSRopeNodeIterator iter(thing);
|
||||
JSString *str = iter.init();
|
||||
JSRopeNodeIterator iter;
|
||||
if (str->isRope()) {
|
||||
if (str->asCell()->isMarked())
|
||||
return;
|
||||
str = iter.init(str);
|
||||
goto not_static;
|
||||
}
|
||||
do {
|
||||
for (;;) {
|
||||
if (JSString::isStatic(str))
|
||||
break;
|
||||
not_static:
|
||||
JS_ASSERT(JSTRACE_STRING == GetFinalizableTraceKind(str->asCell()->arena()->header()->thingKind));
|
||||
if (!str->asCell()->markIfUnmarked())
|
||||
break;
|
||||
|
|
|
@ -616,7 +616,7 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
|
|||
args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
|
||||
args.thisv() = vp[1];
|
||||
args[0] = obj->getSlot(JSSLOT_SAVED_ID);
|
||||
JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2);
|
||||
JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2);
|
||||
if (!argsobj)
|
||||
return JS_FALSE;
|
||||
args[1].setObject(*argsobj);
|
||||
|
@ -908,7 +908,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||
if (script->isEmpty()) {
|
||||
if (result)
|
||||
result->setUndefined();
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
LeaveTrace(cx);
|
||||
|
@ -2506,9 +2506,6 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
|
|||
Value *argv = regs.fp->maybeFormalArgs();
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
|
||||
JS_ASSERT(!script->isEmpty());
|
||||
JS_ASSERT(script->length >= 1);
|
||||
|
||||
#if defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
|
||||
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
|
||||
|
@ -4078,8 +4075,8 @@ BEGIN_CASE(JSOP_UNBRANDTHIS)
|
|||
Value &thisv = regs.fp->thisValue();
|
||||
if (thisv.isObject()) {
|
||||
JSObject *obj = &thisv.toObject();
|
||||
if (obj->isNative() && !obj->unbrand(cx))
|
||||
goto error;
|
||||
if (obj->isNative())
|
||||
obj->unbrand(cx);
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_UNBRANDTHIS)
|
||||
|
@ -4293,8 +4290,7 @@ END_CASE(JSOP_CALLPROP)
|
|||
|
||||
BEGIN_CASE(JSOP_UNBRAND)
|
||||
JS_ASSERT(regs.sp - regs.fp->slots() >= 1);
|
||||
if (!regs.sp[-1].toObject().unbrand(cx))
|
||||
goto error;
|
||||
regs.sp[-1].toObject().unbrand(cx);
|
||||
END_CASE(JSOP_UNBRAND)
|
||||
|
||||
BEGIN_CASE(JSOP_SETGNAME)
|
||||
|
@ -4677,7 +4673,7 @@ BEGIN_CASE(JSOP_FUNCALL)
|
|||
if (newfun->isInterpreted())
|
||||
inline_call:
|
||||
{
|
||||
JSScript *newscript = newfun->u.i.script;
|
||||
JSScript *newscript = newfun->script();
|
||||
if (JS_UNLIKELY(newscript->isEmpty())) {
|
||||
vp->setUndefined();
|
||||
regs.sp = vp + 1;
|
||||
|
@ -5892,7 +5888,7 @@ BEGIN_CASE(JSOP_NEWINIT)
|
|||
JSObject *obj;
|
||||
|
||||
if (i == JSProto_Array) {
|
||||
obj = js_NewArrayObject(cx, 0, NULL);
|
||||
obj = NewDenseEmptyArray(cx);
|
||||
} else {
|
||||
gc::FinalizeKind kind = GuessObjectGCKind(0, false);
|
||||
obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
|
||||
|
@ -5909,10 +5905,8 @@ END_CASE(JSOP_NEWINIT)
|
|||
BEGIN_CASE(JSOP_NEWARRAY)
|
||||
{
|
||||
unsigned count = GET_UINT24(regs.pc);
|
||||
JSObject *obj = js_NewArrayObject(cx, count, NULL);
|
||||
|
||||
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
|
||||
if (!obj || !obj->ensureSlots(cx, count))
|
||||
JSObject *obj = NewDenseAllocatedArray(cx, count);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
PUSH_OBJECT(*obj);
|
||||
|
@ -6069,7 +6063,7 @@ BEGIN_CASE(JSOP_DEFSHARP)
|
|||
obj = &lref.toObject();
|
||||
} else {
|
||||
JS_ASSERT(lref.isUndefined());
|
||||
obj = js_NewArrayObject(cx, 0, NULL);
|
||||
obj = NewDenseEmptyArray(cx);
|
||||
if (!obj)
|
||||
goto error;
|
||||
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.
|
||||
*/
|
||||
if (script->trynotesOffset == 0)
|
||||
if (!JSScript::isValidOffset(script->trynotesOffset))
|
||||
goto no_catch;
|
||||
|
||||
offset = (uint32)(regs.pc - script->main);
|
||||
|
|
|
@ -61,7 +61,7 @@ struct JSFrameRegs
|
|||
/* Flags to toggle js::Interpret() execution. */
|
||||
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_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */
|
||||
JSINTERP_PROFILE = 3 /* interpreter should profile a loop */
|
||||
|
@ -76,8 +76,8 @@ enum JSFrameFlags
|
|||
JSFRAME_DUMMY = 0x4, /* frame pushed for bookkeeping */
|
||||
|
||||
/* Frame subtypes */
|
||||
JSFRAME_EVAL = 0x8, /* frame pushed by js::Execute */
|
||||
JSFRAME_DEBUGGER = 0x10, /* frame pushed by JS_EvaluateInStackFrame */
|
||||
JSFRAME_EVAL = 0x8, /* frame pushed for eval() or debugger eval */
|
||||
JSFRAME_DEBUGGER = 0x10, /* frame pushed for debugger eval */
|
||||
JSFRAME_GENERATOR = 0x20, /* frame is associated with a generator */
|
||||
JSFRAME_FLOATING_GENERATOR = 0x40, /* frame is is in generator obj, not on stack */
|
||||
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 };
|
||||
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(vec), vec);
|
||||
|
||||
JSObject *aobj = js_NewArrayObject(cx, 2, vec);
|
||||
JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
|
||||
if (!aobj)
|
||||
return false;
|
||||
rval->setObject(*aobj);
|
||||
|
@ -615,6 +615,14 @@ EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, AutoIdVe
|
|||
|
||||
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
|
||||
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->getProto()) {
|
||||
vp->setObject(*last);
|
||||
UpdateNativeIterator(lastni, obj);
|
||||
RegisterEnumerator(cx, last, lastni);
|
||||
return true;
|
||||
}
|
||||
|
@ -691,6 +700,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
|
|||
Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
|
||||
vp->setObject(*iterobj);
|
||||
|
||||
UpdateNativeIterator(ni, obj);
|
||||
RegisterEnumerator(cx, iterobj, ni);
|
||||
if (shapes.length() == 2)
|
||||
JS_THREAD_DATA(cx)->lastNativeIterator = iterobj;
|
||||
|
@ -967,6 +977,7 @@ public:
|
|||
bool
|
||||
js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
|
||||
{
|
||||
id = js_CheckForStringIndex(id);
|
||||
return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
|
||||
}
|
||||
|
||||
|
|
|
@ -1055,7 +1055,7 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static
|
|||
int i = 1;
|
||||
|
||||
if (objarray->length == 1) {
|
||||
if (script->regexpsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->regexpsOffset)) {
|
||||
objarray = script->regexps();
|
||||
i = 0;
|
||||
} else {
|
||||
|
@ -1806,7 +1806,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
|
|||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
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)
|
||||
return false;
|
||||
|
||||
|
@ -3538,9 +3538,9 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
|
|||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
obj = *objp;
|
||||
parent = obj->getParent();
|
||||
parentId = (xdr->script->objectsOffset == 0)
|
||||
? NO_PARENT_INDEX
|
||||
: FindObjectIndex(xdr->script->objects(), parent);
|
||||
parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
|
||||
? FindObjectIndex(xdr->script->objects(), parent)
|
||||
: NO_PARENT_INDEX;
|
||||
depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
|
||||
count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
|
||||
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.
|
||||
* This avoids extra shape guard branch exits in the tracejitted code.
|
||||
*/
|
||||
if (fs && !proto->brand(cx))
|
||||
goto bad;
|
||||
if (ctor != proto && static_fs && !ctor->brand(cx))
|
||||
goto bad;
|
||||
if (fs)
|
||||
proto->brand(cx);
|
||||
if (ctor != proto && static_fs)
|
||||
ctor->brand(cx);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
JS_ASSERT(IsFunctionObject(*vp));
|
||||
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
||||
|
||||
JSObject *funobj = &vp->toObject();
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
|
|
|
@ -337,16 +337,19 @@ struct JSObject : js::gc::Cell {
|
|||
inline bool nativeContains(const js::Shape &shape);
|
||||
|
||||
enum {
|
||||
DELEGATE = 0x01,
|
||||
SYSTEM = 0x02,
|
||||
NOT_EXTENSIBLE = 0x04,
|
||||
BRANDED = 0x08,
|
||||
GENERIC = 0x10,
|
||||
METHOD_BARRIER = 0x20,
|
||||
INDEXED = 0x40,
|
||||
OWN_SHAPE = 0x80,
|
||||
BOUND_FUNCTION = 0x100,
|
||||
HAS_EQUALITY = 0x200
|
||||
DELEGATE = 0x01,
|
||||
SYSTEM = 0x02,
|
||||
NOT_EXTENSIBLE = 0x04,
|
||||
BRANDED = 0x08,
|
||||
GENERIC = 0x10,
|
||||
METHOD_BARRIER = 0x20,
|
||||
INDEXED = 0x40,
|
||||
OWN_SHAPE = 0x80,
|
||||
BOUND_FUNCTION = 0x100,
|
||||
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); }
|
||||
|
||||
/*
|
||||
* 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 unbrand(JSContext *cx);
|
||||
|
||||
bool generic() { return !!(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); }
|
||||
void assertSpecialEqualitySynced() const {
|
||||
JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality());
|
||||
|
|
|
@ -105,8 +105,13 @@ inline bool
|
|||
JSObject::unbrand(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(isNative());
|
||||
if (!branded())
|
||||
setGeneric();
|
||||
if (branded()) {
|
||||
generateOwnShape(cx);
|
||||
if (js_IsPropertyCacheDisabled(cx)) // check for rt->shapeGen overflow
|
||||
return false;
|
||||
flags &= ~BRANDED;
|
||||
}
|
||||
setGeneric();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -189,14 +194,12 @@ ChangesMethodValue(const js::Value &prev, const js::Value &v)
|
|||
inline bool
|
||||
JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v)
|
||||
{
|
||||
if (flags & (BRANDED | METHOD_BARRIER)) {
|
||||
if (shape.slot != SHAPE_INVALID_SLOT) {
|
||||
const js::Value &prev = nativeGetSlot(shape.slot);
|
||||
if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) {
|
||||
const js::Value &prev = nativeGetSlot(shape.slot);
|
||||
|
||||
if (ChangesMethodValue(prev, v)) {
|
||||
JS_FUNCTION_METER(cx, mwritebarrier);
|
||||
return methodShapeChange(cx, shape);
|
||||
}
|
||||
if (ChangesMethodValue(prev, v)) {
|
||||
JS_FUNCTION_METER(cx, mwritebarrier);
|
||||
return methodShapeChange(cx, shape);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -205,7 +208,7 @@ JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Va
|
|||
inline bool
|
||||
JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
|
||||
{
|
||||
if (flags & (BRANDED | METHOD_BARRIER)) {
|
||||
if (brandedOrHasMethodBarrier()) {
|
||||
const js::Value &prev = nativeGetSlot(slot);
|
||||
|
||||
if (ChangesMethodValue(prev, v)) {
|
||||
|
@ -931,6 +934,18 @@ namespace WithProto {
|
|||
* Note that as a template, there will be lots of instantiations, which means
|
||||
* 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
|
||||
{
|
||||
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. */
|
||||
if (withProto == WithProto::Class && !proto) {
|
||||
JSProtoKey protoKey = GetClassProtoKey(clasp);
|
||||
if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
|
||||
return NULL;
|
||||
if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
|
||||
return NULL;
|
||||
if (!FindProto (cx, clasp, parent, &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);
|
||||
}
|
||||
|
||||
/* 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
|
||||
* the object, zero if the final size is unknown.
|
||||
|
|
|
@ -680,7 +680,7 @@ js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= false*/
|
|||
if (!cx)
|
||||
return NULL;
|
||||
|
||||
JSObject *arr = js_NewArrayObject(cx, 0, NULL);
|
||||
JSObject *arr = NewDenseEmptyArray(cx);
|
||||
if (!arr)
|
||||
return NULL;
|
||||
|
||||
|
@ -856,7 +856,7 @@ static JSBool
|
|||
OpenArray(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
// Add an array to an existing array or object
|
||||
JSObject *arr = js_NewArrayObject(cx, 0, NULL);
|
||||
JSObject *arr = NewDenseEmptyArray(cx);
|
||||
if (!arr)
|
||||
return JS_FALSE;
|
||||
|
||||
|
|
|
@ -1337,7 +1337,7 @@ GetLocal(SprintStack *ss, jsint i)
|
|||
* not in a block. In either case, return GetStr(ss, i).
|
||||
*/
|
||||
JSScript *script = ss->printer->script;
|
||||
if (script->objectsOffset == 0)
|
||||
if (!JSScript::isValidOffset(script->objectsOffset))
|
||||
return GetStr(ss, i);
|
||||
|
||||
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(FUN_INTERPRETED(jp->fun));
|
||||
JS_ASSERT(jp->script != jp->fun->u.i.script);
|
||||
JS_ASSERT(jp->script->upvarsOffset != 0);
|
||||
JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
|
||||
}
|
||||
#endif
|
||||
uva = jp->script->upvars();
|
||||
|
|
|
@ -640,9 +640,9 @@ NameNode::initCommon(JSTreeContext *tc)
|
|||
{
|
||||
pn_expr = NULL;
|
||||
pn_cookie.makeFree();
|
||||
pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
|
||||
if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
|
||||
pn_dflags |= PND_BLOCKCHILD;
|
||||
pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
|
||||
? PND_BLOCKCHILD
|
||||
: 0;
|
||||
pn_blockid = tc->blockid();
|
||||
}
|
||||
|
||||
|
@ -865,6 +865,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
|
|||
#endif
|
||||
|
||||
inDirectivePrologue = true;
|
||||
tokenStream.setOctalCharacterEscape(false);
|
||||
for (;;) {
|
||||
tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt <= TOK_EOF) {
|
||||
|
@ -879,8 +880,8 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
|
|||
goto out;
|
||||
JS_ASSERT(!cg.blockNode);
|
||||
|
||||
if (inDirectivePrologue)
|
||||
inDirectivePrologue = parser.recognizeDirectivePrologue(pn);
|
||||
if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
|
||||
goto out;
|
||||
|
||||
if (!js_FoldConstants(cx, pn, &cg))
|
||||
goto out;
|
||||
|
@ -978,7 +979,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
|
|||
JS_DumpArenaStats(stdout);
|
||||
#endif
|
||||
script = JSScript::NewScriptFromCG(cx, &cg);
|
||||
if (script && funbox && script != script->emptyScript())
|
||||
if (script && funbox)
|
||||
script->savedCallerFun = true;
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
|
@ -1069,7 +1070,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
|
|||
JSScript *inner = worklist.back();
|
||||
worklist.popBack();
|
||||
|
||||
if (inner->objectsOffset != 0) {
|
||||
if (JSScript::isValidOffset(inner->objectsOffset)) {
|
||||
JSObjectArray *arr = inner->objects();
|
||||
for (size_t i = 0; i < arr->length; i++) {
|
||||
JSObject *obj = arr->vector[i];
|
||||
|
@ -1078,14 +1079,16 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
|
|||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JSScript *inner = fun->u.i.script;
|
||||
if (inner->globalsOffset == 0 && inner->objectsOffset == 0)
|
||||
if (!JSScript::isValidOffset(inner->globalsOffset) &&
|
||||
!JSScript::isValidOffset(inner->objectsOffset)) {
|
||||
continue;
|
||||
}
|
||||
if (!worklist.append(inner))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (inner->globalsOffset == 0)
|
||||
if (!JSScript::isValidOffset(inner->globalsOffset))
|
||||
continue;
|
||||
|
||||
GlobalSlotArray *globalUses = inner->globals();
|
||||
|
@ -1479,6 +1482,8 @@ Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
|
|||
ALE_SET_DEFN(ale, pn);
|
||||
pn->pn_defn = true;
|
||||
pn->pn_dflags &= ~PND_PLACEHOLDER;
|
||||
if (!tc->parent)
|
||||
pn->pn_dflags |= PND_TOPLEVEL;
|
||||
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;
|
||||
} else if (dn->kind() == JSDefinition::FUNCTION) {
|
||||
JS_ASSERT(dn->isTopLevel());
|
||||
JS_ASSERT(dn->pn_op == JSOP_NOP);
|
||||
dn->pn_type = TOK_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);
|
||||
|
||||
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)
|
||||
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
|
||||
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
|
||||
*
|
||||
* Also treat function sub-statements (non-lambda, non-top-level functions)
|
||||
* as escaping funargs, since we can't statically analyze their definitions
|
||||
* Treat function sub-statements (non-lambda, non-body-level functions) as
|
||||
* escaping funargs, since we can't statically analyze their definitions
|
||||
* and uses.
|
||||
*/
|
||||
bool topLevel = tc->atTopLevel();
|
||||
pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
|
||||
bool bodyLevel = tc->atBodyLevel();
|
||||
pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
pn->pn_defn = true;
|
||||
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))
|
||||
return NULL;
|
||||
}
|
||||
} else if (topLevel) {
|
||||
} else if (bodyLevel) {
|
||||
/*
|
||||
* If this function was used before it was defined, claim the
|
||||
* 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
|
||||
* local variable to bind its name to its value, and not an activation
|
||||
* object property (it might also need the activation property, if the
|
||||
* outer function contains with statements, e.g., but the stack slot
|
||||
* wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
|
||||
* A function directly inside another's body needs only a local
|
||||
* variable to bind its name to its value, and not an activation object
|
||||
* property (it might also need the activation property, if the outer
|
||||
* function contains with statements, e.g., but the stack slot wins
|
||||
* when jsemit.cpp's BindNameToSlot can optimize a JSOP_NAME into a
|
||||
* JSOP_GETLOCAL bytecode).
|
||||
*/
|
||||
if (topLevel) {
|
||||
pn->pn_dflags |= PND_TOPLEVEL;
|
||||
if (bodyLevel && tc->inFunction()) {
|
||||
/*
|
||||
* 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()) {
|
||||
JSLocalKind localKind;
|
||||
uintN index;
|
||||
case JSLOCAL_VAR:
|
||||
pn->pn_cookie.set(tc->staticLevel, index);
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
break;
|
||||
|
||||
/*
|
||||
* 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:;
|
||||
}
|
||||
default:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3104,11 +3099,12 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
outertc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
} else {
|
||||
/*
|
||||
* If this function is a named statement function not at top-level
|
||||
* (i.e. not a top-level function definiton or expression), then our
|
||||
* enclosing function, if any, must be heavyweight.
|
||||
* If this function is not at body level of a program or function (i.e.
|
||||
* it is a function statement that is not a direct child of a program
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -3134,7 +3130,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
|||
result->pn_pos = pn->pn_pos;
|
||||
result->pn_kid = pn;
|
||||
op = JSOP_LAMBDA;
|
||||
} else if (!topLevel) {
|
||||
} else if (!bodyLevel) {
|
||||
/*
|
||||
* ECMA ed. 3 extension: a function expression statement not at the
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (!outertc->inFunction() && topLevel && funAtom && !lambda &&
|
||||
outertc->compiling()) {
|
||||
if (!outertc->inFunction() && bodyLevel && funAtom && !lambda && outertc->compiling()) {
|
||||
JS_ASSERT(pn->pn_cookie.isFree());
|
||||
if (!DefineGlobal(pn, (JSCodeGenerator *)outertc, funAtom))
|
||||
if (!DefineGlobal(pn, outertc->asCodeGenerator(), funAtom))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3225,13 +3220,33 @@ Parser::functionExpr()
|
|||
* if it can't possibly be a directive, now or in the future.
|
||||
*/
|
||||
bool
|
||||
Parser::recognizeDirectivePrologue(JSParseNode *pn)
|
||||
Parser::recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember)
|
||||
{
|
||||
if (!pn->isDirectivePrologueMember())
|
||||
return false;
|
||||
*isDirectivePrologueMember = pn->isDirectivePrologueMember();
|
||||
if (!*isDirectivePrologueMember)
|
||||
return true;
|
||||
if (pn->isDirective()) {
|
||||
JSAtom *directive = pn->pn_kid->pn_atom;
|
||||
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;
|
||||
tokenStream.setStrictMode();
|
||||
}
|
||||
|
@ -3249,7 +3264,6 @@ Parser::statements()
|
|||
{
|
||||
JSParseNode *pn, *pn2, *saveBlock;
|
||||
TokenKind tt;
|
||||
bool inDirectivePrologue = tc->atTopLevel();
|
||||
|
||||
JS_CHECK_RECURSION(context, return NULL);
|
||||
|
||||
|
@ -3262,6 +3276,8 @@ Parser::statements()
|
|||
saveBlock = tc->blockNode;
|
||||
tc->blockNode = pn;
|
||||
|
||||
bool inDirectivePrologue = tc->atBodyLevel();
|
||||
tokenStream.setOctalCharacterEscape(false);
|
||||
for (;;) {
|
||||
tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt <= TOK_EOF || tt == TOK_RC) {
|
||||
|
@ -3279,20 +3295,20 @@ Parser::statements()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (inDirectivePrologue)
|
||||
inDirectivePrologue = recognizeDirectivePrologue(pn2);
|
||||
if (inDirectivePrologue && !recognizeDirectivePrologue(pn2, &inDirectivePrologue))
|
||||
return NULL;
|
||||
|
||||
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
|
||||
* rest of nodes.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
if (tc->atTopLevel())
|
||||
if (tc->atBodyLevel())
|
||||
pn->pn_xflags |= PNX_FUNCDEFS;
|
||||
else
|
||||
tc->flags |= TCF_HAS_FUNCTION_STMT;
|
||||
|
@ -3362,10 +3378,10 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
|||
jsint n;
|
||||
|
||||
/*
|
||||
* Top-level 'let' is the same as 'var' currently -- this may change in a
|
||||
* successor standard to ES3.1 that specifies 'let'.
|
||||
* Body-level 'let' is the same as 'var' currently -- this may change in a
|
||||
* successor standard to ES5 that specifies 'let'.
|
||||
*/
|
||||
JS_ASSERT(!tc->atTopLevel());
|
||||
JS_ASSERT(!tc->atBodyLevel());
|
||||
|
||||
pn = data->pn;
|
||||
if (!CheckStrictBinding(cx, tc, atom, pn))
|
||||
|
@ -3583,7 +3599,7 @@ BindGvar(JSParseNode *pn, JSTreeContext *tc)
|
|||
if (!tc->compiling() || tc->parser->callerFrame)
|
||||
return true;
|
||||
|
||||
JSCodeGenerator *cg = (JSCodeGenerator *) tc;
|
||||
JSCodeGenerator *cg = tc->asCodeGenerator();
|
||||
|
||||
if (pn->pn_dflags & PND_CONST)
|
||||
return true;
|
||||
|
|
|
@ -479,7 +479,7 @@ public:
|
|||
#define PND_CONST 0x02 /* const binding (orthogonal to let) */
|
||||
#define PND_INITIALIZED 0x04 /* initialized declaration */
|
||||
#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_GVAR 0x40 /* gvar binding, can't close over
|
||||
because it could be deleted */
|
||||
|
@ -529,7 +529,6 @@ public:
|
|||
bool isLet() const { return test(PND_LET); }
|
||||
bool isConst() const { return test(PND_CONST); }
|
||||
bool isInitialized() const { return test(PND_INITIALIZED); }
|
||||
bool isTopLevel() const { return test(PND_TOPLEVEL); }
|
||||
bool isBlockChild() const { return test(PND_BLOCKCHILD); }
|
||||
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
|
||||
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
|
||||
|
@ -537,6 +536,20 @@ public:
|
|||
bool isFunArg() const { return test(PND_FUNARG); }
|
||||
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. */
|
||||
void setFunArg();
|
||||
|
||||
|
@ -1118,7 +1131,7 @@ private:
|
|||
/*
|
||||
* Additional JS parsers.
|
||||
*/
|
||||
bool recognizeDirectivePrologue(JSParseNode *pn);
|
||||
bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
|
||||
|
||||
enum FunctionType { GETTER, SETTER, GENERAL };
|
||||
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
|
||||
|
|
|
@ -335,8 +335,6 @@ GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
|
|||
static bool
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -672,6 +670,7 @@ bool
|
|||
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
|
||||
PropertyDescriptor *desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc);
|
||||
}
|
||||
|
@ -679,6 +678,7 @@ JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set
|
|||
bool
|
||||
JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
AutoPropertyDescriptorRooter desc(cx);
|
||||
return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) &&
|
||||
|
@ -689,6 +689,7 @@ bool
|
|||
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
|
||||
PropertyDescriptor *desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
|
||||
}
|
||||
|
@ -696,6 +697,7 @@ JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool
|
|||
bool
|
||||
JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
AutoPropertyDescriptorRooter desc(cx);
|
||||
return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) &&
|
||||
|
@ -705,6 +707,7 @@ JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool
|
|||
bool
|
||||
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
|
||||
}
|
||||
|
@ -712,6 +715,7 @@ JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescrip
|
|||
bool
|
||||
JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
AutoPropertyDescriptorRooter desc(cx);
|
||||
return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
|
||||
|
@ -721,6 +725,7 @@ JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
|
|||
bool
|
||||
JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
|
||||
}
|
||||
|
@ -728,6 +733,7 @@ JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props
|
|||
bool
|
||||
JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
|
||||
}
|
||||
|
@ -735,6 +741,7 @@ JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
|||
bool
|
||||
JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->enumerate(cx, proxy, props);
|
||||
}
|
||||
|
@ -742,6 +749,7 @@ JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
|
|||
bool
|
||||
JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->fix(cx, proxy, vp);
|
||||
}
|
||||
|
@ -749,6 +757,7 @@ JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
|
|||
bool
|
||||
JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->has(cx, proxy, id, bp);
|
||||
}
|
||||
|
@ -756,6 +765,7 @@ JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
|||
bool
|
||||
JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
|
||||
}
|
||||
|
@ -763,6 +773,7 @@ JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
|
|||
bool
|
||||
JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
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
|
||||
JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
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
|
||||
JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
|
||||
}
|
||||
|
@ -784,6 +797,7 @@ JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
|
|||
bool
|
||||
JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
|
||||
}
|
||||
|
@ -791,6 +805,7 @@ JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
|
|||
bool
|
||||
JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
|
||||
}
|
||||
|
@ -798,6 +813,7 @@ JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
|
|||
bool
|
||||
JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
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
|
||||
JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp);
|
||||
}
|
||||
|
@ -812,6 +829,8 @@ JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *
|
|||
JSType
|
||||
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);
|
||||
return proxy->getProxyHandler()->typeOf(cx, proxy);
|
||||
}
|
||||
|
@ -819,6 +838,7 @@ JSProxy::typeOf(JSContext *cx, JSObject *proxy)
|
|||
JSString *
|
||||
JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->obj_toString(cx, proxy);
|
||||
}
|
||||
|
@ -826,6 +846,7 @@ JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
|
|||
JSString *
|
||||
JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return NULL);
|
||||
AutoPendingProxyOperation pending(cx, proxy);
|
||||
return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
|
||||
}
|
||||
|
|
|
@ -510,7 +510,7 @@ NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst)
|
|||
bool
|
||||
NodeBuilder::newArray(NodeVector &elts, Value *dst)
|
||||
{
|
||||
JSObject *array = js_NewArrayObject(cx, 0, NULL);
|
||||
JSObject *array = NewDenseEmptyArray(cx);
|
||||
if (!array)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC
|
|||
* 0: matched string
|
||||
* 1..pairCount-1: paren matches
|
||||
*/
|
||||
JSObject *array = js_NewSlowArrayObject(cx);
|
||||
JSObject *array = NewSlowEmptyArray(cx);
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -1192,6 +1192,7 @@ TokenStream::getTokenInternal()
|
|||
JSMSG_DEPRECATED_OCTAL)) {
|
||||
goto error;
|
||||
}
|
||||
setOctalCharacterEscape();
|
||||
}
|
||||
if ('0' <= c && c < '8') {
|
||||
val = 8 * val + JS7_UNDEC(c);
|
||||
|
@ -1586,7 +1587,7 @@ TokenStream::getTokenInternal()
|
|||
}
|
||||
}
|
||||
filenameBuf[i] = '\0';
|
||||
if (c == '\n') {
|
||||
if (c == EOF || c == '\n') {
|
||||
if (i > 0) {
|
||||
if (flags & TSF_OWNFILENAME)
|
||||
cx->free((void *) filename);
|
||||
|
|
|
@ -263,6 +263,7 @@ enum TokenStreamFlags
|
|||
TSF_XMLTAGMODE = 0x200, /* scanning within an XML tag in E4X */
|
||||
TSF_XMLTEXTMODE = 0x400, /* scanning XMLText terminal from E4X */
|
||||
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
|
||||
|
@ -333,12 +334,15 @@ class TokenStream
|
|||
void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); }
|
||||
void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); }
|
||||
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 isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); }
|
||||
bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); }
|
||||
bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); }
|
||||
bool isEOF() const { return !!(flags & TSF_EOF); }
|
||||
bool isError() const { return !!(flags & TSF_ERROR); }
|
||||
bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; }
|
||||
|
||||
/* Mutators. */
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -73,28 +73,6 @@
|
|||
using namespace js;
|
||||
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
|
||||
|
||||
enum ScriptBits {
|
||||
|
@ -108,8 +86,7 @@ enum ScriptBits {
|
|||
};
|
||||
|
||||
JSBool
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
||||
JSBool *hasMagic)
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSScript *script, *oldscript;
|
||||
|
@ -133,7 +110,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
|||
notes = NULL;
|
||||
|
||||
/* 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)
|
||||
magic = JSXDR_MAGIC_SCRIPT_CURRENT;
|
||||
|
@ -152,45 +129,10 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
|||
if (hasMagic)
|
||||
*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)
|
||||
length = (script == JSScript::emptyScript()) ? 0 : script->length;
|
||||
length = script->length;
|
||||
if (!JS_XDRUint32(xdr, &length))
|
||||
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) {
|
||||
prologLength = script->main - script->code;
|
||||
|
@ -208,15 +150,15 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
|||
nsrcnotes = sn - notes;
|
||||
nsrcnotes++; /* room for the terminator */
|
||||
|
||||
if (script->objectsOffset != 0)
|
||||
if (JSScript::isValidOffset(script->objectsOffset))
|
||||
nobjects = script->objects()->length;
|
||||
if (script->upvarsOffset != 0)
|
||||
if (JSScript::isValidOffset(script->upvarsOffset))
|
||||
nupvars = script->upvars()->length;
|
||||
if (script->regexpsOffset != 0)
|
||||
if (JSScript::isValidOffset(script->regexpsOffset))
|
||||
nregexps = script->regexps()->length;
|
||||
if (script->trynotesOffset != 0)
|
||||
if (JSScript::isValidOffset(script->trynotesOffset))
|
||||
ntrynotes = script->trynotes()->length;
|
||||
if (script->constOffset != 0)
|
||||
if (JSScript::isValidOffset(script->constOffset))
|
||||
nconsts = script->consts()->length;
|
||||
|
||||
nClosedArgs = script->nClosedArgs;
|
||||
|
@ -956,39 +898,52 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
|||
script->length = length;
|
||||
script->setVersion(cx->findVersion());
|
||||
|
||||
uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
|
||||
|
||||
cursor = (uint8 *)script + sizeof(JSScript);
|
||||
if (nobjects != 0) {
|
||||
script->objectsOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->objectsOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSObjectArray);
|
||||
} else {
|
||||
script->objectsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (nupvars != 0) {
|
||||
script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->upvarsOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSUpvarArray);
|
||||
} else {
|
||||
script->upvarsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (nregexps != 0) {
|
||||
script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->regexpsOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSObjectArray);
|
||||
} else {
|
||||
script->regexpsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (ntrynotes != 0) {
|
||||
script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->trynotesOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSTryNoteArray);
|
||||
} else {
|
||||
script->trynotesOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
if (nglobals != 0) {
|
||||
script->globalsOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->globalsOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(GlobalSlotArray);
|
||||
} else {
|
||||
script->globalsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
JS_ASSERT((cursor - (uint8 *)script) <= 0xFF);
|
||||
JS_ASSERT((cursor - (uint8 *)script) < 0xFF);
|
||||
if (nconsts != 0) {
|
||||
script->constOffset = (uint8)(cursor - (uint8 *)script);
|
||||
script->constOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSConstArray);
|
||||
} else {
|
||||
script->constOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSScript) +
|
||||
sizeof(JSObjectArray) +
|
||||
JS_STATIC_ASSERT(sizeof(JSObjectArray) +
|
||||
sizeof(JSUpvarArray) +
|
||||
sizeof(JSObjectArray) +
|
||||
sizeof(JSTryNoteArray) +
|
||||
sizeof(GlobalSlotArray) <= 0xFF);
|
||||
sizeof(GlobalSlotArray) < 0xFF);
|
||||
|
||||
if (natoms != 0) {
|
||||
script->atomMap.length = natoms;
|
||||
|
@ -1097,84 +1052,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
mainLength = CG_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);
|
||||
uint16 nClosedArgs = uint16(cg->closedArgs.length());
|
||||
JS_ASSERT(nClosedArgs == cg->closedArgs.length());
|
||||
|
@ -1268,7 +1145,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
if (cg->inFunction()) {
|
||||
fun = cg->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);
|
||||
else
|
||||
fun->u.i.nupvars = 0;
|
||||
|
@ -1286,10 +1163,19 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
js_CallNewScriptHook(cx, script, fun);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
jsrefcount newLive = JS_RUNTIME_METER(cx->runtime, liveScripts);
|
||||
jsrefcount newEmptyLive = cx->runtime->liveEmptyScripts;
|
||||
jsrefcount newTotal =
|
||||
JS_RUNTIME_METER(cx->runtime, totalScripts) + cx->runtime->totalEmptyScripts;
|
||||
jsrefcount newEmptyLive, newLive, newTotal;
|
||||
if (script->isEmpty()) {
|
||||
newEmptyLive = JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
|
||||
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;
|
||||
if (newEmptyLive + newLive > oldHigh) {
|
||||
JS_ATOMIC_SET(&cx->runtime->highWaterLiveScripts, newEmptyLive + newLive);
|
||||
|
@ -1311,8 +1197,6 @@ bad:
|
|||
JS_FRIEND_API(void)
|
||||
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
|
||||
{
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
|
||||
JSNewScriptHook hook;
|
||||
|
||||
hook = cx->debugHooks->newScriptHook;
|
||||
|
@ -1326,8 +1210,6 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
|
|||
JS_FRIEND_API(void)
|
||||
js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
|
||||
JSDestroyScriptHook hook;
|
||||
|
||||
hook = cx->debugHooks->destroyScriptHook;
|
||||
|
@ -1338,13 +1220,11 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
|
|||
static void
|
||||
DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
|
||||
{
|
||||
if (script == JSScript::emptyScript()) {
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
|
||||
return;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
js_CallDestroyScriptHook(cx, script);
|
||||
|
@ -1437,7 +1317,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
|
|||
JSAtomMap *map = &script->atomMap;
|
||||
MarkAtomRange(trc, map->length, map->vector, "atomMap");
|
||||
|
||||
if (script->objectsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->objectsOffset)) {
|
||||
JSObjectArray *objarray = script->objects();
|
||||
uintN i = objarray->length;
|
||||
do {
|
||||
|
@ -1449,7 +1329,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
|
|||
} while (i != 0);
|
||||
}
|
||||
|
||||
if (script->regexpsOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->regexpsOffset)) {
|
||||
JSObjectArray *objarray = script->regexps();
|
||||
uintN i = objarray->length;
|
||||
do {
|
||||
|
@ -1461,7 +1341,7 @@ js_TraceScript(JSTracer *trc, JSScript *script)
|
|||
} while (i != 0);
|
||||
}
|
||||
|
||||
if (script->constOffset != 0) {
|
||||
if (JSScript::isValidOffset(script->constOffset)) {
|
||||
JSConstArray *constarray = script->consts();
|
||||
MarkValueRange(trc, constarray->length, constarray->vector, "consts");
|
||||
}
|
||||
|
@ -1481,7 +1361,6 @@ js_NewScriptObject(JSContext *cx, JSScript *script)
|
|||
AutoScriptRooter root(cx, script);
|
||||
|
||||
JS_ASSERT(!script->u.object);
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
|
||||
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
|
||||
if (!obj)
|
||||
|
@ -1733,7 +1612,6 @@ class DisablePrincipalsTranscoding {
|
|||
JSScript *
|
||||
js_CloneScript(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JS_ASSERT(script != JSScript::emptyScript());
|
||||
JS_ASSERT(cx->compartment != script->compartment);
|
||||
JS_ASSERT(script->compartment);
|
||||
|
||||
|
@ -1770,7 +1648,7 @@ js_CloneScript(JSContext *cx, JSScript *script)
|
|||
JS_XDRMemSetData(w, NULL, 0);
|
||||
|
||||
// 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;
|
||||
|
||||
JS_XDRDestroy(r);
|
||||
|
@ -1789,4 +1667,3 @@ JSScript::copyClosedSlotsTo(JSScript *other)
|
|||
{
|
||||
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
|
||||
* are responsible for notifying the debugger after successfully creating any
|
||||
* 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,
|
||||
uint32 nobjects, uint32 nupvars, uint32 nregexps,
|
||||
|
@ -219,19 +213,22 @@ struct JSScript {
|
|||
uint16 version; /* JS version under which script was compiled */
|
||||
uint16 nfixed; /* number of slots besides stack operands in
|
||||
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,
|
||||
block, scope, xml and one-time regexps
|
||||
objects or 0 if none */
|
||||
objects */
|
||||
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
|
||||
regexps or 0 if none. */
|
||||
uint8 trynotesOffset; /* offset to the array of try notes or
|
||||
0 if none */
|
||||
uint8 globalsOffset; /* offset to the array of global slots or
|
||||
0 if none */
|
||||
uint8 constOffset; /* offset to the array of constants or
|
||||
0 if none */
|
||||
regexps */
|
||||
uint8 trynotesOffset; /* offset to the array of try notes */
|
||||
uint8 globalsOffset; /* offset to the array of global slots */
|
||||
uint8 constOffset; /* offset to the array of constants */
|
||||
|
||||
bool noScriptRval:1; /* no need for result value of last
|
||||
expression statement */
|
||||
bool savedCallerFun:1; /* object 0 is caller function */
|
||||
|
@ -322,34 +319,37 @@ struct JSScript {
|
|||
/* Script notes are allocated right after the code. */
|
||||
jssrcnote *notes() { return (jssrcnote *)(code + length); }
|
||||
|
||||
static const uint8 INVALID_OFFSET = 0xFF;
|
||||
static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; }
|
||||
|
||||
JSObjectArray *objects() {
|
||||
JS_ASSERT(objectsOffset != 0);
|
||||
return (JSObjectArray *)((uint8 *) this + objectsOffset);
|
||||
JS_ASSERT(isValidOffset(objectsOffset));
|
||||
return (JSObjectArray *)((uint8 *) (this + 1) + objectsOffset);
|
||||
}
|
||||
|
||||
JSUpvarArray *upvars() {
|
||||
JS_ASSERT(upvarsOffset != 0);
|
||||
return (JSUpvarArray *) ((uint8 *) this + upvarsOffset);
|
||||
JS_ASSERT(isValidOffset(upvarsOffset));
|
||||
return (JSUpvarArray *) ((uint8 *) (this + 1) + upvarsOffset);
|
||||
}
|
||||
|
||||
JSObjectArray *regexps() {
|
||||
JS_ASSERT(regexpsOffset != 0);
|
||||
return (JSObjectArray *) ((uint8 *) this + regexpsOffset);
|
||||
JS_ASSERT(isValidOffset(regexpsOffset));
|
||||
return (JSObjectArray *) ((uint8 *) (this + 1) + regexpsOffset);
|
||||
}
|
||||
|
||||
JSTryNoteArray *trynotes() {
|
||||
JS_ASSERT(trynotesOffset != 0);
|
||||
return (JSTryNoteArray *) ((uint8 *) this + trynotesOffset);
|
||||
JS_ASSERT(isValidOffset(trynotesOffset));
|
||||
return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset);
|
||||
}
|
||||
|
||||
js::GlobalSlotArray *globals() {
|
||||
JS_ASSERT(globalsOffset != 0);
|
||||
return (js::GlobalSlotArray *) ((uint8 *)this + globalsOffset);
|
||||
JS_ASSERT(isValidOffset(globalsOffset));
|
||||
return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset);
|
||||
}
|
||||
|
||||
JSConstArray *consts() {
|
||||
JS_ASSERT(constOffset != 0);
|
||||
return (JSConstArray *) ((uint8 *) this + constOffset);
|
||||
JS_ASSERT(isValidOffset(constOffset));
|
||||
return (JSConstArray *) ((uint8 *) (this + 1) + constOffset);
|
||||
}
|
||||
|
||||
JSAtom *getAtom(size_t index) {
|
||||
|
@ -397,20 +397,10 @@ struct JSScript {
|
|||
/*
|
||||
* The isEmpty method tells whether this script has code that computes any
|
||||
* 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
|
||||
* |this| is the emptyScript singleton, but it also checks this->length and
|
||||
* this->code, to handle debugger-generated mutable empty scripts.
|
||||
* JSVAL_VOID, or any other effects.
|
||||
*/
|
||||
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) {
|
||||
JS_ASSERT(index < nClosedArgs);
|
||||
return closedSlots[index];
|
||||
|
@ -422,16 +412,6 @@ struct JSScript {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -575,18 +555,11 @@ js_CloneScript(JSContext *cx, JSScript *script);
|
|||
* If magic is null, js_XDRScript returns false on bad magic number errors,
|
||||
* which it reports.
|
||||
*
|
||||
* NB: after a successful JSXDR_DECODE, and provided that *scriptp is not the
|
||||
* JSScript::emptyScript() immutable singleton, js_XDRScript callers must do
|
||||
* any required subsequent set-up of owning function or script object and then
|
||||
* 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.
|
||||
* NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any
|
||||
* required subsequent set-up of owning function or script object and then call
|
||||
* js_CallNewScriptHook.
|
||||
*/
|
||||
extern JSBool
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
|
||||
JSBool *hasMagic);
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic);
|
||||
|
||||
#endif /* jsscript_h___ */
|
||||
|
|
|
@ -70,23 +70,13 @@ JSScript::getRegExp(size_t index)
|
|||
inline bool
|
||||
JSScript::isEmpty() const
|
||||
{
|
||||
return (this == emptyScript());
|
||||
if (length > 3)
|
||||
return false;
|
||||
|
||||
// See bug 603044 comment #21.
|
||||
#if 0
|
||||
if (this == emptyScript())
|
||||
return true;
|
||||
|
||||
if (length <= 3) {
|
||||
jsbytecode *pc = code;
|
||||
|
||||
if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
|
||||
++pc;
|
||||
if (JSOp(*pc) == JSOP_STOP)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
jsbytecode *pc = code;
|
||||
if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
|
||||
++pc;
|
||||
return JSOp(*pc) == JSOP_STOP;
|
||||
}
|
||||
|
||||
#endif /* jsscriptinlines_h___ */
|
||||
|
|
|
@ -1384,8 +1384,8 @@ RopeMatch(JSString *textstr, const jschar *pat, jsuint patlen)
|
|||
*/
|
||||
size_t textstrlen = textstr->length();
|
||||
size_t threshold = textstrlen >> sRopeMatchThresholdRatioLog2;
|
||||
JSRopeLeafIterator iter(textstr);
|
||||
for (JSString *str = iter.init(); str; str = iter.next()) {
|
||||
JSRopeLeafIterator iter;
|
||||
for (JSString *str = iter.init(textstr); str; str = iter.next()) {
|
||||
if (threshold-- == 0 || !strs.append(str))
|
||||
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. */
|
||||
JSObject *obj = js_NewSlowArrayObject(cx);
|
||||
JSObject *obj = NewSlowEmptyArray(cx);
|
||||
if (!obj)
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
|
@ -1896,7 +1896,7 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
|
|||
|
||||
JSObject *&arrayobj = *static_cast<MatchArgType>(p);
|
||||
if (!arrayobj) {
|
||||
arrayobj = js_NewArrayObject(cx, 0, NULL);
|
||||
arrayobj = NewDenseEmptyArray(cx);
|
||||
if (!arrayobj)
|
||||
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
|
||||
* through it, building a new rope.
|
||||
*/
|
||||
JSRopeLeafIterator iter(textstr);
|
||||
JSRopeLeafIterator iter;
|
||||
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 strEnd = pos + len;
|
||||
if (pos < matchEnd && strEnd > match) {
|
||||
|
@ -2679,7 +2679,7 @@ str_split(JSContext *cx, uintN argc, Value *vp)
|
|||
|
||||
if (argc == 0) {
|
||||
Value v = StringValue(str);
|
||||
JSObject *aobj = js_NewArrayObject(cx, 1, &v);
|
||||
JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
|
||||
if (!aobj)
|
||||
return false;
|
||||
vp->setObject(*aobj);
|
||||
|
@ -2762,7 +2762,7 @@ str_split(JSContext *cx, uintN argc, Value *vp)
|
|||
if (j == -2)
|
||||
return false;
|
||||
|
||||
JSObject *aobj = js_NewArrayObject(cx, splits.length(), splits.begin());
|
||||
JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
if (!aobj)
|
||||
return false;
|
||||
vp->setObject(*aobj);
|
||||
|
|
|
@ -630,20 +630,13 @@ class JSRopeNodeIterator {
|
|||
static const size_t DONE_RIGHT = 0x2;
|
||||
|
||||
public:
|
||||
JSRopeNodeIterator(JSString *str)
|
||||
: mUsedFlags(0)
|
||||
{
|
||||
mStr = 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;
|
||||
}
|
||||
JSRopeNodeIterator()
|
||||
: mStr(NULL), mUsedFlags(0)
|
||||
{}
|
||||
|
||||
JSString *init(JSString *str) {
|
||||
/* Move to the farthest-left leaf in the rope. */
|
||||
mStr = str;
|
||||
while (mStr->isInteriorNode())
|
||||
mStr = mStr->interiorNodeParent();
|
||||
while (mStr->ropeLeft()->isInteriorNode())
|
||||
|
@ -697,14 +690,9 @@ class JSRopeLeafIterator {
|
|||
JSRopeNodeIterator mNodeIterator;
|
||||
|
||||
public:
|
||||
|
||||
JSRopeLeafIterator(JSString *topNode) :
|
||||
mNodeIterator(topNode) {
|
||||
JS_ASSERT(topNode->isTopNode());
|
||||
}
|
||||
|
||||
inline JSString *init() {
|
||||
JSString *str = mNodeIterator.init();
|
||||
inline JSString *init(JSString *str) {
|
||||
JS_ASSERT(str->isTopNode());
|
||||
str = mNodeIterator.init(str);
|
||||
while (str->isRope()) {
|
||||
str = mNodeIterator.next();
|
||||
JS_ASSERT(str);
|
||||
|
|
|
@ -2221,7 +2221,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag
|
|||
global_slots(NULL),
|
||||
callDepth(anchor ? anchor->calldepth : 0),
|
||||
atoms(FrameAtomBase(cx, cx->fp())),
|
||||
consts(cx->fp()->script()->constOffset
|
||||
consts(JSScript::isValidOffset(cx->fp()->script()->constOffset)
|
||||
? cx->fp()->script()->consts()->vector
|
||||
: NULL),
|
||||
strictModeCode_ins(NULL),
|
||||
|
@ -2446,7 +2446,7 @@ TraceRecorder::finishAbort(const char* reason)
|
|||
|
||||
AUDIT(recorderAborted);
|
||||
#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",
|
||||
tree->treeFileName,
|
||||
tree->treeLineNumber,
|
||||
|
@ -2788,6 +2788,13 @@ TraceMonitor::sweep()
|
|||
JS_ASSERT(!ontrace());
|
||||
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) {
|
||||
TreeFragment** fragp = &vmfragments[i];
|
||||
while (TreeFragment* frag = *fragp) {
|
||||
|
@ -2805,7 +2812,9 @@ TraceMonitor::sweep()
|
|||
JS_ASSERT(frag->root == frag);
|
||||
*fragp = frag->next;
|
||||
do {
|
||||
verbose_only( FragProfiling_FragFinalizer(frag, this); )
|
||||
verbose_only( FragProfiling_FragFinalizer(frag, this); );
|
||||
if (recorderTree == frag)
|
||||
shouldAbortRecording = true;
|
||||
TrashTree(frag);
|
||||
frag = frag->peer;
|
||||
} while (frag);
|
||||
|
@ -2815,7 +2824,7 @@ TraceMonitor::sweep()
|
|||
}
|
||||
}
|
||||
|
||||
if (recorder && HasUnreachableGCThings(recorder->getTree()))
|
||||
if (shouldAbortRecording)
|
||||
recorder->finishAbort("dead GC things");
|
||||
}
|
||||
|
||||
|
@ -4289,7 +4298,7 @@ TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit,
|
|||
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
|
||||
* to detect that case and avoid calling guard(). Otherwise, change
|
||||
* 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
|
||||
* 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();
|
||||
atoms = FrameAtomBase(cx, cx->fp());
|
||||
consts = cx->fp()->hasImacropc() || script->constOffset == 0
|
||||
? 0
|
||||
: script->consts()->vector;
|
||||
consts = (cx->fp()->hasImacropc() || !JSScript::isValidOffset(script->constOffset))
|
||||
? 0
|
||||
: script->consts()->vector;
|
||||
strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
|
||||
}
|
||||
|
||||
|
@ -7894,7 +7903,7 @@ JS_REQUIRES_STACK void
|
|||
TraceRecorder::updateAtoms(JSScript *script)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -10839,13 +10848,20 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, Value* argv, Value* rval)
|
|||
CHECK_STATUS(getClassPrototype(ctor, proto_ins));
|
||||
|
||||
LIns *arr_ins;
|
||||
if (argc == 0 || (argc == 1 && argv[0].isNumber())) {
|
||||
LIns *args[] = { argc == 0 ? w.immi(0) : d2i(get(argv)), proto_ins, cx_ins };
|
||||
arr_ins = w.call(&js_NewEmptyArray_ci, args);
|
||||
if (argc == 0) {
|
||||
LIns *args[] = { proto_ins, cx_ins };
|
||||
arr_ins = w.call(&js::NewDenseEmptyArray_ci, args);
|
||||
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 {
|
||||
LIns *args[] = { w.nameImmi(argc), proto_ins, cx_ins };
|
||||
arr_ins = w.call(&js_NewPreallocatedArray_ci, args);
|
||||
LIns *args[] = { proto_ins, w.nameImmi(argc), cx_ins };
|
||||
arr_ins = w.call(&js::NewDenseAllocatedArray_ci, args);
|
||||
guard(false, w.eqp0(arr_ins), OOM_EXIT);
|
||||
|
||||
// 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) {
|
||||
LIns* a = get(&vp[2]);
|
||||
if (IsPromoteInt(a)) {
|
||||
if (IsPromoteInt(a) && vp[2].toNumber() != INT_MIN) {
|
||||
a = w.demote(a);
|
||||
/* abs(INT_MIN) can't be done using integers; exit if we see it. */
|
||||
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();
|
||||
if (native == js_str_charAt) {
|
||||
jsdouble i = vp[2].toNumber();
|
||||
if (JSDOUBLE_IS_NaN(i))
|
||||
i = 0;
|
||||
if (i < 0 || i >= str->length())
|
||||
RETURN_STOP("charAt out of bounds");
|
||||
LIns* str_ins = get(&vp[1]);
|
||||
|
@ -11194,6 +11212,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
|
|||
return RECORD_CONTINUE;
|
||||
} else if (native == js_str_charCodeAt) {
|
||||
jsdouble i = vp[2].toNumber();
|
||||
if (JSDOUBLE_IS_NaN(i))
|
||||
i = 0;
|
||||
if (i < 0 || i >= str->length())
|
||||
RETURN_STOP("charCodeAt out of bounds");
|
||||
LIns* str_ins = get(&vp[1]);
|
||||
|
@ -11447,7 +11467,7 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
|
|||
|
||||
if (Probes::callTrackingActive(cx)) {
|
||||
JSScript *script = FUN_SCRIPT(fun);
|
||||
if (! script || ! script->isEmpty()) {
|
||||
if (!script || !script->isEmpty()) {
|
||||
LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins };
|
||||
LIns* call_ins = w.call(&functionProbe_ci, args);
|
||||
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));
|
||||
|
||||
// Ensure idx >= 0 && idx < length (by using uint32)
|
||||
guard(true,
|
||||
w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
|
||||
OVERFLOW_EXIT);
|
||||
CHECK_STATUS_A(guard(true,
|
||||
w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
|
||||
"inRange"),
|
||||
OVERFLOW_EXIT, /* abortIfAlwaysExits = */true));
|
||||
|
||||
// We're now ready to store
|
||||
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,
|
||||
* so we can optimize for the empty script singleton right away. 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.
|
||||
* so we can optimize away the function call if the corresponding script is
|
||||
* empty. No need to worry about crossing globals or relocating argv, even,
|
||||
* in this case!
|
||||
*/
|
||||
if (fun->u.i.script->isEmpty()) {
|
||||
if (fun->script()->isEmpty()) {
|
||||
LIns* rval_ins;
|
||||
if (constructing) {
|
||||
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.
|
||||
*/
|
||||
guard(true,
|
||||
w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)),
|
||||
w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(priv_ins)), "inRange"),
|
||||
BRANCH_EXIT);
|
||||
|
||||
/* We are now ready to load. Do a different type of load
|
||||
|
@ -14075,8 +14094,8 @@ TraceRecorder::record_JSOP_NEWINIT()
|
|||
|
||||
LIns *v_ins;
|
||||
if (key == JSProto_Array) {
|
||||
LIns *args[] = { w.immi(0), proto_ins, cx_ins };
|
||||
v_ins = w.call(&js_NewPreallocatedArray_ci, args);
|
||||
LIns *args[] = { proto_ins, cx_ins };
|
||||
v_ins = w.call(&NewDenseEmptyArray_ci, args);
|
||||
} else {
|
||||
LIns *args[] = { w.immpNull(), proto_ins, cx_ins };
|
||||
v_ins = w.call(&js_InitializerObject_ci, args);
|
||||
|
@ -14095,8 +14114,8 @@ TraceRecorder::record_JSOP_NEWARRAY()
|
|||
CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins));
|
||||
|
||||
unsigned count = GET_UINT24(cx->regs->pc);
|
||||
LIns *args[] = { w.immi(count), proto_ins, cx_ins };
|
||||
LIns *v_ins = w.call(&js_NewPreallocatedArray_ci, args);
|
||||
LIns *args[] = { proto_ins, w.immi(count), cx_ins };
|
||||
LIns *v_ins = w.call(&NewDenseAllocatedArray_ci, args);
|
||||
|
||||
guard(false, w.eqp0(v_ins), OOM_EXIT);
|
||||
stack(0, v_ins);
|
||||
|
|
|
@ -1213,7 +1213,7 @@ class TypedArrayTemplate
|
|||
bool
|
||||
copyFromWithOverlap(JSContext *cx, TypedArray *tarray, jsuint offset = 0)
|
||||
{
|
||||
JS_ASSERT(offset < length);
|
||||
JS_ASSERT(offset <= length);
|
||||
|
||||
NativeType *dest = static_cast<NativeType*>(data) + offset;
|
||||
|
||||
|
|
|
@ -789,10 +789,10 @@ extern "C++"
|
|||
# endif /* defined(__cplusplus) */
|
||||
|
||||
/* Internal helper macros */
|
||||
#define JSVAL_BITS(v) (v.asBits)
|
||||
#define JSVAL_BITS(v) ((v).asBits)
|
||||
#define JSVAL_FROM_LAYOUT(l) (l)
|
||||
#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) */
|
||||
|
||||
|
|
|
@ -526,12 +526,27 @@ CanReify(Value *vp)
|
|||
(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
|
||||
Reify(JSContext *cx, JSCompartment *origin, Value *vp)
|
||||
{
|
||||
JSObject *iterObj = &vp->toObject();
|
||||
NativeIterator *ni = iterObj->getNativeIterator();
|
||||
|
||||
AutoCloseIterator close(cx, iterObj);
|
||||
|
||||
/* Wrap the iteratee. */
|
||||
JSObject *obj = ni->obj;
|
||||
if (!origin->wrap(cx, &obj))
|
||||
|
@ -556,6 +571,7 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp)
|
|||
}
|
||||
}
|
||||
|
||||
close.clear();
|
||||
return js_CloseIterator(cx, iterObj) &&
|
||||
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) &&
|
||||
VectorToValueIterator(cx, obj, ni->flags, vals, vp);
|
||||
}
|
||||
|
|
|
@ -669,13 +669,12 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
|
|||
JS_PUBLIC_API(JSBool)
|
||||
JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
|
||||
{
|
||||
if (!js_XDRScript(xdr, scriptp, true, NULL))
|
||||
if (!js_XDRScript(xdr, scriptp, NULL))
|
||||
return JS_FALSE;
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
js_CallNewScriptHook(xdr->cx, *scriptp, NULL);
|
||||
if (*scriptp != JSScript::emptyScript() &&
|
||||
!js_NewScriptObject(xdr->cx, *scriptp)) {
|
||||
if (!js_NewScriptObject(xdr->cx, *scriptp)) {
|
||||
js_DestroyScript(xdr->cx, *scriptp);
|
||||
*scriptp = NULL;
|
||||
return JS_FALSE;
|
||||
|
|
|
@ -5774,7 +5774,7 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
|
|||
static bool
|
||||
NamespacesToJSArray(JSContext *cx, JSXMLArray *array, jsval *rval)
|
||||
{
|
||||
JSObject *arrayobj = js_NewArrayObject(cx, 0, NULL);
|
||||
JSObject *arrayobj = NewDenseEmptyArray(cx);
|
||||
if (!arrayobj)
|
||||
return false;
|
||||
*rval = OBJECT_TO_JSVAL(arrayobj);
|
||||
|
@ -7466,7 +7466,7 @@ GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|||
if (VALUE_IS_FUNCTION(cx, *vp))
|
||||
return true;
|
||||
target = target->getProto();
|
||||
if (target == NULL)
|
||||
if (target == NULL || !target->isNative())
|
||||
break;
|
||||
tvr.setObject(target);
|
||||
}
|
||||
|
|
|
@ -122,7 +122,6 @@ mjit::Compiler::Compiler(JSContext *cx, JSStackFrame *fp)
|
|||
CompileStatus
|
||||
mjit::Compiler::compile()
|
||||
{
|
||||
JS_ASSERT(!script->isEmpty());
|
||||
JS_ASSERT_IF(isConstructing, !script->jitCtor);
|
||||
JS_ASSERT_IF(!isConstructing, !script->jitNormal);
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ FindExceptionHandler(JSContext *cx)
|
|||
JSScript *script = fp->script();
|
||||
|
||||
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.
|
||||
unsigned offset = cx->regs->pc - script->main;
|
||||
|
||||
|
@ -308,18 +308,6 @@ stubs::CompileFunction(VMFrame &f, uint32 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()) {
|
||||
fp = (JSStackFrame *)FixupArity(f, nactual);
|
||||
if (!fp)
|
||||
|
@ -339,7 +327,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
|||
|
||||
CompileStatus status = CanMethodJIT(cx, script, fp);
|
||||
if (status == Compile_Okay)
|
||||
return script->getJIT(callingNew)->invokeEntry;
|
||||
return script->getJIT(fp->isConstructing())->invokeEntry;
|
||||
|
||||
/* Function did not compile... interpret it. */
|
||||
JSBool ok = Interpret(cx, fp);
|
||||
|
@ -422,9 +410,7 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
|||
Value *vp = f.regs.sp - (argc + 2);
|
||||
|
||||
/* Try to do a fast inline call before the general Invoke path. */
|
||||
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted() &&
|
||||
!ucr->fun->script()->isEmpty())
|
||||
{
|
||||
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
|
||||
ucr->callee = &vp->toObject();
|
||||
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc))
|
||||
THROW();
|
||||
|
@ -476,12 +462,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
|
|||
ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
|
||||
|
||||
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))
|
||||
THROW();
|
||||
return;
|
||||
|
|
|
@ -1368,14 +1368,10 @@ stubs::Neg(VMFrame &f)
|
|||
JSObject * JS_FASTCALL
|
||||
stubs::NewInitArray(VMFrame &f, uint32 count)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
gc::FinalizeKind kind = GuessObjectGCKind(count, true);
|
||||
|
||||
JSObject *obj = NewArrayWithKind(cx, kind);
|
||||
if (!obj || !obj->ensureSlots(cx, count))
|
||||
JSObject *obj = NewDenseAllocatedArray(f.cx, count);
|
||||
if (!obj)
|
||||
THROWV(NULL);
|
||||
|
||||
obj->setArrayLength(count);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -2574,8 +2570,8 @@ stubs::Unbrand(VMFrame &f)
|
|||
if (!thisv.isObject())
|
||||
return;
|
||||
JSObject *obj = &thisv.toObject();
|
||||
if (obj->isNative() && !obj->unbrand(f.cx))
|
||||
THROW();
|
||||
if (obj->isNative())
|
||||
obj->unbrand(f.cx);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
|
|
|
@ -1975,7 +1975,7 @@ TryNotes(JSContext *cx, JSScript *script)
|
|||
{
|
||||
JSTryNote *tn, *tnlimit;
|
||||
|
||||
if (script->trynotesOffset == 0)
|
||||
if (!JSScript::isValidOffset(script->trynotesOffset))
|
||||
return JS_TRUE;
|
||||
|
||||
tn = script->trynotes()->vector;
|
||||
|
@ -2053,7 +2053,7 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
|
|||
SrcNotes(cx, script);
|
||||
TryNotes(cx, script);
|
||||
|
||||
if (recursive && script->objectsOffset != 0) {
|
||||
if (recursive && JSScript::isValidOffset(script->objectsOffset)) {
|
||||
JSObjectArray *objects = script->objects();
|
||||
for (uintN i = 0; i != objects->length; ++i) {
|
||||
JSObject *obj = objects->vector[i];
|
||||
|
@ -2139,11 +2139,6 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp)
|
|||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
if (script->isEmpty()) {
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSObject *obj = JS_NewScriptObject(cx, script);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
@ -4293,7 +4288,7 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FN("startTraceVis", StartTraceVisNative, 1,0),
|
||||
JS_FN("stopTraceVis", StopTraceVisNative, 0,0),
|
||||
#endif
|
||||
#ifdef DEBUG_ARRAYS
|
||||
#ifdef DEBUG
|
||||
JS_FN("arrayInfo", js_ArrayInfo, 1,0),
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
|
@ -4420,7 +4415,7 @@ static const char *const shell_help_messages[] = {
|
|||
"startTraceVis(filename) Start TraceVis recording (stops any current recording)",
|
||||
"stopTraceVis() Stop TraceVis recording",
|
||||
#endif
|
||||
#ifdef DEBUG_ARRAYS
|
||||
#ifdef DEBUG
|
||||
"arrayInfo(a1, a2, ...) Report statistics about arrays",
|
||||
#endif
|
||||
#ifdef JS_THREADSAFE
|
||||
|
|
|
@ -2,4 +2,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Array/
|
|||
script sort-01.js
|
||||
script toString-01.js
|
||||
script toLocaleString-01.js
|
||||
script regress-599159.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/
|
||||
script 11.1.5-01.js
|
||||
script named-accessor-function.js
|
||||
script nested-delete-name-in-evalcode.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 regress-bug607284.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.12.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.2.js
|
||||
script 15.10.7.js
|
||||
|
@ -37,3 +37,4 @@ script regress-532254.js
|
|||
script regress-532041.js
|
||||
script unbrand-this.js
|
||||
script this-for-function-expression-recursion.js
|
||||
script directive-prologue-01.js
|
||||
|
|
|
@ -236,6 +236,7 @@ function test()
|
|||
|
||||
empty.set([]);
|
||||
empty.set([], 0);
|
||||
empty.set(empty);
|
||||
|
||||
checkThrows(function() empty.set([1]));
|
||||
checkThrows(function() empty.set([1], 0));
|
||||
|
@ -244,6 +245,7 @@ function test()
|
|||
a.set([]);
|
||||
a.set([], 3);
|
||||
a.set([], 9);
|
||||
a.set(a);
|
||||
|
||||
a.set(empty);
|
||||
a.set(empty, 3);
|
||||
|
|
|
@ -56,3 +56,5 @@ script regress-610026.js
|
|||
script regress-609617.js
|
||||
script regress-617405-1.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)
|
||||
return JS_TRUE;
|
||||
|
||||
JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
|
||||
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;
|
||||
return nsXPConnect::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -259,36 +230,7 @@ Btoa(JSContext *cx, uintN argc, jsval *vp)
|
|||
if (!argc)
|
||||
return JS_TRUE;
|
||||
|
||||
JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
|
||||
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;
|
||||
return nsXPConnect::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
|
||||
}
|
||||
|
||||
static JSFunctionSpec gGlobalFun[] = {
|
||||
|
|
|
@ -64,6 +64,8 @@
|
|||
|
||||
#include "jsdIDebuggerService.h"
|
||||
|
||||
#include "xpcquickstubs.h"
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect,
|
||||
nsIXPConnect,
|
||||
nsISupportsWeakReference,
|
||||
|
@ -2615,8 +2617,7 @@ nsXPConnect::GetCaller(JSContext **aJSContext, JSObject **aObj)
|
|||
|
||||
// static
|
||||
nsresult
|
||||
nsXPConnect::Base64Encode(const nsACString &aBinaryData,
|
||||
nsACString &aString)
|
||||
nsXPConnect::Base64Encode(const nsACString &aBinaryData, nsACString &aString)
|
||||
{
|
||||
// Check for overflow.
|
||||
if(aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3)
|
||||
|
@ -2645,8 +2646,7 @@ nsXPConnect::Base64Encode(const nsACString &aBinaryData,
|
|||
|
||||
// static
|
||||
nsresult
|
||||
nsXPConnect::Base64Encode(const nsAString &aString,
|
||||
nsAString &aBinaryData)
|
||||
nsXPConnect::Base64Encode(const nsAString &aString, nsAString &aBinaryData)
|
||||
{
|
||||
NS_LossyConvertUTF16toASCII string(aString);
|
||||
nsCAutoString binaryData;
|
||||
|
@ -2660,10 +2660,37 @@ nsXPConnect::Base64Encode(const nsAString &aString,
|
|||
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
|
||||
nsresult
|
||||
nsXPConnect::Base64Decode(const nsACString &aString,
|
||||
nsACString &aBinaryData)
|
||||
nsXPConnect::Base64Decode(const nsACString &aString, nsACString &aBinaryData)
|
||||
{
|
||||
// Check for overflow.
|
||||
if(aString.Length() > PR_UINT32_MAX / 3)
|
||||
|
@ -2700,8 +2727,7 @@ nsXPConnect::Base64Decode(const nsACString &aString,
|
|||
|
||||
// static
|
||||
nsresult
|
||||
nsXPConnect::Base64Decode(const nsAString &aBinaryData,
|
||||
nsAString &aString)
|
||||
nsXPConnect::Base64Decode(const nsAString &aBinaryData, nsAString &aString)
|
||||
{
|
||||
NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
|
||||
nsCAutoString string;
|
||||
|
@ -2715,6 +2741,34 @@ nsXPConnect::Base64Decode(const nsAString &aBinaryData,
|
|||
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
|
||||
nsXPConnect::SetDebugModeWhenPossible(PRBool mode)
|
||||
{
|
||||
|
|
|
@ -570,12 +570,18 @@ public:
|
|||
static nsresult Base64Encode(const nsAString &aString,
|
||||
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,
|
||||
nsACString &aString);
|
||||
|
||||
static nsresult Base64Decode(const nsAString &aBinaryData,
|
||||
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
|
||||
NS_IMETHOD RootAndUnlinkJSObjects(void *p);
|
||||
NS_IMETHOD Unlink(void *p);
|
||||
|
|
|
@ -709,14 +709,24 @@ xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
|
|||
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;
|
||||
// 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)
|
||||
return;
|
||||
|
||||
size_t len = JS_GetStringEncodingLength(cx, s);
|
||||
if(len == size_t(-1))
|
||||
{
|
||||
mValid = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
JSAutoByteString bytes(cx, s);
|
||||
if(!bytes)
|
||||
{
|
||||
|
@ -724,7 +734,7 @@ xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval)
|
|||
return;
|
||||
}
|
||||
|
||||
new(mBuf) implementation_type(bytes.ptr(), strlen(bytes.ptr()));
|
||||
new(mBuf) implementation_type(bytes.ptr(), len);
|
||||
mValid = JS_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -439,7 +439,9 @@ public:
|
|||
class xpc_qsACString : public xpc_qsBasicString<nsACString, nsCString>
|
||||
{
|
||||
public:
|
||||
xpc_qsACString(JSContext *cx, jsval v, jsval *pval);
|
||||
xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
|
||||
StringificationBehavior nullBehavior = eNull,
|
||||
StringificationBehavior undefinedBehavior = eNull);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче