mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge TM -> JM
This commit is contained in:
commit
eaea79df6e
@ -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
|
||||
|
@ -30,6 +30,7 @@
|
||||
namespace JSC {
|
||||
|
||||
size_t ExecutableAllocator::pageSize = 0;
|
||||
size_t ExecutableAllocator::largeAllocSize = 0;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,39 +439,39 @@ 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;
|
||||
if (report.lineno == lineno) {
|
||||
size_t linelength = userbuf.findEOL() - linebase;
|
||||
|
||||
linelimit = userbuf.findEOL();
|
||||
linelength = linelimit - linebase;
|
||||
linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
|
||||
if (!linechars) {
|
||||
warning = false;
|
||||
goto out;
|
||||
}
|
||||
memcpy(linechars, linebase, linelength * sizeof(jschar));
|
||||
linechars[linelength] = 0;
|
||||
linebytes = js_DeflateString(cx, linechars, linelength);
|
||||
if (!linebytes) {
|
||||
warning = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
|
||||
if (!linechars) {
|
||||
warning = false;
|
||||
goto out;
|
||||
/* Unicode and char versions of the offending source line, without final \n */
|
||||
report.linebuf = linebytes;
|
||||
report.uclinebuf = linechars;
|
||||
|
||||
/* The lineno check above means we should only see single-line tokens here. */
|
||||
JS_ASSERT(tp->begin.lineno == tp->end.lineno);
|
||||
report.tokenptr = report.linebuf + tp->begin.index;
|
||||
report.uctokenptr = report.uclinebuf + tp->begin.index;
|
||||
}
|
||||
memcpy(linechars, linebase, linelength * sizeof(jschar));
|
||||
linechars[linelength] = 0;
|
||||
linebytes = js_DeflateString(cx, linechars, linelength);
|
||||
if (!linebytes) {
|
||||
warning = false;
|
||||
goto out;
|
||||
}
|
||||
/* Unicode and char versions of the offending source line, without final \n */
|
||||
report.linebuf = linebytes;
|
||||
report.uclinebuf = linechars;
|
||||
|
||||
/* The lineno check above means we should only see single-line tokens here. */
|
||||
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:
|
||||
|
427
js/src/jsstr.cpp
427
js/src/jsstr.cpp
@ -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.
|
||||
*/
|
||||
if (re) {
|
||||
size_t index;
|
||||
Value rval;
|
||||
/*
|
||||
* 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 (!result.isFailure())
|
||||
return NewDenseEmptyArray(cx);
|
||||
|
||||
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;
|
||||
if (!rval.isTrue()) {
|
||||
/* Mismatch: ensure our caller advances i past end of string. */
|
||||
sep->length = 1;
|
||||
return length;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
Value v = StringValue(str);
|
||||
return NewDenseCopiedArray(cx, 1, &v);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* Step 12. */
|
||||
size_t lastEndIndex = 0;
|
||||
size_t index = 0;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* 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;
|
||||
if (!re->execute(cx, res, str, &index, true, &rval))
|
||||
return false;
|
||||
if (!rval.isTrue()) {
|
||||
result->setFailure();
|
||||
return true;
|
||||
}
|
||||
JSSubString sep;
|
||||
res->getLastMatch(&sep);
|
||||
|
||||
result->setResult(sep.length, index);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class SplitStringMatcher {
|
||||
const jschar *sepChars;
|
||||
size_t sepLength;
|
||||
|
||||
public:
|
||||
static const bool returnsCaptures = false;
|
||||
SplitStringMatcher(JSLinearString *sep) {
|
||||
sepChars = sep->chars();
|
||||
sepLength = sep->length();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
JSLinearString *strlin = str->ensureLinear(cx);
|
||||
if (!strlin)
|
||||
return false;
|
||||
|
||||
JSObject *aobj = NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
/* Steps 11-15. */
|
||||
JSObject *aobj;
|
||||
if (re) {
|
||||
aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(re, cx->regExpStatics()));
|
||||
} else {
|
||||
// NB: sepstr is anchored through its storage in vp[2].
|
||||
aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr));
|
||||
}
|
||||
if (!aobj)
|
||||
return false;
|
||||
|
||||
/* Step 16. */
|
||||
aobj->setType(type);
|
||||
vp->setObject(*aobj);
|
||||
return true;
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
* Speculatively emit an integer operation, betting that at runtime we
|
||||
* will get integer results again.
|
||||
*/
|
||||
VMSideExit* exit = NULL;
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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,9 +8457,8 @@ 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)
|
||||
guard(false, w.eqiN(d0, 0x80000000), exit);
|
||||
} else if (d1->immI() == -1) {
|
||||
guard(false, w.eqiN(d0, 0x80000000), exit);
|
||||
}
|
||||
v = LIR_divi;
|
||||
result = w.divi(d0, d1);
|
||||
@ -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 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))) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
guard(false, w.ltiN(d0, 0), exit);
|
||||
w.label(mbr);
|
||||
}
|
||||
@ -8513,50 +8504,16 @@ 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));
|
||||
}
|
||||
}
|
||||
default:
|
||||
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());
|
||||
|
@ -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,8 +1274,8 @@ 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,
|
||||
nanojit::LIns* s0, nanojit::LIns* s1);
|
||||
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);
|
||||
nanojit::LIns* d2u(nanojit::LIns* d);
|
||||
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
190
js/src/methodjit/TrampolineMingwX64.s
Normal file
190
js/src/methodjit/TrampolineMingwX64.s
Normal 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
|
@ -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)
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
47
js/src/tests/ecma_5/String/split-01.js
Normal file
47
js/src/tests/ecma_5/String/split-01.js
Normal 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!");
|
37
js/src/tests/ecma_5/String/split-undefined-separator.js
Normal file
37
js/src/tests/ecma_5/String/split-undefined-separator.js
Normal 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!");
|
118
js/src/tests/ecma_5/String/split-xregexp.js
Normal file
118
js/src/tests/ecma_5/String/split-xregexp.js
Normal 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!");
|
@ -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
|
||||
|
12
js/src/tests/js1_8_5/extensions/regress-558541.js
Normal file
12
js/src/tests/js1_8_5/extensions/regress-558541.js
Normal 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);
|
@ -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
|
||||
|
15
js/src/tests/js1_8_5/regress/regress-640075.js
Normal file
15
js/src/tests/js1_8_5/regress/regress-640075.js
Normal 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");
|
Loading…
Reference in New Issue
Block a user