Merge TM -> JM

This commit is contained in:
Brian Hackett 2011-03-10 09:20:42 -08:00
commit eaea79df6e
28 changed files with 979 additions and 555 deletions

View File

@ -339,6 +339,11 @@ ifeq (x86_64, $(TARGET_CPU))
ifdef _MSC_VER
ASFILES += TrampolineMasmX64.asm
endif
ifeq ($(OS_ARCH),WINNT)
ifdef GNU_CC
ASFILES += TrampolineMingwX64.s
endif
endif
ifdef SOLARIS_SUNPRO_CXX
ASFILES += TrampolineSUNWX64.s
endif

View File

@ -30,6 +30,7 @@
namespace JSC {
size_t ExecutableAllocator::pageSize = 0;
size_t ExecutableAllocator::largeAllocSize = 0;
}

View File

@ -54,16 +54,6 @@
extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags);
#endif
#define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
/*
* On Windows, VirtualAlloc effectively allocates in 64K chunks. (Technically,
* it allocates in page chunks, but the starting address is always a multiple
* of 64K, so each allocation uses up 64K of address space.) So a size less
* than that would be pointless. But it turns out that 64KB is a reasonable
* size for all platforms.
*/
#define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 16)
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
#define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
#define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
@ -72,38 +62,13 @@ extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLeng
#define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
#endif
namespace JSC {
// Something included via windows.h defines a macro with this name,
// which causes the function below to fail to compile.
#ifdef _MSC_VER
# undef max
#endif
const size_t OVERSIZE_ALLOCATION = size_t(-1);
inline size_t roundUpAllocationSize(size_t request, size_t granularity)
{
if ((std::numeric_limits<size_t>::max() - granularity) <= request)
return OVERSIZE_ALLOCATION;
// Round up to next page boundary
size_t size = request + (granularity - 1);
size = size & ~(granularity - 1);
JS_ASSERT(size >= request);
return size;
}
}
#if ENABLE_ASSEMBLER
//#define DEBUG_STRESS_JSC_ALLOCATOR
namespace JSC {
// These are reference-counted. A new one (from the constructor or create)
// starts with a count of 1.
// These are reference-counted. A new one starts with a count of 1.
class ExecutablePool {
friend class ExecutableAllocator;
private:
@ -130,7 +95,18 @@ public:
// remember whether m_destroy was computed for the currently active GC.
size_t m_gcNumber;
public:
void release(bool willDestroy = false)
{
JS_ASSERT(m_refCount != 0);
JS_ASSERT_IF(willDestroy, m_refCount = 1);
if (--m_refCount == 0) {
/* We can't (easily) use js_delete() here because the destructor is private. */
this->~ExecutablePool();
js_free(this);
}
}
private:
// It should be impossible for us to roll over, because only small
// pools have multiple holders, and they have one holder per chunk
// of generated code, and they only hold 16KB or so of code.
@ -140,41 +116,11 @@ public:
++m_refCount;
}
void release()
{
JS_ASSERT(m_refCount != 0);
if (--m_refCount == 0)
this->destroy();
}
private:
static ExecutablePool* create(size_t n)
{
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutablePool));
ExecutablePool *pool = memory ? new(memory) ExecutablePool(n) : NULL;
if (!pool || !pool->m_freePtr) {
pool->destroy();
return NULL;
}
return pool;
}
void* alloc(size_t n)
{
JS_ASSERT(n != OVERSIZE_ALLOCATION);
JS_ASSERT(n <= available());
void *result = m_freePtr;
m_freePtr += n;
return result;
}
void destroy()
{
/* We can't (easily) use js_delete() here because the destructor is private. */
this->~ExecutablePool();
js_free(this);
}
ExecutablePool(Allocation a)
: m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), m_refCount(1),
m_destroy(false), m_gcNumber(0)
{ }
~ExecutablePool()
{
@ -182,6 +128,14 @@ private:
ExecutablePool::systemRelease(m_allocation);
}
void* alloc(size_t n)
{
JS_ASSERT(n <= available());
void *result = m_freePtr;
m_freePtr += n;
return result;
}
size_t available() const {
JS_ASSERT(m_end >= m_freePtr);
return m_end - m_freePtr;
@ -190,43 +144,34 @@ private:
// On OOM, this will return an Allocation where pages is NULL.
static Allocation systemAlloc(size_t n);
static void systemRelease(const Allocation& alloc);
ExecutablePool(size_t n);
};
class ExecutableAllocator {
enum ProtectionSeting { Writable, Executable };
// Initialization can fail so we use a create method instead.
ExecutableAllocator() {}
public:
static size_t pageSize;
// Returns NULL on OOM.
static ExecutableAllocator *create()
ExecutableAllocator()
{
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutableAllocator));
ExecutableAllocator *allocator = memory ? new(memory) ExecutableAllocator() : NULL;
if (!allocator)
return allocator;
if (!pageSize)
intializePageSize();
ExecutablePool *pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
if (!pool) {
js_delete(allocator);
return NULL;
if (!pageSize) {
pageSize = determinePageSize();
/*
* On Windows, VirtualAlloc effectively allocates in 64K chunks.
* (Technically, it allocates in page chunks, but the starting
* address is always a multiple of 64K, so each allocation uses up
* 64K of address space.) So a size less than that would be
* pointless. But it turns out that 64KB is a reasonable size for
* all platforms. (This assumes 4KB pages.)
*/
largeAllocSize = pageSize * 16;
}
JS_ASSERT(allocator->m_smallAllocationPools.empty());
allocator->m_smallAllocationPools.append(pool);
return allocator;
JS_ASSERT(m_smallAllocationPools.empty());
}
~ExecutableAllocator()
{
for (size_t i = 0; i < m_smallAllocationPools.length(); i++)
m_smallAllocationPools[i]->destroy();
m_smallAllocationPools[i]->release(/* willDestroy = */true);
}
// alloc() returns a pointer to some memory, and also (by reference) a
@ -255,6 +200,47 @@ public:
}
private:
static size_t pageSize;
static size_t largeAllocSize;
static const size_t OVERSIZE_ALLOCATION = size_t(-1);
static size_t roundUpAllocationSize(size_t request, size_t granularity)
{
// Something included via windows.h defines a macro with this name,
// which causes the function below to fail to compile.
#ifdef _MSC_VER
# undef max
#endif
if ((std::numeric_limits<size_t>::max() - granularity) <= request)
return OVERSIZE_ALLOCATION;
// Round up to next page boundary
size_t size = request + (granularity - 1);
size = size & ~(granularity - 1);
JS_ASSERT(size >= request);
return size;
}
ExecutablePool* createPool(size_t n)
{
size_t allocSize = roundUpAllocationSize(n, pageSize);
if (allocSize == OVERSIZE_ALLOCATION)
return NULL;
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(size_t(4294967291));
#else
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(allocSize);
#endif
if (!a.pages)
return NULL;
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutablePool));
return memory ? new(memory) ExecutablePool(a) : NULL;
}
ExecutablePool* poolForSize(size_t n)
{
#ifndef DEBUG_STRESS_JSC_ALLOCATOR
@ -276,11 +262,11 @@ private:
#endif
// If the request is large, we just provide a unshared allocator
if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
return ExecutablePool::create(n);
if (n > largeAllocSize)
return createPool(n);
// Create a new allocator
ExecutablePool* pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
ExecutablePool* pool = createPool(largeAllocSize);
if (!pool)
return NULL;
// At this point, local |pool| is the owner.
@ -423,32 +409,9 @@ private:
static const size_t maxSmallPools = 4;
typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy > SmallExecPoolVector;
SmallExecPoolVector m_smallAllocationPools;
static void intializePageSize();
static size_t determinePageSize();
};
// This constructor can fail due to OOM. If it does, m_freePtr will be
// set to NULL.
inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1), m_destroy(false), m_gcNumber(0)
{
size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
if (allocSize == OVERSIZE_ALLOCATION) {
m_freePtr = NULL;
return;
}
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
Allocation mem = systemAlloc(size_t(4294967291));
#else
Allocation mem = systemAlloc(allocSize);
#endif
if (!mem.pages) {
m_freePtr = NULL;
return;
}
m_allocation = mem;
m_freePtr = mem.pages;
m_end = m_freePtr + allocSize;
}
}
#endif // ENABLE(ASSEMBLER)

View File

@ -33,9 +33,9 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
ExecutableAllocator::pageSize = 4096u;
return 4096u;
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

View File

@ -33,9 +33,9 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
ExecutableAllocator::pageSize = getpagesize();
return getpagesize();
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

View File

@ -32,18 +32,18 @@ const size_t MOVING_MEM_PAGE_SIZE = 256 * 1024;
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
#if WTF_CPU_ARMV5_OR_LOWER
// The moving memory model (as used in ARMv5 and earlier platforms)
// on Symbian OS limits the number of chunks for each process to 16.
// To mitigate this limitation increase the pagesize to
// allocate less of larger chunks.
ExecutableAllocator::pageSize = MOVING_MEM_PAGE_SIZE;
return MOVING_MEM_PAGE_SIZE;
#else
TInt page_size;
UserHal::PageSizeInBytes(page_size);
ExecutableAllocator::pageSize = page_size;
return page_size;
#endif
}

View File

@ -32,11 +32,11 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
ExecutableAllocator::pageSize = system_info.dwPageSize;
return system_info.dwPageSize;
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

View File

@ -151,7 +151,7 @@ JSCompartment::init(JSContext *cx)
#endif
#if ENABLE_YARR_JIT
regExpAllocator = JSC::ExecutableAllocator::create();
regExpAllocator = js_new<JSC::ExecutableAllocator>();
if (!regExpAllocator)
return false;
#endif

View File

@ -3110,6 +3110,13 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
fn->pn_type = TOK_FUNCTION;
fn->pn_arity = PN_FUNC;
fn->pn_pos.begin = pn->pn_pos.begin;
/*
* Set fn->pn_pos.end too, in case of error before we parse the
* closing brace. See bug 640075.
*/
fn->pn_pos.end = pn->pn_pos.end;
fn->pn_body = NULL;
fn->pn_cookie.makeFree();

View File

@ -138,10 +138,6 @@ class RegExpStatics
JS_ASSERT(pairNum < pairCount());
}
bool pairIsPresent(size_t pairNum) const {
return get(pairNum, 0) >= 0;
}
/* Precondition: paren is present. */
size_t getParenLength(size_t pairNum) const {
checkParenNum(pairNum);
@ -273,6 +269,10 @@ class RegExpStatics
JS_CALL_STRING_TRACER(trc, matchPairsInput, "res->matchPairsInput");
}
bool pairIsPresent(size_t pairNum) const {
return get(pairNum, 0) >= 0;
}
/* Value creators. */
bool createPendingInput(JSContext *cx, Value *out) const;

View File

@ -399,9 +399,7 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
{
JSErrorReport report;
char *message;
size_t linelength;
jschar *linechars;
const jschar *linelimit;
char *linebytes;
bool warning;
JSBool ok;
@ -441,18 +439,16 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
/*
* Given a token, T, that we want to complain about: if T's (starting)
* lineno doesn't match TokenStream's lineno, that means we've scanned
* past the line that T starts on, which makes it hard to print some or
* all of T's (starting) line for context. So we don't even try, leaving
* report.linebuf and friends zeroed. This means that any error involving
* a multi-line token (eg. an unterminated multi-line string literal)
* won't have a context printed.
* lineno doesn't match TokenStream's lineno, that means we've scanned past
* the line that T starts on, which makes it hard to print some or all of
* T's (starting) line for context.
*
* So we don't even try, leaving report.linebuf and friends zeroed. This
* means that any error involving a multi-line token (eg. an unterminated
* multi-line string literal) won't have a context printed.
*/
if (report.lineno != lineno)
goto report;
linelimit = userbuf.findEOL();
linelength = linelimit - linebase;
if (report.lineno == lineno) {
size_t linelength = userbuf.findEOL() - linebase;
linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
if (!linechars) {
@ -466,6 +462,7 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
warning = false;
goto out;
}
/* Unicode and char versions of the offending source line, without final \n */
report.linebuf = linebytes;
report.uclinebuf = linechars;
@ -474,6 +471,7 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
JS_ASSERT(tp->begin.lineno == tp->end.lineno);
report.tokenptr = report.linebuf + tp->begin.index;
report.uctokenptr = report.uclinebuf + tp->begin.index;
}
/*
* If there's a runtime exception type associated with this error
@ -491,7 +489,6 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
* XXX it'd probably be best if there was only one call to this
* function, but there seem to be two error reporter call points.
*/
report:
onError = cx->errorReporter;
/*
@ -809,6 +806,7 @@ IsTokenSane(Token *tp)
/* Only certain token kinds can be multi-line. */
switch (tp->type) {
case TOK_STRING:
case TOK_XMLATTR:
case TOK_XMLSPACE:
case TOK_XMLTEXT:
case TOK_XMLCOMMENT:

View File

@ -2550,111 +2550,218 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp)
return BuildFlatReplacement(cx, rdata.str, rdata.repstr, *fm, vp);
}
/*
* Subroutine used by str_split to find the next split point in str, starting
* at offset *ip and looking either for the separator substring given by sep, or
* for the next re match. In the re case, return the matched separator in *sep,
* and the possibly updated offset in *ip.
*
* Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
* separator occurrence if found, or str->length if no separator is found.
*/
static jsint
find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip,
JSSubString *sep)
class SplitMatchResult {
size_t endIndex_;
size_t length_;
public:
void setFailure() {
JS_STATIC_ASSERT(SIZE_MAX > JSString::MAX_LENGTH);
endIndex_ = SIZE_MAX;
}
bool isFailure() const {
return (endIndex_ == SIZE_MAX);
}
size_t endIndex() const {
JS_ASSERT(!isFailure());
return endIndex_;
}
size_t length() const {
JS_ASSERT(!isFailure());
return length_;
}
void setResult(size_t length, size_t endIndex) {
length_ = length;
endIndex_ = endIndex;
}
};
template<class Matcher>
static JSObject *
SplitHelper(JSContext *cx, JSLinearString *str, uint32 limit, Matcher splitMatch)
{
/*
* Stop if past end of string. If at end of string, we will compare the
* null char stored there (by js_NewString*) to sep->chars[j] in the while
* loop at the end of this function, so that
*
* "ab,".split(',') => ["ab", ""]
*
* and the resulting array converts back to the string "ab," for symmetry.
* However, we ape Perl and do this only if there is a sufficiently large
* limit argument (see str_split).
*/
jsint i = *ip;
size_t length = str->length();
if ((size_t)i > length)
return -1;
size_t strLength = str->length();
SplitMatchResult result;
const jschar *chars = str->getChars(cx);
if (!chars)
return -2;
/* Step 11. */
if (strLength == 0) {
if (!splitMatch(cx, str, 0, &result))
return NULL;
/*
* Match a regular expression against the separator at or above index i.
* Call js_ExecuteRegExp with true for the test argument. On successful
* match, get the separator from cx->regExpStatics.lastMatch.
* NB: Unlike in the non-empty string case, it's perfectly fine
* (indeed the spec requires it) if we match at the end of the
* string. Thus these cases should hold:
*
* var a = "".split("");
* assertEq(a.length, 0);
* var b = "".split(/.?/);
* assertEq(b.length, 0);
*/
if (re) {
size_t index;
if (!result.isFailure())
return NewDenseEmptyArray(cx);
Value v = StringValue(str);
return NewDenseCopiedArray(cx, 1, &v);
}
/* Step 12. */
size_t lastEndIndex = 0;
size_t index = 0;
/* Step 13. */
AutoValueVector splits(cx);
while (index < strLength) {
/* Step 13(a). */
if (!splitMatch(cx, str, index, &result))
return NULL;
/*
* Step 13(b).
*
* Our match algorithm differs from the spec in that it returns the
* next index at which a match happens. If no match happens we're
* done.
*
* But what if the match is at the end of the string (and the string is
* not empty)? Per 13(c)(ii) this shouldn't be a match, so we have to
* specially exclude it. Thus this case should hold:
*
* var a = "abc".split(/\b/);
* assertEq(a.length, 1);
* assertEq(a[0], "abc");
*/
if (result.isFailure())
break;
/* Step 13(c)(i). */
size_t sepLength = result.length();
size_t endIndex = result.endIndex();
if (sepLength == 0 && endIndex == strLength)
break;
/* Step 13(c)(ii). */
if (endIndex == lastEndIndex) {
index++;
continue;
}
/* Step 13(c)(iii). */
JS_ASSERT(lastEndIndex < endIndex);
JS_ASSERT(sepLength <= strLength);
JS_ASSERT(lastEndIndex + sepLength <= endIndex);
/* Steps 13(c)(iii)(1-3). */
size_t subLength = size_t(endIndex - sepLength - lastEndIndex);
JSString *sub = js_NewDependentString(cx, str, lastEndIndex, subLength);
if (!sub || !splits.append(StringValue(sub)))
return NULL;
/* Step 13(c)(iii)(4). */
if (splits.length() == limit)
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
/* Step 13(c)(iii)(5). */
lastEndIndex = endIndex;
/* Step 13(c)(iii)(6-7). */
if (Matcher::returnsCaptures) {
RegExpStatics *res = cx->regExpStatics();
for (size_t i = 0; i < res->parenCount(); i++) {
/* Steps 13(c)(iii)(7)(a-c). */
if (res->pairIsPresent(i + 1)) {
JSSubString parsub;
res->getParen(i + 1, &parsub);
sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
if (!sub || !splits.append(StringValue(sub)))
return NULL;
} else {
if (!splits.append(UndefinedValue()))
return NULL;
}
/* Step 13(c)(iii)(7)(d). */
if (splits.length() == limit)
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
}
}
/* Step 13(c)(iii)(8). */
index = lastEndIndex;
}
/* Steps 14-15. */
JSString *sub = js_NewDependentString(cx, str, lastEndIndex, strLength - lastEndIndex);
if (!sub || !splits.append(StringValue(sub)))
return NULL;
/* Step 16. */
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
}
/*
* The SplitMatch operation from ES5 15.5.4.14 is implemented using different
* matchers for regular expression and string separators.
*
* The algorithm differs from the spec in that the matchers return the next
* index at which a match happens.
*/
class SplitRegExpMatcher {
RegExpStatics *res;
RegExp *re;
public:
static const bool returnsCaptures = true;
SplitRegExpMatcher(RegExp *re, RegExpStatics *res) : res(res), re(re) {
}
inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
SplitMatchResult *result) {
Value rval;
again:
/* JS1.2 deviated from Perl by never matching at end of string. */
index = (size_t)i;
if (!re->execute(cx, res, str, &index, true, &rval))
return -2;
return false;
if (!rval.isTrue()) {
/* Mismatch: ensure our caller advances i past end of string. */
sep->length = 1;
return length;
result->setFailure();
return true;
}
i = (jsint)index;
JS_ASSERT(sep);
res->getLastMatch(sep);
if (sep->length == 0) {
/*
* Empty string match: never split on an empty match at the start
* of a find_split cycle. Same rule as for an empty global match
* in DoMatch.
*/
if (i == *ip) {
/*
* "Bump-along" to avoid sticking at an empty match, but don't
* bump past end of string -- our caller must do that by adding
* sep->length to our return value.
*/
if ((size_t)i == length)
return -1;
i++;
goto again;
JSSubString sep;
res->getLastMatch(&sep);
result->setResult(sep.length, index);
return true;
}
if ((size_t)i == length) {
/*
* If there was a trivial zero-length match at the end of the
* split, then we shouldn't output the matched string at the end
* of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
*/
sep->chars = NULL;
}
}
JS_ASSERT((size_t)i >= sep->length);
return i - sep->length;
};
class SplitStringMatcher {
const jschar *sepChars;
size_t sepLength;
public:
static const bool returnsCaptures = false;
SplitStringMatcher(JSLinearString *sep) {
sepChars = sep->chars();
sepLength = sep->length();
}
/*
* Special case: if sep is the empty string, split str into one character
* substrings. Let our caller worry about whether to split once at end of
* string into an empty substring.
*/
if (sep->length == 0)
return ((size_t)i == length) ? -1 : i + 1;
/*
* Now that we know sep is non-empty, search starting at i in str for an
* occurrence of all of sep's chars. If we find them, return the index of
* the first separator char. Otherwise, return length.
*/
jsint match = StringMatch(chars + i, length - i, sep->chars, sep->length);
return match == -1 ? length : match + i;
inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
SplitMatchResult *res) {
JS_ASSERT(index == 0 || index < str->length());
const jschar *chars = str->chars();
jsint match = StringMatch(chars + index, str->length() - index, sepChars, sepLength);
if (match == -1)
res->setFailure();
else
res->setResult(sepLength, index + match + sepLength);
return true;
}
};
/* ES5 15.5.4.14 */
static JSBool
str_split(JSContext *cx, uintN argc, Value *vp)
{
/* Steps 1-2. */
JSString *str = ThisToStringForStringProto(cx, vp);
if (!str)
return false;
@ -2663,7 +2770,48 @@ str_split(JSContext *cx, uintN argc, Value *vp)
if (!type || !cx->addTypeProperty(type, NULL, types::TYPE_STRING))
return false;
if (argc == 0) {
/* Step 5: Use the second argument as the split limit, if given. */
uint32 limit;
if (argc > 1 && !vp[3].isUndefined()) {
jsdouble d;
if (!ValueToNumber(cx, vp[3], &d))
return false;
limit = js_DoubleToECMAUint32(d);
} else {
limit = UINT32_MAX;
}
/* Step 8. */
RegExp *re = NULL;
JSLinearString *sepstr = NULL;
bool sepUndefined = (argc == 0 || vp[2].isUndefined());
if (!sepUndefined) {
if (VALUE_IS_REGEXP(cx, vp[2])) {
re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
} else {
JSString *sep = js_ValueToString(cx, vp[2]);
if (!sep)
return false;
vp[2].setString(sep);
sepstr = sep->ensureLinear(cx);
if (!sepstr)
return false;
}
}
/* Step 9. */
if (limit == 0) {
JSObject *aobj = NewDenseEmptyArray(cx);
if (!aobj)
return false;
aobj->setType(type);
vp->setObject(*aobj);
return true;
}
/* Step 10. */
if (sepUndefined) {
Value v = StringValue(str);
JSObject *aobj = NewDenseCopiedArray(cx, 1, &v);
if (!aobj)
@ -2672,89 +2820,22 @@ str_split(JSContext *cx, uintN argc, Value *vp)
vp->setObject(*aobj);
return true;
}
JSLinearString *strlin = str->ensureLinear(cx);
if (!strlin)
return false;
RegExp *re;
JSSubString *sep, tmp;
if (VALUE_IS_REGEXP(cx, vp[2])) {
re = static_cast<RegExp *>(vp[2].toObject().getPrivate());
sep = &tmp;
/* Set a magic value so we can detect a successful re match. */
sep->chars = NULL;
sep->length = 0;
/* Steps 11-15. */
JSObject *aobj;
if (re) {
aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(re, cx->regExpStatics()));
} else {
JSString *sepstr = js_ValueToString(cx, vp[2]);
if (!sepstr)
return false;
vp[2].setString(sepstr);
/*
* Point sep at a local copy of sepstr's header because find_split
* will modify sep->length.
*/
tmp.length = sepstr->length();
tmp.chars = sepstr->getChars(cx);
if (!tmp.chars)
return false;
re = NULL;
sep = &tmp;
// NB: sepstr is anchored through its storage in vp[2].
aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr));
}
/* Use the second argument as the split limit, if given. */
uint32 limit = 0; /* Avoid warning. */
bool limited = (argc > 1) && !vp[3].isUndefined();
if (limited) {
jsdouble d;
if (!ValueToNumber(cx, vp[3], &d))
return false;
/* Clamp limit between 0 and 1 + string length. */
limit = js_DoubleToECMAUint32(d);
if (limit > str->length())
limit = 1 + str->length();
}
AutoValueVector splits(cx);
RegExpStatics *res = cx->regExpStatics();
jsint i, j;
uint32 len = i = 0;
while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) {
if (limited && len >= limit)
break;
JSString *sub = js_NewDependentString(cx, str, i, size_t(j - i));
if (!sub || !splits.append(StringValue(sub)))
return false;
len++;
/*
* Imitate perl's feature of including parenthesized substrings that
* matched part of the delimiter in the new array, after the split
* substring that was delimited.
*/
if (re && sep->chars) {
for (uintN num = 0; num < res->parenCount(); num++) {
if (limited && len >= limit)
break;
JSSubString parsub;
res->getParen(num + 1, &parsub);
sub = js_NewStringCopyN(cx, parsub.chars, parsub.length);
if (!sub || !splits.append(StringValue(sub)))
return false;
len++;
}
sep->chars = NULL;
}
i = j + sep->length;
}
if (j == -2)
return false;
JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
if (!aobj)
return false;
/* Step 16. */
aobj->setType(type);
vp->setObject(*aobj);
return true;

View File

@ -1714,51 +1714,6 @@ fcallinfo(LIns *ins)
return ins->isop(LIR_calld) ? ins->callInfo() : NULL;
}
/*
* Determine whether this operand is guaranteed to not overflow the specified
* integer operation.
*/
static void
ChecksRequired(LOpcode op, LIns* op1, LIns* op2,
bool* needsOverflowCheck, bool* needsNegZeroCheck)
{
Interval x = Interval::of(op1, 3);
Interval y = Interval::of(op2, 3);
Interval z(0, 0);
switch (op) {
case LIR_addi:
z = Interval::add(x, y);
*needsNegZeroCheck = false;
break;
case LIR_subi:
z = Interval::sub(x, y);
*needsNegZeroCheck = false;
break;
case LIR_muli: {
z = Interval::mul(x, y);
// A would-be negative zero result can only occur if we have
// mul(0, -n) or mul(-n, 0), where n != 0. In particular, a multiply
// where one operand is a positive immediate cannot result in negative
// zero.
//
// This assumes that -0 cannot be an operand; if one had occurred we
// would have already exited the trace in order to promote the
// computation back to doubles.
*needsNegZeroCheck = (x.canBeZero() && y.canBeNegative()) ||
(y.canBeZero() && x.canBeNegative());
break;
}
default:
JS_NOT_REACHED("needsOverflowCheck");
}
*needsOverflowCheck = z.hasOverflowed;
}
/*
* JSStackFrame::numActualArgs is only defined for function frames. Since the
* actual arguments of the entry frame are kept on trace, argc is included in
@ -4470,30 +4425,6 @@ TraceRecorder::guard(bool expected, LIns* cond, ExitType exitType,
return guard(expected, cond, snapshot(exitType), abortIfAlwaysExits);
}
/*
* Emit a guard a 32-bit integer arithmetic operation op(d0, d1) and
* using the supplied side exit if it overflows.
*/
JS_REQUIRES_STACK LIns*
TraceRecorder::guard_xov(LOpcode op, LIns* d0, LIns* d1, VMSideExit* exit)
{
JS_ASSERT(exit->exitType == OVERFLOW_EXIT);
GuardRecord* guardRec = createGuardRecord(exit);
switch (op) {
case LIR_addi:
return w.addxovi(d0, d1, guardRec);
case LIR_subi:
return w.subxovi(d0, d1, guardRec);
case LIR_muli:
return w.mulxovi(d0, d1, guardRec);
default:
break;
}
JS_NOT_REACHED("unexpected opcode");
return NULL;
}
JS_REQUIRES_STACK VMSideExit*
TraceRecorder::copy(VMSideExit* copy)
{
@ -8390,17 +8321,31 @@ TraceRecorder::guardNonNeg(LIns* d0, LIns* d1, VMSideExit* exit)
}
JS_REQUIRES_STACK LIns*
TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
TraceRecorder::tryToDemote(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
{
/*
* To even consider this operation for demotion, both operands have to be
* integers and the oracle must not give us a negative hint for the
* instruction.
* If the operands and result of an arithmetic operation are all integers
* at record-time, and the oracle doesn't direct us otherwise, we
* speculatively emit a demoted (integer) operation, betting that at
* runtime we will get integer results again.
*
* We also have to protect against various edge cases. For example,
* to protect against overflow we emit a guard that will inform the oracle
* on overflow and cause a non-demoted trace to be attached that uses
* floating-point math for this operation; the exception to this case is
* if the operands guarantee that the result will be an integer (e.g.
* z = d0 * d1 with 0 <= (d0|d1) <= 0xffff guarantees z <= fffe0001).
*/
if (!oracle || oracle->isInstructionUndemotable(cx->regs->pc) ||
!IsPromotedInt32(s0) || !IsPromotedInt32(s1)) {
out:
!IsPromotedInt32(s0) || !IsPromotedInt32(s1))
{
undemotable:
if (v == LIR_modd) {
/*
* LIR_modd is a placeholder that Nanojit doesn't actually support!
* Convert it to a call.
*/
LIns* args[] = { s1, s0 };
return w.call(&js_dmod_ci, args);
}
@ -8409,58 +8354,96 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
return result;
}
jsdouble r;
switch (v) {
case LIR_addd:
r = v0 + v1;
break;
case LIR_subd:
r = v0 - v1;
break;
case LIR_muld:
r = v0 * v1;
if (r == 0.0 && (v0 < 0.0 || v1 < 0.0))
goto out;
break;
#if defined NANOJIT_IA32 || defined NANOJIT_X64
case LIR_divd:
if (v1 == 0)
goto out;
r = v0 / v1;
break;
case LIR_modd:
if (v0 < 0 || v1 == 0 || (s1->isImmD() && v1 < 0))
goto out;
r = js_dmod(v0, v1);
break;
#endif
default:
goto out;
}
/*
* The result must be an integer at record time, otherwise there is no
* point in trying to demote it.
*/
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto out;
LIns* d0 = w.demoteToInt32(s0);
LIns* d1 = w.demoteToInt32(s1);
jsdouble r;
LIns* result;
switch (v) {
case LIR_addd: {
r = v0 + v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::add(i0, i1).hasOverflowed
? w.addxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.addi(d0, d1);
break;
}
case LIR_subd: {
r = v0 - v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::sub(i0, i1).hasOverflowed
? w.subxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.subi(d0, d1);
break;
}
case LIR_muld: {
r = v0 * v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
if (r == 0.0 && (v0 < 0.0 || v1 < 0.0))
goto undemotable;
Interval i0 = Interval::of(d0, 3);
Interval i1 = Interval::of(d1, 3);
result = Interval::mul(i0, i1).hasOverflowed
? w.mulxovi(d0, d1, createGuardRecord(snapshot(OVERFLOW_EXIT)))
: w.muli(d0, d1);
/*
* Speculatively emit an integer operation, betting that at runtime we
* will get integer results again.
* A would-be negative zero result can only occur if we have
* mul(0, -n) or mul(-n, 0), where n != 0. In particular, a multiply
* where one operand is a positive immediate cannot result in negative
* zero.
*
* This assumes that -0 cannot be an operand; if one had occurred we
* would have already exited the trace in order to promote the
* computation back to doubles.
*/
VMSideExit* exit = NULL;
LIns* result;
switch (v) {
bool needsNegZeroCheck = (i0.canBeZero() && i1.canBeNegative()) ||
(i1.canBeZero() && i0.canBeNegative());
if (needsNegZeroCheck) {
/*
* Make sure we don't lose a -0. We exit if the result is zero and if
* either operand is negative. We start out using a weaker guard, checking
* if either argument is negative. If this ever fails, we recompile with
* a stronger, but slower, guard.
*/
if (v0 < 0.0 || v1 < 0.0 || oracle->isInstructionSlowZeroTest(cx->regs->pc)) {
guard(true,
w.eqi0(w.andi(w.eqi0(result),
w.ori(w.ltiN(d0, 0),
w.ltiN(d1, 0)))),
snapshot(OVERFLOW_EXIT));
} else {
guardNonNeg(d0, d1, snapshot(MUL_ZERO_EXIT));
}
}
break;
}
#if defined NANOJIT_IA32 || defined NANOJIT_X64
case LIR_divd:
case LIR_divd: {
if (v1 == 0)
goto undemotable;
r = v0 / v1;
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
/* Check for this case ourselves; Nanojit won't do it for us. */
if (d0->isImmI() && d1->isImmI())
return w.i2d(w.immi(jsint(r)));
exit = snapshot(OVERFLOW_EXIT);
VMSideExit* exit = snapshot(OVERFLOW_EXIT);
/*
* If the divisor is greater than zero its always safe to execute
@ -8474,8 +8457,7 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
w.eqiN(d1, -1))), exit);
w.label(mbr);
}
} else {
if (d1->immI() == -1)
} else if (d1->immI() == -1) {
guard(false, w.eqiN(d0, 0x80000000), exit);
}
v = LIR_divi;
@ -8486,13 +8468,22 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
/* Don't lose a -0. */
guard(false, w.eqi0(result), exit);
break;
}
case LIR_modd: {
if (v0 < 0 || v1 == 0 || (s1->isImmD() && v1 < 0))
goto undemotable;
r = js_dmod(v0, v1);
if (jsint(r) != r || JSDOUBLE_IS_NEGZERO(r))
goto undemotable;
/* Check for this case ourselves; Nanojit won't do it for us. */
if (d0->isImmI() && d1->isImmI())
return w.i2d(w.immi(jsint(r)));
exit = snapshot(OVERFLOW_EXIT);
VMSideExit* exit = snapshot(OVERFLOW_EXIT);
/* Make sure we don't trigger division by zero at runtime. */
if (!d1->isImmI())
@ -8500,12 +8491,12 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
v = LIR_modi;
result = w.modi(w.divi(d0, d1));
/* If the result is not 0, it is always within the integer domain. */
if (MaybeBranch mbr = w.jf(w.eqi0(result))) {
/*
* If the result is zero, we must exit if the lhs is negative since
* the result is -0 in this case, which is not in the integer domain.
* If the result is not 0, it is always within the integer domain.
* Otherwise, we must exit if the lhs is negative since the result is
* -0 in this case, which is not in the integer domain.
*/
if (MaybeBranch mbr = w.jf(w.eqi0(result))) {
guard(false, w.ltiN(d0, 0), exit);
w.label(mbr);
}
@ -8514,49 +8505,15 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1)
#endif
default:
v = arithOpcodeD2I(v);
JS_ASSERT(v == LIR_addi || v == LIR_muli || v == LIR_subi);
/*
* If the operands guarantee that the result will be an integer (e.g.
* z = x * y with 0 <= (x|y) <= 0xffff guarantees z <= fffe0001), we
* don't have to guard against an overflow. Otherwise we emit a guard
* that will inform the oracle and cause a non-demoted trace to be
* attached that uses floating-point math for this operation.
*/
bool needsOverflowCheck = true, needsNegZeroCheck = true;
ChecksRequired(v, d0, d1, &needsOverflowCheck, &needsNegZeroCheck);
if (needsOverflowCheck) {
exit = snapshot(OVERFLOW_EXIT);
result = guard_xov(v, d0, d1, exit);
} else {
result = w.ins2(v, d0, d1);
}
if (needsNegZeroCheck) {
JS_ASSERT(v == LIR_muli);
/*
* Make sure we don't lose a -0. We exit if the result is zero and if
* either operand is negative. We start out using a weaker guard, checking
* if either argument is negative. If this ever fails, we recompile with
* a stronger, but slower, guard.
*/
if (v0 < 0.0 || v1 < 0.0
|| !oracle || oracle->isInstructionSlowZeroTest(cx->regs->pc))
{
if (!exit)
exit = snapshot(OVERFLOW_EXIT);
guard(true,
w.eqi0(w.andi(w.eqi0(result),
w.ori(w.ltiN(d0, 0),
w.ltiN(d1, 0)))),
exit);
} else {
guardNonNeg(d0, d1, snapshot(MUL_ZERO_EXIT));
}
}
JS_NOT_REACHED("tryToDemote");
result = NULL;
break;
}
/*
* Successful demotion! Convert result to a double. This i2d will be
* removed if the result feeds into another integer or demoted operation.
*/
JS_ASSERT_IF(d0->isImmI() && d1->isImmI(), result->isImmI(jsint(r)));
return w.i2d(result);
}
@ -8862,7 +8819,7 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, Value &v_after,
AutoValueRooter tvr(cx);
*tvr.addr() = v;
ValueToNumber(cx, tvr.value(), &num);
v_ins_after = alu(LIR_addd, num, incr, v_ins, w.immd(incr));
v_ins_after = tryToDemote(LIR_addd, num, incr, v_ins, w.immd(incr));
v_after.setDouble(num + incr);
}
@ -9316,17 +9273,13 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond)
}
JS_REQUIRES_STACK RecordingStatus
TraceRecorder::unary(LOpcode op)
TraceRecorder::unaryIntOp(LOpcode op)
{
Value& v = stackval(-1);
bool intop = retTypes[op] == LTy_I;
JS_ASSERT(retTypes[op] == LTy_I);
if (v.isNumber()) {
LIns* a = get(&v);
if (intop)
a = d2i(a);
a = w.ins1(op, a);
if (intop)
a = w.i2d(a);
a = w.i2d(w.ins1(op, d2i(a)));
set(&v, a);
return RECORD_CONTINUE;
}
@ -9410,12 +9363,12 @@ TraceRecorder::binary(LOpcode op)
}
if (leftIsNumber && rightIsNumber) {
if (intop) {
a = (op == LIR_rshui) ? d2u(a) : d2i(a);
b = d2i(b);
a = (op == LIR_rshui)
? w.ui2d(w.ins2(op, d2u(a), d2i(b)))
: w.i2d(w.ins2(op, d2i(a), d2i(b)));
} else {
a = tryToDemote(op, lnum, rnum, a, b);
}
a = alu(op, lnum, rnum, a, b);
if (intop)
a = (op == LIR_rshui) ? w.ui2d(a) : w.i2d(a);
set(&l, a);
return RECORD_CONTINUE;
}
@ -10833,7 +10786,7 @@ TraceRecorder::record_JSOP_NOT()
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_BITNOT()
{
return InjectStatus(unary(LIR_noti));
return InjectStatus(unaryIntOp(LIR_noti));
}
JS_REQUIRES_STACK AbortableRecordingStatus
@ -10862,7 +10815,7 @@ TraceRecorder::record_JSOP_NEG()
-v.toNumber() == (int)-v.toNumber())
{
VMSideExit* exit = snapshot(OVERFLOW_EXIT);
a = guard_xov(LIR_subi, w.immi(0), w.demoteToInt32(a), exit);
a = w.subxovi(w.immi(0), w.demoteToInt32(a), createGuardRecord(exit));
if (!a->isImmI() && a->isop(LIR_subxovi)) {
guard(false, w.eqiN(a, 0), exit); // make sure we don't lose a -0
}
@ -15754,7 +15707,7 @@ TraceRecorder::record_JSOP_ARGCNT()
// interpreter, so we have to check for that in the trace entry frame.
// We also have to check that arguments.length has not been mutated
// at record time, because if so we will generate incorrect constant
// LIR, which will assert in alu().
// LIR, which will assert in tryToDemote().
if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());

View File

@ -1199,8 +1199,6 @@ class TraceRecorder
bool abortIfAlwaysExits = false);
JS_REQUIRES_STACK RecordingStatus guard(bool expected, nanojit::LIns* cond, VMSideExit* exit,
bool abortIfAlwaysExits = false);
JS_REQUIRES_STACK nanojit::LIns* guard_xov(nanojit::LOpcode op, nanojit::LIns* d0,
nanojit::LIns* d1, VMSideExit* exit);
nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset,
bool shouldDemoteToInt32);
@ -1276,7 +1274,7 @@ class TraceRecorder
JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i);
JS_REQUIRES_STACK void guardNonNeg(nanojit::LIns* d0, nanojit::LIns* d1, VMSideExit* exit);
JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1,
JS_REQUIRES_STACK nanojit::LIns* tryToDemote(nanojit::LOpcode op, jsdouble v0, jsdouble v1,
nanojit::LIns* s0, nanojit::LIns* s1);
nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false);
@ -1313,7 +1311,7 @@ class TraceRecorder
Value& rval);
JS_REQUIRES_STACK AbortableRecordingStatus relational(nanojit::LOpcode op, bool tryBranchAfterCond);
JS_REQUIRES_STACK RecordingStatus unary(nanojit::LOpcode op);
JS_REQUIRES_STACK RecordingStatus unaryIntOp(nanojit::LOpcode op);
JS_REQUIRES_STACK RecordingStatus binary(nanojit::LOpcode op);
JS_REQUIRES_STACK RecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj,

View File

@ -118,7 +118,7 @@ struct Registers {
#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
static const RegisterID ReturnReg = JSC::X86Registers::eax;
# if defined(JS_CPU_X86) || defined(_MSC_VER)
# if defined(JS_CPU_X86) || defined(_WIN64)
static const RegisterID ArgReg0 = JSC::X86Registers::ecx;
static const RegisterID ArgReg1 = JSC::X86Registers::edx;
# if defined(JS_CPU_X64)
@ -158,7 +158,7 @@ struct Registers {
# if defined(JS_CPU_X64)
| (1 << JSC::X86Registers::r8)
| (1 << JSC::X86Registers::r9)
# if !defined(_MSC_VER)
# if !defined(_WIN64)
| (1 << JSC::X86Registers::esi)
| (1 << JSC::X86Registers::edi)
# endif
@ -172,7 +172,7 @@ struct Registers {
// r13 is TypeMaskReg.
// r14 is PayloadMaskReg.
| (1 << JSC::X86Registers::r15)
# if defined(_MSC_VER)
# if defined(_WIN64)
| (1 << JSC::X86Registers::esi)
| (1 << JSC::X86Registers::edi)
# endif

View File

@ -168,7 +168,7 @@ JS_STATIC_ASSERT(offsetof(JSFrameRegs, sp) == 0);
# define HIDE_SYMBOL(name)
#endif
#if defined(__GNUC__)
#if defined(__GNUC__) && !defined(_WIN64)
/* If this assert fails, you need to realign VMFrame to 16 bytes. */
#ifdef JS_CPU_ARM
@ -559,9 +559,7 @@ SYMBOL_STRING(JaegerStubVeneer) ":" "\n"
# else
# error "Unsupported CPU!"
# endif
#elif defined(_MSC_VER)
#if defined(JS_CPU_X86)
#elif defined(_MSC_VER) && defined(JS_CPU_X86)
/*
* *** DANGER ***
@ -667,7 +665,9 @@ extern "C" {
}
}
#elif defined(JS_CPU_X64)
// Windows x64 uses assembler version since compiler doesn't support
// inline assembler
#elif defined(_WIN64)
/*
* *** DANGER ***
@ -679,18 +679,12 @@ JS_STATIC_ASSERT(offsetof(VMFrame, regs.fp) == 0x38);
JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
// Windows x64 uses assembler version since compiler doesn't support
// inline assembler
#else
# error "Unsupported CPU!"
#endif
#endif /* _MSC_VER */
#endif /* _WIN64 */
bool
JaegerCompartment::Initialize()
{
execAlloc_ = JSC::ExecutableAllocator::create();
execAlloc_ = js_new<JSC::ExecutableAllocator>();
if (!execAlloc_)
return false;

View File

@ -510,7 +510,7 @@ JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc)
return native;
}
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(_WIN64)
extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
#else
extern "C" void JaegerThrowpoline();

View File

@ -0,0 +1,190 @@
# -*- Mode: C++# tab-width: 4# indent-tabs-mode: nil# c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License")# you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Mozilla Japan.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Makoto Kato <m_kato@ga2.so-net.ne.jp>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
.extern js_InternalThrow
.extern SetVMFrameRegs
.extern PushActiveVMFrame
.extern PopActiveVMFrame
.text
.intel_syntax noprefix
# JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
# Value *stackLimit, void *safePoint)#
.globl JaegerTrampoline
.def JaegerTrampoline
.scl 3
.type 46
.endef
JaegerTrampoline:
push rbp
# .PUSHREG rbp
mov rbp, rsp
# .SETFRAME rbp, 0
push r12
# .PUSHREG r12
push r13
# .PUSHREG r13
push r14
# .PUSHREG r14
push r15
# .PUSHREG r15
push rdi
# .PUSHREG rdi
push rsi
# .PUSHREG rsi
push rbx
# .PUSHREG rbx
# .ENDPROLOG
# Load mask registers
mov r13, 0xffff800000000000
mov r14, 0x7fffffffffff
# Build the JIT frame.
# rcx = cx
# rdx = fp
# r9 = inlineCallCount
# fp must go into rbx
push rdx # entryFp
push r9 # inlineCallCount
push rcx # cx
push rdx # fp
mov rbx, rdx
# Space for the rest of the VMFrame.
sub rsp, 0x28
# This is actually part of the VMFrame.
mov r10, [rbp+8*5+8]
push r10
# Set cx->regs and set the active frame. Save r8 and align frame in one
push r8
mov rcx, rsp
sub rsp, 0x20
call SetVMFrameRegs
lea rcx, [rsp+0x20]
call PushActiveVMFrame
add rsp, 0x20
# Jump into the JIT code.
jmp qword ptr [rsp]
# void JaegerTrampolineReturn()#
.globl JaegerTrampolineReturn
.def JaegerTrampolineReturn
.scl 3
.type 46
.endef
JaegerTrampolineReturn:
# .ENDPROLOG
or rcx, rdx
mov qword ptr [rbx + 0x30], rcx
sub rsp, 0x20
lea rcx, [rsp+0x20]
call PopActiveVMFrame
add rsp, 0x58+0x20
pop rbx
pop rsi
pop rdi
pop r15
pop r14
pop r13
pop r12
pop rbp
mov rax, 1
ret
# void JaegerThrowpoline()
.globl JaegerThrowpoline
.def JaegerTrampoline
.scl 3
.type 46
.endef
JaegerThrowpoline:
# .ENDPROLOG
# For Windows x64 stub calls, we pad the stack by 32 before
# calling, so we must account for that here. See doStubCall.
lea rcx, [rsp+0x20]
call js_InternalThrow
test rax, rax
je throwpoline_exit
add rsp, 0x20
jmp rax
throwpoline_exit:
lea rcx, [rsp+0x20]
call PopActiveVMFrame
add rsp, 0x58+0x20
pop rbx
pop rsi
pop rdi
pop r15
pop r14
pop r13
pop r12
pop rbp
xor rax, rax
ret
# void InjectJaegerReturn()#
.globl InjectJaegerReturn
.def InjectJaegerReturn
.scl 3
.type 46
.endef
InjectJaegerReturn:
# .ENDPROLOG
mov rcx, qword ptr [rbx+0x30] # load fp->rval_ into typeReg
mov rax, qword ptr [rbx+0x28] # fp->ncode_
# Reimplementation of PunboxAssembler::loadValueAsComponents()
mov rdx, r14
and rdx, rcx
xor rcx, rdx
# For Windows x64 stub calls, we pad the stack by 32 before
# calling, so we must account for that here. See doStubCall.
mov rbx, qword ptr [rsp+0x38+0x20] # f.fp
add rsp, 0x20
jmp rax # return

View File

@ -4772,8 +4772,6 @@ static const char *const shell_help_messages[] = {
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT ==
JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */);
#undef PROFILING_FUNCTION_COUNT
#ifdef DEBUG
static void
CheckHelpMessages()
@ -4782,7 +4780,7 @@ CheckHelpMessages()
const char *lp;
/* Messages begin with "function_name(" prefix and don't end with \n. */
for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - PROFILING_FUNCTION_COUNT; ++m) {
lp = strchr(*m, '(');
JS_ASSERT(lp);
JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
@ -4794,6 +4792,8 @@ CheckHelpMessages()
# define CheckHelpMessages() ((void) 0)
#endif
#undef PROFILING_FUNCTION_COUNT
static JSBool
Help(JSContext *cx, uintN argc, jsval *vp)
{

View File

@ -118,11 +118,11 @@ for ( var i = 0; i < TEST_STRING.length; i++ ) {
eval("var s = new String( TEST_STRING ); s.split('')["+i+"]") );
}
// case where the value of the separator is undefined. in this case. the value of the separator
// should be ToString( separator ), or "undefined".
// case where the value of the separator is undefined. in this case,
// the this value is returned.
var TEST_STRING = "thisundefinedisundefinedaundefinedstringundefinedobject";
var EXPECT_STRING = new Array( "this", "is", "a", "string", "object" );
var EXPECT_STRING = new Array( TEST_STRING );
new TestCase( SECTION,
"var s = new String( "+ TEST_STRING +" ); s.split(void 0).length",

View File

@ -2,3 +2,6 @@ url-prefix ../../jsreftest.html?test=ecma_5/String/
script 15.5.4.2.js
script 15.5.4.7.js
script 15.5.4.11-01.js
script split-01.js
script split-undefined-separator.js
script split-xregexp.js

View File

@ -0,0 +1,47 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 614608;
var summary = "String.prototype.split tests";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
function assertEqArr(a1, a2) {
assertEq(a1.length, a2.length);
for(var i=0; i<a1.length; i++) {
assertEq(a1[i], a2[i]);
}
}
var order = "";
var o1 = { toString: function() { order += "b"; return "-"; }};
var o2 = { valueOf: function() { order += "a"; return 1; }};
var res = "xyz-xyz".split(o1, o2);
assertEq(order, "ab");
assertEqArr(res, ["xyz"]);
assertEqArr("".split(/.?/), []);
assertEqArr("abc".split(/\b/), ["abc"]);
assertEqArr("abc".split(/((()))./, 2), ["",""]);
assertEqArr("abc".split(/((((()))))./, 9), ["","","","","","","","",""]);
// from ES5 15.5.4.14
assertEqArr("ab".split(/a*?/), ["a", "b"]);
assertEqArr("ab".split(/a*/), ["", "b"]);
assertEqArr("A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/),
["A", undefined, "B", "bold", "/", "B", "and", undefined,
"CODE", "coded", "/", "CODE", ""]);
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

View File

@ -0,0 +1,37 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 614608;
var summary = "String.prototype.split with undefined separator";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
function assertEqArr(a1, a2) {
assertEq(a1.length, a2.length);
for(var i=0; i<a1.length; i++) {
assertEq(a1[i], a2[i]);
}
}
var s = '--undefined--undefined--';
assertEqArr(s.split(undefined, undefined), [s]);
assertEqArr(s.split(undefined, -1), [s]);
assertEqArr(s.split(undefined, 1), [s]);
assertEqArr(s.split("undefined", 1), ["--"]);
assertEqArr(s.split("-", 0), []);
assertEqArr(s.split(undefined, 0), []);
assertEqArr(s.split(s, 0), []);
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

View File

@ -0,0 +1,118 @@
/*
* Tests from http://stevenlevithan.com/demo/split.cfm
*
* Copyright (C) 2007 by Steven Levithan <stevenlevithan.com>
*
* Distributed under the terms of the MIT license.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var BUGNUMBER = 614608;
var summary = "String.prototype.split with regexp separator";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var ecmaSampleRe = /<(\/)?([^<>]+)>/;
var testCode = [
["''.split()", [""]],
["''.split(/./)", [""]],
["''.split(/.?/)", []],
["''.split(/.??/)", []],
["'ab'.split(/a*/)", ["", "b"]],
["'ab'.split(/a*?/)", ["a", "b"]],
["'ab'.split(/(?:ab)/)", ["", ""]],
["'ab'.split(/(?:ab)*/)", ["", ""]],
["'ab'.split(/(?:ab)*?/)", ["a", "b"]],
["'test'.split('')", ["t", "e", "s", "t"]],
["'test'.split()", ["test"]],
["'111'.split(1)", ["", "", "", ""]],
["'test'.split(/(?:)/, 2)", ["t", "e"]],
["'test'.split(/(?:)/, -1)", ["t", "e", "s", "t"]],
["'test'.split(/(?:)/, undefined)", ["t", "e", "s", "t"]],
["'test'.split(/(?:)/, null)", []],
["'test'.split(/(?:)/, NaN)", []],
["'test'.split(/(?:)/, true)", ["t"]],
["'test'.split(/(?:)/, '2')", ["t", "e"]],
["'test'.split(/(?:)/, 'two')", []],
["'a'.split(/-/)", ["a"]],
["'a'.split(/-?/)", ["a"]],
["'a'.split(/-??/)", ["a"]],
["'a'.split(/a/)", ["", ""]],
["'a'.split(/a?/)", ["", ""]],
["'a'.split(/a??/)", ["a"]],
["'ab'.split(/-/)", ["ab"]],
["'ab'.split(/-?/)", ["a", "b"]],
["'ab'.split(/-??/)", ["a", "b"]],
["'a-b'.split(/-/)", ["a", "b"]],
["'a-b'.split(/-?/)", ["a", "b"]],
["'a-b'.split(/-??/)", ["a", "-", "b"]],
["'a--b'.split(/-/)", ["a", "", "b"]],
["'a--b'.split(/-?/)", ["a", "", "b"]],
["'a--b'.split(/-??/)", ["a", "-", "-", "b"]],
["''.split(/()()/)", []],
["'.'.split(/()()/)", ["."]],
["'.'.split(/(.?)(.?)/)", ["", ".", "", ""]],
["'.'.split(/(.??)(.??)/)", ["."]],
["'.'.split(/(.)?(.)?/)", ["", ".", undefined, ""]],
["'A<B>bold</B>and<CODE>coded</CODE>'.split(ecmaSampleRe)",
["A", undefined, "B", "bold", "/", "B",
"and", undefined, "CODE", "coded", "/",
"CODE", ""]],
["'tesst'.split(/(s)*/)", ["t", undefined, "e", "s", "t"]],
["'tesst'.split(/(s)*?/)", ["t", undefined, "e", undefined, "s",
undefined, "s", undefined, "t"]],
["'tesst'.split(/(s*)/)", ["t", "", "e", "ss", "t"]],
["'tesst'.split(/(s*?)/)", ["t", "", "e", "", "s", "", "s", "", "t"]],
["'tesst'.split(/(?:s)*/)", ["t", "e", "t"]],
["'tesst'.split(/(?=s+)/)", ["te", "s", "st"]],
["'test'.split('t')", ["", "es", ""]],
["'test'.split('es')", ["t", "t"]],
["'test'.split(/t/)", ["", "es", ""]],
["'test'.split(/es/)", ["t", "t"]],
["'test'.split(/(t)/)", ["", "t", "es", "t", ""]],
["'test'.split(/(es)/)", ["t", "es", "t"]],
["'test'.split(/(t)(e)(s)(t)/)", ["", "t", "e", "s", "t", ""]],
["'.'.split(/(((.((.??)))))/)", ["", ".", ".", ".", "", "", ""]],
["'.'.split(/(((((.??)))))/)", ["."]]
];
function testSplit() {
for (var i = 0; i < testCode.length; i++) {
var actual = eval(testCode[i][0]);
var expected = testCode[i][1];
assertEq(actual.length, expected.length);
for(var j=0; j<actual.length; j++) {
assertEq(actual[j], expected[j], testCode[i][0]);
}
}
}
testSplit();
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

View File

@ -8,6 +8,7 @@ skip-if(!xulRuntime.shell) script worker-init.js
skip-if(!xulRuntime.shell) script worker-simple.js
skip-if(!xulRuntime.shell) script worker-terminate.js
skip-if(!xulRuntime.shell) script worker-timeout.js
script regress-558541.js
script scripted-proxies.js
script watch-undefined-setter.js
script array-length-protochange.js

View File

@ -0,0 +1,12 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor: Jeff Walden <jwalden+code@mit.edu>
*/
options("strict");
for (var i = 0; i < 5; i++)
Boolean.prototype = 42;
reportCompare(true, true);

View File

@ -92,3 +92,4 @@ script regress-634210-4.js
script regress-635195.js
script regress-636394.js
script regress-636364.js
script regress-640075.js

View File

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
"use strict";
try {
eval("(function() { eval(); function eval() {} })");
assertEq(0, 1);
} catch (e) {
assertEq(e.name, "SyntaxError");
}
reportCompare(0, 0, "ok");