This commit is contained in:
Robert Sayre 2009-09-25 20:35:33 -07:00
Родитель d3a304adc2 e8a6dd8b70
Коммит 536de5d141
87 изменённых файлов: 2800 добавлений и 2356 удалений

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

@ -340,7 +340,7 @@ function execute(n, x) {
} catch (e if e == BREAK && x.target == n) {
break;
} catch (e if e == CONTINUE && x.target == n) {
continue;
// Must run the update expression.
}
n.update && getValue(execute(n.update, x));
}

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

@ -476,6 +476,20 @@ function Statement(t, x) {
if (--i < 0)
throw t.newSyntaxError("Label not found");
} while (ss[i].label != label);
/*
* Both break and continue to label need to be handled specially
* within a labeled loop, so that they target that loop. If not in
* a loop, then break targets its labeled statement. Labels can be
* nested so we skip all labels immediately enclosing the nearest
* non-label statement.
*/
while (i < ss.length - 1 && ss[i+1].type == LABEL)
i++;
if (i < ss.length - 1 && ss[i+1].isLoop)
i++;
else if (tt == CONTINUE)
throw t.newSyntaxError("Invalid continue");
} else {
do {
if (--i < 0) {

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

@ -348,12 +348,20 @@ check-sync-dirs = $(PYTHON) $(srcdir)/config/check-sync-dirs.py
check::
$(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config
$(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build
check-valgrind::
$(check-sync-dirs) $(srcdir)/config $(MOZ_SYNC_BUILD_FILES)/config
$(check-sync-dirs) $(srcdir)/build $(MOZ_SYNC_BUILD_FILES)/build
endif
ifdef ENABLE_JIT
check::
$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \
--no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX)
check-valgrind::
$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/trace-test/trace-test.py \
--valgrind --no-slow --no-progress --tinderbox $(DIST)/bin/js$(BIN_SUFFIX)
endif
DIST_GARBAGE = config.cache config.log config.status \

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

@ -2481,6 +2481,7 @@ i?86-*)
;;
x86_64*-*)
AC_DEFINE(AVMPLUS_AMD64)
AC_DEFINE(AVMPLUS_64BIT)
;;
arm*-*)
AC_DEFINE(AVMPLUS_ARM)

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

@ -54,6 +54,7 @@ JITSTAT(returnLoopExits)
JITSTAT(mergedLoopExits)
JITSTAT(noCompatInnerTrees)
JITSTAT(blacklisted)
JITSTAT(cacheFlushed)
JITSTAT(archIsIA32)
JITSTAT(archIsAMD64)
JITSTAT(archIs64BIT)

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

@ -52,6 +52,7 @@ CPPSRCS = \
testXDR.cpp \
testIntString.cpp \
testSameValue.cpp \
testDefineGetterSetterNonEnumerable.cpp \
$(NULL)
DEFINES += -DEXPORT_JS_API

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

@ -0,0 +1,51 @@
#include "tests.h"
#include "jsxdrapi.h"
static JSBool
native(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
static const char PROPERTY_NAME[] = "foo";
BEGIN_TEST(testDefineGetterSetterNonEnumerable)
{
jsvalRoot vobj(cx);
JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
CHECK(obj);
vobj = OBJECT_TO_JSVAL(obj);
jsvalRoot vget(cx);
JSFunction *funGet = JS_NewFunction(cx, native, 0, 0, NULL, "get");
CHECK(funGet);
jsvalRoot vset(cx);
JSFunction *funSet = JS_NewFunction(cx, native, 1, 0, NULL, "set");
CHECK(funSet);
CHECK(JS_DefineProperty(cx, JSVAL_TO_OBJECT(vobj), PROPERTY_NAME,
JSVAL_VOID,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, jsval(vget)),
JS_DATA_TO_FUNC_PTR(JSPropertyOp, jsval(vset)),
JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE));
CHECK(JS_DefineProperty(cx, JSVAL_TO_OBJECT(vobj), PROPERTY_NAME,
JSVAL_VOID,
JS_DATA_TO_FUNC_PTR(JSPropertyOp, jsval(vget)),
JS_DATA_TO_FUNC_PTR(JSPropertyOp, jsval(vset)),
JSPROP_GETTER | JSPROP_SETTER | JSPROP_PERMANENT));
JSBool found = JS_FALSE;
uintN attrs = 0;
CHECK(JS_GetPropertyAttributes(cx, JSVAL_TO_OBJECT(vobj), PROPERTY_NAME,
&attrs, &found));
CHECK(found);
CHECK(attrs & JSPROP_GETTER);
CHECK(attrs & JSPROP_SETTER);
CHECK(attrs & JSPROP_PERMANENT);
CHECK(!(attrs & JSPROP_ENUMERATE));
return true;
}
END_TEST(testDefineGetterSetterNonEnumerable)

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

@ -572,61 +572,51 @@ JS_ValueToSource(JSContext *cx, jsval v)
JS_PUBLIC_API(JSBool)
JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
{
JSTempValueRooter tvr;
CHECK_REQUEST(cx);
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
*dp = js_ValueToNumber(cx, &tvr.u.value);
JS_POP_TEMP_ROOT(cx, &tvr);
return !JSVAL_IS_NULL(tvr.u.value);
JSAutoTempValueRooter tvr(cx, v);
*dp = js_ValueToNumber(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JS_PUBLIC_API(JSBool)
JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
{
JSTempValueRooter tvr;
CHECK_REQUEST(cx);
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
*ip = js_ValueToECMAInt32(cx, &tvr.u.value);
JS_POP_TEMP_ROOT(cx, &tvr);
return !JSVAL_IS_NULL(tvr.u.value);
JSAutoTempValueRooter tvr(cx, v);
*ip = js_ValueToECMAInt32(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JS_PUBLIC_API(JSBool)
JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
{
JSTempValueRooter tvr;
CHECK_REQUEST(cx);
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
*ip = js_ValueToECMAUint32(cx, &tvr.u.value);
JS_POP_TEMP_ROOT(cx, &tvr);
return !JSVAL_IS_NULL(tvr.u.value);
JSAutoTempValueRooter tvr(cx, v);
*ip = js_ValueToECMAUint32(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JS_PUBLIC_API(JSBool)
JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
{
JSTempValueRooter tvr;
CHECK_REQUEST(cx);
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
*ip = js_ValueToInt32(cx, &tvr.u.value);
JS_POP_TEMP_ROOT(cx, &tvr);
return !JSVAL_IS_NULL(tvr.u.value);
JSAutoTempValueRooter tvr(cx, v);
*ip = js_ValueToInt32(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JS_PUBLIC_API(JSBool)
JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
{
JSTempValueRooter tvr;
CHECK_REQUEST(cx);
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
*ip = js_ValueToUint16(cx, &tvr.u.value);
JS_POP_TEMP_ROOT(cx, &tvr);
return !JSVAL_IS_NULL(tvr.u.value);
JSAutoTempValueRooter tvr(cx, v);
*ip = js_ValueToUint16(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JS_PUBLIC_API(JSBool)
@ -850,10 +840,6 @@ JS_DestroyRuntime(JSRuntime *rt)
{
#ifdef DEBUG
/* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
if (rt->nativeEnumerators) {
fprintf(stderr,
"JS engine warning: leak of native enumerators is detected.\n");
}
if (!JS_CLIST_IS_EMPTY(&rt->contextList)) {
JSContext *cx, *iter = NULL;
uintN cxcount = 0;
@ -2626,6 +2612,7 @@ JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type)
if (!str)
return NULL;
str->initFlat(chars, length);
cx->updateMallocCounter((length + 1) * sizeof(jschar));
return str;
}
@ -3045,7 +3032,6 @@ JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
return NULL;
if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs,
0, 0)) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
return NULL;
}
return nobj;
@ -3956,6 +3942,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
ida = NULL;
iter_state = JSVAL_NULL;
JSAutoEnumStateRooter tvr(cx, obj, &iter_state);
/* Get the number of properties to enumerate. */
if (!obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties))
@ -3996,7 +3983,7 @@ JS_Enumerate(JSContext *cx, JSObject *obj)
return SetIdArrayLength(cx, ida, i);
error:
if (iter_state != JSVAL_NULL)
if (!JSVAL_IS_NULL(iter_state))
obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0);
if (ida)
JS_DestroyIdArray(cx, ida);

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

@ -401,7 +401,7 @@ IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp,
JSBool createAtom = JS_FALSE)
{
if (index <= JSVAL_INT_MAX) {
*idp = INT_TO_JSID(index);
*idp = INT_TO_JSID(int(index));
return JS_TRUE;
}
@ -555,22 +555,16 @@ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length)
JSBool
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
JSErrorReporter older;
JSTempValueRooter tvr;
jsid id;
JSBool ok;
older = JS_SetErrorReporter(cx, NULL);
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
ok = obj->getProperty(cx, id, &tvr.u.value);
JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
JSBool ok = obj->getProperty(cx, id, tvr.addr());
JS_SetErrorReporter(cx, older);
if (ok) {
*lengthp = ValueIsLength(cx, &tvr.u.value);
ok = !JSVAL_IS_NULL(tvr.u.value);
}
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
if (!ok)
return false;
*lengthp = ValueIsLength(cx, tvr.addr());
return !JSVAL_IS_NULL(tvr.value());
}
JSBool
@ -1718,7 +1712,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsva
if (!dp)
return JS_FALSE;
tmp[0] = DOUBLE_TO_JSVAL(dp);
JSAutoTempValueRooter(cx, JS_ARRAY_LENGTH(tmp), tmp);
JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp);
JSAutoTempIdRooter idr(cx);
do {
tmp[1] = *vector++;
@ -1809,12 +1803,8 @@ array_join(JSContext *cx, uintN argc, jsval *vp)
static JSBool
array_reverse(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj;
JSTempValueRooter tvr;
jsuint len, half, i;
JSBool ok, hole, hole2;
obj = JS_THIS_OBJECT(cx, vp);
jsuint len;
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !js_GetLengthProperty(cx, obj, &len))
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj);
@ -1852,22 +1842,19 @@ array_reverse(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
ok = JS_TRUE;
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
half = len / 2;
for (i = 0; i < half; i++) {
ok = JS_CHECK_OPERATION_LIMIT(cx) &&
GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&
GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&
SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&
SetOrDeleteArrayElement(cx, obj, i, hole2, *vp);
if (!ok)
break;
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
for (jsuint i = 0, half = len / 2; i < half; i++) {
JSBool hole, hole2;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetArrayElement(cx, obj, i, &hole, tvr.addr()) ||
!GetArrayElement(cx, obj, len - i - 1, &hole2, vp) ||
!SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) ||
!SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) {
return false;
}
}
JS_POP_TEMP_ROOT(cx, &tvr);
*vp = OBJECT_TO_JSVAL(obj);
return ok;
return true;
}
typedef struct MSortArgs {
@ -2609,7 +2596,7 @@ array_unshift(JSContext *cx, uintN argc, jsval *vp)
argv = JS_ARGV(cx, vp);
if (length > 0) {
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
!INDEX_TOO_SPARSE(obj, newlen + argc)) {
!INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
JS_ASSERT(newlen + argc == length + argc);
if (!EnsureCapacity(cx, obj, length + argc))
return JS_FALSE;
@ -2810,8 +2797,7 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
JSObject *aobj, *nobj;
jsuint length, alength, slot;
uintN i;
JSBool hole, ok;
JSTempValueRooter tvr;
JSBool hole;
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
argv = JS_ARGV(cx, vp) - 1;
@ -2849,14 +2835,12 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
length = 0;
}
MUST_FLOW_THROUGH("out");
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
for (i = 0; i <= argc; i++) {
ok = JS_CHECK_OPERATION_LIMIT(cx);
if (!ok)
goto out;
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
v = argv[i];
if (!JSVAL_IS_PRIMITIVE(v)) {
JSObject *wobj;
@ -2864,30 +2848,25 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
aobj = JSVAL_TO_OBJECT(v);
wobj = js_GetWrappedObject(cx, aobj);
if (OBJ_IS_ARRAY(cx, wobj)) {
ok = aobj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
&tvr.u.value);
if (!ok)
goto out;
alength = ValueIsLength(cx, &tvr.u.value);
ok = !JSVAL_IS_NULL(tvr.u.value);
if (!ok)
goto out;
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
if (!aobj->getProperty(cx, id, tvr.addr()))
return false;
alength = ValueIsLength(cx, tvr.addr());
if (JSVAL_IS_NULL(tvr.value()))
return false;
for (slot = 0; slot < alength; slot++) {
ok = JS_CHECK_OPERATION_LIMIT(cx) &&
GetArrayElement(cx, aobj, slot, &hole,
&tvr.u.value);
if (!ok)
goto out;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetArrayElement(cx, aobj, slot, &hole, tvr.addr())) {
return false;
}
/*
* Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
* properties.
*/
if (!hole) {
ok = SetArrayElement(cx, nobj, length + slot,
tvr.u.value);
if (!ok)
goto out;
if (!hole &&
!SetArrayElement(cx, nobj, length+slot, tvr.value())) {
return false;
}
}
length += alength;
@ -2895,17 +2874,12 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
}
}
ok = SetArrayElement(cx, nobj, length, v);
if (!ok)
goto out;
if (!SetArrayElement(cx, nobj, length, v))
return false;
length++;
}
ok = js_SetLengthProperty(cx, nobj, length);
out:
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
return js_SetLengthProperty(cx, nobj, length);
}
static JSBool
@ -3412,14 +3386,11 @@ js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JS_STATIC_ASSERT(JSSLOT_PRIVATE == JSSLOT_ARRAY_LENGTH);
JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH + 1 == JSSLOT_ARRAY_COUNT);
#ifdef JS_TRACER
JSObject* FASTCALL
JSObject* JS_FASTCALL
js_NewEmptyArray(JSContext* cx, JSObject* proto)
{
JS_ASSERT(OBJ_IS_ARRAY(cx, proto));
JS_ASSERT(JS_ON_TRACE(cx));
JSObject* obj = js_NewGCObject(cx, GCX_OBJECT);
if (!obj)
return NULL;
@ -3437,12 +3408,13 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto)
obj->dslots = NULL;
return obj;
}
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0)
#endif
JSObject* FASTCALL
js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
JSObject* JS_FASTCALL
js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
{
JS_ASSERT(JS_ON_TRACE(cx));
JSObject* obj = js_NewEmptyArray(cx, proto);
if (!obj)
return NULL;
@ -3451,9 +3423,9 @@ js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
return NULL;
return obj;
}
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32, 0, 0)
#endif /* JS_TRACER */
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT32, 0, 0)
#endif
JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj)

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

@ -97,6 +97,13 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
extern bool
js_InitContextBusyArrayTable(JSContext *cx);
/*
* Creates a new array with the given length and proto (NB: NULL is not
* translated to Array.prototype), with len slots preallocated.
*/
extern JSObject * JS_FASTCALL
js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len);
extern JSObject *
js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector,
JSBool holey = JS_FALSE);

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

@ -123,7 +123,7 @@ struct JSNativeTraceInfo {
#define _JS_PTR_ARGSIZE nanojit::ARGSIZE_P
#define _JS_PTR_RETSIZE nanojit::ARGSIZE_P
class ClosureVarInfo;
struct ClosureVarInfo;
/*
* Supported types for builtin functions.
@ -224,6 +224,7 @@ class ClosureVarInfo;
#define _JS_CTYPE_FRAGMENT _JS_CTYPE(nanojit::Fragment *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_CLASS _JS_CTYPE(JSClass *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_DOUBLEPTR _JS_CTYPE(double *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_CHARPTR _JS_CTYPE(char *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_APNPTR _JS_CTYPE(js_ArgsPrivateNative *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_CVIPTR _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE)
@ -471,7 +472,7 @@ JS_DECLARE_CALLINFO(js_Array_dense_setelem)
JS_DECLARE_CALLINFO(js_Array_dense_setelem_int)
JS_DECLARE_CALLINFO(js_Array_dense_setelem_double)
JS_DECLARE_CALLINFO(js_NewEmptyArray)
JS_DECLARE_CALLINFO(js_NewUninitializedArray)
JS_DECLARE_CALLINFO(js_NewArrayWithSlots)
JS_DECLARE_CALLINFO(js_ArrayCompPush)
/* Defined in jsfun.cpp. */

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

@ -95,6 +95,8 @@ FinishThreadData(JSThreadData *data)
/* All GC-related things must be already removed at this point. */
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
JS_ASSERT(!data->scriptsToGC[i]);
for (size_t i = 0; i != JS_ARRAY_LENGTH(data->nativeEnumCache); ++i)
JS_ASSERT(!data->nativeEnumCache[i]);
#endif
js_FinishGSNCache(&data->gsnCache);
@ -117,14 +119,12 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
tm->reservedDoublePoolPtr = tm->reservedDoublePool;
/*
* If we are about to regenerate shapes, we have to flush the JIT cache, too.
* If we are about to regenerate shapes, we have to flush the JIT cache,
* which will eventually abort any current recording.
*/
if (cx->runtime->gcRegenShapes)
tm->needFlush = JS_TRUE;
if (tm->recorder)
tm->recorder->deepAbort();
/*
* We want to keep tm->reservedObjects after the GC. So, unless we are
* shutting down, we don't purge them here and rather mark them during
@ -136,6 +136,8 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
/* Destroy eval'ed scripts. */
js_DestroyScriptsToGC(cx, data);
js_PurgeCachedNativeEnumerators(cx, data);
}
#ifdef JS_THREADSAFE
@ -261,6 +263,12 @@ thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
JS_ASSERT(cx->thread != thread);
js_DestroyScriptsToGC(cx, &thread->data);
/*
* The following is potentially suboptimal as it also zeros the cache
* in data, but the code simplicity wins here.
*/
js_PurgeCachedNativeEnumerators(cx, &thread->data);
DestroyThread(thread);
return JS_DHASH_REMOVE;
}
@ -557,7 +565,8 @@ DumpEvalCacheMeter(JSContext *cx)
);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
fprintf(fp, "%-8.8s %llu\n",
table[i].name, *(uint64 *)((uint8 *)ecm + table[i].offset));
table[i].name,
(unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
}
fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);

119
js/src/jscntxt.h Normal file → Executable file
Просмотреть файл

@ -101,7 +101,7 @@ namespace nanojit {
class LabelMap;
#endif
extern "C++" {
template<typename K> class DefaultHash;
template<typename K> struct DefaultHash;
template<typename K, typename V, typename H> class HashMap;
template<typename T> class Seq;
}
@ -148,15 +148,15 @@ struct JSTraceMonitor {
* last-ditch GC and suppress calls to JS_ReportOutOfMemory.
*
* !tracecx && !recorder: not on trace
* !tracecx && recorder && !recorder->deepAborted: recording
* !tracecx && recorder && recorder->deepAborted: deep aborted
* !tracecx && recorder: recording
* tracecx && !recorder: executing a trace
* tracecx && recorder: executing inner loop, recording outer loop
*/
JSContext *tracecx;
CLS(VMAllocator) allocator; // A chunk allocator for LIR.
CLS(nanojit::CodeAlloc) codeAlloc; // A general allocator for native code.
CLS(VMAllocator) dataAlloc; /* A chunk allocator for LIR. */
CLS(VMAllocator) tempAlloc; /* A temporary chunk allocator. */
CLS(nanojit::CodeAlloc) codeAlloc; /* An allocator for native code. */
CLS(nanojit::Assembler) assembler;
CLS(nanojit::LirBuffer) lirbuf;
CLS(nanojit::LirBuffer) reLirBuf;
@ -197,8 +197,10 @@ struct JSTraceMonitor {
*/
CLS(REHashMap) reFragments;
/* Keep a list of recorders we need to abort on cache flush. */
CLS(TraceRecorder) abortStack;
/*
* A temporary allocator for RE recording.
*/
CLS(VMAllocator) reTempAlloc;
#ifdef DEBUG
/* Fields needed for fragment/guard profiling. */
@ -298,6 +300,20 @@ struct JSThreadData {
*/
size_t gcMallocBytes;
/*
* Cache of reusable JSNativeEnumerators mapped by shape identifiers (as
* stored in scope->shape). This cache is nulled by the GC and protected
* by gcLock.
*/
#define NATIVE_ENUM_CACHE_LOG2 8
#define NATIVE_ENUM_CACHE_MASK JS_BITMASK(NATIVE_ENUM_CACHE_LOG2)
#define NATIVE_ENUM_CACHE_SIZE JS_BIT(NATIVE_ENUM_CACHE_LOG2)
#define NATIVE_ENUM_CACHE_HASH(shape) \
((((shape) >> NATIVE_ENUM_CACHE_LOG2) ^ (shape)) & NATIVE_ENUM_CACHE_MASK)
jsuword nativeEnumCache[NATIVE_ENUM_CACHE_SIZE];
#ifdef JS_THREADSAFE
/*
* Deallocator task for this thread.
@ -612,12 +628,6 @@ struct JSRuntime {
JSObject *anynameObject;
JSObject *functionNamespaceObject;
/*
* A helper list for the GC, so it can mark native iterator states. See
* js_TraceNativeEnumerators for details.
*/
JSNativeEnumerator *nativeEnumerators;
#ifndef JS_THREADSAFE
JSThreadData threadData;
@ -642,20 +652,6 @@ struct JSRuntime {
/* Literal table maintained by jsatom.c functions. */
JSAtomState atomState;
/*
* Cache of reusable JSNativeEnumerators mapped by shape identifiers (as
* stored in scope->shape). This cache is nulled by the GC and protected
* by gcLock.
*/
#define NATIVE_ENUM_CACHE_LOG2 8
#define NATIVE_ENUM_CACHE_MASK JS_BITMASK(NATIVE_ENUM_CACHE_LOG2)
#define NATIVE_ENUM_CACHE_SIZE JS_BIT(NATIVE_ENUM_CACHE_LOG2)
#define NATIVE_ENUM_CACHE_HASH(shape) \
((((shape) >> NATIVE_ENUM_CACHE_LOG2) ^ (shape)) & NATIVE_ENUM_CACHE_MASK)
jsuword nativeEnumCache[NATIVE_ENUM_CACHE_SIZE];
/*
* Various metering fields are defined at the end of JSRuntime. In this
* way there is no need to recompile all the code that refers to other
@ -866,6 +862,10 @@ typedef struct JSLocalRootStack {
#define JSTVU_WEAK_ROOTS (-4) /* u.weakRoots points to saved weak roots */
#define JSTVU_COMPILER (-5) /* u.compiler roots JSCompiler* */
#define JSTVU_SCRIPT (-6) /* u.script roots JSScript* */
#define JSTVU_ENUMERATOR (-7) /* a pointer to JSTempValueRooter points
to an instance of JSAutoEnumStateRooter
with u.object storing the enumeration
object */
/*
* Here single JSTVU_SINGLE covers both jsval and pointers to almost (see note
@ -929,7 +929,6 @@ typedef struct JSLocalRootStack {
#define JS_PUSH_TEMP_ROOT_SCRIPT(cx,script_,tvr) \
JS_PUSH_TEMP_ROOT_COMMON(cx, script_, tvr, JSTVU_SCRIPT, script)
#define JSRESOLVE_INFER 0xffff /* infer bits from current bytecode */
struct JSContext {
@ -1135,6 +1134,15 @@ struct JSContext {
return p;
}
inline void* mallocNoReport(size_t bytes) {
JS_ASSERT(bytes != 0);
void *p = runtime->malloc(bytes);
if (!p)
return NULL;
updateMallocCounter(bytes);
return p;
}
inline void* calloc(size_t bytes) {
JS_ASSERT(bytes != 0);
void *p = runtime->calloc(bytes);
@ -1294,6 +1302,61 @@ private:
JSTempValueRooter mTvr;
};
class JSAutoIdArray {
public:
JSAutoIdArray(JSContext *cx, JSIdArray *ida) : cx(cx), idArray(ida) {
if (ida)
JS_PUSH_TEMP_ROOT(cx, ida->length, ida->vector, &tvr);
}
~JSAutoIdArray() {
if (idArray) {
JS_POP_TEMP_ROOT(cx, &tvr);
JS_DestroyIdArray(cx, idArray);
}
}
bool operator!() {
return idArray == NULL;
}
jsid operator[](size_t i) const {
JS_ASSERT(idArray);
JS_ASSERT(i < size_t(idArray->length));
return idArray->vector[i];
}
size_t length() const {
return idArray->length;
}
private:
JSContext * const cx;
JSIdArray * const idArray;
JSTempValueRooter tvr;
};
/* The auto-root for enumeration object and its state. */
class JSAutoEnumStateRooter : public JSTempValueRooter
{
public:
JSAutoEnumStateRooter(JSContext *cx, JSObject *obj, jsval *statep)
: mContext(cx), mStatep(statep)
{
JS_ASSERT(obj);
JS_ASSERT(statep);
JS_PUSH_TEMP_ROOT_COMMON(cx, obj, this, JSTVU_ENUMERATOR, object);
}
~JSAutoEnumStateRooter() {
JS_POP_TEMP_ROOT(mContext, this);
}
void mark(JSTracer *trc) {
JS_CALL_OBJECT_TRACER(trc, u.object, "enumerator_obj");
js_MarkEnumeratorState(trc, u.object, *mStatep);
}
private:
JSContext *mContext;
jsval *mStatep;
};
class JSAutoResolveFlags
{
public:

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

@ -229,7 +229,7 @@ static intN
DaysInMonth(jsint year, jsint month)
{
JSBool leap = (DaysInYear(year) == 366);
intN result = DayFromMonth(month, leap) - DayFromMonth(month-1, leap);
intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
return result;
}

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

@ -2312,16 +2312,14 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(op != JSOP_CALLEE);
JS_ASSERT((cg->fun->flags & JSFUN_LAMBDA) && atom == cg->fun->atom);
switch (op) {
default:
/*
* Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as
* we cannot be sure cg->fun is not something of the form:
* Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight, as we
* cannot be sure cg->fun is not something of the form:
*
* var ff = (function f(s) { eval(s); return f; });
*
* where a caller invokes ff("var f = 42"). The result returned
* for such an invocation must be 42, since the callee name is
* where a caller invokes ff("var f = 42"). The result returned for
* such an invocation must be 42, since the callee name is
* lexically bound in an outer declarative environment from the
* function's activation. See jsfun.cpp:call_resolve.
*/
@ -2330,8 +2328,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
op = JSOP_CALLEE;
pn->pn_dflags |= PND_CONST;
}
break;
}
pn->pn_op = op;
pn->pn_dflags |= PND_BOUND;
return JS_TRUE;

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

@ -429,7 +429,7 @@ struct JSCodeGenerator : public JSTreeContext
~JSCodeGenerator();
bool hasSharps() {
bool rv = flags & TCF_HAS_SHARPS;
bool rv = !!(flags & TCF_HAS_SHARPS);
JS_ASSERT((sharpSlotBase >= 0) == rv);
return rv;
}

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

@ -81,43 +81,6 @@
#include "jsatominlines.h"
/*
* Reserved slot structure for Arguments objects:
*
* JSSLOT_PRIVATE - the corresponding frame until the frame exits.
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating
* whether arguments.length was overwritten.
* JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was
* overwritten.
* JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after
* the frame exists. The slot's value will be JSVAL_HOLE
* if arguments[i] was deleted or overwritten.
*/
const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2;
const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3;
/* Number of extra fixed slots besides JSSLOT_PRIVATE. */
const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START -
JSSLOT_ARGS_LENGTH;
/*
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval.
* Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that
* we check first that the shift does not overflow uint32.
*/
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
static inline bool
IsOverriddenArgsLength(JSObject *obj)
{
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
jsval v = obj->fslots[JSSLOT_ARGS_LENGTH];
return (JSVAL_TO_INT(v) & 1) != 0;
}
static inline void
SetOverriddenArgsLength(JSObject *obj)
{
@ -136,7 +99,7 @@ InitArgsLengthSlot(JSObject *obj, uint32 argc)
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(obj->fslots[JSSLOT_ARGS_LENGTH] == JSVAL_VOID);
obj->fslots[JSSLOT_ARGS_LENGTH] = INT_TO_JSVAL(argc << 1);
JS_ASSERT(!IsOverriddenArgsLength(obj));
JS_ASSERT(!js_IsOverriddenArgsLength(obj));
}
static inline uint32
@ -225,7 +188,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
}
} else if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj);
if (argsobj && IsOverriddenArgsLength(argsobj))
if (argsobj && js_IsOverriddenArgsLength(argsobj))
return argsobj->getProperty(cx, id, vp);
*vp = INT_TO_JSVAL(jsint(fp->argc));
}
@ -321,6 +284,8 @@ js_Arguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee,
double *argv, js_ArgsPrivateNative *apn)
{
JSObject *argsobj = NewArguments(cx, parent, argc, callee);
if (!argsobj)
return NULL;
apn->argv = argv;
SetArgsPrivateNative(argsobj, apn);
return argsobj;
@ -559,7 +524,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
}
}
} else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
if (!IsOverriddenArgsLength(obj))
if (!js_IsOverriddenArgsLength(obj))
*vp = INT_TO_JSVAL(GetArgsLength(obj));
} else {
JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.calleeAtom));
@ -639,7 +604,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
id = INT_JSVAL_TO_JSID(idval);
}
} else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
if (!IsOverriddenArgsLength(obj))
if (!js_IsOverriddenArgsLength(obj))
id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
} else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) {
@ -1590,7 +1555,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
nupvars = flagsword >> 16;
fun->flags = uint16(flagsword);
fun->u.i.skipmin = uint16(firstword >> 2);
fun->u.i.wrapper = (firstword >> 1) & 1;
fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
}
/* do arguments and local vars */
@ -2377,20 +2342,16 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
return NULL;
fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
if (!fun)
goto bad;
return NULL;
fun->u.i.script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
if (!fun->u.i.script)
goto bad;
return NULL;
fun->u.i.script->code[0] = JSOP_STOP;
*fun->u.i.script->notes() = SRC_NULL;
#ifdef CHECK_SCRIPT_OWNER
fun->u.i.script->owner = NULL;
#endif
return proto;
bad:
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
return NULL;
}
JSFunction *
@ -2955,7 +2916,7 @@ get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
entry->localKind == JSLOCAL_CONST ||
entry->localKind == JSLOCAL_UPVAR);
JS_ASSERT(entry->index < args->fun->u.i.nvars + args->fun->u.i.nupvars);
JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars + args->fun->u.i.nupvars);
JS_ASSERT(args->nCopiedVars++ < unsigned(args->fun->u.i.nvars + args->fun->u.i.nupvars));
i = args->fun->nargs;
if (entry->localKind == JSLOCAL_UPVAR)
i += args->fun->u.i.nvars;

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

@ -334,6 +334,43 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
extern void
js_PutArgsObject(JSContext *cx, JSStackFrame *fp);
/*
* Reserved slot structure for Arguments objects:
*
* JSSLOT_PRIVATE - the corresponding frame until the frame exits.
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating
* whether arguments.length was overwritten.
* JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was
* overwritten.
* JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after
* the frame exists. The slot's value will be JSVAL_HOLE
* if arguments[i] was deleted or overwritten.
*/
const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2;
const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3;
/* Number of extra fixed slots besides JSSLOT_PRIVATE. */
const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START -
JSSLOT_ARGS_LENGTH;
/*
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval.
* Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that
* we check first that the shift does not overflow uint32.
*/
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
JS_INLINE bool
js_IsOverriddenArgsLength(JSObject *obj)
{
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass);
jsval v = obj->fslots[JSSLOT_ARGS_LENGTH];
return (JSVAL_TO_INT(v) & 1) != 0;
}
extern JSBool
js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);

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

@ -2852,7 +2852,6 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
JS_ASSERT(nslots >= fp->script->nfixed);
} else {
nslots = fp->script->nfixed;
JS_ASSERT_IF(!fp->regs->sp, nslots == 0);
}
TRACE_JSVALS(trc, nslots, fp->slots, "slot");
}
@ -3036,6 +3035,9 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
case JSTVU_SCRIPT:
js_TraceScript(trc, tvr->u.script);
break;
case JSTVU_ENUMERATOR:
static_cast<JSAutoEnumStateRooter *>(tvr)->mark(trc);
break;
default:
JS_ASSERT(tvr->count >= 0);
TRACE_JSVALS(trc, tvr->count, tvr->u.array, "tvr->u.array");
@ -3095,7 +3097,6 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
if (rt->gcLocksHash)
JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);
js_TraceAtomState(trc, allAtoms);
js_TraceNativeEnumerators(trc);
js_TraceRuntimeNumberState(trc);
iter = NULL;

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

@ -326,22 +326,6 @@ struct JSWeakRoots {
#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
/*
* Increase runtime->gcBytes by sz bytes to account for an allocation outside
* the GC that will be freed only after the GC is run. The function may run
* the last ditch GC to ensure that gcBytes does not exceed gcMaxBytes. It will
* fail if the latter is not possible.
*
* This function requires that runtime->gcLock is held on entry. On successful
* return the lock is still held and on failure it will be released with
* the error reported.
*/
extern JSBool
js_AddAsGCBytes(JSContext *cx, size_t sz);
extern void
js_RemoveAsGCBytes(JSRuntime* rt, size_t sz);
#ifdef JS_THREADSAFE
class JSFreePointerListTask : public JSBackgroundTask {
void *head;

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

@ -997,49 +997,35 @@ JSClass js_NoSuchMethodClass = {
JS_STATIC_INTERPRET JSBool
js_OnUnknownMethod(JSContext *cx, jsval *vp)
{
JSObject *obj;
jsid id;
JSTempValueRooter tvr;
JSBool ok;
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
obj = JSVAL_TO_OBJECT(vp[1]);
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
MUST_FLOW_THROUGH("out");
id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &tvr.u.value);
if (!ok)
goto out;
if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
vp[0] = tvr.u.value;
JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr()))
return false;
if (JSVAL_IS_PRIMITIVE(tvr.value())) {
vp[0] = tvr.value();
} else {
#if JS_HAS_XML_SUPPORT
/* Extract the function name from function::name qname. */
if (!JSVAL_IS_PRIMITIVE(vp[0])) {
obj = JSVAL_TO_OBJECT(vp[0]);
ok = js_IsFunctionQName(cx, obj, &id);
if (!ok)
goto out;
if (!js_IsFunctionQName(cx, obj, &id))
return false;
if (id != 0)
vp[0] = ID_TO_VALUE(id);
}
#endif
obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass,
NULL, NULL);
if (!obj) {
ok = JS_FALSE;
goto out;
}
obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
if (!obj)
return false;
obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.value();
obj->fslots[JSSLOT_SAVED_ID] = vp[0];
vp[0] = OBJECT_TO_JSVAL(obj);
}
ok = JS_TRUE;
out:
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
return true;
}
static JS_REQUIRES_STACK JSBool
@ -1502,7 +1488,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.callobj = down->callobj;
frame.argsobj = down->argsobj;
frame.varobj = down->varobj;
frame.fun = down->fun;
frame.fun = (script->staticLevel > 0) ? down->fun : NULL;
frame.thisv = down->thisv;
if (down->flags & JSFRAME_COMPUTED_THIS)
flags |= JSFRAME_COMPUTED_THIS;
@ -1555,9 +1541,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
} else {
sharps[0] = sharps[1] = JSVAL_VOID;
}
} else
}
#endif
JS_ASSERT_IF(down, script->nfixed == 0);
} else {
frame.slots = NULL;
}
@ -1876,10 +1861,8 @@ js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
/* Now we have an object with a constructor method; call it. */
vp[1] = OBJECT_TO_JSVAL(obj);
if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT))
return JS_FALSE;
}
/* Check the return value and if it's primitive, force it to be obj. */
rval = *vp;
@ -2554,7 +2537,7 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
JSObject *obj, *pobj;
JSProperty *prop;
bool ok;
JSBool ok;
if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
@ -2805,18 +2788,9 @@ js_Interpret(JSContext *cx)
#endif /* !JS_THREADED_INTERP */
#ifdef JS_TRACER
/* We had better not be entering the interpreter from JIT-compiled code. */
TraceRecorder *tr = TRACE_RECORDER(cx);
SET_TRACE_RECORDER(cx, NULL);
/* If a recorder is pending and we try to re-enter the interpreter, flag
the recorder to be destroyed when we return. */
if (tr) {
if (tr->wasDeepAborted())
tr->removeFragmentReferences();
else
tr->pushAbortStack();
}
/* We cannot reenter the interpreter while recording. */
if (TRACE_RECORDER(cx))
js_AbortRecording(cx, "attempt to reenter interpreter while recording");
#endif
/* Check for too deep of a native thread stack. */
@ -3308,15 +3282,6 @@ js_Interpret(JSContext *cx)
js_SetVersion(cx, originalVersion);
--cx->interpLevel;
#ifdef JS_TRACER
if (tr) {
SET_TRACE_RECORDER(cx, tr);
if (!tr->wasDeepAborted()) {
tr->popAbortStack();
tr->deepAbort();
}
}
#endif
return ok;
atom_not_defined:

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

@ -99,7 +99,7 @@ js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
return;
/* Protect against failure to fully initialize obj. */
iterable = STOBJ_GET_PARENT(iterobj);
iterable = iterobj->getParent();
if (iterable) {
#if JS_HAS_XML_SUPPORT
uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
@ -113,13 +113,33 @@ js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
}
static void
iterator_trace(JSTracer *trc, JSObject *obj)
{
/*
* The GC marks iter_state during the normal slot scanning if
* JSVAL_IS_TRACEABLE(iter_state) is true duplicating the efforts of
* js_MarkEnumeratorState. But this is rare so we optimize for code
* simplicity.
*/
JSObject *iterable = obj->getParent();
if (!iterable) {
/* for (x in null) creates an iterator object with a null parent. */
return;
}
jsval iter_state = obj->fslots[JSSLOT_ITER_STATE];
js_MarkEnumeratorState(trc, iterable, iter_state);
}
JSClass js_IteratorClass = {
"Iterator",
JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
JSCLASS_MARK_IS_TRACE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
JSCLASS_NO_OPTIONAL_MEMBERS
NULL, NULL, NULL, NULL,
NULL, NULL, JS_CLASS_TRACE(iterator_trace), NULL
};
static JSBool

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

@ -940,7 +940,6 @@ js_ValueToNumber(JSContext *cx, jsval *vp)
const jschar *bp, *end, *ep;
jsdouble d, *dp;
JSObject *obj;
JSTempValueRooter tvr;
v = *vp;
for (;;) {
@ -1004,12 +1003,11 @@ js_ValueToNumber(JSContext *cx, jsval *vp)
* vp roots obj so we cannot use it as an extra root for
* obj->defaultValue result when calling the hook.
*/
JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
if (!obj->defaultValue(cx, JSTYPE_NUMBER, &tvr.u.value))
JSAutoTempValueRooter tvr(cx, v);
if (!obj->defaultValue(cx, JSTYPE_NUMBER, tvr.addr()))
obj = NULL;
else
v = *vp = tvr.u.value;
JS_POP_TEMP_ROOT(cx, &tvr);
v = *vp = tvr.value();
if (!obj) {
*vp = JSVAL_NULL;
return 0.0;

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

@ -41,6 +41,8 @@
/*
* JS object implementation.
*/
#define __STDC_LIMIT_MACROS
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
@ -71,6 +73,7 @@
#include "jsscript.h"
#include "jsscriptinlines.h"
#include "jsstaticcheck.h"
#include "jsstdint.h"
#include "jsstr.h"
#include "jstracer.h"
#include "jsdbgapi.h"
@ -253,8 +256,10 @@ obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
return JS_FALSE;
/* Get the number of properties to enumerate. */
iter_state = JSVAL_NULL;
JSAutoEnumStateRooter tvr(cx, obj, &iter_state);
/* Get the number of properties to enumerate. */
ok = obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties);
if (!ok)
goto out;
@ -267,8 +272,8 @@ obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
*vp = num_properties;
out:
if (iter_state != JSVAL_NULL)
ok = obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0);
if (!JSVAL_IS_NULL(iter_state))
ok &= obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0);
return ok;
}
@ -1248,13 +1253,18 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */
#if JS_HAS_EVAL_THIS_SCOPE
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
JSObject *setCallerScopeChain = NULL;
JSBool setCallerVarObj = JS_FALSE;
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
#endif
fp = js_GetTopStackFrame(cx);
caller = js_GetScriptedCaller(cx, fp);
indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
if (!caller) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_INDIRECT_CALL, js_eval_str);
return JS_FALSE;
}
indirectCall = (caller->regs && *caller->regs->pc != JSOP_EVAL);
/*
* This call to js_GetWrappedObject is safe because of the security checks
@ -1296,7 +1306,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* object, then we need to provide one for the compiler to stick any
* declared (var) variables into.
*/
if (caller && !caller->varobj && !js_GetCallObject(cx, caller))
if (!caller->varobj && !js_GetCallObject(cx, caller))
return JS_FALSE;
/* Accept an optional trailing argument that overrides the scope object. */
@ -1309,39 +1319,45 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* From here on, control must exit through label out with ok set. */
MUST_FLOW_THROUGH("out");
uintN staticLevel = caller->script->staticLevel + 1;
if (!scopeobj) {
#if JS_HAS_EVAL_THIS_SCOPE
/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
/*
* If we see an indirect call, then run eval in the global scope. We do
* this so the compiler can make assumptions about what bindings may or
* may not exist in the current frame if it doesn't see 'eval'.
*/
if (indirectCall) {
/* Pretend that we're top level. */
staticLevel = 0;
callerScopeChain = js_GetScopeChain(cx, caller);
if (!callerScopeChain) {
ok = JS_FALSE;
goto out;
}
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj) {
ok = JS_FALSE;
goto out;
}
if (obj != callerScopeChain) {
ok = js_CheckPrincipalsAccess(cx, obj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom);
if (!ok)
goto out;
scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
if (!scopeobj) {
ok = JS_FALSE;
goto out;
}
/* NB: We know obj is a global object here. */
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
scopeobj = obj;
/* Set fp->scopeChain too, for the compiler. */
caller->scopeChain = fp->scopeChain = scopeobj;
/* Remember scopeobj so we can null its private when done. */
setCallerScopeChain = scopeobj;
}
setCallerScopeChain = JS_TRUE;
callerVarObj = caller->varobj;
if (obj != callerVarObj) {
@ -1358,13 +1374,11 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* NB: This means that native callers (who reach this point through
* the C API) must use the two parameter form.
*/
if (caller) {
scopeobj = js_GetScopeChain(cx, caller);
if (!scopeobj) {
ok = JS_FALSE;
goto out;
}
}
} else {
scopeobj = js_GetWrappedObject(cx, scopeobj);
OBJ_TO_INNER_OBJECT(cx, scopeobj);
@ -1372,19 +1386,15 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
ok = JS_FALSE;
goto out;
}
ok = js_CheckPrincipalsAccess(cx, scopeobj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom);
if (!ok)
goto out;
scopeobj = js_NewWithObject(cx, scopeobj,
JS_GetGlobalForObject(cx, scopeobj), -1);
if (!scopeobj) {
ok = JS_FALSE;
goto out;
}
argv[1] = OBJECT_TO_JSVAL(scopeobj);
/* We're pretending that we're in global code. */
staticLevel = 0;
}
/* Ensure we compile this eval with the right object in the scope chain. */
@ -1394,23 +1404,16 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
goto out;
}
tcflags = TCF_COMPILE_N_GO;
if (caller) {
tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1);
tcflags = TCF_COMPILE_N_GO | TCF_PUT_STATIC_LEVEL(staticLevel);
principals = JS_EvalFramePrincipals(cx, fp, caller);
file = js_ComputeFilename(cx, caller, principals, &line);
} else {
principals = NULL;
file = NULL;
line = 0;
}
str = JSVAL_TO_STRING(argv[0]);
script = NULL;
/* Cache local eval scripts indexed by source qualified by scope. */
bucket = EvalCacheHash(cx, str);
if (caller->fun) {
if (!indirectCall && caller->fun) {
uintN count = 0;
JSScript **scriptp = bucket;
@ -1475,7 +1478,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
if (!script) {
script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags,
JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
script = JSCompiler::compileScript(cx, scopeobj, callerFrame,
principals, tcflags,
str->chars(), str->length(),
NULL, file, line, str);
if (!script) {
@ -1486,7 +1491,6 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (argc < 2) {
/* Execute using caller's new scope object (might be a Call object). */
if (caller)
scopeobj = caller->scopeChain;
}
@ -1508,11 +1512,8 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
out:
#if JS_HAS_EVAL_THIS_SCOPE
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
if (setCallerScopeChain) {
if (setCallerScopeChain)
caller->scopeChain = callerScopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
setCallerScopeChain->setPrivate(NULL);
}
if (setCallerVarObj)
caller->varobj = callerVarObj;
#endif
@ -1946,7 +1947,7 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
}
if (JSVAL_IS_PRIMITIVE(vp[2])) {
char *bytes = js_DecompileValueGenerator(cx, -argc, vp[2], NULL);
char *bytes = js_DecompileValueGenerator(cx, 0 - argc, vp[2], NULL);
if (!bytes)
return JS_FALSE;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
@ -2044,6 +2045,57 @@ obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, jsval *vp)
return ok;
}
static JSBool
obj_keys(JSContext *cx, uintN argc, jsval *vp)
{
jsval v = argc == 0 ? JSVAL_VOID : vp[2];
if (JSVAL_IS_PRIMITIVE(v)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
return JS_FALSE;
}
JSObject *obj = JSVAL_TO_OBJECT(v);
JSAutoIdArray ida(cx, JS_Enumerate(cx, obj));
if (!ida)
return JS_FALSE;
JSObject *proto;
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_Array), &proto))
return JS_FALSE;
vp[1] = OBJECT_TO_JSVAL(proto);
JS_ASSERT(ida.length() <= UINT32_MAX);
JSObject *aobj = js_NewArrayWithSlots(cx, proto, uint32(ida.length()));
if (!aobj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(aobj);
jsval *slots = aobj->dslots;
size_t len = ida.length();
JS_ASSERT(js_DenseArrayCapacity(aobj) >= len);
for (size_t i = 0; i < len; i++) {
jsid id = ida[i];
if (JSID_IS_INT(id)) {
if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &slots[i]))
return JS_FALSE;
} else {
/*
* Object-valued ids are a possibility admitted by SpiderMonkey for
* the purposes of E4X. It's unclear whether they could ever be
* detected here -- the "obvious" possibility, a property referred
* to by a QName, actually appears as a string jsid -- but in the
* interests of fidelity we pass object jsids through unchanged.
*/
slots[i] = ID_TO_VALUE(id);
}
}
JS_ASSERT(len <= UINT32_MAX);
aobj->fslots[JSSLOT_ARRAY_COUNT] = len;
return JS_TRUE;
}
#if JS_HAS_OBJ_WATCHPOINT
const char js_watch_str[] = "watch";
@ -2092,6 +2144,7 @@ static JSFunctionSpec object_methods[] = {
static JSFunctionSpec object_static_methods[] = {
JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
JS_FN("keys", obj_keys, 1,0),
JS_FS_END
};
@ -2738,7 +2791,7 @@ block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
}
JSBool
js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, int16 index)
js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index)
{
JS_ASSERT(obj->getClass() == &js_BlockClass);
JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
@ -2779,7 +2832,6 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
JSObject *obj, *parent;
uint16 depth, count, i;
uint32 tmp;
JSTempValueRooter tvr;
JSScopeProperty *sprop;
jsid propid;
JSAtom *atom;
@ -2827,12 +2879,10 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
STOBJ_SET_PARENT(obj, parent);
}
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
JSAutoTempValueRooter tvr(cx, obj);
if (!JS_XDRUint32(xdr, &tmp)) {
JS_POP_TEMP_ROOT(cx, &tvr);
return JS_FALSE;
}
if (!JS_XDRUint32(xdr, &tmp))
return false;
if (xdr->mode == JSXDR_DECODE) {
depth = (uint16)(tmp >> 16);
@ -2866,15 +2916,12 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
/* XDR the real id, then the shortid. */
if (!js_XDRStringAtom(xdr, &atom) ||
!JS_XDRUint16(xdr, (uint16 *)&shortid)) {
ok = JS_FALSE;
break;
return false;
}
if (xdr->mode == JSXDR_DECODE) {
if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid)) {
ok = JS_FALSE;
break;
}
if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid))
return false;
}
}
@ -2882,9 +2929,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
/* Do as the parser does and make this block scope shareable. */
OBJ_SCOPE(obj)->object = NULL;
}
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
return true;
}
#endif
@ -3390,29 +3435,22 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
{
jsid id;
jsval cval, rval;
JSTempValueRooter argtvr, tvr;
JSObject *obj, *ctor;
JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
JSAutoTempValueRooter argtvr(cx, argc, argv);
if (!js_GetClassId(cx, clasp, &id) ||
!js_FindClassObject(cx, parent, id, &cval)) {
JS_POP_TEMP_ROOT(cx, &argtvr);
return NULL;
}
if (JSVAL_IS_PRIMITIVE(cval)) {
js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
JS_POP_TEMP_ROOT(cx, &argtvr);
return NULL;
}
/*
* Protect cval in case a crazy getter for .prototype uproots it. After
* this point, all control flow must exit through label out with obj set.
*/
JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
MUST_FLOW_THROUGH("out");
/* Protect cval in case a crazy getter for .prototype uproots it. */
JSAutoTempValueRooter tvr(cx, cval);
/*
* If proto or parent are NULL, set them to Constructor.prototype and/or
@ -3424,8 +3462,7 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
if (!proto) {
if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
&rval)) {
obj = NULL;
goto out;
return NULL;
}
if (JSVAL_IS_OBJECT(rval))
proto = JSVAL_TO_OBJECT(rval);
@ -3433,14 +3470,13 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
obj = js_NewObject(cx, clasp, proto, parent);
if (!obj)
goto out;
return NULL;
if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
goto bad;
return NULL;
if (JSVAL_IS_PRIMITIVE(rval))
goto out;
obj = JSVAL_TO_OBJECT(rval);
return obj;
/*
* If the instance's class differs from what was requested, throw a type
@ -3449,24 +3485,16 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
* private data set at this point, then the constructor was replaced and
* we should throw a type error.
*/
obj = JSVAL_TO_OBJECT(rval);
if (OBJ_GET_CLASS(cx, obj) != clasp ||
(!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
JSCLASS_CONSTRUCT_PROTOTYPE)) &&
!obj->getPrivate())) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_WRONG_CONSTRUCTOR, clasp->name);
goto bad;
return NULL;
}
out:
JS_POP_TEMP_ROOT(cx, &tvr);
JS_POP_TEMP_ROOT(cx, &argtvr);
return obj;
bad:
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
obj = NULL;
goto out;
}
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
@ -3746,7 +3774,8 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
if (sprop &&
pobj == obj &&
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
sprop = OBJ_SCOPE(obj)->change(cx, sprop, attrs, sprop->attrs,
sprop = OBJ_SCOPE(obj)->change(cx, sprop, attrs,
JSPROP_GETTER | JSPROP_SETTER,
(attrs & JSPROP_GETTER)
? getter
: sprop->getter,
@ -4959,69 +4988,81 @@ out:
* in the object. Instead for the empty enumerator the code uses JSVAL_ZERO as
* the enumeration state.
*
* JSRuntime.nativeEnumCache caches the enumerators using scope's shape to
* JSThreadData.nativeEnumCache caches the enumerators using scope's shape to
* avoid repeated scanning of scopes for enumerable properties. The cache
* entry is either JSNativeEnumerator* or, for the empty enumerator, the shape
* value itself. The latter is stored as (shape << 1) | 1 to ensure that it is
* always different from JSNativeEnumerator* values.
*
* We cache the enumerators in the JSENUMERATE_INIT case of js_Enumerate, not
* during JSENUMERATE_DESTROY. The GC can invoke the latter case during the
* finalization when JSNativeEnumerator contains finalized ids and the
* enumerator must be freed.
*/
struct JSNativeEnumerator {
/*
* The index into the ids array. It runs from the length down to 1 when
* the enumerator is running. It is 0 when the enumerator is finished and
* can be reused on a cache hit. Its type is jsword, not uint32, for
* compatibility with js_CompareAndSwap.
* can be reused on a cache hit.
*/
jsword cursor;
uint32 cursor;
uint32 length; /* length of ids array */
uint32 shape; /* "shape" number -- see jsscope.h */
JSNativeEnumerator *next; /* list linking */
jsid ids[1]; /* enumeration id array */
static inline size_t size(uint32 length) {
JS_ASSERT(length != 0);
return offsetof(JSNativeEnumerator, ids) +
(size_t) length * sizeof(jsid);
}
bool isFinished() const {
return cursor == 0;
}
void mark(JSTracer *trc) {
JS_ASSERT(length >= 1);
jsid *cursor = ids;
jsid *end = ids + length;
do {
js_TraceId(trc, *cursor);
} while (++cursor != end);
}
};
/* The tagging of shape values requires one bit. */
JS_STATIC_ASSERT((jsuword) SHAPE_OVERFLOW_BIT <=
((jsuword) 1 << (JS_BITS_PER_WORD - 1)));
static inline size_t
NativeEnumeratorSize(uint32 length)
static void
SetEnumeratorCache(JSContext *cx, jsuword *cachep, jsuword newcache)
{
JS_ASSERT(length != 0);
return offsetof(JSNativeEnumerator, ids) + (size_t) length * sizeof(jsid);
jsuword old = *cachep;
*cachep = newcache;
if (!(old & jsuword(1)) && old) {
/* Free the cached enumerator unless it is running. */
JSNativeEnumerator *ne = reinterpret_cast<JSNativeEnumerator *>(old);
if (ne->isFinished())
cx->free(ne);
}
}
/*
* This function is used to enumerate the properties of native JSObjects
* and those host objects that do not define a JSNewEnumerateOp-style iterator
* function.
*/
JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JSClass *clasp;
JSEnumerateOp enumerate;
JSNativeEnumerator *ne;
uint32 length, shape;
size_t allocated;
JSScope *scope;
jsuword *cachep, oldcache;
JSScopeProperty *sprop;
jsid *ids;
jsword newcursor;
clasp = OBJ_GET_CLASS(cx, obj);
enumerate = clasp->enumerate;
/* Here cx is JSTracer when enum_op is JSENUMERATE_MARK. */
JSClass *clasp = obj->getClass();
JSEnumerateOp enumerate = clasp->enumerate;
if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
JS_ASSERT(enumerate != JS_EnumerateStub);
return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
}
switch (enum_op) {
case JSENUMERATE_INIT:
case JSENUMERATE_INIT: {
if (!enumerate(cx, obj))
return JS_FALSE;
return false;
/*
* The set of all property ids is pre-computed when the iterator is
@ -5029,51 +5070,45 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
* during the iteration.
*
* Use a do-while(0) loop to avoid too many nested ifs. If ne is null
* after the loop, it indicates an empty enumerator. If allocated is
* not zero after the loop, we add the newly allocated ne to the cache
* and runtime->nativeEnumerators list.
* after the loop, it indicates an empty enumerator.
*/
ne = NULL;
length = 0;
allocated = (size_t) 0;
JS_LOCK_OBJ(cx, obj);
scope = OBJ_SCOPE(obj);
JSNativeEnumerator *ne;
uint32 length;
do {
uint32 shape = OBJ_SHAPE(obj);
ENUM_CACHE_METER(nativeEnumProbes);
shape = scope->shape;
cachep = &cx->runtime->
jsuword *cachep = &JS_THREAD_DATA(cx)->
nativeEnumCache[NATIVE_ENUM_CACHE_HASH(shape)];
oldcache = *cachep;
jsuword oldcache = *cachep;
if (oldcache & (jsuword) 1) {
if ((uint32) (oldcache >> 1) == shape) {
if (uint32(oldcache >> 1) == shape) {
/* scope has a shape with no enumerable properties. */
break;
}
} else if (oldcache != (jsuword) 0) {
/*
* We can safely read ne->shape without taking the GC lock as
* ne is deleted only when running the GC and ne->shape is
* read-only after initialization.
*/
ne = (JSNativeEnumerator *) *cachep;
JS_ASSERT(ne->length >= 1);
if (ne->shape == shape) {
/*
* Check that ne is not running with another enumerator
* and, if so, reuse and mark it as running from now.
*/
length = ne->length;
if (js_CompareAndSwap(&ne->cursor, 0, length))
break;
length = 0;
}
ne = NULL;
length = 0;
break;
}
} else if (oldcache != jsuword(0)) {
ne = reinterpret_cast<JSNativeEnumerator *>(oldcache);
JS_ASSERT(ne->length >= 1);
if (ne->shape == shape && ne->isFinished()) {
/* Mark ne as active. */
ne->cursor = ne->length;
length = ne->length;
JS_ASSERT(!ne->isFinished());
break;
}
}
ENUM_CACHE_METER(nativeEnumMisses);
JS_LOCK_OBJ(cx, obj);
/* Count all enumerable properties in object's scope. */
JS_ASSERT(length == 0);
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
JSScope *scope = OBJ_SCOPE(obj);
length = 0;
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope);
sprop;
sprop = sprop->parent) {
if ((sprop->attrs & JSPROP_ENUMERATE) &&
!(sprop->flags & SPROP_IS_ALIAS) &&
(!scope->hadMiddleDelete() || scope->has(sprop))) {
@ -5085,22 +5120,31 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
* Cache the scope without enumerable properties unless its
* shape overflows, see bug 440834.
*/
if (shape < SHAPE_OVERFLOW_BIT)
*cachep = ((jsuword) shape << 1) | (jsuword) 1;
JS_UNLOCK_SCOPE(cx, scope);
if (shape < SHAPE_OVERFLOW_BIT) {
SetEnumeratorCache(cx, cachep,
(jsuword(shape) << 1) | jsuword(1));
}
ne = NULL;
break;
}
allocated = NativeEnumeratorSize(length);
ne = (JSNativeEnumerator *) cx->malloc(allocated);
ne = (JSNativeEnumerator *)
cx->mallocNoReport(JSNativeEnumerator::size(length));
if (!ne) {
/* Report the OOM error outside the lock. */
JS_UNLOCK_SCOPE(cx, scope);
return JS_FALSE;
JS_ReportOutOfMemory(cx);
return false;
}
ne->cursor = length;
ne->length = length;
ne->shape = shape;
ids = ne->ids;
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
jsid *ids = ne->ids;
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope);
sprop;
sprop = sprop->parent) {
if ((sprop->attrs & JSPROP_ENUMERATE) &&
!(sprop->flags & SPROP_IS_ALIAS) &&
(!scope->hadMiddleDelete() || scope->has(sprop))) {
@ -5109,126 +5153,100 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
}
}
JS_ASSERT(ids == ne->ids + length);
} while (0);
JS_UNLOCK_SCOPE(cx, scope);
if (!ne) {
JS_ASSERT(length == 0);
JS_ASSERT(allocated == 0);
*statep = JSVAL_ZERO;
} else {
JS_ASSERT(length != 0);
JS_ASSERT(ne->cursor == (jsword) length);
if (allocated != 0) {
JS_LOCK_GC(cx->runtime);
if (!js_AddAsGCBytes(cx, allocated)) {
/* js_AddAsGCBytes releases the GC lock on failures. */
cx->free(ne);
return JS_FALSE;
}
ne->next = cx->runtime->nativeEnumerators;
cx->runtime->nativeEnumerators = ne;
JS_ASSERT(((jsuword) ne & (jsuword) 1) == (jsuword) 0);
/*
* Do not cache enumerators for objects with with a shape
* that had overflowed, see bug 440834.
*/
if (shape < SHAPE_OVERFLOW_BIT)
*cachep = (jsuword) ne;
JS_UNLOCK_GC(cx->runtime);
}
SetEnumeratorCache(cx, cachep, reinterpret_cast<jsuword>(ne));
} while (0);
if (!ne) {
JS_ASSERT(length == 0);
*statep = JSVAL_ZERO;
} else {
JS_ASSERT(length != 0);
JS_ASSERT(ne->cursor == length);
JS_ASSERT(!(reinterpret_cast<jsuword>(ne) & jsuword(1)));
*statep = PRIVATE_TO_JSVAL(ne);
}
if (idp)
*idp = INT_TO_JSVAL(length);
break;
}
case JSENUMERATE_NEXT:
case JSENUMERATE_DESTROY:
case JSENUMERATE_DESTROY: {
if (*statep == JSVAL_ZERO) {
*statep = JSVAL_NULL;
break;
}
ne = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
JSNativeEnumerator *ne = (JSNativeEnumerator *)
JSVAL_TO_PRIVATE(*statep);
JS_ASSERT(ne->length >= 1);
JS_ASSERT(ne->cursor >= 1);
/*
* We must not access ne->cursor when we set it to zero as it means
* that ne is free and another thread can grab it from the cache. So
* we set the state to JSVAL_ZERO in the NEXT case to avoid touching
* ne->length again in the DESTROY case.
*/
if (enum_op == JSENUMERATE_NEXT) {
newcursor = ne->cursor - 1;
uint32 newcursor = ne->cursor - 1;
*idp = ne->ids[newcursor];
if (newcursor != 0) {
ne->cursor = newcursor;
if (newcursor == 0)
*statep = JSVAL_ZERO;
break;
}
} else {
/* The enumerator has not iterated over all ids. */
JS_ASSERT(enum_op == JSENUMERATE_DESTROY);
ne->cursor = 0;
}
*statep = JSVAL_ZERO;
/*
* Force on shutdown an extra GC cycle so all native enumerators
* on the rt->nativeEnumerators list will be removed when the GC
* calls js_TraceNativeEnumerators. See bug 499570.
*/
if (cx->runtime->state == JSRTS_LANDING)
cx->runtime->gcPoke = true;
jsuword *cachep = &JS_THREAD_DATA(cx)->
nativeEnumCache[NATIVE_ENUM_CACHE_HASH(ne->shape)];
if (reinterpret_cast<jsuword>(ne) == *cachep) {
/* Mark the cached iterator as available. */
ne->cursor = 0;
} else {
cx->free(ne);
}
break;
}
return JS_TRUE;
}
return true;
}
void
js_TraceNativeEnumerators(JSTracer *trc)
js_MarkEnumeratorState(JSTracer *trc, JSObject *obj, jsval state)
{
JSRuntime *rt;
JSNativeEnumerator **nep, *ne;
jsid *cursor, *end;
if (JSVAL_IS_TRACEABLE(state)) {
JS_CALL_TRACER(trc, JSVAL_TO_TRACEABLE(state),
JSVAL_TRACE_KIND(state), "enumerator_value");
} else if (obj->map->ops->enumerate == js_Enumerate &&
!(obj->getClass()->flags & JSCLASS_NEW_ENUMERATE)) {
/* Check if state stores JSNativeEnumerator. */
JS_ASSERT(JSVAL_IS_INT(state) ||
JSVAL_IS_NULL(state) ||
JSVAL_IS_VOID(state));
if (JSVAL_IS_INT(state) && state != JSVAL_ZERO)
((JSNativeEnumerator *) JSVAL_TO_PRIVATE(state))->mark(trc);
}
}
/*
* Purge native enumerators cached by shape id when the GC is about to
* re-number shapes due to shape generation overflow. Do this also when
* shutting down the runtime.
*/
rt = trc->context->runtime;
bool doGC = IS_GC_MARKING_TRACER(trc) &&
(rt->gcRegenShapes || rt->state == JSRTS_LANDING);
void
js_PurgeCachedNativeEnumerators(JSContext *cx, JSThreadData *data)
{
jsuword *cachep = &data->nativeEnumCache[0];
jsuword *end = cachep + JS_ARRAY_LENGTH(data->nativeEnumCache);
for (; cachep != end; ++cachep)
SetEnumeratorCache(cx, cachep, jsuword(0));
if (doGC) {
memset(&rt->nativeEnumCache, 0, sizeof rt->nativeEnumCache);
#ifdef JS_DUMP_ENUM_CACHE_STATS
printf("nativeEnumCache hit rate %g%%\n",
100.0 * (rt->nativeEnumProbes - rt->nativeEnumMisses) /
rt->nativeEnumProbes);
100.0 * (cx->runtime->nativeEnumProbes -
cx->runtime->nativeEnumMisses) /
cx->runtime->nativeEnumProbes);
#endif
}
nep = &rt->nativeEnumerators;
while ((ne = *nep) != NULL) {
JS_ASSERT(ne->length != 0);
if (ne->cursor != 0) {
/* Trace ids of the running enumerator. */
cursor = ne->ids;
end = cursor + ne->length;
do {
js_TraceId(trc, *cursor);
} while (++cursor != end);
} else if (doGC) {
js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length));
*nep = ne->next;
trc->context->free(ne);
continue;
}
nep = &ne->next;
}
}
JSBool
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
jsval *vp, uintN *attrsp)

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

@ -257,8 +257,9 @@ struct JSObject {
}
JSBool defineProperty(JSContext *cx, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs) {
JSPropertyOp getter = JS_PropertyStub,
JSPropertyOp setter = JS_PropertyStub,
uintN attrs = JSPROP_ENUMERATE) {
return map->ops->defineProperty(cx, this, id, value, getter, setter, attrs);
}
@ -350,17 +351,25 @@ struct JSObject {
#define STOBJ_NSLOTS(obj) \
((obj)->dslots ? (uint32)(obj)->dslots[-1] : (uint32)JS_INITIAL_NSLOTS)
#define STOBJ_GET_SLOT(obj,slot) \
((slot) < JS_INITIAL_NSLOTS \
? (obj)->fslots[(slot)] \
: (JS_ASSERT((slot) < (uint32)(obj)->dslots[-1]), \
(obj)->dslots[(slot) - JS_INITIAL_NSLOTS]))
inline jsval&
STOBJ_GET_SLOT(JSObject *obj, uintN slot)
{
return (slot < JS_INITIAL_NSLOTS)
? obj->fslots[slot]
: (JS_ASSERT(slot < (uint32)obj->dslots[-1]),
obj->dslots[slot - JS_INITIAL_NSLOTS]);
}
#define STOBJ_SET_SLOT(obj,slot,value) \
((slot) < JS_INITIAL_NSLOTS \
? (obj)->fslots[(slot)] = (value) \
: (JS_ASSERT((slot) < (uint32)(obj)->dslots[-1]), \
(obj)->dslots[(slot) - JS_INITIAL_NSLOTS] = (value)))
inline void
STOBJ_SET_SLOT(JSObject *obj, uintN slot, jsval value)
{
if (slot < JS_INITIAL_NSLOTS) {
obj->fslots[slot] = value;
} else {
JS_ASSERT(slot < (uint32)obj->dslots[-1]);
obj->dslots[slot - JS_INITIAL_NSLOTS] = value;
}
}
inline JSClass*
STOBJ_GET_CLASS(const JSObject* obj)
@ -492,7 +501,7 @@ OBJ_IS_CLONED_BLOCK(JSObject *obj)
}
extern JSBool
js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, int16 index);
js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index);
#define OBJ_BLOCK_COUNT(cx,obj) \
(OBJ_SCOPE(obj)->entryCount)
@ -872,7 +881,10 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp);
extern void
js_TraceNativeEnumerators(JSTracer *trc);
js_MarkEnumeratorState(JSTracer *trc, JSObject *obj, jsval state);
extern void
js_PurgeCachedNativeEnumerators(JSContext *cx, JSThreadData *data);
extern JSBool
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

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

@ -64,6 +64,11 @@
#include "jsatominlines.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4351)
#endif
struct JSONParser
{
JSONParser(JSContext *cx)
@ -83,6 +88,10 @@ struct JSONParser
js::Vector<jschar, 8> buffer;
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
JSClass js_JSONClass = {
js_JSON_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
@ -97,7 +106,7 @@ js_json_parse(JSContext *cx, uintN argc, jsval *vp)
JSString *s = NULL;
jsval *argv = vp + 2;
jsval reviver = JSVAL_NULL;
JSAutoTempValueRooter(cx, 1, &reviver);
JSAutoTempValueRooter tvr(cx, 1, &reviver);
if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver))
return JS_FALSE;
@ -121,8 +130,8 @@ js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
jsval *argv = vp + 2;
JSObject *replacer = NULL;
jsval space = JSVAL_NULL;
JSAutoTempValueRooter(cx, replacer);
JSAutoTempValueRooter(cx, 1, &space);
JSAutoTempValueRooter tvr(cx, replacer);
JSAutoTempValueRooter tvr2(cx, 1, &space);
// Must throw an Error if there isn't a first arg
if (!JS_ConvertArguments(cx, argc, argv, "v / o v", vp, &replacer, &space))

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

@ -3097,11 +3097,9 @@
restore_scope:
/* Restore fp->scopeChain now that obj is defined in fp->varobj. */
fp->scopeChain = obj2;
if (!ok) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
if (!ok)
goto error;
}
}
END_CASE(JSOP_DEFFUN)
BEGIN_CASE(JSOP_DEFFUN_FC)
@ -3148,10 +3146,8 @@
}
}
if (!ok) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
if (!ok)
goto error;
}
END_CASE(JSOP_DEFFUN_FC)
BEGIN_CASE(JSOP_DEFLOCALFUN)

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

@ -1316,6 +1316,7 @@ LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
JS_ASSERT(pn != dn->dn_uses);
pn->pn_link = dn->dn_uses;
dn->dn_uses = pn;
dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
pn->pn_used = true;
pn->pn_lexdef = dn;
}
@ -4309,7 +4310,6 @@ RebindLets(JSParseNode *pn, JSTreeContext *tc)
JSDefinition *dn = ALE_DEFN(ale);
dn->pn_type = TOK_NAME;
dn->pn_op = JSOP_NOP;
dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
}
LinkUseToDef(pn, ALE_DEFN(ale), tc);
}

124
js/src/jsregexp.cpp Normal file → Executable file
Просмотреть файл

@ -2003,17 +2003,13 @@ CompileRegExpToAST(JSContext* cx, JSTokenStream* ts,
#ifdef JS_TRACER
typedef js::Vector<LIns *, 4, js::ContextAllocPolicy> LInsList;
/* Dummy GC for nanojit placement new. */
static GC gc;
static avmplus::AvmCore s_core = avmplus::AvmCore();
/* Return the cached fragment for the given regexp, or create one. */
static Fragment*
LookupNativeRegExp(JSContext* cx, uint16 re_flags,
const jschar* re_chars, size_t re_length)
{
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
VMAllocator &alloc = *tm->allocator;
VMAllocator &alloc = *tm->dataAlloc;
REHashMap &table = *tm->reFragments;
REHashKey k(re_length, re_flags, re_chars);
@ -2274,6 +2270,7 @@ enumerateNextChars(JSContext *cx, RENode *node, CharSet &set)
class RegExpNativeCompiler {
private:
VMAllocator& tempAlloc;
JSContext* cx;
JSRegExp* re;
CompilerState* cs; /* RegExp to compile */
@ -2290,6 +2287,10 @@ class RegExpNativeCompiler {
LIns* state;
LIns* cpend;
bool outOfMemory() {
return tempAlloc.outOfMemory() || JS_TRACE_MONITOR(cx).dataAlloc->outOfMemory();
}
JSBool isCaseInsensitive() const { return (cs->flags & JSREG_FOLD) != 0; }
void targetCurrentPoint(LIns *ins)
@ -2480,7 +2481,6 @@ class RegExpNativeCompiler {
LIns* compileFlat(RENode *&node, LIns* pos, LInsList& fails)
{
VMAllocator *alloc = JS_TRACE_MONITOR(cx).allocator;
#ifdef USE_DOUBLE_CHAR_MATCH
if (node->u.flat.length == 1) {
if (node->next && node->next->op == REOP_FLAT &&
@ -2496,7 +2496,7 @@ class RegExpNativeCompiler {
} else {
size_t i;
for (i = 0; i < node->u.flat.length - 1; i += 2) {
if (alloc->outOfMemory())
if (outOfMemory())
return 0;
pos = compileFlatDoubleChar(((jschar*) node->kid)[i],
((jschar*) node->kid)[i+1],
@ -2514,7 +2514,7 @@ class RegExpNativeCompiler {
return compileFlatSingleChar(node->u.flat.chr, pos, fails);
} else {
for (size_t i = 0; i < node->u.flat.length; i++) {
if (alloc->outOfMemory())
if (outOfMemory())
return 0;
pos = compileFlatSingleChar(((jschar*) node->kid)[i], pos, fails);
if (!pos)
@ -2536,16 +2536,16 @@ class RegExpNativeCompiler {
*/
RECharSet *charSet = &re->classList[node->u.ucclass.index];
size_t bitmapLen = (charSet->length >> 3) + 1;
/* insSkip() can't hold large data blocks. */
/* Arbitrary size limit on bitmap. */
if (bitmapLen > 1024)
return NULL;
Allocator &alloc = *JS_TRACE_MONITOR(cx).dataAlloc;
/* The following line allocates charSet.u.bits if successful. */
if (!charSet->converted && !ProcessCharSet(cx, re, charSet))
return NULL;
LIns* skip = lirBufWriter->insSkip(bitmapLen);
if (JS_TRACE_MONITOR(cx).allocator->outOfMemory())
void* bitmapData = alloc.alloc(bitmapLen);
if (outOfMemory())
return NULL;
void* bitmapData = skip->payload();
memcpy(bitmapData, charSet->u.bits, bitmapLen);
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_plt, pos, cpend), 0);
@ -2962,9 +2962,8 @@ class RegExpNativeCompiler {
*/
LIns *compileNode(RENode *node, LIns *pos, bool atEnd, LInsList &fails)
{
VMAllocator *alloc = JS_TRACE_MONITOR(cx).allocator;
for (; pos && node; node = node->next) {
if (alloc->outOfMemory())
if (outOfMemory())
return NULL;
bool childNextIsEnd = atEnd && !node->next;
@ -3040,7 +3039,7 @@ class RegExpNativeCompiler {
/* Failed to match on first character, so fail whole match. */
lir->ins0(LIR_regfence);
lir->ins1(LIR_ret, lir->insImm(0));
return !JS_TRACE_MONITOR(cx).allocator->outOfMemory();
return !outOfMemory();
}
/* Compile normal regular expressions that can match starting at any char. */
@ -3056,7 +3055,7 @@ class RegExpNativeCompiler {
lir->insStorei(lir->ins2(LIR_piadd, start, lir->insImmWord(2)), state,
offsetof(REGlobalData, skipped));
return !JS_TRACE_MONITOR(cx).allocator->outOfMemory();
return !outOfMemory();
}
inline LIns*
@ -3082,16 +3081,17 @@ class RegExpNativeCompiler {
lir->ins1(LIR_live, lirbuf->param1);
}
LIns* skip = lirBufWriter->insSkip(sizeof(GuardRecord) +
sizeof(SideExit) +
Allocator &alloc = *JS_TRACE_MONITOR(cx).dataAlloc;
size_t len = (sizeof(GuardRecord) +
sizeof(VMSideExit) +
(re_length-1) * sizeof(jschar));
GuardRecord* guard = (GuardRecord *) skip->payload();
memset(guard, 0, sizeof(*guard));
SideExit* exit = (SideExit*)(guard+1);
GuardRecord* guard = (GuardRecord *) alloc.alloc(len);
VMSideExit* exit = (VMSideExit*)(guard+1);
guard->exit = exit;
guard->exit->target = fragment;
fragment->lastIns = lir->insGuard(LIR_x, NULL, skip);
// guard->profCount is memset'd to zero
fragment->lastIns = lir->insGuard(LIR_x, NULL, guard);
// guard->profCount is calloc'd to zero
verbose_only(
guard->profGuardID = fragment->guardNumberer++;
guard->nextInFrag = fragment->guardsForFrag;
@ -3101,10 +3101,17 @@ class RegExpNativeCompiler {
}
public:
RegExpNativeCompiler(JSRegExp* re, CompilerState* cs, Fragment* fragment)
: re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL) { }
RegExpNativeCompiler(JSContext* cx, JSRegExp* re, CompilerState* cs, Fragment* fragment)
: tempAlloc(*JS_TRACE_MONITOR(cx).reTempAlloc), cx(cx),
re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL) { }
JSBool compile(JSContext* cx)
~RegExpNativeCompiler() {
/* Purge the tempAlloc used during recording. */
tempAlloc.reset();
JS_TRACE_MONITOR(cx).reLirBuf->clear();
}
JSBool compile()
{
GuardRecord* guard = NULL;
LIns* pos;
@ -3112,10 +3119,9 @@ class RegExpNativeCompiler {
size_t re_length;
JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
Assembler *assm = tm->assembler;
VMAllocator& alloc = *tm->allocator;
LIns* loopLabel = NULL;
if (alloc.outOfMemory() || js_OverfullJITCache(tm))
if (outOfMemory() || js_OverfullJITCache(tm))
return JS_FALSE;
re->source->getCharsAndLength(re_chars, re_length);
@ -3128,25 +3134,24 @@ class RegExpNativeCompiler {
return JS_FALSE;
}
this->cx = cx;
/* At this point we have an empty fragment. */
LirBuffer* lirbuf = fragment->lirbuf;
if (alloc.outOfMemory())
if (outOfMemory())
goto fail;
/* FIXME Use bug 463260 smart pointer when available. */
lir = lirBufWriter = new (&gc) LirBufWriter(lirbuf);
lir = lirBufWriter = new LirBufWriter(lirbuf);
/* FIXME Use bug 463260 smart pointer when available. */
#ifdef NJ_VERBOSE
debug_only_stmt(
if (js_LogController.lcbits & LC_TMRegexp) {
lir = verbose_filter = new (&gc) VerboseWriter(alloc, lir, lirbuf->names,
lir = verbose_filter = new VerboseWriter(tempAlloc, lir, lirbuf->names,
&js_LogController);
}
)
#endif
#ifdef DEBUG
lir = sanity_filter = new (&gc) SanityFilter(lir);
lir = sanity_filter = new SanityFilter(lir);
#endif
/*
@ -3189,9 +3194,9 @@ class RegExpNativeCompiler {
guard = insertGuard(loopLabel, re_chars, re_length);
if (alloc.outOfMemory())
if (outOfMemory())
goto fail;
::compile(assm, fragment, alloc verbose_only(, tm->labels));
::compile(assm, fragment verbose_only(, tempAlloc, tm->labels));
if (assm->error() != nanojit::None)
goto fail;
@ -3205,7 +3210,7 @@ class RegExpNativeCompiler {
#endif
return JS_TRUE;
fail:
if (alloc.outOfMemory() || js_OverfullJITCache(tm)) {
if (outOfMemory() || js_OverfullJITCache(tm)) {
delete lirBufWriter;
// recover profiling data from expiring Fragments
verbose_only(
@ -3221,6 +3226,9 @@ class RegExpNativeCompiler {
re->flags |= JSREG_NOCOMPILE;
delete lirBufWriter;
}
#ifdef DEBUG
delete sanity_filter;
#endif
#ifdef NJ_VERBOSE
debug_only_stmt( if (js_LogController.lcbits & LC_TMRegexp)
delete lir; )
@ -3238,14 +3246,14 @@ CompileRegExpToNative(JSContext* cx, JSRegExp* re, Fragment* fragment)
JSBool rv = JS_FALSE;
void* mark;
CompilerState state;
RegExpNativeCompiler rc(re, &state, fragment);
RegExpNativeCompiler rc(cx, re, &state, fragment);
JS_ASSERT(!fragment->code());
mark = JS_ARENA_MARK(&cx->tempPool);
if (!CompileRegExpToAST(cx, NULL, re->source, re->flags, state)) {
goto out;
}
rv = rc.compile(cx);
rv = rc.compile();
out:
JS_ARENA_RELEASE(&cx->tempPool, mark);
return rv;
@ -4084,8 +4092,7 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op,
JS_ASSERT(charSet->converted);
ch = *x->cp;
index = ch >> 3;
if (charSet->length != 0 &&
ch <= charSet->length &&
if (ch <= charSet->length &&
(charSet->u.bits[index] & (1 << (ch & 0x7)))) {
result = x;
result->cp++;
@ -4100,8 +4107,7 @@ SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op,
JS_ASSERT(charSet->converted);
ch = *x->cp;
index = ch >> 3;
if (charSet->length == 0 ||
ch > charSet->length ||
if (ch > charSet->length ||
!(charSet->u.bits[index] & (1 << (ch & 0x7)))) {
result = x;
result->cp++;
@ -4197,8 +4203,7 @@ ExecuteREBytecode(REGlobalData *gData, REMatchState *x)
goto bad;
matchCh1 = *x->cp;
k = matchCh1 >> 3;
if ((charSet->length == 0 ||
matchCh1 > charSet->length ||
if ((matchCh1 > charSet->length ||
!(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^
charSet->sense) {
goto doAlt;
@ -4904,17 +4909,13 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
ok = js_DefineProperty(cx, obj, id, val, \
JS_PropertyStub, JS_PropertyStub, \
JSPROP_ENUMERATE); \
if (!ok) { \
cx->weakRoots.newborn[GCX_OBJECT] = NULL; \
cx->weakRoots.newborn[GCX_STRING] = NULL; \
if (!ok) \
goto out; \
} \
}
matchstr = js_NewDependentString(cx, str, cp - str->chars(),
matchlen);
if (!matchstr) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
ok = JS_FALSE;
goto out;
}
@ -4951,8 +4952,6 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
res->moreLength * sizeof(JSSubString));
}
if (!morepar) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
cx->weakRoots.newborn[GCX_STRING] = NULL;
ok = JS_FALSE;
goto out;
}
@ -4976,20 +4975,15 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
str->chars(),
parsub->length);
if (!parstr) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
cx->weakRoots.newborn[GCX_STRING] = NULL;
ok = JS_FALSE;
goto out;
}
ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), STRING_TO_JSVAL(parstr),
NULL, NULL, JSPROP_ENUMERATE);
}
if (!ok) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
cx->weakRoots.newborn[GCX_STRING] = NULL;
if (!ok)
goto out;
}
}
if (parsub->index == -1) {
res->lastParen = js_EmptySubString;
} else {
@ -5830,3 +5824,19 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent)
return clone;
}
bool
js_ContainsRegExpMetaChars(const jschar *chars, size_t length)
{
for (size_t i = 0; i < length; ++i) {
jschar c = chars[i];
switch (c) {
/* Taken from the PatternCharacter production in 15.10.1. */
case '^': case '$': case '\\': case '.': case '*': case '+':
case '?': case '(': case ')': case '[': case ']': case '{':
case '}': case '|':
return true;
default:;
}
}
return false;
}

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

@ -195,6 +195,10 @@ js_ClearRegExpLastIndex(JSObject *obj)
obj->fslots[JSSLOT_REGEXP_LAST_INDEX] = JSVAL_ZERO;
}
/* Return whether the given character array contains RegExp meta-characters. */
extern bool
js_ContainsRegExpMetaChars(const jschar *chars, size_t length);
JS_END_EXTERN_C
#endif /* jsregexp_h___ */

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

@ -176,12 +176,21 @@ js_IsIdentifier(JSString *str)
return JS_TRUE;
}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4351)
#endif
/* Initialize members that aren't initialized in |init|. */
JSTokenStream::JSTokenStream(JSContext *cx)
: tokens(), cursor(), lookahead(), ungetpos(), ungetbuf(), flags(), linelen(),
linepos(), file(), listenerTSData(), saveEOL(), tokenbuf(cx)
{}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
bool
JSTokenStream::init(JSContext *cx, const jschar *base, size_t length,
FILE *fp, const char *fn, uintN ln)
@ -656,7 +665,7 @@ static JSBool
GetXMLEntity(JSContext *cx, JSTokenStream *ts)
{
ptrdiff_t offset, length, i;
int32 c, d;
int c, d;
JSBool ispair;
jschar *bp, digit;
char *bytes;
@ -858,7 +867,7 @@ JSTokenType
js_GetToken(JSContext *cx, JSTokenStream *ts)
{
JSTokenType tt;
int32 c, qc;
int c, qc;
JSToken *tp;
JSAtom *atom;
JSBool hadUnicodeEscape;

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

@ -297,6 +297,7 @@ JSScope::searchTable(jsid id, bool adding)
uint32 sizeMask;
JS_ASSERT(table);
JS_ASSERT(!JSVAL_IS_NULL(id));
/* Compute the primary hash address. */
METER(hashes);
@ -448,7 +449,8 @@ js_HashScopeProperty(JSDHashTable *table, const void *key)
#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \
aflags, ashortid) \
((sprop)->id == (aid) && \
(JS_ASSERT(!JSVAL_IS_NULL((sprop)->id)), JS_ASSERT(!JSVAL_IS_NULL(aid)), \
(sprop)->id == (aid) && \
SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \
aflags, ashortid))
@ -585,6 +587,7 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
uintN i;
JS_ASSERT(!parent || child->parent != parent);
JS_ASSERT(!JSVAL_IS_NULL(child->id));
if (!parent) {
table = &rt->propertyTreeHash;
@ -617,6 +620,7 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
}
} else {
JS_ASSERT(!JSVAL_IS_NULL(parent->id));
childp = &parent->kids;
kids = *childp;
if (kids) {
@ -729,6 +733,7 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
*/
table = &rt->propertyTreeHash;
} else {
JS_ASSERT(!JSVAL_IS_NULL(parent->id));
kids = parent->kids;
if (KIDS_IS_CHUNKY(kids)) {
list = chunk = KIDS_TO_CHUNK(kids);
@ -832,6 +837,8 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
PropTreeKidsChunk *chunk;
uintN i, n;
JS_ASSERT(!JSVAL_IS_NULL(child->id));
rt = cx->runtime;
if (!parent) {
JS_LOCK_GC(rt);
@ -846,6 +853,8 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
if (sprop)
goto out;
} else {
JS_ASSERT(!JSVAL_IS_NULL(parent->id));
/*
* Because chunks are appended at the end and never deleted except by
* the GC, we can search without taking the runtime's GC lock. We may
@ -1013,9 +1022,28 @@ JSScope::reportReadOnlyScope(JSContext *cx)
void
JSScope::generateOwnShape(JSContext *cx)
{
if (object)
#ifdef JS_TRACER
if (object) {
js_LeaveTraceIfGlobalObject(cx, object);
/*
* The JIT must have arranged to re-guard after any unpredictable shape
* change, so if we are on trace here, we should already be prepared to
* bail off trace.
*/
JS_ASSERT_IF(JS_ON_TRACE(cx), cx->bailExit);
/*
* If we are recording, here is where we forget already-guarded shapes.
* Any subsequent property operation upon object on the trace currently
* being recorded will re-guard (and re-memoize).
*/
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
if (TraceRecorder *tr = tm->recorder)
tr->forgetGuardedShapesForObject(object);
}
#endif
shape = js_GenerateShape(cx, false);
setOwnShape();
}
@ -1037,6 +1065,8 @@ JSScope::add(JSContext *cx, jsid id,
JS_ASSERT_IF(attrs & JSPROP_GETTER, getter);
JS_ASSERT_IF(attrs & JSPROP_SETTER, setter);
JS_ASSERT(!JSVAL_IS_NULL(id));
/*
* You can't add properties to a sealed scope. But note well that you can
* change property attributes in a sealed scope, even though that replaces
@ -1391,6 +1421,8 @@ JSScope::change(JSContext *cx, JSScopeProperty *sprop,
CHECK_ANCESTOR_LINE(this, true);
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
/* Allow only shared (slot-less) => unshared (slot-full) transition. */
attrs |= sprop->attrs & mask;
JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) ||
@ -1571,12 +1603,14 @@ JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v)
void
JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
{
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
generateOwnShape(cx);
}
bool
JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval)
{
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
if (sprop->isMethod()) {
#ifdef DEBUG
jsval prev = LOCKED_OBJ_GET_SLOT(object, sprop->slot);
@ -1610,6 +1644,7 @@ JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
generateOwnShape(cx);
} else {
for (JSScopeProperty *sprop = lastProp; sprop; sprop = sprop->parent) {
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
if (sprop->slot == slot && (!hadMiddleDelete() || has(sprop)))
return methodShapeChange(cx, sprop, toval);
}
@ -1626,6 +1661,7 @@ JSScope::protoShapeChange(JSContext *cx)
void
JSScope::replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProperty *newsprop)
{
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
if (shape == sprop->shape)
shape = newsprop->shape;
else
@ -1641,6 +1677,7 @@ JSScope::sealingShapeChange(JSContext *cx)
void
JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop)
{
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
generateOwnShape(cx);
}
@ -1665,6 +1702,7 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
sprop = (JSScopeProperty *)trc->debugPrintArg;
id = sprop->id;
JS_ASSERT(!JSVAL_IS_NULL(id));
name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
if (JSID_IS_ATOM(id)) {
@ -1689,6 +1727,7 @@ PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
sprop = (JSScopeProperty *)trc->debugPrintArg;
id = sprop->id;
JS_ASSERT(!JSVAL_IS_NULL(id));
JS_ASSERT(JSID_IS_ATOM(id));
n = js_PutEscapedString(buf, bufsize - 1, ATOM_TO_STRING(JSID_TO_ATOM(id)), 0);
@ -1785,6 +1824,7 @@ DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp)
fprintf(fp, "%*sid ", level, "");
v = ID_TO_VALUE(sprop->id);
JS_ASSERT(!JSVAL_IS_NULL(v));
if (JSID_IS_INT(sprop->id)) {
fprintf(fp, "%d", JSVAL_TO_INT(v));
} else {

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

@ -50,6 +50,11 @@
#include "jsprvtd.h"
#include "jspubtd.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4800)
#endif
JS_BEGIN_EXTERN_C
/*
@ -418,9 +423,14 @@ OBJ_SHAPE(JSObject *obj)
* A little information hiding for scope->lastProp, in case it ever becomes
* a tagged pointer again.
*/
#define SCOPE_LAST_PROP(scope) ((scope)->lastProp)
#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \
(scope)->lastProp->parent)
#define SCOPE_LAST_PROP(scope) \
(JS_ASSERT_IF((scope)->lastProp, !JSVAL_IS_NULL((scope)->lastProp->id)), \
(scope)->lastProp)
#define SCOPE_REMOVE_LAST_PROP(scope) \
(JS_ASSERT_IF((scope)->lastProp->parent, \
!JSVAL_IS_NULL((scope)->lastProp->parent->id)), \
(scope)->lastProp = (scope)->lastProp->parent)
/*
* Helpers for reinterpreting JSPropertyOp as JSObject* for scripted getters
* and setters.
@ -763,6 +773,7 @@ inline bool
JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp)
{
JS_ASSERT(!SPROP_HAS_STUB_GETTER(this));
JS_ASSERT(!JSVAL_IS_NULL(this->id));
if (attrs & JSPROP_GETTER) {
JS_ASSERT(!isMethod());
@ -831,4 +842,8 @@ js_FinishPropertyTree(JSRuntime *rt);
JS_END_EXTERN_C
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif /* jsscope_h___ */

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

@ -100,6 +100,10 @@ typedef JSUintPtr uintptr_t;
* we would require compiler assistance, and at the moment we don't need
* preprocessor-correctness.
*/
#ifdef _MSC_VER
#undef SIZE_MAX
#endif
#define INTPTR_MAX ((intptr_t) (UINTPTR_MAX >> 1))
#define INTPTR_MIN (intptr_t(uintptr_t(INTPTR_MAX) + uintptr_t(1)))
#define UINTPTR_MAX ((uintptr_t) -1)

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

@ -78,14 +78,8 @@
#define JSSTRDEP_RECURSION_LIMIT 100
static JS_ALWAYS_INLINE JSBool
UWordInRootedValue(JSContext *cx, size_t i, jsval *vp)
{
if (i >= (size_t)JSVAL_INT_MAX)
return js_NewNumberInRootedValue(cx, i, vp);
*vp = INT_TO_JSVAL(i);
return JS_TRUE;
}
JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
JS_STATIC_ASSERT(INT_FITS_IN_JSVAL(JSString::MAX_LENGTH));
static size_t
MinimizeDependentStrings(JSString *str, int level, JSString **basep)
@ -1049,108 +1043,141 @@ JS_DEFINE_CALLINFO_1(extern, INT32, js_String_p_charCodeAt0_int, STRING,
#endif
jsint
js_BoyerMooreHorspool(const jschar *text, jsint textlen,
const jschar *pat, jsint patlen,
jsint start)
js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
const jschar *pat, jsuint patlen)
{
jsint i, j, k, m;
uint8 skip[BMH_CHARSET_SIZE];
jschar c;
uint8 skip[sBMHCharSetSize];
JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX);
for (i = 0; i < BMH_CHARSET_SIZE; i++)
JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax);
for (jsuint i = 0; i < sBMHCharSetSize; i++)
skip[i] = (uint8)patlen;
m = patlen - 1;
for (i = 0; i < m; i++) {
c = pat[i];
if (c >= BMH_CHARSET_SIZE)
return BMH_BAD_PATTERN;
jsuint m = patlen - 1;
for (jsuint i = 0; i < m; i++) {
jschar c = pat[i];
if (c >= sBMHCharSetSize)
return sBMHBadPattern;
skip[c] = (uint8)(m - i);
}
for (k = start + m;
jschar c;
for (jsuint k = m;
k < textlen;
k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) {
for (i = k, j = m; ; i--, j--) {
if (j < 0)
return i + 1;
k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) {
for (jsuint i = k, j = m; ; i--, j--) {
if (text[i] != pat[j])
break;
if (j == 0)
return static_cast<jsint>(i); /* safe: max string size */
}
}
return -1;
}
static JS_ALWAYS_INLINE jsint
StringMatch(const jschar *text, jsuint textlen,
const jschar *pat, jsuint patlen)
{
if (patlen == 0)
return 0;
if (textlen < patlen)
return -1;
/* XXX tune the BMH threshold (512) */
if (textlen >= 512 && patlen <= sBMHPatLenMax) {
jsint index = js_BoyerMooreHorspool(text, textlen, pat, patlen);
if (index != sBMHBadPattern)
return index;
}
const jschar *textend = text + textlen - (patlen - 1);
const jschar *patend = pat + patlen;
const jschar p0 = *pat;
const jschar *t = text;
uint8 fixup;
/* Credit: Duff */
switch ((textend - text) & 7) {
do {
case 0: if (*t++ == p0) { fixup = 8; goto match; }
case 7: if (*t++ == p0) { fixup = 7; goto match; }
case 6: if (*t++ == p0) { fixup = 6; goto match; }
case 5: if (*t++ == p0) { fixup = 5; goto match; }
case 4: if (*t++ == p0) { fixup = 4; goto match; }
case 3: if (*t++ == p0) { fixup = 3; goto match; }
case 2: if (*t++ == p0) { fixup = 2; goto match; }
case 1: if (*t++ == p0) { fixup = 1; goto match; }
continue;
do {
if (*t++ == p0) {
match:
for (const jschar *p1 = pat + 1, *t1 = t;
p1 != patend;
++p1, ++t1) {
if (*p1 != *t1)
goto failed_match;
}
return t - text - 1;
}
failed_match:;
} while (--fixup > 0);
} while(t != textend);
}
return -1;
}
static JSBool
str_indexOf(JSContext *cx, uintN argc, jsval *vp)
{
jsval t;
JSString *str, *str2;
const jschar *text, *pat;
jsint i, j, index, textlen, patlen;
jsdouble d;
t = vp[1];
if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_STRING(vp[2])) {
str = JSVAL_TO_STRING(t);
str2 = JSVAL_TO_STRING(vp[2]);
} else {
str = NormalizeThis(cx, vp);
if (!str)
JSString *str;
NORMALIZE_THIS(cx, vp, str);
JSString *patstr = ArgToRootedString(cx, argc, vp, 0);
if (!patstr)
return JS_FALSE;
str2 = ArgToRootedString(cx, argc, vp, 0);
if (!str2)
return JS_FALSE;
}
text = str->chars();
textlen = (jsint) str->length();
pat = str2->chars();
patlen = (jsint) str2->length();
const jschar *text = str->chars();
jsuint textlen = str->length();
const jschar *pat = patstr->chars();
jsuint patlen = patstr->length();
jsuint start;
if (argc > 1) {
d = js_ValueToNumber(cx, &vp[3]);
jsval indexVal = vp[3];
if (JSVAL_IS_INT(indexVal)) {
jsint i = JSVAL_TO_INT(indexVal);
if (i <= 0) {
start = 0;
} else if (jsuint(i) > textlen) {
start = 0;
textlen = 0;
} else {
start = i;
text += start;
textlen -= start;
}
} else {
jsdouble d = js_ValueToNumber(cx, &vp[3]);
if (JSVAL_IS_NULL(vp[3]))
return JS_FALSE;
d = js_DoubleToInteger(d);
if (d < 0)
i = 0;
else if (d > textlen)
i = textlen;
else
i = (jsint)d;
if (d <= 0) {
start = 0;
} else if (d > textlen) {
start = 0;
textlen = 0;
} else {
i = 0;
start = (jsint)d;
text += start;
textlen -= start;
}
if (patlen == 0) {
*vp = INT_TO_JSVAL(i);
return JS_TRUE;
}
/* XXX tune the BMH threshold (512) */
if (textlen - i >= 512 && (jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2) {
index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i);
if (index != BMH_BAD_PATTERN)
goto out;
}
index = -1;
j = 0;
while (i + j < textlen) {
if (text[i + j] == pat[j]) {
if (++j == patlen) {
index = i;
break;
}
} else {
i++;
j = 0;
}
start = 0;
}
out:
*vp = INT_TO_JSVAL(index);
return JS_TRUE;
jsint match = StringMatch(text, textlen, pat, patlen);
*vp = INT_TO_JSVAL((match == -1) ? -1 : start + match);
return true;
}
static JSBool
@ -1274,7 +1301,17 @@ str_trimRight(JSContext *cx, uintN argc, jsval *vp)
* Perl-inspired string functions.
*/
/* Utility to extract the re/reobj pair from vp and manage the reference count. */
/*
* RegExpGuard factors logic out of String regexp operations. After each
* operation completes, RegExpGuard data members become available, according to
* the comments below.
*
* Notes on parameters to RegExpGuard member functions:
* - 'optarg' indicates in which argument position RegExp flags will be found,
* if present. This is a Mozilla extension and not part of any ECMA spec.
* - 'flat' indicates that the given pattern string will not be interpreted as
* a regular expression, hence regexp meta-characters are ignored.
*/
class RegExpGuard
{
RegExpGuard(const RegExpGuard &);
@ -1285,52 +1322,89 @@ class RegExpGuard
JSRegExp *mRe;
public:
RegExpGuard() : mRe(NULL) {}
/*
* 'optarg' indicates in which argument position the flags will be found,
* if present. This is a Mozilla extension and not part of any ECMA spec.
*
* If 'flat' is set, the first argument is to be converted to a string to
* match in a "flat" sense (without regular expression metachars having
* special meanings) UNLESS the first arg is a RegExp object. This is the
* case with String.prototype.replace.
*/
bool
initFromArgs(JSContext *cx, uintN optarg, bool flat, uintN argc, jsval *vp)
{
mCx = cx;
if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
mReobj = JSVAL_TO_OBJECT(vp[2]);
mRe = (JSRegExp *) mReobj->getPrivate();
HOLD_REGEXP(cx, mRe);
} else {
JSString *src = ArgToRootedString(cx, argc, vp, 0);
if (!src)
return false;
JSString *opt;
if (optarg < argc) {
opt = js_ValueToString(cx, vp[2 + optarg]);
if (!opt)
return false;
} else {
opt = NULL;
}
mRe = js_NewRegExpOpt(cx, src, opt, flat);
if (!mRe)
return false;
mReobj = NULL;
}
return true;
}
RegExpGuard(JSContext *cx) : mCx(cx), mRe(NULL) {}
~RegExpGuard() {
if (mRe)
DROP_REGEXP(mCx, mRe);
}
JSObject *reobj() const { return mReobj; }
JSRegExp *re() const { return mRe; }
/* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
bool
init(uintN argc, jsval *vp)
{
jsval patval = vp[2];
if (argc != 0 && VALUE_IS_REGEXP(mCx, patval)) {
mReobj = JSVAL_TO_OBJECT(patval);
mRe = (JSRegExp *) mReobj->getPrivate();
HOLD_REGEXP(mCx, mRe);
} else {
patstr = ArgToRootedString(mCx, argc, vp, 0);
if (!patstr)
return false;
}
return true;
}
/*
* Upper bound on the number of characters we are willing to potentially
* waste on searching for RegExp meta-characters.
*/
static const size_t sMaxFlatPatLen = 256;
/*
* Attempt to match |patstr| with |textstr|. Return false if flat matching
* could not be used.
*/
bool
tryFlatMatch(JSString *textstr, bool flat, uintN optarg, uintN argc)
{
if (mRe)
return false;
patstr->getCharsAndLength(pat, patlen);
if (optarg < argc ||
(!flat &&
(patlen > sMaxFlatPatLen || js_ContainsRegExpMetaChars(pat, patlen)))) {
return false;
}
textstr->getCharsAndLength(text, textlen);
match = StringMatch(text, textlen, pat, patlen);
return true;
}
/* Data available on successful return from |tryFlatMatch|. */
JSString *patstr;
const jschar *pat;
size_t patlen;
const jschar *text;
size_t textlen;
jsint match;
/* If the pattern is not already a regular expression, make it so. */
bool
normalizeRegExp(bool flat, uintN optarg, uintN argc, jsval *vp)
{
/* If we don't have a RegExp, build RegExp from pattern string. */
if (mRe)
return true;
JSString *opt;
if (optarg < argc) {
opt = js_ValueToString(mCx, vp[2 + optarg]);
if (!opt)
return false;
} else {
opt = NULL;
}
mRe = js_NewRegExpOpt(mCx, patstr, opt, flat);
if (!mRe)
return false;
mReobj = NULL;
return true;
}
/* Data available on successful return from |normalizeRegExp|. */
JSObject *reobj() const { return mReobj; } /* nullable */
JSRegExp *re() const { return mRe; } /* non-null */
};
/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
@ -1378,8 +1452,8 @@ DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g,
}
} else {
/* single match */
bool testSingle = flags & TEST_SINGLE_BIT,
callbackOnSingle = flags & CALLBACK_ON_SINGLE_BIT;
bool testSingle = !!(flags & TEST_SINGLE_BIT),
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
size_t i = 0;
if (!js_ExecuteRegExp(cx, g.re(), str, &i, testSingle, vp))
return false;
@ -1398,10 +1472,7 @@ DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g,
static bool
MatchCallback(JSContext *cx, size_t count, void *p)
{
if (count >= JSVAL_INT_MAX) {
js_ReportAllocationOverflow(cx);
return false;
}
JS_ASSERT(count <= JSVAL_INT_MAX); /* by max string length */
jsval &arrayval = *static_cast<jsval *>(p);
JSObject *arrayobj = JSVAL_TO_OBJECT(arrayval);
@ -1423,7 +1494,29 @@ MatchCallback(JSContext *cx, size_t count, void *p)
jsval v = STRING_TO_JSVAL(matchstr);
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
return arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v);
}
static bool
BuildFlatMatchArray(JSContext *cx, JSString *textstr, const RegExpGuard &g,
jsval *vp)
{
if (g.match < 0) {
*vp = JSVAL_NULL;
return true;
}
/* For this non-global match, produce a RegExp.exec-style array. */
JSObject *obj = js_NewSlowArrayObject(cx);
if (!obj)
return false;
*vp = OBJECT_TO_JSVAL(obj);
return obj->defineProperty(cx, INT_TO_JSID(0), STRING_TO_JSVAL(g.patstr)) &&
obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.indexAtom),
INT_TO_JSVAL(g.match)) &&
obj->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.inputAtom),
STRING_TO_JSVAL(textstr));
}
static JSBool
@ -1432,15 +1525,19 @@ str_match(JSContext *cx, uintN argc, jsval *vp)
JSString *str;
NORMALIZE_THIS(cx, vp, str);
RegExpGuard g;
if (!g.initFromArgs(cx, 1, false, argc, vp))
RegExpGuard g(cx);
if (!g.init(argc, vp))
return false;
if (g.tryFlatMatch(str, false, 1, argc))
return BuildFlatMatchArray(cx, str, g, vp);
if (!g.normalizeRegExp(false, 1, argc, vp))
return false;
JSAutoTempValueRooter array(cx, JSVAL_NULL);
if (!DoMatch(cx, vp, str, g, MatchCallback, array.addr(), MATCH_ARGS))
return false;
/* When not global, DoMatch will leave the (RegEx.exec()) in *vp. */
/* When not global, DoMatch will leave |RegEx.exec()| in *vp. */
if (g.re()->flags & JSREG_GLOB)
*vp = array.value();
return true;
@ -1452,8 +1549,14 @@ str_search(JSContext *cx, uintN argc, jsval *vp)
JSString *str;
NORMALIZE_THIS(cx, vp, str);
RegExpGuard g;
if (!g.initFromArgs(cx, 1, false, argc, vp))
RegExpGuard g(cx);
if (!g.init(argc, vp))
return false;
if (g.tryFlatMatch(str, false, 1, argc)) {
*vp = INT_TO_JSVAL(g.match);
return true;
}
if (!g.normalizeRegExp(false, 1, argc, vp))
return false;
size_t i = 0;
@ -1461,13 +1564,14 @@ str_search(JSContext *cx, uintN argc, jsval *vp)
return false;
if (*vp == JSVAL_TRUE)
return UWordInRootedValue(cx, cx->regExpStatics.leftContext.length, vp);
*vp = INT_TO_JSVAL(cx->regExpStatics.leftContext.length);
else
*vp = INT_TO_JSVAL(-1);
return true;
}
struct ReplaceData {
ReplaceData(JSContext *cx) : cb(cx) {}
ReplaceData(JSContext *cx) : g(cx), cb(cx) {}
JSString *str; /* 'this' parameter object as a string */
RegExpGuard g; /* regexp parameter object and private data */
JSObject *lambda; /* replacement function object or null */
@ -1730,16 +1834,41 @@ ReplaceCallback(JSContext *cx, size_t count, void *p)
return true;
}
static bool
BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
const RegExpGuard &g, jsval *vp)
{
if (g.match == -1) {
*vp = STRING_TO_JSVAL(textstr);
return true;
}
const jschar *rep;
size_t replen;
repstr->getCharsAndLength(rep, replen);
JSCharBuffer cb(cx);
if (!cb.reserve(g.textlen - g.patlen + replen) ||
!cb.append(g.text, static_cast<size_t>(g.match)) ||
!cb.append(rep, replen) ||
!cb.append(g.text + g.match + g.patlen, g.text + g.textlen)) {
return false;
}
JSString *str = js_NewStringFromCharBuffer(cx, cb);
if (!str)
return false;
*vp = STRING_TO_JSVAL(str);
return true;
}
static JSBool
str_replace(JSContext *cx, uintN argc, jsval *vp)
{
ReplaceData rdata(cx);
NORMALIZE_THIS(cx, vp, rdata.str);
if (!rdata.g.initFromArgs(cx, 2, true, argc, vp))
return false;
/* Extract replacement string/function. */
if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
rdata.lambda = JSVAL_TO_OBJECT(vp[3]);
rdata.repstr = NULL;
@ -1758,6 +1887,15 @@ str_replace(JSContext *cx, uintN argc, jsval *vp)
rdata.dollarEnd);
}
if (!rdata.g.init(argc, vp))
return false;
if (!rdata.dollar && !rdata.lambda &&
rdata.g.tryFlatMatch(rdata.str, true, 2, argc)) {
return BuildFlatReplacement(cx, rdata.str, rdata.repstr, rdata.g, vp);
}
if (!rdata.g.normalizeRegExp(true, 2, argc, vp))
return false;
rdata.index = 0;
rdata.leftIndex = 0;
rdata.calledBack = false;
@ -3134,9 +3272,6 @@ js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb)
JS_FRIEND_API(JSString *)
js_ValueToSource(JSContext *cx, jsval v)
{
JSTempValueRooter tvr;
JSString *str;
if (JSVAL_IS_VOID(v))
return ATOM_TO_STRING(cx->runtime->atomState.void0Atom);
if (JSVAL_IS_STRING(v))
@ -3152,16 +3287,11 @@ js_ValueToSource(JSContext *cx, jsval v)
return js_ValueToString(cx, v);
}
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v),
cx->runtime->atomState.toSourceAtom,
0, NULL, &tvr.u.value)) {
str = NULL;
} else {
str = js_ValueToString(cx, tvr.u.value);
}
JS_POP_TEMP_ROOT(cx, &tvr);
return str;
JSAtom *atom = cx->runtime->atomState.toSourceAtom;
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), atom, 0, NULL, tvr.addr()))
return NULL;
return js_ValueToString(cx, tvr.value());
}
/*

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

@ -142,7 +142,11 @@ struct JSString {
ATOMIZED = JSSTRING_BIT(JS_BITS_PER_WORD - 3),
DEFLATED = JSSTRING_BIT(JS_BITS_PER_WORD - 4),
#if JS_BITS_PER_WORD > 32
LENGTH_BITS = 28,
#else
LENGTH_BITS = JS_BITS_PER_WORD - 4,
#endif
LENGTH_MASK = JSSTRING_BITMASK(LENGTH_BITS),
/*
@ -676,20 +680,17 @@ js_CompareStrings(JSString *str1, JSString *str2);
/*
* Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.
* The patlen argument must be positive and no greater than BMH_PATLEN_MAX.
* The start argument tells where in text to begin the search.
* The patlen argument must be positive and no greater than sBMHPatLenMax.
*
* Return the index of pat in text, or -1 if not found.
*/
#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */
#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */
#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */
static const jsuint sBMHCharSetSize = 256; /* ISO-Latin-1 */
static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8 */
static const jsint sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */
extern jsint
js_BoyerMooreHorspool(const jschar *text, jsint textlen,
const jschar *pat, jsint patlen,
jsint start);
js_BoyerMooreHorspool(const jschar *text, jsuint textlen,
const jschar *pat, jsuint patlen);
extern size_t
js_strlen(const jschar *s);

893
js/src/jstracer.cpp Normal file → Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

81
js/src/jstracer.h Normal file → Executable file
Просмотреть файл

@ -47,6 +47,7 @@
#include "jstypes.h"
#include "jsbuiltins.h"
#include "jscntxt.h"
#include "jsdhash.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
@ -157,19 +158,20 @@ public:
/*
* Tracker is used to keep track of values being manipulated by the interpreter
* during trace recording.
* during trace recording. Note that tracker pages aren't necessarily the
* same size as OS pages, they just are a moderate-sized chunk of memory.
*/
class Tracker {
struct Page {
struct Page* next;
struct TrackerPage {
struct TrackerPage* next;
jsuword base;
nanojit::LIns* map[1];
};
struct Page* pagelist;
struct TrackerPage* pagelist;
jsuword getPageBase(const void* v) const;
struct Page* findPage(const void* v) const;
struct Page* addPage(const void* v);
jsuword getTrackerPageBase(const void* v) const;
struct TrackerPage* findTrackerPage(const void* v) const;
struct TrackerPage* addTrackerPage(const void* v);
public:
Tracker();
~Tracker();
@ -510,7 +512,7 @@ struct FrameInfo {
void set_argc(uint16 argc, bool constructing) {
this->argc = uint32(argc) | (constructing ? CONSTRUCTING_FLAG: 0);
}
uint16 get_argc() const { return argc & ~CONSTRUCTING_FLAG; }
uint16 get_argc() const { return uint16(argc & ~CONSTRUCTING_FLAG); }
bool is_constructing() const { return (argc & CONSTRUCTING_FLAG) != 0; }
// The typemap just before the callee is called.
@ -690,14 +692,14 @@ enum TypeConsensus
TypeConsensus_Bad /* Typemaps are not compatible */
};
class TraceRecorder : public avmplus::GCObject {
class TraceRecorder {
VMAllocator& tempAlloc;
JSContext* cx;
JSTraceMonitor* traceMonitor;
JSObject* globalObj;
JSObject* lexicalBlock;
Tracker tracker;
Tracker nativeFrameTracker;
char* entryTypeMap;
unsigned callDepth;
JSAtom** atoms;
VMSideExit* anchor;
@ -722,7 +724,6 @@ class TraceRecorder : public avmplus::GCObject {
nanojit::LIns* inner_sp_ins;
nanojit::LIns* native_rval_ins;
nanojit::LIns* newobj_ins;
bool deepAborted;
bool trashSelf;
Queue<nanojit::Fragment*> whichTreesToTrash;
Queue<jsbytecode*> cfgMerges;
@ -731,8 +732,6 @@ class TraceRecorder : public avmplus::GCObject {
JSSpecializedNative* pendingSpecializedNative;
jsval* pendingUnboxSlot;
nanojit::LIns* pendingGuardCondition;
TraceRecorder* nextRecorderToAbort;
bool wasRootFragment;
jsbytecode* outer; /* outer trace header PC */
uint32 outerArgc; /* outer trace deepest frame argc */
bool loop;
@ -839,10 +838,18 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK JSRecordingStatus unary(nanojit::LOpcode op);
JS_REQUIRES_STACK JSRecordingStatus binary(nanojit::LOpcode op);
JS_REQUIRES_STACK void guardShape(nanojit::LIns* obj_ins, JSObject* obj,
uint32 shape, const char* guardName,
JS_REQUIRES_STACK JSRecordingStatus guardShape(nanojit::LIns* obj_ins, JSObject* obj,
uint32 shape, const char* name,
nanojit::LIns* map_ins, VMSideExit* exit);
JSDHashTable guardedShapeTable;
#ifdef DEBUG
void dumpGuardedShapes(const char* prefix);
#endif
void forgetGuardedShapes();
inline nanojit::LIns* map(nanojit::LIns *obj_ins);
JS_REQUIRES_STACK bool map_is_native(JSObjectMap* map, nanojit::LIns* map_ins,
nanojit::LIns*& ops_ins, size_t op_offset = 0);
@ -981,6 +988,19 @@ class TraceRecorder : public avmplus::GCObject {
JS_REQUIRES_STACK jsatomid getFullIndex(ptrdiff_t pcoff = 0);
public:
inline void*
operator new(size_t size)
{
return calloc(1, size);
}
inline void
operator delete(void *p)
{
free(p);
}
JS_REQUIRES_STACK
TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap,
@ -988,6 +1008,8 @@ public:
uint32 outerArgc);
~TraceRecorder();
bool outOfMemory();
static JS_REQUIRES_STACK JSRecordingStatus monitorRecording(JSContext* cx, TraceRecorder* tr,
JSOp op);
@ -1011,11 +1033,11 @@ public:
* The instruction is suitable for use as the final argument of a single
* call to LirBuffer::insGuard; do not reuse the returned value.
*/
JS_REQUIRES_STACK nanojit::LIns* createGuardRecord(VMSideExit* exit);
JS_REQUIRES_STACK nanojit::GuardRecord* createGuardRecord(VMSideExit* exit);
nanojit::Fragment* getFragment() const { return fragment; }
TreeInfo* getTreeInfo() const { return treeInfo; }
JS_REQUIRES_STACK void compile(JSTraceMonitor* tm);
JS_REQUIRES_STACK bool compile(JSTraceMonitor* tm);
JS_REQUIRES_STACK bool closeLoop(TypeConsensus &consensus);
JS_REQUIRES_STACK bool closeLoop(SlotMap& slotMap, VMSideExit* exit, TypeConsensus &consensus);
JS_REQUIRES_STACK void endLoop();
@ -1026,10 +1048,6 @@ public:
JS_REQUIRES_STACK void prepareTreeCall(VMFragment* inner);
JS_REQUIRES_STACK void emitTreeCall(VMFragment* inner, VMSideExit* exit);
unsigned getCallDepth() const;
void pushAbortStack();
void popAbortStack();
void removeFragmentReferences();
void deepAbort();
JS_REQUIRES_STACK JSRecordingStatus record_EnterFrame();
JS_REQUIRES_STACK JSRecordingStatus record_LeaveFrame();
@ -1038,8 +1056,21 @@ public:
JS_REQUIRES_STACK JSRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
JS_REQUIRES_STACK JSRecordingStatus record_NativeCallComplete();
bool wasDeepAborted() { return deepAborted; }
TreeInfo* getTreeInfo() { return treeInfo; }
void forgetGuardedShapesForObject(JSObject* obj);
#ifdef DEBUG
void tprint(const char *format, int count, nanojit::LIns *insa[]);
void tprint(const char *format);
void tprint(const char *format, nanojit::LIns *ins);
void tprint(const char *format, nanojit::LIns *ins1, nanojit::LIns *ins2);
void tprint(const char *format, nanojit::LIns *ins1, nanojit::LIns *ins2, nanojit::LIns *ins3);
void tprint(const char *format, nanojit::LIns *ins1, nanojit::LIns *ins2, nanojit::LIns *ins3,
nanojit::LIns *ins4);
void tprint(const char *format, nanojit::LIns *ins1, nanojit::LIns *ins2, nanojit::LIns *ins3,
nanojit::LIns *ins4, nanojit::LIns *ins5);
void tprint(const char *format, nanojit::LIns *ins1, nanojit::LIns *ins2, nanojit::LIns *ins3,
nanojit::LIns *ins4, nanojit::LIns *ins5, nanojit::LIns *ins6);
#endif
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
JS_REQUIRES_STACK JSRecordingStatus record_##op();
@ -1068,10 +1099,10 @@ public:
#define TRACE_ARGS_(x,args) \
JS_BEGIN_MACRO \
TraceRecorder* tr_ = TRACE_RECORDER(cx); \
if (tr_ && !tr_->wasDeepAborted()) { \
if (TraceRecorder* tr_ = TRACE_RECORDER(cx)) { \
JSRecordingStatus status = tr_->record_##x args; \
if (STATUS_ABORTS_RECORDING(status)) { \
if (TRACE_RECORDER(cx)) \
js_AbortRecording(cx, #x); \
if (status == JSRS_ERROR) \
goto error; \

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

@ -5466,20 +5466,14 @@ xml_attribute(JSContext *cx, uintN argc, jsval *vp)
static JSBool
xml_attributes(JSContext *cx, uintN argc, jsval *vp)
{
jsval name;
JSObject *qn;
JSTempValueRooter tvr;
JSBool ok;
name = ATOM_KEY(cx->runtime->atomState.starAtom);
qn = ToAttributeName(cx, name);
jsval name = ATOM_KEY(cx->runtime->atomState.starAtom);
JSObject *qn = ToAttributeName(cx, name);
if (!qn)
return JS_FALSE;
name = OBJECT_TO_JSVAL(qn);
JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr);
ok = GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
JSAutoTempValueRooter tvr(cx, name);
return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
}
static JSXML *

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

@ -40,16 +40,25 @@
DEPTH = ..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
VPATH = $(srcdir)
VPATH += $(srcdir)/../nanojit
include $(DEPTH)/config/autoconf.mk
PROGRAM = lirasm$(BIN_SUFFIX)
CPPSRCS = lirasm.cpp
CPPSRCS = lirasm.cpp \
Assembler.cpp \
Allocator.cpp \
CodeAlloc.cpp \
Containers.cpp \
Fragmento.cpp \
LIR.cpp \
RegAlloc.cpp \
avmplus.cpp \
Native$(NANOJIT_ARCH).cpp \
$(NULL)
DEFINES += -DEXPORT_JS_API
LIBS = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
LOCAL_INCLUDES += -I$(topsrcdir) -I..
@ -63,12 +72,6 @@ CFLAGS += -EHsc
CXXFLAGS += -EHsc
endif
ifdef MOZ_SHARK
CFLAGS += -F/System/Library/PrivateFrameworks
CXXFLAGS += -F/System/Library/PrivateFrameworks
LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD
endif
check::
$(srcdir)/testlirc.sh

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

@ -57,20 +57,65 @@
#include <assert.h>
#include "nanojit/nanojit.h"
#include "jstracer.h"
using namespace nanojit;
using namespace std;
static avmplus::GC gc;
/* Allocator SPI implementation. */
void*
nanojit::Allocator::allocChunk(size_t nbytes)
{
void *p = malloc(nbytes);
if (!p)
exit(1);
return p;
}
void
nanojit::Allocator::freeChunk(void *p) {
free(p);
}
void
nanojit::Allocator::postReset() {
}
struct LasmSideExit : public SideExit {
size_t line;
};
typedef JS_FASTCALL int32_t (*RetInt)();
typedef JS_FASTCALL double (*RetFloat)();
typedef JS_FASTCALL GuardRecord* (*RetGuard)();
/* LIR SPI implementation */
void
nanojit::StackFilter::getTops(LIns* guard, int& spTop, int& rpTop)
{
spTop = 0;
rpTop = 0;
}
#if defined NJ_VERBOSE
void
nanojit::LirNameMap::formatGuard(LIns *i, char *out)
{
LasmSideExit *x;
x = (LasmSideExit *)i->record()->exit;
sprintf(out,
"%s: %s %s -> line=%d (GuardID=%03d)",
formatRef(i),
lirNames[i->opcode()],
i->oprnd1() ? formatRef(i->oprnd1()) : "",
x->line,
i->record()->profGuardID);
}
#endif
typedef FASTCALL int32_t (*RetInt)();
typedef FASTCALL double (*RetFloat)();
typedef FASTCALL GuardRecord* (*RetGuard)();
struct Function {
const char *name;
@ -211,11 +256,15 @@ public:
void assemble(istream &in);
void lookupFunction(const string &name, CallInfo *&ci);
Fragmento *mFragmento;
LirBuffer *mLirbuf;
verbose_only( LabelMap *mLabelMap; )
LogControl mLogc;
avmplus::AvmCore mCore;
Allocator mAlloc;
CodeAlloc mCodeAlloc;
bool mVerbose;
Fragments mFragments;
Assembler mAssm;
map<string, pair<LOpcode, size_t> > mOpMap;
void bad(const string &msg) {
@ -225,8 +274,6 @@ public:
private:
void handlePatch(LirTokenStream &in);
avmplus::AvmCore mCore;
};
class FragmentAssembler {
@ -234,14 +281,19 @@ public:
FragmentAssembler(Lirasm &parent, const string &fragmentName);
~FragmentAssembler();
void assembleFragment(LirTokenStream &in, bool implicitBegin, const LirToken *firstToken);
void assembleFragment(LirTokenStream &in,
bool implicitBegin,
const LirToken *firstToken);
private:
static uint32_t sProfId;
// Prohibit copying.
FragmentAssembler(const FragmentAssembler &);
FragmentAssembler & operator=(const FragmentAssembler &);
LasmSideExit *createSideExit();
GuardRecord *createGuardRecord(LasmSideExit *exit);
Lirasm *mParent;
Lirasm &mParent;
const string mFragName;
Fragment *mFragment;
vector<CallInfo*> mCallInfos;
@ -263,7 +315,6 @@ private:
void tokenizeLine(LirTokenStream &in, LirToken &token);
void need(size_t);
LIns *ref(const string &);
LIns *do_skip(size_t);
LIns *assemble_call(const string &);
LIns *assemble_general();
LIns *assemble_guard();
@ -353,6 +404,9 @@ dep_u32(char *&buf, uint32_t word, uint32_t &cksum)
void
dump_srecords(ostream &out, Fragment *frag)
{
// FIXME: Disabled until we work out a sane way to walk through
// code chunks under the new CodeAlloc regime.
/*
// Write S-records. Can only do 4-byte addresses at the moment.
// FIXME: this presently dumps out the entire set of code pages
@ -395,26 +449,34 @@ dump_srecords(ostream &out, Fragment *frag)
out << string(buf) << endl;
}
}
*/
}
FragmentAssembler::FragmentAssembler(Lirasm &parent, const string &fragmentName)
: mParent(&parent), mFragName(fragmentName)
{
mFragment = new (&gc) Fragment(NULL);
mFragment->lirbuf = mParent->mLirbuf;
mFragment->anchor = mFragment;
mFragment->root = mFragment;
mParent->mFragments[mFragName].fragptr = mFragment;
mBufWriter = new (&gc) LirBufWriter(mParent->mLirbuf);
mCseFilter = new (&gc) CseFilter(mBufWriter, &gc);
mExprFilter = new (&gc) ExprFilter(mCseFilter);
uint32_t
FragmentAssembler::sProfId = 0;
FragmentAssembler::FragmentAssembler(Lirasm &parent, const string &fragmentName)
: mParent(parent), mFragName(fragmentName)
{
mFragment = new Fragment(NULL verbose_only(, sProfId++));
mFragment->lirbuf = mParent.mLirbuf;
mFragment->root = mFragment;
mParent.mFragments[mFragName].fragptr = mFragment;
mBufWriter = new LirBufWriter(mParent.mLirbuf);
mCseFilter = new CseFilter(mBufWriter, mParent.mAlloc);
mExprFilter = new ExprFilter(mCseFilter);
mVerboseWriter = NULL;
mLir = mExprFilter;
#ifdef DEBUG
if (mParent->mVerbose) {
mVerboseWriter = new (&gc) VerboseWriter(&gc, mExprFilter, mParent->mLirbuf->names, &mParent->mLogc);
if (mParent.mVerbose) {
mVerboseWriter = new VerboseWriter(mParent.mAlloc,
mExprFilter,
mParent.mLirbuf->names,
&mParent.mLogc);
mLir = mVerboseWriter;
}
#endif
@ -431,11 +493,9 @@ FragmentAssembler::~FragmentAssembler()
delete mExprFilter;
delete mCseFilter;
delete mBufWriter;
for (size_t i = 0; i < mCallInfos.size(); ++i)
delete mCallInfos[i];
}
void
FragmentAssembler::bad(const string &msg)
{
@ -460,14 +520,6 @@ FragmentAssembler::ref(const string &lab)
return mLabels.find(lab)->second;
}
LIns *
FragmentAssembler::do_skip(size_t i)
{
LIns *s = mLir->insSkip(i);
memset(s->payload(), 0xba, i);
return s;
}
LIns *
FragmentAssembler::assemble_jump()
{
@ -513,7 +565,7 @@ FragmentAssembler::assemble_load()
LIns *
FragmentAssembler::assemble_call(const string &op)
{
CallInfo *ci = new CallInfo();
CallInfo *ci = new (mParent.mAlloc) CallInfo();
mCallInfos.push_back(ci);
LIns *args[MAXARGS];
@ -530,9 +582,13 @@ FragmentAssembler::assemble_call(const string &op)
string func = pop_front(mTokens);
string abi = pop_front(mTokens);
AbiKind _abi;
if (abi == "fastcall")
AbiKind _abi = ABI_CDECL;
if (abi == "fastcall") {
#ifdef NO_FASTCALL
bad("no fastcall support");
#endif
_abi = ABI_FASTCALL;
}
else if (abi == "stdcall")
_abi = ABI_STDCALL;
else if (abi == "thiscall")
@ -556,7 +612,7 @@ FragmentAssembler::assemble_call(const string &op)
ci->_name = "fn";
#endif
} else {
mParent->lookupFunction(func, ci);
mParent.lookupFunction(func, ci);
if (ci == NULL)
bad("invalid function reference " + func);
if (_abi != ci->_abi)
@ -574,7 +630,7 @@ FragmentAssembler::assemble_call(const string &op)
// Select return type from opcode.
// FIXME: callh needs special treatment currently
// missing from here.
if (mOpcode == LIR_call)
if (mOpcode == LIR_icall)
ci->_argtypes |= ARGSIZE_LO;
else
ci->_argtypes |= ARGSIZE_F;
@ -582,25 +638,35 @@ FragmentAssembler::assemble_call(const string &op)
return mLir->insCall(ci, args);
}
LIns *
FragmentAssembler::assemble_guard()
LasmSideExit*
FragmentAssembler::createSideExit()
{
LIns *exitIns = do_skip(sizeof(LasmSideExit));
LasmSideExit* exit = (LasmSideExit*) exitIns->payload();
LasmSideExit* exit = new (mParent.mAlloc) LasmSideExit();
memset(exit, 0, sizeof(LasmSideExit));
exit->from = mFragment;
exit->target = NULL;
exit->line = mLineno;
return exit;
}
LIns *guardRec = do_skip(sizeof(GuardRecord));
GuardRecord *rec = (GuardRecord*) guardRec->payload();
GuardRecord*
FragmentAssembler::createGuardRecord(LasmSideExit *exit)
{
GuardRecord *rec = new (mParent.mAlloc) GuardRecord();
memset(rec, 0, sizeof(GuardRecord));
rec->exit = exit;
exit->addGuard(rec);
return rec;
}
LIns *
FragmentAssembler::assemble_guard()
{
GuardRecord* guard = createGuardRecord(createSideExit());
need(mOpcount);
if (mOpcode != LIR_loop)
mReturnTypeBits |= RT_GUARD;
LIns *ins_cond;
@ -612,7 +678,7 @@ FragmentAssembler::assemble_guard()
if (!mTokens.empty())
bad("too many arguments");
return mLir->insGuard(mOpcode, ins_cond, guardRec);
return mLir->insGuard(mOpcode, ins_cond, guard);
}
LIns *
@ -662,25 +728,17 @@ FragmentAssembler::endFragment()
cerr << "warning: multiple return types in fragment '"
<< mFragName << "'" << endl;
}
LIns *exitIns = do_skip(sizeof(SideExit));
SideExit* exit = (SideExit*) exitIns->payload();
memset(exit, 0, sizeof(SideExit));
exit->guards = NULL;
exit->from = exit->target = mFragment;
mFragment->lastIns = mLir->insGuard(LIR_loop, NULL, exitIns);
::compile(mParent->mFragmento->assm(), mFragment);
mFragment->lastIns =
mLir->insGuard(LIR_x, NULL, createGuardRecord(createSideExit()));
if (mParent->mFragmento->assm()->error() != nanojit::None) {
::compile(&mParent.mAssm, mFragment, mParent.mAlloc
verbose_only(, mParent.mLabelMap));
if (mParent.mAssm.error() != nanojit::None) {
cerr << "error during assembly: ";
switch (mParent->mFragmento->assm()->error()) {
case nanojit::OutOMem: cerr << "OutOMem"; break;
switch (mParent.mAssm.error()) {
case nanojit::StackFull: cerr << "StackFull"; break;
case nanojit::RegionFull: cerr << "RegionFull"; break;
case nanojit::MaxLength: cerr << "MaxLength"; break;
case nanojit::MaxExit: cerr << "MaxExit"; break;
case nanojit::MaxXJump: cerr << "MaxXJump"; break;
case nanojit::UnknownPrim: cerr << "UnknownPrim"; break;
case nanojit::UnknownBranch: cerr << "UnknownBranch"; break;
case nanojit::None: cerr << "None"; break;
}
@ -689,24 +747,24 @@ FragmentAssembler::endFragment()
}
LirasmFragment *f;
f = &mParent->mFragments[mFragName];
f = &mParent.mFragments[mFragName];
switch (mReturnTypeBits) {
case RT_GUARD:
f->rguard = reinterpret_cast<RetGuard>(mFragment->code());
f->rguard = (RetGuard)((uintptr_t)mFragment->code());
f->mReturnType = RT_GUARD;
break;
case RT_FLOAT:
f->rfloat = reinterpret_cast<RetFloat>(mFragment->code());
f->rfloat = (RetFloat)((uintptr_t)mFragment->code());
f->mReturnType = RT_FLOAT;
break;
default:
f->rint = reinterpret_cast<RetInt>(mFragment->code());
f->rint = (RetInt)((uintptr_t)mFragment->code());
f->mReturnType = RT_INT32;
break;
}
mParent->mFragments[mFragName].mLabels = mLabels;
mParent.mFragments[mFragName].mLabels = mLabels;
}
void
@ -786,10 +844,10 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
assert(!mTokens.empty());
op = pop_front(mTokens);
if (mParent->mOpMap.find(op) == mParent->mOpMap.end())
if (mParent.mOpMap.find(op) == mParent.mOpMap.end())
bad("unknown instruction '" + op + "'");
pair<LOpcode, size_t> entry = mParent->mOpMap[op];
pair<LOpcode, size_t> entry = mParent.mOpMap[op];
mOpcode = entry.first;
mOpcount = entry.second;
@ -842,24 +900,17 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
break;
case LIR_skip:
need(1);
{
int32_t count = imm(mTokens[0]);
if (uint32_t(count) > NJ_MAX_SKIP_PAYLOAD_SZB)
bad("oversize skip");
ins = do_skip(count);
}
bad("skip instruction is deprecated");
break;
case LIR_xt:
case LIR_xf:
case LIR_x:
case LIR_xbarrier:
case LIR_loop:
ins = assemble_guard();
break;
case LIR_call:
case LIR_icall:
case LIR_callh:
case LIR_fcall:
ins = assemble_call(op);
@ -874,27 +925,23 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
if (!lab.empty())
mLabels.insert(make_pair(lab, ins));
if (mParent->mLirbuf->outOMem()) {
cerr << "lirbuf out of memory" << endl;
exit(1);
}
}
endFragment();
}
Lirasm::Lirasm(bool verbose)
Lirasm::Lirasm(bool verbose) :
mAssm(mCodeAlloc, mAlloc, &mCore, &mLogc)
{
mVerbose = verbose;
nanojit::AvmCore::config.tree_opt = true;
mLogc.lcbits = 0;
mFragmento = new (&gc) Fragmento(&mCore, &mLogc, 32);
mFragmento->labels = NULL;
mLirbuf = new (&gc) LirBuffer(mFragmento);
mLirbuf = new (mAlloc) LirBuffer(mAlloc);
#ifdef DEBUG
if (mVerbose) {
mLogc.lcbits = LC_Assembly;
mFragmento->labels = new (&gc) LabelMap(&mCore);
mLirbuf->names = new (&gc) LirNameMap(&gc, mFragmento->labels);
mLabelMap = new (mAlloc) LabelMap(mAlloc, &mLogc);
mLirbuf->names = new (mAlloc) LirNameMap(mAlloc, mLabelMap);
}
#endif
@ -916,14 +963,11 @@ Lirasm::~Lirasm()
{
Fragments::iterator i;
for (i = mFragments.begin(); i != mFragments.end(); ++i) {
i->second.fragptr->releaseCode(mFragmento);
delete i->second.fragptr;
}
delete mLirbuf;
delete mFragmento->labels;
delete mFragmento;
}
void
Lirasm::lookupFunction(const string &name, CallInfo *&ci)
{
@ -938,13 +982,17 @@ Lirasm::lookupFunction(const string &name, CallInfo *&ci)
Fragments::const_iterator func = mFragments.find(name);
if (func != mFragments.end()) {
if (func->second.mReturnType == RT_FLOAT) {
CallInfo target = {(uintptr_t) func->second.rfloat, ARGSIZE_F, 0,
0, nanojit::ABI_FASTCALL, func->first.c_str()};
CallInfo target = {(uintptr_t) func->second.rfloat,
ARGSIZE_F, 0, 0,
nanojit::ABI_FASTCALL
verbose_only(, func->first.c_str()) };
*ci = target;
} else {
CallInfo target = {(uintptr_t) func->second.rint, ARGSIZE_LO, 0,
0, nanojit::ABI_FASTCALL, func->first.c_str()};
CallInfo target = {(uintptr_t) func->second.rint,
ARGSIZE_LO, 0, 0,
nanojit::ABI_FASTCALL
verbose_only(, func->first.c_str()) };
*ci = target;
}
} else {
@ -960,10 +1008,7 @@ Lirasm::assemble(istream &in)
LirToken token;
while (ts.get(token)) {
if (mLirbuf->outOMem()) {
cerr << "lirbuf out of memory" << endl;
exit(1);
}
if (token.type == NEWLINE)
continue;
if (token.type != NAME)
@ -992,11 +1037,6 @@ Lirasm::assemble(istream &in)
bad("unexpected stray opcode '" + op + "'");
}
}
if (mLirbuf->outOMem()) {
cerr << "lirbuf out of memory" << endl;
exit(1);
}
}
void
@ -1025,7 +1065,7 @@ Lirasm::handlePatch(LirTokenStream &in)
bad("invalid guard reference");
ins->record()->exit->target = i->second.fragptr;
mFragmento->assm()->patch(ins->record()->exit);
mAssm.patch(ins->record()->exit);
}
bool

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

@ -7,5 +7,5 @@ c = int 67
sti c ptr 2
zero = int 0
sti zero ptr 3
ss = call puts cdecl ptr
ss = icall puts cdecl ptr
ret ss

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

@ -8,17 +8,17 @@ c = int 67
sti c ptr 2
zero = int 0
sti zero ptr 3
ss = call puts cdecl ptr
ss = icall puts cdecl ptr
ret ss
.end
.begin b
rr = call a fastcall
rr = icall a fastcall
ret rr
.end
.begin main
ans = call b fastcall
ans = icall b fastcall
five = int 5
res = add five ans
ret res

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

@ -10,6 +10,6 @@ ret avg
.begin main
oneh = int 100
twoh = int 200
res = call avg fastcall twoh oneh
res = icall avg fastcall twoh oneh
ret res
.end

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

@ -39,6 +39,8 @@
#include "nanojit.h"
#ifdef FEATURE_NANOJIT
namespace nanojit
{
Allocator::Allocator()
@ -90,3 +92,5 @@ namespace nanojit
current_limit = (char*)mem + chunkbytes;
}
}
#endif // FEATURE_NANOJIT

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

@ -99,9 +99,19 @@ inline void* operator new(size_t size, nanojit::Allocator &a) {
return a.alloc(size);
}
/** global new overload enabling this pattern: new (allocator) T(...) */
inline void* operator new(size_t size, nanojit::Allocator *a) {
return a->alloc(size);
}
/** global new[] overload enabling this pattern: new (allocator) T[] */
inline void* operator new[](size_t size, nanojit::Allocator& a) {
return a.alloc(size);
}
/** global new[] overload enabling this pattern: new (allocator) T[] */
inline void* operator new[](size_t size, nanojit::Allocator* a) {
return a->alloc(size);
}
#endif // __nanojit_Allocator__

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

@ -83,7 +83,7 @@ namespace nanojit
LInsp i = in->read();
const char* str = _names->formatIns(i);
char* cpy = new (_alloc) char[strlen(str)+1];
strcpy(cpy, str);
VMPI_strcpy(cpy, str);
_strs.insert(cpy);
return i;
}
@ -96,15 +96,20 @@ namespace nanojit
* - merging paths ( build a graph? ), possibly use external rep to drive codegen
*/
Assembler::Assembler(CodeAlloc& codeAlloc, Allocator& alloc, AvmCore* core, LogControl* logc)
: codeList(0)
: codeList(NULL)
, alloc(alloc)
, _codeAlloc(codeAlloc)
, _thisfrag(NULL)
, _branchStateMap(alloc)
, _patches(alloc)
, _labels(alloc)
, _epilogue(NULL)
, _err(None)
, config(core->config)
{
VMPI_memset(&_stats, 0, sizeof(_stats));
nInit(core);
(void)logc;
verbose_only( _logc = logc; )
verbose_only( _outputCache = 0; )
verbose_only( outlineEOL[0] = '\0'; )
@ -152,10 +157,10 @@ namespace nanojit
Register r = nRegisterAllocFromSet(set);
return r;
}
counter_increment(steals);
// nothing free, steal one
// LSRA says pick the one with the furthest use
counter_increment(steals);
LIns* vic = findVictim(allow);
NanoAssert(vic);
@ -214,14 +219,11 @@ namespace nanojit
}
#endif
#endif
#ifdef _DEBUG
void Assembler::resourceConsistencyCheck()
{
if (error()) return;
NanoAssert(!error());
#ifdef NANOJIT_IA32
NanoAssert((_allocator.active[FST0] && _fpuStkDepth == -1) ||
@ -267,7 +269,7 @@ namespace nanojit
{
// check registers
RegAlloc *regs = &_allocator;
uint32_t managed = regs->managed;
RegisterMask managed = regs->managed;
Register r = FirstReg;
while(managed)
{
@ -385,7 +387,7 @@ namespace nanojit
// Existing reservation with a known register allocated, but
// the register is not allowed.
RegisterMask prefer = hint(ins, allow);
#ifdef AVMPLUS_IA32
#ifdef NANOJIT_IA32
if (((rmask(r)&XmmRegs) && !(allow&XmmRegs)) ||
((rmask(r)&x87Regs) && !(allow&x87Regs)))
{
@ -396,6 +398,15 @@ namespace nanojit
ins->setReg(r);
_allocator.addActive(r, ins);
} else
#elif defined(NANOJIT_PPC)
if (((rmask(r)&GpRegs) && !(allow&GpRegs)) ||
((rmask(r)&FpRegs) && !(allow&FpRegs)))
{
evict(r, ins);
r = registerAlloc(prefer);
ins->setReg(r);
_allocator.addActive(r, ins);
} else
#endif
{
// The post-state register holding 'ins' is 's', the pre-state
@ -426,7 +437,6 @@ namespace nanojit
return r;
}
int Assembler::findMemFor(LIns *ins)
{
if (!ins->isUsed())
@ -783,7 +793,7 @@ namespace nanojit
// save entry point pointers
frag->fragEntry = fragEntry;
frag->setCode(_nIns);
// PERFM_NVPROF("code", CodeAlloc::size(codeList));
PERFM_NVPROF("code", CodeAlloc::size(codeList));
#ifdef NANOJIT_IA32
NanoAssertMsgf(_fpuStkDepth == 0,"_fpuStkDepth %d\n",_fpuStkDepth);
@ -837,7 +847,6 @@ namespace nanojit
#define countlir_label() _nvprof("lir-label",1)
#define countlir_xcc() _nvprof("lir-xcc",1)
#define countlir_x() _nvprof("lir-x",1)
#define countlir_loop() _nvprof("lir-loop",1)
#define countlir_call() _nvprof("lir-call",1)
#else
#define countlir_live()
@ -863,7 +872,6 @@ namespace nanojit
#define countlir_label()
#define countlir_xcc()
#define countlir_x()
#define countlir_loop()
#define countlir_call()
#endif
@ -871,13 +879,13 @@ namespace nanojit
{
NanoAssert(_thisfrag->nStaticExits == 0);
// trace must end with LIR_x, LIR_loop, LIR_[f]ret, LIR_xtbl, or LIR_[f]live
// trace must end with LIR_x, LIR_[f]ret, LIR_xtbl, or LIR_[f]live
NanoAssert(reader->pos()->isop(LIR_x) ||
reader->pos()->isop(LIR_ret) ||
reader->pos()->isop(LIR_fret) ||
reader->pos()->isop(LIR_xtbl) ||
reader->pos()->isop(LIR_live) ||
reader->pos()->isop(LIR_flive));
reader->pos()->isop(LIR_flive) ||
reader->pos()->isop(LIR_live));
InsList pending_lives(alloc);
@ -958,11 +966,12 @@ namespace nanojit
break;
}
case LIR_ret:
case LIR_fret:
case LIR_ret: {
countlir_ret();
asm_ret(ins);
break;
}
// allocate some stack space. the value of this instruction
// is the address of the stack space.
@ -1356,12 +1365,28 @@ namespace nanojit
// field in another machine instruction).
//
if (_logc->lcbits & LC_Assembly) {
outputf(" %s", _thisfrag->lirbuf->names->formatIns(ins));
// Special case: a guard condition won't get printed next time
// around the loop, so do it now.
LirNameMap* names = _thisfrag->lirbuf->names;
outputf(" %s", names->formatIns(ins));
if (ins->isGuard() && ins->oprnd1()) {
outputf(" %s # handled by the guard",
_thisfrag->lirbuf->names->formatIns(ins->oprnd1()));
// Special case: code is generated for guard conditions at
// the same time that code is generated for the guard
// itself. If the condition is only used by the guard, we
// must print it now otherwise it won't get printed. So
// we do print it now, with an explanatory comment. If
// the condition *is* used again we'll end up printing it
// twice, but that's ok.
outputf(" %s # codegen'd with the %s",
names->formatIns(ins->oprnd1()), lirNames[op]);
} else if (ins->isop(LIR_cmov) || ins->isop(LIR_qcmov)) {
// Likewise for cmov conditions.
outputf(" %s # codegen'd with the %s",
names->formatIns(ins->oprnd1()), lirNames[op]);
} else if (ins->isop(LIR_mod)) {
// There's a similar case when a div feeds into a mod.
outputf(" %s # codegen'd with the mod",
names->formatIns(ins->oprnd1()));
}
}
#endif
@ -1385,7 +1410,7 @@ namespace nanojit
underrunProtect(si->count * sizeof(NIns*) + 20);
_nIns = reinterpret_cast<NIns*>(uintptr_t(_nIns) & ~(sizeof(NIns*) - 1));
for (uint32_t i = 0; i < si->count; ++i) {
_nIns = (NIns*) (((uint8*) _nIns) - sizeof(NIns*));
_nIns = (NIns*) (((intptr_t) _nIns) - sizeof(NIns*));
*(NIns**) _nIns = target;
}
si->table = (NIns**) _nIns;
@ -1437,13 +1462,19 @@ namespace nanojit
else
findRegFor(op1, i->isop(LIR_flive) ? FpRegs : GpRegs);
}
// clear this list since we have now dealt with those lifetimes. extending
// their lifetimes again later (earlier in the code) serves no purpose.
pending_lives.clear();
}
void Assembler::arFree(uint32_t idx)
{
verbose_only( printActivationState(" >FP"); )
AR &ar = _activation;
LIns *i = ar.entry[idx];
NanoAssert(i != 0);
do {
ar.entry[idx] = 0;
idx--;
@ -1451,52 +1482,44 @@ namespace nanojit
}
#ifdef NJ_VERBOSE
void Assembler::printActivationState()
void Assembler::printActivationState(const char* what)
{
bool verbose_activation = false;
if (!verbose_activation)
if (!(_logc->lcbits & LC_Activation))
return;
#ifdef NANOJIT_ARM
// @todo Why is there here?!? This routine should be indep. of platform
verbose_only(
if (_logc->lcbits & LC_Assembly) {
char* s = &outline[0];
memset(s, ' ', 51); s[51] = '\0';
s += strlen(s);
sprintf(s, " SP ");
s += strlen(s);
for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
LInsp ins = _activation.entry[i];
if (ins && ins !=_activation.entry[i+1]) {
sprintf(s, "%d(%s) ", 4*i, _thisfrag->lirbuf->names->formatRef(ins));
s += strlen(s);
}
}
output(&outline[0]);
}
)
#else
verbose_only(
char* s = &outline[0];
if (_logc->lcbits & LC_Assembly) {
memset(s, ' ', 51); s[51] = '\0';
s += strlen(s);
sprintf(s, " ebp ");
s += strlen(s);
VMPI_memset(s, ' ', 45); s[45] = '\0';
s += VMPI_strlen(s);
VMPI_sprintf(s, "%s", what);
s += VMPI_strlen(s);
for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
LInsp ins = _activation.entry[i];
int32_t max = _activation.tos < NJ_MAX_STACK_ENTRY ? _activation.tos : NJ_MAX_STACK_ENTRY;
for(int32_t i = _activation.lowwatermark; i < max; i++) {
LIns *ins = _activation.entry[i];
if (ins) {
sprintf(s, "%d(%s) ", -4*i,_thisfrag->lirbuf->names->formatRef(ins));
s += strlen(s);
const char* n = _thisfrag->lirbuf->names->formatRef(ins);
if (ins->isop(LIR_alloc)) {
int32_t count = ins->size()>>2;
VMPI_sprintf(s," %d-%d(%s)", 4*i, 4*(i+count-1), n);
count += i-1;
while (i < count) {
NanoAssert(_activation.entry[i] == ins);
i++;
}
}
else if (ins->isQuad()) {
VMPI_sprintf(s," %d+(%s)", 4*i, n);
NanoAssert(_activation.entry[i+1] == ins);
i++;
}
else {
VMPI_sprintf(s," %d(%s)", 4*i, n);
}
}
s += VMPI_strlen(s);
}
output(&outline[0]);
}
)
#endif
}
#endif
bool canfit(int32_t size, int32_t loc, AR &ar) {
@ -1515,6 +1538,7 @@ namespace nanojit
int32_t start = ar.lowwatermark;
int32_t i = 0;
NanoAssert(start>0);
verbose_only( printActivationState(" <FP"); )
if (size == 1) {
// easy most common case -- find a hole, or make the frame bigger
@ -1811,7 +1835,7 @@ namespace nanojit
// Add the EOL string to the output, ensuring that we leave enough
// space for the terminating NULL character, then reset it so it
// doesn't repeat on the next outputf.
strncat(outline, outlineEOL, sizeof(outline)-(outline_len+1));
VMPI_strncat(outline, outlineEOL, sizeof(outline)-(outline_len+1));
outlineEOL[0] = '\0';
output(outline);
@ -1822,7 +1846,7 @@ namespace nanojit
if (_outputCache)
{
char* str = new (alloc) char[VMPI_strlen(s)+1];
strcpy(str, s);
VMPI_strcpy(str, s);
_outputCache->insert(str);
}
else
@ -1839,7 +1863,7 @@ namespace nanojit
// Add the EOL string to the output, ensuring that we leave enough
// space for the terminating NULL character, then reset it so it
// doesn't repeat on the next outputf.
strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1));
VMPI_strncat(outline, outlineEOL, sizeof(outline)-(strlen(outline)+1));
outlineEOL[0] = '\0';
output(s);
@ -1847,13 +1871,13 @@ namespace nanojit
char* Assembler::outputAlign(char *s, int col)
{
int len = strlen(s);
int len = (int)VMPI_strlen(s);
int add = ((col-len)>0) ? col-len : 1;
memset(&s[len], ' ', add);
VMPI_memset(&s[len], ' ', add);
s[col] = '\0';
return &s[col];
}
#endif // verbose
#endif // NJ_VERBOSE
uint32_t CallInfo::_count_args(uint32_t mask) const
{
@ -1893,3 +1917,4 @@ namespace nanojit
return labels.get(label);
}
}
#endif /* FEATURE_NANOJIT */

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

@ -157,7 +157,7 @@ namespace nanojit
void output_asm(const char* s);
bool outputAddr, vpad[3]; // if outputAddr=true then next asm instr. will include address in output
void printActivationState();
void printActivationState(const char* what);
StringList* _outputCache;
@ -176,13 +176,11 @@ namespace nanojit
void beginAssembly(Fragment *frag);
void releaseRegisters();
void patch(GuardRecord *lr);
void patch(SideExit *exit);
#ifdef NANOJIT_IA32
void patch(SideExit *exit, SwitchInfo* si);
#endif
AssmError error() { return _err; }
void setError(AssmError e) { _err = e; }
@ -244,7 +242,7 @@ namespace nanojit
Allocator& alloc;
CodeAlloc& _codeAlloc;
DWB(Fragment*) _thisfrag;
Fragment* _thisfrag;
RegAllocMap _branchStateMap;
NInsMap _patches;
LabelStateMap _labels;
@ -275,7 +273,6 @@ namespace nanojit
void asm_spilli(LInsp i, bool pop);
void asm_spill(Register rr, int d, bool pop, bool quad);
void asm_load64(LInsp i);
void asm_pusharg(LInsp p);
void asm_ret(LInsp p);
void asm_quad(LInsp i);
void asm_fcond(LInsp i);
@ -286,7 +283,6 @@ namespace nanojit
void asm_cmov(LInsp i);
void asm_param(LInsp i);
void asm_int(LInsp i);
void asm_short(LInsp i);
void asm_qlo(LInsp i);
void asm_qhi(LInsp i);
void asm_fneg(LInsp ins);
@ -297,7 +293,6 @@ namespace nanojit
Register asm_prep_fcall(Reservation *rR, LInsp ins);
void asm_nongp_copy(Register r, Register s);
void asm_call(LInsp);
void asm_arg(ArgSize, LInsp, Register);
Register asm_binop_rhs_reg(LInsp ins);
NIns* asm_branch(bool branchOnFalse, LInsp cond, NIns* targ);
void asm_switch(LIns* ins, NIns* target);

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

@ -84,8 +84,9 @@ namespace nanojit
}
CodeList* CodeAlloc::firstBlock(CodeList* term) {
char* end = (char*)alignUp(term, bytesPerPage);
return (CodeList*) (end - bytesPerAlloc);
// use uintptr_t, rather than char*, to avoid "increases required alignment" warning
uintptr_t end = (uintptr_t)alignUp(term, bytesPerPage);
return (CodeList*) (end - (uintptr_t)bytesPerAlloc);
}
int round(size_t x) {
@ -166,7 +167,7 @@ namespace nanojit
CodeList *coalescedBlock = blk->higher;
if ( coalescedBlock->size() >= minAllocSize ) {
// Unlink higher from the available block chain.
// Unlink coalescedBlock from the available block chain.
if ( availblocks == coalescedBlock ) {
removeBlock(availblocks);
}
@ -183,7 +184,7 @@ namespace nanojit
}
}
// combine blk->higher into blk (destroy blk->higher)
// combine blk->higher into blk (destroy coalescedBlock)
blk->higher = higher;
higher->lower = blk;
}
@ -299,12 +300,20 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len);
}
#elif defined AVMPLUS_UNIX
#ifdef ANDROID
void CodeAlloc::flushICache(CodeList* &blocks) {
for (CodeList *b = blocks; b != 0; b = b->next) {
cacheflush((int)b->start(), (int)b->start()+b->size(), 0);
}
}
#else
// fixme: __clear_cache is a libgcc feature, test for libgcc or gcc
void CodeAlloc::flushICache(CodeList* &blocks) {
for (CodeList *b = blocks; b != 0; b = b->next) {
__clear_cache((char*)b->start(), (char*)b->start()+b->size());
}
}
#endif
#endif // AVMPLUS_MAC && NANOJIT_PPC
void CodeAlloc::addBlock(CodeList* &blocks, CodeList* b) {

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

@ -42,12 +42,9 @@
namespace nanojit
{
// Temporary tracemonkey hack until namespaces are sorted out.
using namespace MMgc;
/** return true if ptr is in the range [start, end) */
/** return true if ptr is in the range [start, end] */
inline bool containsPtr(const NIns* start, const NIns* end, const NIns* ptr) {
return ptr >= start && ptr < end;
return ptr >= start && ptr <= end;
}
/**

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

@ -39,6 +39,8 @@
#include "nanojit.h"
#ifdef FEATURE_NANOJIT
namespace nanojit
{
BitSet::BitSet(Allocator& allocator, int nbits)
@ -89,3 +91,5 @@ namespace nanojit
bits = bits2;
}
}
#endif // FEATURE_NANOJIT

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

@ -279,6 +279,7 @@ namespace nanojit
, nbuckets(nbuckets)
, buckets(new (a) Seq<Node>*[nbuckets])
{
NanoAssert(nbuckets > 0);
clear();
}

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

@ -40,7 +40,6 @@
* ***** END LICENSE BLOCK ***** */
#include "nanojit.h"
#undef MEMORY_INFO
namespace nanojit
{

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

@ -38,13 +38,6 @@
* ***** END LICENSE BLOCK ***** */
#include "nanojit.h"
#include <stdio.h>
#include <ctype.h>
#ifdef PERFM
#include "../vprof/vprof.h"
#endif /* PERFM */
namespace nanojit
{
@ -114,7 +107,7 @@ namespace nanojit
names(NULL),
#endif
abi(ABI_FASTCALL), state(NULL), param1(NULL), sp(NULL), rp(NULL),
_allocator(alloc), _bytesAllocated(0)
_allocator(alloc)
{
clear();
}
@ -253,10 +246,10 @@ namespace nanojit
return ins;
}
LInsp LirBufWriter::insGuard(LOpcode op, LInsp c, LInsp data)
LInsp LirBufWriter::insGuard(LOpcode op, LInsp c, GuardRecord *gr)
{
debug_only( if (LIR_x == op || LIR_xbarrier == op) NanoAssert(!c); )
return ins2(op, c, data);
return ins2(op, c, (LIns*)gr);
}
LInsp LirBufWriter::insBranch(LOpcode op, LInsp condition, LInsp toLabel)
@ -785,7 +778,7 @@ namespace nanojit
return out->ins3(v, oprnd1, oprnd2, oprnd3);
}
LIns* ExprFilter::insGuard(LOpcode v, LInsp c, LInsp x)
LIns* ExprFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr)
{
if (v == LIR_xt || v == LIR_xf) {
if (c->isconst()) {
@ -801,7 +794,7 @@ namespace nanojit
// so assert in debug builds.
NanoAssertMsg(0, "Constantly false guard detected");
#endif
return out->insGuard(LIR_x, NULL, x);
return out->insGuard(LIR_x, NULL, gr);
}
}
else {
@ -814,7 +807,7 @@ namespace nanojit
}
}
}
return out->insGuard(v, c, x);
return out->insGuard(v, c, gr);
}
LIns* ExprFilter::insBranch(LOpcode v, LIns *c, LIns *t)
@ -834,6 +827,19 @@ namespace nanojit
return out->insBranch(v, c, t);
}
LIns* ExprFilter::insLoad(LOpcode op, LIns* base, int32_t off) {
if (base->isconstp() && !isS8(off)) {
// if the effective address is constant, then transform:
// ld const[bigconst] => ld (const+bigconst)[0]
// note: we don't do this optimization for <8bit field offsets,
// under the assumption that we're more likely to CSE-match the
// constant base address if we dont const-fold small offsets.
uintptr_t p = (uintptr_t)base->constvalp() + off;
return out->insLoad(op, insImmPtr((void*)p), 0);
}
return out->insLoad(op, base, off);
}
LIns* LirWriter::ins_eq0(LIns* oprnd1)
{
return ins2i(LIR_eq, oprnd1, 0);
@ -1146,7 +1152,6 @@ namespace nanojit
NanoAssert(i->isLInsOp3());
return hash3(op, i->oprnd1(), i->oprnd2(), i->oprnd3());
}
NanoAssert(0);
}
inline bool LInsHashSet::equals(LInsp a, LInsp b)
@ -1188,7 +1193,6 @@ namespace nanojit
NanoAssert(a->isLInsOp3());
return a->oprnd1() == b->oprnd1() && a->oprnd2() == b->oprnd2() && a->oprnd3() == b->oprnd3();
}
NanoAssert(0);
}
void LInsHashSet::grow()
@ -1444,7 +1448,7 @@ namespace nanojit
total++;
// first handle side-effect instructions
if (!i->isCse())
if (i->isStmt())
{
live.add(i,0);
if (i->isGuard())
@ -1531,7 +1535,7 @@ namespace nanojit
void LirNameMap::copyName(LInsp i, const char *s, int suffix) {
char s2[200];
if (isdigit(s[VMPI_strlen(s)-1])) {
if (VMPI_isdigit(s[VMPI_strlen(s)-1])) {
// if s ends with a digit, add '_' to clarify the suffix
VMPI_sprintf(s2,"%s_%d", s, suffix);
} else {
@ -1704,7 +1708,7 @@ namespace nanojit
formatGuard(i, s);
break;
case LIR_add:
case LIR_add: case LIR_qiadd:
case LIR_iaddp: case LIR_qaddp:
case LIR_sub:
case LIR_mul:
@ -1713,10 +1717,10 @@ namespace nanojit
case LIR_fsub:
case LIR_fmul:
case LIR_fdiv:
case LIR_and:
case LIR_or:
case LIR_and: case LIR_qiand:
case LIR_or: case LIR_qior:
case LIR_xor: case LIR_qxor:
case LIR_lsh:
case LIR_lsh: case LIR_qilsh:
case LIR_rsh: case LIR_qirsh:
case LIR_ush: case LIR_qursh:
case LIR_eq: case LIR_qeq:
@ -1733,10 +1737,6 @@ namespace nanojit
case LIR_fle:
case LIR_fgt:
case LIR_fge:
case LIR_qiadd:
case LIR_qiand:
case LIR_qilsh:
case LIR_qior:
VMPI_sprintf(s, "%s = %s %s, %s", formatRef(i), lirNames[op],
formatRef(i->oprnd1()),
formatRef(i->oprnd2()));
@ -1876,7 +1876,7 @@ namespace nanojit
return out->insLoad(v,base,disp);
}
LInsp CseFilter::insGuard(LOpcode v, LInsp c, LInsp x)
LInsp CseFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr)
{
// LIR_xt and LIR_xf guards are CSEable. Note that we compare the
// opcode and condition when determining if two guards are equivalent
@ -1902,9 +1902,9 @@ namespace nanojit
LInsp found = exprs.find1(v, c, k);
if (found)
return 0;
return exprs.add(out->insGuard(v,c,x), k);
return exprs.add(out->insGuard(v,c,gr), k);
}
return out->insGuard(v, c, x);
return out->insGuard(v, c, gr);
}
LInsp CseFilter::insCall(const CallInfo *ci, LInsp args[])
@ -1920,7 +1920,7 @@ namespace nanojit
return out->insCall(ci, args);
}
void compile(Assembler* assm, Fragment* frag, Allocator& alloc verbose_only(, LabelMap* labels))
void compile(Assembler* assm, Fragment* frag verbose_only(, Allocator& alloc, LabelMap* labels))
{
verbose_only(
LogControl *logc = assm->_logc;

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

@ -51,8 +51,6 @@
*/
namespace nanojit
{
using namespace MMgc;
enum LOpcode
#if defined(_MSC_VER) && _MSC_VER >= 1400
#pragma warning(disable:4480) // nonstandard extension used: specifying underlying type for enum
@ -930,7 +928,7 @@ namespace nanojit
GuardRecord *LIns::record() const {
NanoAssert(isGuard());
return (GuardRecord*)oprnd2()->payload();
return (GuardRecord*)oprnd2();
}
int32_t LIns::disp() const {
@ -1012,15 +1010,14 @@ namespace nanojit
return toLInsC()->ci;
}
// make it a GCObject so we can explicitly delete it early
class LirWriter : public GCObject
class LirWriter
{
public:
LirWriter *out;
virtual ~LirWriter() {}
LirWriter(LirWriter* out)
: out(out) {}
virtual ~LirWriter() {}
virtual LInsp ins0(LOpcode v) {
return out->ins0(v);
@ -1034,8 +1031,8 @@ namespace nanojit
virtual LInsp ins3(LOpcode v, LIns* a, LIns* b, LIns* c) {
return out->ins3(v, a, b, c);
}
virtual LInsp insGuard(LOpcode v, LIns *c, LIns *x) {
return out->insGuard(v, c, x);
virtual LInsp insGuard(LOpcode v, LIns *c, GuardRecord *gr) {
return out->insGuard(v, c, gr);
}
virtual LInsp insBranch(LOpcode v, LInsp condition, LInsp to) {
return out->insBranch(v, condition, to);
@ -1070,6 +1067,13 @@ namespace nanojit
virtual LInsp insSkip(size_t size) {
return out->insSkip(size);
}
void insAssert(LIns* expr) {
#if defined DEBUG
LIns* branch = insBranch(LIR_jt, expr, NULL);
ins0(LIR_dbreak);
branch->setTarget(ins0(LIR_label));
#endif
}
// convenience functions
@ -1203,8 +1207,8 @@ namespace nanojit
}
}
LIns* insGuard(LOpcode op, LInsp cond, LIns *x) {
return add_flush(out->insGuard(op,cond,x));
LIns* insGuard(LOpcode op, LInsp cond, GuardRecord *gr) {
return add_flush(out->insGuard(op,cond,gr));
}
LIns* insBranch(LOpcode v, LInsp condition, LInsp to) {
@ -1262,8 +1266,9 @@ namespace nanojit
LIns* ins1(LOpcode v, LIns* a);
LIns* ins2(LOpcode v, LIns* a, LIns* b);
LIns* ins3(LOpcode v, LIns* a, LIns* b, LIns* c);
LIns* insGuard(LOpcode, LIns *cond, LIns *);
LIns* insGuard(LOpcode, LIns *cond, GuardRecord *);
LIns* insBranch(LOpcode, LIns *cond, LIns *target);
LIns* insLoad(LOpcode op, LInsp base, int32_t off);
};
// @todo, this could be replaced by a generic HashMap or HashSet, if we had one
@ -1319,7 +1324,7 @@ namespace nanojit
LIns* ins3(LOpcode v, LInsp, LInsp, LInsp);
LIns* insLoad(LOpcode op, LInsp cond, int32_t d);
LIns* insCall(const CallInfo *call, LInsp args[]);
LIns* insGuard(LOpcode op, LInsp cond, LIns *x);
LIns* insGuard(LOpcode op, LInsp cond, GuardRecord *gr);
};
class LirBuffer
@ -1395,7 +1400,7 @@ namespace nanojit
LInsp insImmq(uint64_t imm);
LInsp insImmf(double d);
LInsp insCall(const CallInfo *call, LInsp args[]);
LInsp insGuard(LOpcode op, LInsp cond, LIns *x);
LInsp insGuard(LOpcode op, LInsp cond, GuardRecord *gr);
LInsp insBranch(LOpcode v, LInsp condition, LInsp to);
LInsp insAlloc(int32_t size);
LInsp insSkip(size_t);
@ -1432,14 +1437,11 @@ namespace nanojit
LInsp pos() {
return _i;
}
void setpos(LIns *i) {
_i = i;
}
};
class Assembler;
void compile(Assembler *assm, Fragment *frag, Allocator& alloc verbose_only(, LabelMap*));
void compile(Assembler *assm, Fragment *frag verbose_only(, Allocator& alloc, LabelMap*));
verbose_only(void live(Allocator& alloc, Fragment *frag, LirBuffer *lirbuf);)
class StackFilter: public LirFilter

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

@ -74,7 +74,7 @@
OPDEF(start, 0, 0, Op0) // start of a fragment
OPDEF(regfence, 1, 0, Op0) // register fence, no register allocation is allowed across this meta instruction
OPDEF(skip, 2, 1, Sk) // holds blobs ("payloads") of data; also links pages
OPDEF(unused3, 3,-1, None)
OPDEF(dbreak, 3, 0, Op0)
OPDEF(unused4, 4,-1, None)
OPDEF(unused5, 5,-1, None)
OPDEF(unused6, 6,-1, None)
@ -223,6 +223,7 @@ OPDEF64(i2q, 26, 1, Op1) // sign-extend i32 to i64
OPDEF64(u2q, 27, 1, Op1) // zero-extend u32 to u64
OPDEF64(i2f, 28, 1, Op1) // convert a signed 32-bit integer to a float
OPDEF64(u2f, 29, 1, Op1) // convert an unsigned 32-bit integer to a float
OPDEF64(unused30_64, 30,-1, None)
OPDEF64(unused31_64, 31,-1, None)
OPDEF64(unused32_64, 32,-1, None)

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

@ -59,7 +59,7 @@
#elif defined(NANOJIT_ARM)
#include "NativeARM.h"
#elif defined(NANOJIT_PPC)
#include "NativePpc.h"
#include "NativePPC.h"
#elif defined(NANOJIT_SPARC)
#include "NativeSparc.h"
#elif defined(NANOJIT_X64)
@ -69,8 +69,6 @@
#endif
namespace nanojit {
const size_t NJ_PAGE_SIZE = 1 << NJ_LOG2_PAGE_SIZE;
class Fragment;
struct SideExit;
struct SwitchInfo;
@ -121,9 +119,9 @@ namespace nanojit {
if (_logc->lcbits & LC_Assembly) { \
outline[0]='\0'; \
if (outputAddr) \
sprintf(outline, "%010lx ", (unsigned long)_nIns); \
VMPI_sprintf(outline, "%010lx ", (unsigned long)_nIns); \
else \
memset(outline, (int)' ', 10+3); \
VMPI_memset(outline, (int)' ', 10+3); \
sprintf(&outline[13], ##__VA_ARGS__); \
Assembler::outputAlign(outline, 35); \
_allocator.formatRegisters(outline, _thisfrag); \

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

@ -554,18 +554,6 @@ Assembler::genEpilogue()
return _nIns;
}
/*
* This should never be called; ARM only uses the longer form.
* TODO: We should delete this as it is never called from outside this file. It
* should be declared in the DECLARE_PLATFORM_ASSEMBLER block of each native
* back-end where required.
*/
void
Assembler::asm_arg(ArgSize sz, LInsp p, Register r)
{
NanoAssert(0);
}
/*
* asm_arg will encode the specified argument according to the current ABI, and
* will update r and stkd as appropriate so that the next argument can be

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

@ -56,9 +56,6 @@
namespace nanojit
{
const int NJ_LOG2_PAGE_SIZE = 12; // 4K
// only d0-d6 are actually used; we'll use d7 as s14-s15 for i2f/u2f/etc.
#define NJ_VFP_MAX_REGISTERS 8
#define NJ_MAX_REGISTERS (11 + NJ_VFP_MAX_REGISTERS)
@ -93,7 +90,7 @@ typedef enum {
LR = 14,
PC = 15,
// FP regs
// VFP regs (we currently only use D0-D6 and S14)
D0 = 16,
D1 = 17,
D2 = 18,
@ -105,8 +102,8 @@ typedef enum {
// D7 is still listed here for completeness and to facilitate assertions.
D7 = 23,
FirstFloatReg = 16,
LastFloatReg = 22,
FirstFloatReg = D0,
LastFloatReg = D6,
FirstReg = 0,
LastReg = 22, // This excludes D7 from the register allocator.
@ -461,6 +458,7 @@ enum {
// _d = 0 - _r
#define RSBS(_d,_r) ALUi(AL, rsb, 1, _d, _r, 0)
// MVN
// _d = ~_r (one's compliment)
#define MVN(_d,_r) ALUr(AL, mvn, 0, _d, 0, _r)
#define MVNis_chk(_d,_op2imm,_stat,_chk) ALUi_chk(AL, mvn, _stat, _d, 0, op2imm, _chk)

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

@ -142,9 +142,8 @@ namespace nanojit
void Assembler::asm_ld(LIns *ins) {
LIns* base = ins->oprnd1();
LIns* disp = ins->oprnd2();
int d = ins->disp();
Register rr = prepResultReg(ins, GpRegs);
int d = disp->constval();
Register ra = getBaseReg(base, d, GpRegs);
#if !PEDANTIC
@ -197,7 +196,7 @@ namespace nanojit
Register rr = prepResultReg(ins, FpRegs);
#endif
int dr = ins->oprnd2()->constval();
int dr = ins->disp();
Register ra = getBaseReg(base, dr, GpRegs);
#ifdef NANOJIT_64BIT
@ -265,7 +264,7 @@ namespace nanojit
#if !PEDANTIC && !defined NANOJIT_64BIT
if (value->isop(LIR_quad) && isS16(dr) && isS16(dr+4)) {
// quad constant and short offset
uint64_t q = value->constvalq();
uint64_t q = value->imm64();
STW(R0, dr, ra); // hi
asm_li(R0, int32_t(q>>32)); // hi
STW(R0, dr+4, ra); // lo
@ -507,7 +506,7 @@ namespace nanojit
#if !PEDANTIC
if (b->isconst()) {
int32_t d = b->constval();
int32_t d = b->imm32();
if (isS16(d)) {
if (condop >= LIR_eq && condop <= LIR_ge) {
CMPWI(cr, ra, d);
@ -562,7 +561,7 @@ namespace nanojit
void Assembler::asm_ret(LIns *ins) {
genEpilogue();
assignSavedParams();
assignSavedRegs();
LIns *value = ins->oprnd1();
Register r = ins->isop(LIR_ret) ? R3 : F1;
findSpecificRegFor(value, r);
@ -582,9 +581,9 @@ namespace nanojit
}
else if (i->isconst()) {
if (!resv->arIndex) {
reserveFree(i);
i->resv()->clear();
}
asm_li(r, i->constval());
asm_li(r, i->imm32());
}
else {
d = findMemFor(i);
@ -596,10 +595,9 @@ namespace nanojit
} else {
LWZ(r, d, FP);
}
verbose_only(
if (_verbose)
outputf(" restore %s",_thisfrag->lirbuf->names->formatRef(i));
)
verbose_only( if (_logc->lcbits & LC_RegAlloc) {
outputForEOL(" <= restore %s",
_thisfrag->lirbuf->names->formatRef(i)); } )
}
}
@ -609,13 +607,7 @@ namespace nanojit
void Assembler::asm_int(LIns *ins) {
Register rr = prepResultReg(ins, GpRegs);
asm_li(rr, ins->constval());
}
void Assembler::asm_short(LIns *ins) {
int32_t val = ins->imm16();
Register rr = prepResultReg(ins, GpRegs);
LI(rr, val);
asm_li(rr, ins->imm32());
}
void Assembler::asm_fneg(LIns *ins) {
@ -625,8 +617,8 @@ namespace nanojit
}
void Assembler::asm_param(LIns *ins) {
uint32_t a = ins->imm8();
uint32_t kind = ins->imm8b();
uint32_t a = ins->paramArg();
uint32_t kind = ins->paramKind();
if (kind == 0) {
// ordinary param
// first eight args always in R3..R10 for PPC
@ -652,7 +644,7 @@ namespace nanojit
bool indirect;
if (!(indirect = call->isIndirect())) {
verbose_only(if (_verbose)
verbose_only(if (_logc->lcbits & LC_Assembly)
outputf(" %p:", _nIns);
)
br((NIns*)call->_address, 1);
@ -723,7 +715,7 @@ namespace nanojit
#endif
// arg goes in specific register
if (p->isconst()) {
asm_li(r, p->constval());
asm_li(r, p->imm32());
} else {
Reservation* rA = getresv(p);
if (rA) {
@ -802,7 +794,7 @@ namespace nanojit
Register ra = findRegFor(lhs, GpRegs);
if (rhs->isconst()) {
int32_t rhsc = rhs->constval();
int32_t rhsc = rhs->imm32();
if (isS16(rhsc)) {
// ppc arith immediate ops sign-exted the imm16 value
switch (op) {
@ -1005,7 +997,7 @@ namespace nanojit
int32_t hi, lo;
} w;
};
d = ins->constvalf();
d = ins->imm64f();
LFD(r, 12, SP);
STW(R0, 12, SP);
asm_li(R0, w.hi);
@ -1013,7 +1005,7 @@ namespace nanojit
asm_li(R0, w.lo);
}
else {
int64_t q = ins->constvalq();
int64_t q = ins->imm64();
if (isS32(q)) {
asm_li(r, int32_t(q));
return;
@ -1094,7 +1086,7 @@ namespace nanojit
#endif
if (pc - instr - br_size < top) {
// really do need a page break
verbose_only(if (_verbose) outputf("newpage %p:", pc);)
verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);)
codeAlloc();
}
// now emit the jump, but make sure we won't need another page break.
@ -1105,7 +1097,7 @@ namespace nanojit
}
#else
if (pc - instr < top) {
verbose_only(if (_verbose) outputf("newpage %p:", pc);)
verbose_only(if (_logc->lcbits & LC_Assembly) outputf("newpage %p:", pc);)
codeAlloc();
// this jump will call underrunProtect again, but since we're on a new
// page, nothing will happen.
@ -1117,16 +1109,18 @@ namespace nanojit
void Assembler::asm_cmov(LIns *ins) {
NanoAssert(ins->isop(LIR_cmov) || ins->isop(LIR_qcmov));
LIns* cond = ins->oprnd1();
NanoAssert(cond->isCmp());
LIns* iftrue = ins->oprnd2();
LIns* iffalse = ins->oprnd3();
NanoAssert(cond->isCmp());
NanoAssert(iftrue->isQuad() == iffalse->isQuad());
// fixme: we could handle fpu registers here, too, since we're just branching
Register rr = prepResultReg(ins, GpRegs);
findSpecificRegFor(iftrue, rr);
Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr));
NIns *after = _nIns;
verbose_only(if (_verbose) outputf("%p:",after);)
verbose_only(if (_logc->lcbits & LC_Assembly) outputf("%p:",after);)
MR(rr, rf);
asm_branch(false, cond, after);
}
@ -1138,9 +1132,9 @@ namespace nanojit
prefer = rmask(R3);
else if (op == LIR_fcall)
prefer = rmask(F1);
else if (op == LIR_iparam) {
if (i->imm8() < 8) {
prefer = rmask(argRegs[i->imm8()]);
else if (op == LIR_param) {
if (i->paramArg() < 8) {
prefer = rmask(argRegs[i->paramArg()]);
}
}
// narrow the allow set to whatever is preferred and also free
@ -1297,12 +1291,6 @@ namespace nanojit
void Assembler::nFragExit(LIns*) {
TODO(nFragExit);
}
NIns* Assembler::asm_adjustBranch(NIns*, NIns*) {
TODO(asm_adjustBranch);
return 0;
}
} // namespace nanojit
#endif // FEATURE_NANOJIT && NANOJIT_PPC

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

@ -54,8 +54,6 @@
namespace nanojit
{
const int NJ_LOG2_PAGE_SIZE = 12; // 4K
#define NJ_MAX_STACK_ENTRY 256
#define NJ_ALIGN_STACK 16

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

@ -1,6 +1,5 @@
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -66,10 +65,9 @@ namespace nanojit
static const int kLinkageAreaSize = 68;
static const int kcalleeAreaSize = 80; // The max size.
static const int NJ_PAGE_SIZE_SPARC = 8192; // Use sparc page size here.
#define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0)
#define BIT_ROUND_UP(v,q) ( (((uintptr_t)v)+(q)-1) & ~((q)-1) )
#define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0)
void Assembler::nInit(AvmCore* core)
{
@ -97,8 +95,11 @@ namespace nanojit
SETHI(frameSize, G1);
}
verbose_only( verbose_outputf(" %p:",_nIns); )
verbose_only( asm_output(" patch entry:"); )
verbose_only(
if (_logc->lcbits & LC_Assembly) {
outputf(" %p:",_nIns);
output(" patch entry:");
})
NIns *patchEntry = _nIns;
// The frame size in SAVE is faked. We will still re-caculate SP later.
@ -160,11 +161,19 @@ namespace nanojit
ArgSize sizes[MAXARGS];
uint32_t argc = call->get_sizes(sizes);
NanoAssert(ins->isop(LIR_call) || ins->isop(LIR_fcall));
NanoAssert(ins->isop(LIR_pcall) || ins->isop(LIR_fcall));
verbose_only(if (_logc->lcbits & LC_Assembly)
outputf(" %p:", _nIns);
)
bool indirect = call->isIndirect();
if (!indirect) {
CALL(call);
}
else {
argc--;
Register r = findSpecificRegFor(ins->arg(argc), I0);
JMPL(G0, I0, 15);
}
uint32_t GPRIndex = O0;
uint32_t offset = kLinkageAreaSize; // start of parameters stack postion.
@ -521,7 +530,7 @@ namespace nanojit
LOpcode condop = cond->opcode();
// LIR_ov recycles the flags set by arithmetic ops
if ((condop == LIR_ov))
if (condop == LIR_ov)
return;
LInsp lhs = cond->oprnd1();
@ -983,18 +992,12 @@ namespace nanojit
FCMPD(rLhs, rRhs);
}
/** no longer called by patch/unpatch
NIns* Assembler::asm_adjustBranch(NIns* at, NIns* target)
verbose_only(
void Assembler::asm_inc_m32(uint32_t* pCtr)
{
NIns* was;
was = (NIns*)(((*(uint32_t*)&at[0] & 0x3FFFFF) << 10) | (*(uint32_t*)&at[1] & 0x3FF ));
*(uint32_t*)&at[0] &= 0xFFC00000;
*(uint32_t*)&at[0] |= ((intptr_t)target >> 10) & 0x3FFFFF;
*(uint32_t*)&at[1] &= 0xFFFFFC00;
*(uint32_t*)&at[1] |= (intptr_t)target & 0x3FF;
return was;
// TODO(asm_inc_m32);
}
*/
)
void Assembler::nativePageReset()
{
@ -1007,8 +1010,10 @@ namespace nanojit
void Assembler::nativePageSetup()
{
if (!_nIns) codeAlloc(codeStart, codeEnd, _nIns);
if (!_nExitIns) codeAlloc(exitStart, exitEnd, _nExitIns);
if (!_nIns)
codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
if (!_nExitIns)
codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes));
}
void
@ -1019,9 +1024,9 @@ namespace nanojit
// We are done with the current page. Tell Valgrind that new code
// has been generated.
if (_inExit)
codeAlloc(exitStart, exitEnd, _nIns);
codeAlloc(exitStart, exitEnd, _nIns verbose_only(, exitBytes));
else
codeAlloc(codeStart, codeEnd, _nIns);
codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
JMP_long_nocheck((intptr_t)eip);
}
}

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

@ -67,11 +67,6 @@
namespace nanojit
{
#ifdef MMGC_SPARC
const int NJ_LOG2_PAGE_SIZE = 12; // 4K
#else
const int NJ_LOG2_PAGE_SIZE = 13; // 8K
#endif
const int NJ_MAX_REGISTERS = 30; // L0 - L7, I0 - I5, F2 - F14
const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect

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

@ -357,7 +357,7 @@ namespace nanojit
case LIR_rsh: xop = X64_sari; break;
case LIR_lsh: xop = X64_shli; break;
}
int shift = ins->oprnd2()->imm32() & 255;
int shift = ins->oprnd2()->imm32() & 63;
emit8(rexrb(xop | uint64_t(rr&7)<<48, (Register)0, rr), shift);
if (rr != ra)
MR(rr, ra);
@ -423,14 +423,53 @@ namespace nanojit
MR(rr, ra);
}
void Assembler::asm_div_mod(LIns *ins) {
LIns *div;
if (ins->opcode() == LIR_mod) {
// LIR_mod expects the LIR_div to be near
div = ins->oprnd1();
prepResultReg(ins, rmask(RDX));
} else {
div = ins;
evictIfActive(RDX);
}
NanoAssert(div->isop(LIR_div));
LIns *lhs = div->oprnd1();
LIns *rhs = div->oprnd2();
prepResultReg(div, rmask(RAX));
Register rhsReg = findRegFor(rhs, (GpRegs ^ (rmask(RAX)|rmask(RDX))));
Register lhsReg = lhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(lhs, RAX)
: lhs->getReg();
emitr(X64_idiv, rhsReg);
emit8(rexrb(X64_sari | uint64_t(RDX&7)<<48, (Register)0, RDX), 31);
MR(RDX, RAX);
if (RAX != lhsReg)
MR(RAX, lhsReg);
}
// binary op with integer registers
void Assembler::asm_arith(LIns *ins) {
Register rr, ra, rb;
LOpcode op = ins->opcode();
if ((op & ~LIR64) >= LIR_lsh && (op & ~LIR64) <= LIR_ush) {
switch (ins->opcode() & ~LIR64) {
case LIR_lsh:
case LIR_rsh:
case LIR_ush:
asm_shift(ins);
return;
case LIR_mod:
case LIR_div:
asm_div_mod(ins);
return;
default:
break;
}
LIns *b = ins->oprnd2();
if (isImm32(b)) {
asm_arith_imm(ins);

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

@ -58,7 +58,6 @@
namespace nanojit
{
const int NJ_LOG2_PAGE_SIZE = 12; // 4K
#define NJ_MAX_STACK_ENTRY 256
#define NJ_ALIGN_STACK 16
@ -194,6 +193,7 @@ namespace nanojit
X64_divsd = 0xC05E0F40F2000005LL, // divide scalar double r /= b
X64_mulsd = 0xC0590F40F2000005LL, // multiply scalar double r *= b
X64_addsd = 0xC0580F40F2000005LL, // add scalar double r += b
X64_idiv = 0xF8F7400000000003LL, // 32bit signed div (rax = rdx:rax/r, rdx=rdx:rax%r)
X64_imul = 0xC0AF0F4000000004LL, // 32bit signed mul r *= b
X64_imuli = 0xC069400000000003LL, // 32bit signed mul r = b * imm32
X64_imul8 = 0x00C06B4000000004LL, // 32bit signed mul r = b * imm8
@ -371,6 +371,7 @@ namespace nanojit
void asm_cmp_imm(LIns*);\
void fcmp(LIns*, LIns*);\
NIns* asm_fbranch(bool, LIns*, NIns*);\
void asm_div_mod(LIns *i);\
int max_stk_used;
#define swapptrs() { NIns* _tins = _nIns; _nIns=_nExitIns; _nExitIns=_tins; }

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

@ -38,6 +38,7 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nanojit.h"
#ifdef _MAC
// for MakeDataExecutable
@ -49,11 +50,10 @@
#include <errno.h>
#include <stdlib.h>
#endif
#include "nanojit.h"
namespace nanojit
{
#ifdef FEATURE_NANOJIT
#if defined FEATURE_NANOJIT && defined NANOJIT_IA32
#ifdef NJ_VERBOSE
const char *regNames[] = {
@ -80,7 +80,7 @@ namespace nanojit
void Assembler::nInit(AvmCore* core)
{
(void) core;
OSDep::getDate();
VMPI_getDate();
}
void Assembler::nBeginAssembly() {
@ -192,8 +192,9 @@ namespace nanojit
const int32_t pushsize = 4*istack + 8*fargs; // actual stack space used
#if _MSC_VER
// msc is slack, and MIR doesn't do anything extra, so lets use this
// call-site alignment to at least have code size parity with MIR.
// msc only provides 4-byte alignment, anything more than 4 on windows
// x86-32 requires dynamic ESP alignment in prolog/epilog and static
// esp-alignment here.
uint32_t align = 4;//NJ_ALIGN_STACK;
#else
uint32_t align = NJ_ALIGN_STACK;
@ -386,7 +387,7 @@ namespace nanojit
}
}
void Assembler::asm_restore(LInsp i, Reservation *unused, Register r)
void Assembler::asm_restore(LInsp i, Reservation* /*unused*/, Register r)
{
uint32_t arg;
uint32_t abi_regcount;
@ -405,10 +406,15 @@ namespace nanojit
else if (i->isop(LIR_param) && i->paramKind() == 0 &&
(arg = i->paramArg()) >= (abi_regcount = max_abi_regs[_thisfrag->lirbuf->abi])) {
// incoming arg is on stack, can restore it from there instead of spilling
NanoAssert(0);
if (!i->getArIndex()) {
i->markAsClear();
}
// compute position of argument relative to ebp. higher argument
// numbers are at higher positive offsets. the first abi_regcount
// arguments are in registers, rest on stack. +8 accomodates the
// return address and saved ebp value. assuming abi_regcount == 0:
// low-addr ebp
// [frame...][saved-ebp][return-addr][arg0][arg1]...
int d = (arg - abi_regcount) * sizeof(intptr_t) + 8;
LD(r, d, FP);
}
@ -586,7 +592,7 @@ namespace nanojit
if (rmask(rv) & XmmRegs) {
SSE_STQ(dr, rb, rv);
} else {
FSTQ(pop, dr, rb);
FSTQ(pop?1:0, dr, rb);
}
}
@ -686,6 +692,36 @@ namespace nanojit
JMP(exit);
}
// This generates a 'test' or 'cmp' instruction for a condition, which
// causes the condition codes to be set appropriately. It's used with
// conditional branches, conditional moves, and when generating
// conditional values. For example:
//
// LIR: eq1 = eq a, 0
// LIR: xf1: xf eq1 -> ...
// asm: test edx, edx # generated by this function
// asm: je ...
//
// If this is the only use of eq1, then on entry 'cond' is *not* marked as
// used, and we do not allocate a register for it. That's because its
// result ends up in the condition codes rather than a normal register.
// This doesn't get recorded in the regstate and so the asm code that
// consumes the result (eg. a conditional branch like 'je') must follow
// shortly after.
//
// If eq1 is instead used again later, we will also generate code
// (eg. in asm_cond()) to compute it into a normal register, something
// like this:
//
// LIR: eq1 = eq a, 0
// LIR: test edx, edx
// asm: sete ebx
// asm: movzx ebx, ebx
//
// In this case we end up computing the condition twice, but that's ok, as
// it's just as short as testing eq1's value in the code generated for the
// guard.
//
void Assembler::asm_cmp(LIns *cond)
{
LOpcode condop = cond->opcode();
@ -771,10 +807,7 @@ namespace nanojit
LInsp lhs = ins->oprnd1();
if (op == LIR_mod) {
/* LIR_mod expects the LIR_div to be near (no interference from the register allocator) */
findSpecificRegFor(lhs, EDX);
prepResultReg(ins, rmask(EDX));
evictIfActive(EAX);
asm_div_mod(ins);
return;
}
@ -786,6 +819,8 @@ namespace nanojit
switch (op) {
case LIR_div:
// Nb: if the div feeds into a mod it will be handled by
// asm_div_mod() rather than here.
forceReg = true;
rb = findRegFor(rhs, (GpRegs ^ (rmask(EAX)|rmask(EDX))));
allow = rmask(EAX);
@ -804,7 +839,7 @@ namespace nanojit
}
break;
case LIR_add:
case LIR_iaddp:
case LIR_addp:
if (lhs->isop(LIR_alloc) && rhs->isconst()) {
// add alloc+const, use lea
Register rr = prepResultReg(ins, allow);
@ -866,7 +901,6 @@ namespace nanojit
SHR(rr, rb);
break;
case LIR_div:
case LIR_mod:
DIV(rb);
CDQ();
break;
@ -917,6 +951,35 @@ namespace nanojit
MR(rr,ra);
}
// This is called when we have a mod(div(divLhs, divRhs)) sequence.
void Assembler::asm_div_mod(LInsp mod)
{
LInsp div = mod->oprnd1();
// LIR_mod expects the LIR_div to be near (no interference from the register allocator)
NanoAssert(mod->isop(LIR_mod));
NanoAssert(div->isop(LIR_div));
LInsp divLhs = div->oprnd1();
LInsp divRhs = div->oprnd2();
prepResultReg(mod, rmask(EDX));
prepResultReg(div, rmask(EAX));
Register rDivRhs = findRegFor(divRhs, (GpRegs ^ (rmask(EAX)|rmask(EDX))));
Register rDivLhs = ( divLhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(divLhs, EAX)
: divLhs->getReg() );
DIV(rDivRhs);
CDQ(); // sign-extend EAX into EDX:EAX
if ( EAX != rDivLhs )
MR(EAX, rDivLhs);
}
void Assembler::asm_neg_not(LInsp ins)
{
LOpcode op = ins->opcode();
@ -1105,6 +1168,13 @@ namespace nanojit
// 1.0 is extremely frequent and worth special-casing!
static const double k_ONE = 1.0;
LDSDm(rr, &k_ONE);
} else if (d && d == (int)d) {
// can fit in 32bits? then use cvt which is faster
Register gr = registerAlloc(GpRegs);
SSE_CVTSI2SD(rr, gr);
SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls
LDi(gr, (int)d);
_allocator.addFree(gr);
} else {
findMemFor(ins);
const int d = disp(ins);
@ -1337,10 +1407,9 @@ namespace nanojit
if (lhs->isUnusedOrHasUnknownReg()) {
ra = findSpecificRegFor(lhs, rr);
} else if ((rmask(lhs->getReg()) & XmmRegs) == 0) {
/* We need this case on AMD64, because it's possible that
* an earlier instruction has done a quadword load and reserved a
* GPR. If so, ask for a new register.
*/
// We need this case on AMD64, because it's possible that
// an earlier instruction has done a quadword load and reserved a
// GPR. If so, ask for a new register.
ra = findRegFor(lhs, XmmRegs);
} else {
// lhs already has a register assigned but maybe not from the allow set
@ -1402,6 +1471,7 @@ namespace nanojit
// todo support int value in memory
Register gr = findRegFor(ins->oprnd1(), GpRegs);
SSE_CVTSI2SD(rr, gr);
SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls
}
else
{
@ -1410,7 +1480,7 @@ namespace nanojit
}
}
Register Assembler::asm_prep_fcall(Reservation *unused, LInsp ins)
Register Assembler::asm_prep_fcall(Reservation* /*unused*/, LInsp ins)
{
Register rr;
if (ins->isUsed() && (rr = ins->getReg(), isKnownReg(rr)) && (rmask(rr) & XmmRegs)) {
@ -1451,6 +1521,7 @@ namespace nanojit
SSE_ADDSDm(rr, &k_NEGONE);
SSE_CVTSI2SD(rr, gr);
SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls
LIns* op1 = ins->oprnd1();
Register xr;
@ -1653,8 +1724,7 @@ namespace nanojit
)
void Assembler::nativePageReset()
{
}
{}
void Assembler::nativePageSetup()
{

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

@ -42,6 +42,7 @@
#define __nanojit_Nativei386__
#ifdef PERFM
#define DOPROF
#include "../vprof/vprof.h"
#define count_instr() _nvprof("x86",1)
#define count_ret() _nvprof("x86-ret",1); count_instr();
@ -90,7 +91,6 @@
namespace nanojit
{
const int NJ_LOG2_PAGE_SIZE = 12; // 4K
const int NJ_MAX_REGISTERS = 24; // gpregs, x87 regs, xmm regs
#define NJ_MAX_STACK_ENTRY 256
@ -174,8 +174,11 @@ namespace nanojit
void nativePageSetup();\
void underrunProtect(int);\
void asm_farg(LInsp);\
void asm_arg(ArgSize, LIns*, Register);\
void asm_pusharg(LInsp);\
void asm_fcmp(LIns *cond); \
void asm_cmp(LIns *cond); \
void asm_fcmp(LIns *cond);
void asm_div_mod(LIns *cond);
#define swapptrs() { NIns* _tins = _nIns; _nIns=_nExitIns; _nExitIns=_tins; }
@ -184,7 +187,7 @@ namespace nanojit
*((int32_t*)_nIns) = (int32_t)(i)
#define MODRMs(r,d,b,l,i) \
NanoAssert(unsigned(r)<8 && unsigned(b)<8 && unsigned(i)<8); \
NanoAssert(unsigned(i)<8 && unsigned(b)<8 && unsigned(r)<8); \
if ((d) == 0 && (b) != EBP) { \
_nIns -= 2; \
_nIns[0] = (uint8_t) ( 0<<6 | (r)<<3 | 4); \
@ -373,7 +376,7 @@ namespace nanojit
#define LEA(r,d,b) do { count_alu(); ALUm(0x8d, r,d,b); asm_output("lea %s,%d(%s)",gpn(r),d,gpn(b)); } while(0)
// lea %r, d(%i*4)
// This addressing mode is not supported by the MODRMSIB macro.
#define LEAmi4(r,d,i) do { count_alu(); IMM32(d); *(--_nIns) = (2<<6)|(i<<3)|5; *(--_nIns) = (0<<6)|(r<<3)|4; *(--_nIns) = 0x8d; asm_output("lea %s, %p(%s*4)", gpn(r), (void*)d, gpn(i)); } while(0)
#define LEAmi4(r,d,i) do { count_alu(); IMM32(d); *(--_nIns) = (2<<6)|((uint8_t)i<<3)|5; *(--_nIns) = (0<<6)|((uint8_t)r<<3)|4; *(--_nIns) = 0x8d; asm_output("lea %s, %p(%s*4)", gpn(r), (void*)d, gpn(i)); } while(0)
#define CDQ() do { SARi(EDX, 31); MR(EDX, EAX); } while(0)
@ -447,7 +450,7 @@ namespace nanojit
// load 8-bit, zero extend
// note, only 5-bit offsets (!) are supported for this, but that's all we need at the moment
// (movzx actually allows larger offsets mode but 5-bit gives us advantage in Thumb mode)
#define LD8Z(r,d,b) do { NanoAssert((d)>=0&&(d)<=31); ALU2m(0x0fb6,r,d,b); asm_output("movzx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
#define LD8Z(r,d,b) do { count_ld(); NanoAssert((d)>=0&&(d)<=31); ALU2m(0x0fb6,r,d,b); asm_output("movzx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0)
#define LD8Zdm(r,addr) do { \
count_ld(); \

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

@ -56,9 +56,14 @@ namespace nanojit
continue;
NanoAssertMsg(!isFree(r), "Coding error; register is both free and active! " );
s += strlen(s);
if (ins->isop(LIR_param) && ins->paramKind()==1 && r == Assembler::savedRegs[ins->paramArg()]) {
// dont print callee-saved regs that arent used
continue;
}
s += VMPI_strlen(s);
const char* rname = ins->isQuad() ? fpn(r) : gpn(r);
sprintf(s, " %s(%s)", rname, names->formatRef(ins));
VMPI_sprintf(s, " %s(%s)", rname, names->formatRef(ins));
}
}
#endif /* NJ_VERBOSE */

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

@ -46,7 +46,7 @@ namespace nanojit
{
inline RegisterMask rmask(Register r)
{
return 1 << r;
return RegisterMask(1) << r;
}
class RegAlloc
@ -121,7 +121,7 @@ namespace nanojit
debug_only( uint32_t countActive(); )
debug_only( bool isConsistent(Register r, LIns* v); )
debug_only( RegisterMask managed; ) // bitfield of 0..NJ_MAX_REGISTERS denoting which are under our management
debug_only( RegisterMask managed; ) // bitfield denoting which are under our management
LIns* active[LastReg + 1]; // active[r] = OP that defines r
int32_t usepri[LastReg + 1]; // used priority. lower = more likely to spill.

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

@ -48,10 +48,6 @@
using namespace avmplus;
Config AvmCore::config;
static GC _gc;
GC* AvmCore::gc = &_gc;
GCHeap GC::heap;
String* AvmCore::k_str[] = { (String*)"" };
void
avmplus::AvmLog(char const *msg, ...) {

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

@ -39,6 +39,7 @@
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#if defined(AVMPLUS_UNIX) || defined(AVMPLUS_OS2)
@ -57,10 +58,17 @@
#endif
#endif
#define FASTCALL JS_FASTCALL
#if defined(_MSC_VER) && defined(_M_IX86)
#define FASTCALL __fastcall
#elif defined(__GNUC__) && defined(__i386__) && \
((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
#define FASTCALL __attribute__((fastcall))
#else
#define FASTCALL
#define NO_FASTCALL
#endif
#if defined(JS_NO_FASTCALL)
#define NJ_NO_FASTCALL
#if defined(NO_FASTCALL)
#if defined(AVMPLUS_IA32)
#define SIMULATE_FASTCALL(lr, state_ptr, frag_ptr, func_addr) \
asm volatile( \
@ -70,7 +78,7 @@
: "memory", "cc" \
);
#endif /* defined(AVMPLUS_IA32) */
#endif /* defined(JS_NO_FASTCALL) */
#endif /* defined(NO_FASTCALL) */
#ifdef WIN32
#include <windows.h>
@ -159,143 +167,24 @@ static __inline__ unsigned long long rdtsc(void)
struct JSContext;
namespace MMgc {
class GC;
class GCObject
{
public:
inline void*
operator new(size_t size, GC* gc)
{
return calloc(1, size);
}
static void operator delete (void *gcObject)
{
free(gcObject);
}
};
#define MMGC_SUBCLASS_DECL : public avmplus::GCObject
class GCFinalizedObject : public GCObject
{
public:
static void operator delete (void *gcObject)
{
free(gcObject);
}
};
class GCHeap
{
public:
int32_t kNativePageSize;
GCHeap()
{
#if defined _SC_PAGE_SIZE
kNativePageSize = sysconf(_SC_PAGE_SIZE);
#ifdef PERFM
# define PERFM_NVPROF(n,v) _nvprof(n,v)
# define PERFM_NTPROF(n) _ntprof(n)
# define PERFM_TPROF_END() _tprof_end()
#else
kNativePageSize = 4096; // @todo: what is this?
# define PERFM_NVPROF(n,v)
# define PERFM_NTPROF(n)
# define PERFM_TPROF_END()
#endif
}
inline void*
Alloc(uint32_t pages)
{
#ifdef XP_WIN
return VirtualAlloc(NULL,
pages * kNativePageSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
#elif defined AVMPLUS_UNIX
/**
* Don't use normal heap with mprotect+PROT_EXEC for executable code.
* SELinux and friends don't allow this.
*/
return mmap(NULL,
pages * kNativePageSize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0);
#else
return valloc(pages * kNativePageSize);
#endif
}
inline void
Free(void* p, uint32_t pages)
{
#ifdef XP_WIN
VirtualFree(p, 0, MEM_RELEASE);
#elif defined AVMPLUS_UNIX
#if defined SOLARIS
munmap((char*)p, pages * kNativePageSize);
#else
munmap(p, pages * kNativePageSize);
#endif
#else
free(p);
#endif
}
};
class GC
{
static GCHeap heap;
public:
/**
* flags to be passed as second argument to alloc
*/
enum AllocFlags
{
kZero=1,
kContainsPointers=2,
kFinalize=4,
kRCObject=8
};
static inline void*
Alloc(uint32_t bytes, int flags=kZero)
{
if (flags & kZero)
return calloc(1, bytes);
else
return malloc(bytes);
}
static inline void
Free(void* p)
{
free(p);
}
static inline GCHeap*
GetGCHeap()
{
return &heap;
}
};
}
#define DWB(x) x
#define DRCWB(x) x
#define WB(gc, container, addr, value) do { *(addr) = (value); } while(0)
#define WBRC(gc, container, addr, value) do { *(addr) = (value); } while(0)
#define MMGC_MEM_TYPE(x)
#define VMPI_strlen strlen
#define VMPI_strcat strcat
#define VMPI_strncat strncat
#define VMPI_strcpy strcpy
#define VMPI_sprintf sprintf
#define VMPI_memset memset
#define VMPI_isdigit isdigit
#define VMPI_getDate()
extern void VMPI_setPageProtection(void *address,
size_t size,
@ -304,42 +193,10 @@ extern void VMPI_setPageProtection(void *address,
namespace avmplus {
using namespace MMgc;
typedef int FunctionID;
extern void AvmLog(char const *msg, ...);
class String
{
};
typedef class String AvmString;
class StringNullTerminatedUTF8
{
const char* cstr;
public:
StringNullTerminatedUTF8(GC* gc, String* s)
{
cstr = strdup((const char*)s);
}
~StringNullTerminatedUTF8()
{
free((void*)cstr);
}
inline
const char* c_str()
{
return cstr;
}
};
typedef String* Stringp;
class Config
{
public:
@ -450,8 +307,6 @@ namespace avmplus {
AvmConsole console;
static Config config;
static GC* gc;
static String* k_str[];
#ifdef AVMPLUS_IA32
static inline bool
@ -483,457 +338,8 @@ namespace avmplus {
return config.verbose;
}
static inline GC*
GetGC()
{
return gc;
}
static inline String* newString(const char* cstr) {
return (String*)strdup(cstr);
}
static inline void freeString(String* str) {
return free((char*)str);
}
};
class OSDep
{
public:
static inline void
getDate()
{
}
};
/**
* The List<T> template implements a simple List, which can
* be templated to support different types.
*
* Elements can be added to the end, modified in the middle,
* but no holes are allowed. That is for set(n, v) to work
* size() > n
*
* Note that [] operators are provided and you can violate the
* set properties using these operators, if you want a real
* list dont use the [] operators, if you want a general purpose
* array use the [] operators.
*/
enum ListElementType {
LIST_NonGCObjects = 0,
LIST_GCObjects = 1,
LIST_RCObjects = 2
};
template <typename T, ListElementType kElementType>
class List
{
public:
enum { kInitialCapacity = 128 };
List(GC *_gc, uint32_t _capacity=kInitialCapacity) : data(NULL), len(0), capacity(0)
{
ensureCapacity(_capacity);
}
~List()
{
//clear();
destroy();
// zero out in case we are part of an RCObject
len = 0;
}
inline void destroy()
{
if (data)
free(data);
}
const T *getData() const { return data; }
// 'this' steals the guts of 'that' and 'that' gets reset.
void become(List& that)
{
this->destroy();
this->data = that.data;
this->len = that.len;
this->capacity = that.capacity;
that.data = 0;
that.len = 0;
that.capacity = 0;
}
uint32_t add(T value)
{
if (len >= capacity) {
grow();
}
wb(len++, value);
return len-1;
}
inline bool isEmpty() const
{
return len == 0;
}
inline uint32_t size() const
{
return len;
}
inline T get(uint32_t index) const
{
AvmAssert(index < len);
return *(T*)(data + index);
}
void set(uint32_t index, T value)
{
AvmAssert(index < capacity);
if (index >= len)
{
len = index+1;
}
AvmAssert(len <= capacity);
wb(index, value);
}
void add(const List<T, kElementType>& l)
{
ensureCapacity(len+l.size());
// FIXME: make RCObject version
AvmAssert(kElementType != LIST_RCObjects);
arraycopy(l.getData(), 0, data, len, l.size());
len += l.size();
}
inline void clear()
{
zero_range(0, len);
len = 0;
}
int indexOf(T value) const
{
for(uint32_t i=0; i<len; i++)
if (get(i) == value)
return i;
return -1;
}
int lastIndexOf(T value) const
{
for(int32_t i=len-1; i>=0; i--)
if (get(i) == value)
return i;
return -1;
}
inline T last() const
{
return get(len-1);
}
T removeLast()
{
if(isEmpty())
return undef_list_val();
T t = get(len-1);
set(len-1, undef_list_val());
len--;
return t;
}
inline T operator[](uint32_t index) const
{
AvmAssert(index < capacity);
return get(index);
}
void ensureCapacity(uint32_t cap)
{
if (cap > capacity) {
if (data == NULL) {
data = (T*)calloc(1, factor(cap));
} else {
data = (T*)realloc(data, factor(cap));
zero_range(capacity, cap - capacity);
}
capacity = cap;
}
}
void insert(uint32_t index, T value, uint32_t count = 1)
{
AvmAssert(index <= len);
AvmAssert(count > 0);
ensureCapacity(len+count);
memmove(data + index + count, data + index, factor(len - index));
wbzm(index, index+count, value);
len += count;
}
T removeAt(uint32_t index)
{
T old = get(index);
// dec the refcount on the one we're removing
wb(index, undef_list_val());
memmove(data + index, data + index + 1, factor(len - index - 1));
len--;
return old;
}
private:
void grow()
{
// growth is fast at first, then slows at larger list sizes.
uint32_t newMax = 0;
const uint32_t curMax = capacity;
if (curMax == 0)
newMax = kInitialCapacity;
else if(curMax > 15)
newMax = curMax * 3/2;
else
newMax = curMax * 2;
ensureCapacity(newMax);
}
void arraycopy(const T* src, int srcStart, T* dst, int dstStart, int nbr)
{
// we have 2 cases, either closing a gap or opening it.
if ((src == dst) && (srcStart > dstStart) )
{
for(int i=0; i<nbr; i++)
dst[i+dstStart] = src[i+srcStart];
}
else
{
for(int i=nbr-1; i>=0; i--)
dst[i+dstStart] = src[i+srcStart];
}
}
inline void do_wb_nongc(T* slot, T value)
{
*slot = value;
}
inline void do_wb_gc(GCObject** slot, const GCObject** value)
{
*slot = (GCObject*)*value;
}
void wb(uint32_t index, T value)
{
AvmAssert(index < capacity);
AvmAssert(data != NULL);
T* slot = &data[index];
do_wb_nongc(slot, value);
}
// multiple wb call with the same value, and assumption that existing value is all zero bits,
// like
// for (uint32_t u = index; u < index_end; ++u)
// wb(u, value);
void wbzm(uint32_t index, uint32_t index_end, T value)
{
AvmAssert(index < capacity);
AvmAssert(index_end <= capacity);
AvmAssert(index < index_end);
AvmAssert(data != NULL);
T* slot = data + index;
for ( ; index < index_end; ++index, ++slot)
do_wb_nongc(slot, value);
}
inline uint32_t factor(uint32_t index) const
{
return index * sizeof(T);
}
void zero_range(uint32_t _first, uint32_t _count)
{
memset(data + _first, 0, factor(_count));
}
// stuff that needs specialization based on the type
static inline T undef_list_val();
private:
List(const List& toCopy); // unimplemented
void operator=(const List& that); // unimplemented
// ------------------------ DATA SECTION BEGIN
private:
T* data;
uint32_t len;
uint32_t capacity;
// ------------------------ DATA SECTION END
};
// stuff that needs specialization based on the type
template<typename T, ListElementType kElementType>
/* static */ inline T List<T, kElementType>::undef_list_val() { return T(0); }
/**
* The SortedMap<K,T> template implements an object that
* maps keys to values. The keys are sorted
* from smallest to largest in the map. Time of operations
* is as follows:
* put() is O(1) if the key is higher than any existing
* key; O(logN) if the key already exists,
* and O(N) otherwise.
* get() is an O(logN) binary search.
*
* no duplicates are allowed.
*/
template <class K, class T, ListElementType valType>
class SortedMap : public GCObject
{
public:
enum { kInitialCapacity= 64 };
SortedMap(GC* gc, int _capacity=kInitialCapacity)
: keys(gc, _capacity), values(gc, _capacity)
{
}
bool isEmpty() const
{
return keys.size() == 0;
}
int size() const
{
return keys.size();
}
void clear()
{
keys.clear();
values.clear();
}
void destroy()
{
keys.destroy();
values.destroy();
}
T put(K k, T v)
{
if (keys.size() == 0 || k > keys.last())
{
keys.add(k);
values.add(v);
return (T)v;
}
else
{
int i = find(k);
if (i >= 0)
{
T old = values[i];
keys.set(i, k);
values.set(i, v);
return old;
}
else
{
i = -i - 1; // recover the insertion point
AvmAssert(keys.size() != (uint32_t)i);
keys.insert(i, k);
values.insert(i, v);
return v;
}
}
}
T get(K k) const
{
int i = find(k);
return i >= 0 ? values[i] : 0;
}
bool get(K k, T& v) const
{
int i = find(k);
if (i >= 0)
{
v = values[i];
return true;
}
return false;
}
bool containsKey(K k) const
{
int i = find(k);
return (i >= 0) ? true : false;
}
T remove(K k)
{
int i = find(k);
return removeAt(i);
}
T removeAt(int i)
{
T old = values.removeAt(i);
keys.removeAt(i);
return old;
}
T removeFirst() { return isEmpty() ? (T)0 : removeAt(0); }
T removeLast() { return isEmpty() ? (T)0 : removeAt(keys.size()-1); }
T first() const { return isEmpty() ? (T)0 : values[0]; }
T last() const { return isEmpty() ? (T)0 : values[keys.size()-1]; }
K firstKey() const { return isEmpty() ? 0 : keys[0]; }
K lastKey() const { return isEmpty() ? 0 : keys[keys.size()-1]; }
// iterator
T at(int i) const { return values[i]; }
K keyAt(int i) const { return keys[i]; }
int findNear(K k) const {
int i = find(k);
return i >= 0 ? i : -i-2;
}
protected:
List<K, LIST_NonGCObjects> keys;
List<T, valType> values;
int find(K k) const
{
int lo = 0;
int hi = keys.size()-1;
while (lo <= hi)
{
int i = (lo + hi)/2;
K m = keys[i];
if (k > m)
lo = i + 1;
else if (k < m)
hi = i - 1;
else
return i; // key found
}
return -(lo + 1); // key not found, low is the insertion point
}
};
#define GCSortedMap SortedMap
/**
* Bit vectors are an efficent method of keeping True/False information
* on a set of items or conditions. Class BitSet provides functions

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

@ -40,63 +40,34 @@
#ifndef __nanojit_h__
#define __nanojit_h__
#include <stddef.h>
#include "avmplus.h"
#ifdef FEATURE_NANOJIT
#ifdef AVMPLUS_IA32
#if defined AVMPLUS_IA32
#define NANOJIT_IA32
#elif AVMPLUS_ARM
#elif defined AVMPLUS_ARM
#define NANOJIT_ARM
#elif AVMPLUS_PPC
#elif defined AVMPLUS_PPC
#define NANOJIT_PPC
#elif AVMPLUS_SPARC
#elif defined AVMPLUS_SPARC
#define NANOJIT_SPARC
#elif AVMPLUS_AMD64
#elif defined AVMPLUS_AMD64
#define NANOJIT_X64
#define NANOJIT_64BIT
#else
#error "unknown nanojit architecture"
#endif
/*
If we're using MMGC, using operator delete on a GCFinalizedObject is problematic:
in particular, calling it from inside a dtor is risky because the dtor for the sub-object
might already have been called, wrecking its vtable and ending up in the wrong version
of operator delete (the global version rather than the class-specific one). Calling GC::Free
directly is fine (since it ignores the vtable), so we macro-ize to make the distinction.
#ifdef AVMPLUS_64BIT
#define NANOJIT_64BIT
#endif
macro-ization of operator new isn't strictly necessary, but is done to bottleneck both
sides of the new/delete pair to forestall future needs.
*/
#ifdef MMGC_API
// separate overloads because GCObject and GCFinalizedObjects have different dtors
// (GCFinalizedObject's is virtual, GCObject's is not)
inline void mmgc_delete(GCObject* o)
{
GC* g = GC::GetGC(o);
if (g->Collecting())
g->Free(o);
else
delete o;
}
inline void mmgc_delete(GCFinalizedObject* o)
{
GC* g = GC::GetGC(o);
if (g->Collecting())
g->Free(o);
else
delete o;
}
#define NJ_NEW(gc, cls) new (gc) cls
#define NJ_DELETE(obj) do { mmgc_delete(obj); } while (0)
#if defined NANOJIT_64BIT
#define IF_64BIT(...) __VA_ARGS__
#define UNLESS_64BIT(...)
#else
#define NJ_NEW(gc, cls) new (gc) cls
#define NJ_DELETE(obj) do { delete obj; } while (0)
#define IF_64BIT(...)
#define UNLESS_64BIT(...) __VA_ARGS__
#endif
// Embed no-op macros that let Valgrind work with the JIT.
@ -116,10 +87,7 @@ namespace nanojit
* START AVM bridging definitions
* -------------------------------------------
*/
class Fragment;
typedef avmplus::AvmCore AvmCore;
typedef avmplus::OSDep OSDep;
typedef avmplus::GCSortedMap<const void*,Fragment*,avmplus::LIST_GCObjects> FragmentMap;
const uint32_t MAXARGS = 8;
@ -168,9 +136,13 @@ namespace nanojit
}
#ifdef AVMPLUS_VERBOSE
#ifndef NJ_VERBOSE_DISABLED
#define NJ_VERBOSE 1
#endif
#ifndef NJ_PROFILE_DISABLED
#define NJ_PROFILE 1
#endif
#endif
#ifdef MOZ_NO_VARADIC_MACROS
#include <stdio.h>

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

@ -0,0 +1,8 @@
try {
for (var j = 0; j < 2; ++j) {
if (j == 1) {
++(null[2]);
}
}
} catch(e) {
}

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

@ -0,0 +1,14 @@
// Make sure the arch flags are valid on startup, even if nothing has
// been traced yet. We don't know what arch the user is building on,
// but presumably we want at least 1 flag to be set on all supported
// platforms.
if (HAVE_TM) {
assertEq(jitstats.archIsIA32 ||
jitstats.archIs64BIT ||
jitstats.archIsARM ||
jitstats.archIsSPARC ||
jitstats.archIsPPC ||
jitstats.archIsAMD64,
1);
}

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

@ -0,0 +1,14 @@
var count = 0;
function f() {
arguments.length--;
for (var i = 0; i < arguments.length; ++i) {
++count;
}
}
f(1, 2);
f(1, 2);
f(2, 2);
assertEq(count, 3);

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

@ -0,0 +1,12 @@
// don't crash
var q;
function f() {
while (arguments.length > 0) {
q = arguments[arguments.length-1];
arguments.length--;
}
}
f(1, 2, 3, 4, 5);

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

@ -2,7 +2,11 @@ function testEliminatedGuardWithinAnchor() {
for (let i = 0; i < 5; ++i) { i / (i * i); }
return "ok";
}
assertEq(testEliminatedGuardWithinAnchor(), "ok");
if (HAVE_TM) {
checkStats({
sideExitIntoInterpreter: 3
sideExitIntoInterpreter: (jitstats.archIsARM ? 1 : 3)
});
}

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

@ -0,0 +1,9 @@
for (var j = 0; j < 7; ++j)
({x: function () {}}).x;
checkStats({
recorderStarted: 1,
recorderAborted: 0,
traceCompleted: 1,
sideExitIntoInterpreter: 1
});

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

@ -0,0 +1,9 @@
for (var j = 0; j < 7; ++j)
uneval({x: function () {}});
checkStats({
recorderStarted: 1,
recorderAborted: 0,
traceCompleted: 1,
sideExitIntoInterpreter: 4
});

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

@ -1,4 +1,4 @@
// |trace-test| TMFLAGS: full,fragprofile,treevis
// |trace-test| TMFLAGS: full,fragprofile,treevis; valgrind
function testRegExpTest() {
var r = /abc/;

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

@ -1,4 +1,4 @@
// |trace-test| TMFLAGS: full,fragprofile,treevis
// |trace-test| TMFLAGS: full,fragprofile,treevis; valgrind
/* Test the proper operation of the left shift operator. This is especially
* important on ARM as an explicit mask is required at the native instruction

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

@ -1,4 +1,4 @@
// |trace-test| TMFLAGS: full,fragprofile,treevis
// |trace-test| TMFLAGS: full,fragprofile,treevis; valgrind
function testSideExitInConstructor() {
var FCKConfig = {};

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

@ -30,20 +30,22 @@ def _relpath(path, start=None):
os.path.relpath = _relpath
class Test:
def __init__(self, path, slow, allow_oom, tmflags):
def __init__(self, path, slow, allow_oom, tmflags, valgrind):
""" path path to test file
slow True means the test is slow-running
allow_oom True means OOM should not be considered a failure """
allow_oom True means OOM should not be considered a failure
valgrind True means test should run under valgrind """
self.path = path
self.slow = slow
self.allow_oom = allow_oom
self.tmflags = tmflags
self.valgrind = valgrind
COOKIE = '|trace-test|'
@classmethod
def from_file(cls, path):
slow = allow_oom = False
def from_file(cls, path, options):
slow = allow_oom = valgrind = False
tmflags = ''
line = open(path).readline()
@ -67,10 +69,12 @@ class Test:
slow = True
elif name == 'allow-oom':
allow_oom = True
elif name == 'valgrind':
valgrind = options.valgrind
else:
print('warning: unrecognized |trace-test| attribute %s'%part)
return cls(path, slow, allow_oom, tmflags)
return cls(path, slow, allow_oom, tmflags, valgrind or options.valgrind_all)
def find_tests(dir, substring = None):
ans = []
@ -102,6 +106,18 @@ def run_test(test, lib_dir):
else:
env = None
cmd = get_test_cmd(test.path, lib_dir)
if (test.valgrind and
any([os.path.exists(os.path.join(d, 'valgrind'))
for d in os.environ['PATH'].split(os.pathsep)])):
valgrind_prefix = [ 'valgrind',
'--smc-check=all',
'--error-exitcode=1',
'--leak-check=full']
if os.uname()[0] == 'Darwin':
valgrind_prefix += ['--dsymutil=yes']
cmd = valgrind_prefix + cmd
if OPTIONS.show_cmd:
print(cmd)
# close_fds is not supported on Windows and will cause a ValueError.
@ -112,6 +128,8 @@ def run_test(test, lib_dir):
if OPTIONS.show_output:
sys.stdout.write(out)
sys.stdout.write(err)
if test.valgrind:
sys.stdout.write(err)
return (check_output(out, err, p.returncode, test.allow_oom), out, err)
def check_output(out, err, rc, allow_oom):
@ -126,7 +144,7 @@ def check_output(out, err, rc, allow_oom):
if rc != 0:
# Allow a non-zero exit code if we want to allow OOM, but only if we
# actually got OOM.
return allow_oom and ': out of memory\n' in err
return allow_oom and ': out of memory' in err
return True
@ -230,6 +248,10 @@ if __name__ == '__main__':
help='Retest using test list file [FILE]')
op.add_option('-g', '--debug', dest='debug', action='store_true',
help='Run test in gdb')
op.add_option('--valgrind', dest='valgrind', action='store_true',
help='Enable the |valgrind| flag, if valgrind is in $PATH.')
op.add_option('--valgrind-all', dest='valgrind_all', action='store_true',
help='Run all tests with valgrind, if valgrind is in $PATH.')
(OPTIONS, args) = op.parse_args()
if len(args) < 1:
op.error('missing JS_SHELL argument')
@ -277,7 +299,8 @@ if __name__ == '__main__':
print >> sys.stderr, "No tests found matching command line arguments."
sys.exit(0)
test_list = [ Test.from_file(_) for _ in test_list ]
test_list = [ Test.from_file(_, OPTIONS) for _ in test_list ]
if not OPTIONS.run_slow:
test_list = [ _ for _ in test_list if not _.slow ]
@ -293,4 +316,11 @@ if __name__ == '__main__':
call(cmd)
sys.exit()
try:
run_tests(test_list, test_dir, lib_dir)
except OSError:
if not os.path.exists(JS):
print >> sys.stderr, "JS shell argument: file does not exist: '%s'"%JS
sys.exit(1)
else:
raise

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

@ -168,7 +168,7 @@ int _histEntryValue (void* id, int64_t value);
#define NUM_EVARS 4
typedef enum {
enum {
LOCK_IS_FREE = 0,
LOCK_IS_TAKEN = 1
};

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

@ -0,0 +1,91 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = '15.2.3.14-01.js';
//-----------------------------------------------------------------------------
var BUGNUMBER = 307791;
var summary = 'ES5 Object.keys(O)';
var actual = '';
var expect = '';
printBugNumber(BUGNUMBER);
printStatus(summary);
function arraysEqual(a1, a2)
{
return a1.length === a2.length &&
a1.every(function(v, i) { return v === a2[i]; });
}
/**************
* BEGIN TEST *
**************/
assertEq(Object.keys.length, 1);
var o, keys;
o = { a: 3, b: 2 };
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["a", "b"]), true,
"" + keys);
o = { get a() { return 17; }, b: 2 };
keys = Object.keys(o),
assertEq(arraysEqual(keys, ["a", "b"]), true,
"" + keys);
o = { __iterator__: function() { return Iterator({a: 2, b: 3}); } };
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["__iterator__"]), true,
"" + keys);
o = { a: 1, b: 2 };
delete o.a;
o.a = 3;
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["b", "a"]), true,
"" + keys);
o = [0, 1, 2];
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["0", "1", "2"]), true,
"" + keys);
o = /./.exec("abc");
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["0", "index", "input"]), true,
"" + keys);
o = { a: 1, b: 2, c: 3 };
delete o.b;
o.b = 5;
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["a", "c", "b"]), true,
"" + keys);
function f() { }
f.prototype.p = 1;
o = new f();
o.g = 1;
keys = Object.keys(o);
assertEq(arraysEqual(keys, ["g"]), true,
"" + keys);
if (typeof Namespace !== "undefined" && typeof QName !== "undefined")
{
var o2 = {};
var qn = new QName(new Namespace("foo"), "v");
o2.f = 1;
o2[qn] = 3;
o2.baz = 4;
var keys2 = Object.keys(o2);
assertEq(arraysEqual(keys2, ["f", "foo::v", "baz"]), true,
"" + keys2);
}
/******************************************************************************/
printStatus("All tests passed!");

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

@ -274,18 +274,31 @@ function escapeString (str)
}
/*
* assertEq(actual, expected)
* Throw if the two arguments are not ===
* see https://bugzilla.mozilla.org/show_bug.cgi?id=480199
* assertEq(actual, expected [, message])
* Throw if the two arguments are not the same. The sameness of two values
* is determined as follows. If both values are zero, they are the same iff
* their signs are the same. Otherwise, if both values are NaN, they are the
* same. Otherwise, they are the same if they compare equal using ===.
* see https://bugzilla.mozilla.org/show_bug.cgi?id=480199 and
* https://bugzilla.mozilla.org/show_bug.cgi?id=515285
*/
if (typeof assertEq == 'undefined')
{
var assertEq =
function (actual, expected)
function (actual, expected, message)
{
if (actual !== expected)
function SameValue(v1, v2)
{
throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected);
if (v1 === 0 && v2 === 0)
return 1 / v1 === 1 / v2;
if (v1 !== v1 && v2 !== v2)
return true;
return v1 === v2;
}
if (!SameValue(actual, expected))
{
throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected +
(message ? ": " + message : ""));
}
};
}