Merge tracemonkey to mozilla-central. (a=blockers)

This commit is contained in:
Chris Leary 2011-02-15 12:05:57 -08:00
Родитель 5f4f7176ee 8ab3e12379
Коммит c228dde80b
79 изменённых файлов: 1072 добавлений и 321 удалений

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

@ -354,6 +354,7 @@ user_pref("browser.EULA.override", true);
user_pref("javascript.options.tracejit.content", true);
user_pref("javascript.options.methodjit.content", true);
user_pref("javascript.options.jitprofiling.content", true);
user_pref("javascript.options.methodjit_always", false);
user_pref("gfx.color_management.force_srgb", true);
user_pref("network.manage-offline-status", false);
user_pref("test.mousescroll", true);

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

@ -1015,10 +1015,11 @@ static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
#endif
static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content";
static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome";
static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
static const char js_methodjit_always_str[] = JS_OPTIONS_DOT_STR "methodjit_always";
int
nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
@ -1047,6 +1048,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ?
js_profiling_chrome_str :
js_profiling_content_str);
PRBool useMethodJITAlways = nsContentUtils::GetBoolPref(js_methodjit_always_str);
nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
if (xr) {
PRBool safeMode = PR_FALSE;
@ -1055,6 +1057,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
useTraceJIT = PR_FALSE;
useMethodJIT = PR_FALSE;
useProfiling = PR_FALSE;
useMethodJITAlways = PR_TRUE;
}
}
@ -1073,6 +1076,11 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
else
newDefaultJSOptions &= ~JSOPTION_PROFILING;
if (useMethodJITAlways)
newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
else
newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
#ifdef DEBUG
// In debug builds, warnings are enabled in chrome context if
// javascript.options.strict.debug is true

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

@ -2580,7 +2580,12 @@ jsdService::ActivateDebugger (JSRuntime *rt)
return rv;
xpc->InitClasses (cx, glob);
/* Start watching for script creation/destruction and manage jsdScript
* objects accordingly
*/
JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
/* If any of these mFooHook objects are installed, do the required JSD
* hookup now. See also, jsdService::SetFooHook().
*/

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

@ -54,7 +54,7 @@ class Test:
def copy(self):
t = Test(self.path)
t.jitflags = self.jitflags
t.jitflags = self.jitflags[:]
t.slow = self.slow
t.allow_oom = self.allow_oom
t.valgrind = self.valgrind
@ -93,6 +93,8 @@ class Test:
test.allow_oom = True
elif name == 'valgrind':
test.valgrind = options.valgrind
elif name == 'mjitalways':
test.jitflags.append('-a')
else:
print('warning: unrecognized |jit-test| attribute %s'%part)
@ -426,7 +428,7 @@ def main(argv):
for test in test_list:
for jitflags in jitflags_list:
new_test = test.copy()
new_test.jitflags = jitflags
new_test.jitflags.extend(jitflags)
job_list.append(new_test)

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

@ -0,0 +1,11 @@
var N = HOTLOOP + 2;
function f(b) {
var a = [];
for (var i = 0; i < N; i++)
a[i] = {};
a[N-1] = arguments;
for (var i = 0; i < N; i++)
a[i][0] = i;
assertEq(b, N - 1);
}
f(null);

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

@ -0,0 +1,6 @@
Object.defineProperty(Function.prototype, "prototype", {set:function(){}});
var x;
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), Object.prototype);

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

@ -0,0 +1,6 @@
Function.prototype.prototype = function () {};
var x;
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), Function.prototype.prototype);

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

@ -0,0 +1,12 @@
var a = [];
function next() {
var x = {};
a.push(x);
return x;
}
Object.defineProperty(Function.prototype, 'prototype', {get: next});
var b = [];
for (var i = 0; i < HOTLOOP + 2; i++)
b[i] = new Function.prototype;
for (var i = 0; i < HOTLOOP + 2; i++)
assertEq(Object.getPrototypeOf(b[i]), a[i]);

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

@ -0,0 +1,10 @@
Object.defineProperty(Function.prototype, 'prototype',
{get: function () { if (i == HOTLOOP + 1) throw "X"; }});
var x;
try {
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
} catch (exc) {
assertEq(i, HOTLOOP + 1);
assertEq(exc, "X");
}

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

@ -0,0 +1,13 @@
function C(a, b) {
this.a = a;
this.b = b;
}
var f = C.bind(null, 2);
Object.defineProperty(f, "prototype", {get: function () { throw "FAIL"; }});
var x;
for (var i = 0; i < HOTLOOP + 2; i++)
x = new f(i);
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), C.prototype);
assertEq(x.a, 2);
assertEq(x.b, HOTLOOP + 1);

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

@ -0,0 +1,11 @@
var a = [];
var x, i;
for (i = 0; i < HOTLOOP + 10; i++) {
a[i] = function (b) { this.b = b; };
if (i != HOTLOOP + 9)
x = a[i].prototype;
}
for (i = 0; i < HOTLOOP + 10; i++)
x = new a[i];
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), a[HOTLOOP + 9].prototype);

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

@ -0,0 +1,9 @@
var n = 0;
var a = [];
for (var i = 0; i < 20; i++)
a[i] = {};
a[18].watch("p", function () { n++; });
delete a[18].p;
for (var i = 0; i < 20; i++)
a[i].p = 0;
assertEq(n, 1);

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

@ -0,0 +1,13 @@
// vim: set ts=4 sw=4 tw=99 et:
x = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 };
for (i in x)
delete x.d;
x = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 };
y = [];
for (i in x)
y.push(i)
assertEq(y[3], "d");

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

@ -0,0 +1,13 @@
// vim: set ts=4 sw=4 tw=99 et:
var o1 = {p1: 1};
var o2 = {p1: 1, p2: 2};
for(var x in o1) {
for(var y in o2) {
delete o2.p2;
}
}
/* Don't fail cx->enumerators == obj assert, see bug comment #31 */

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

@ -0,0 +1,48 @@
var BUGNUMBER = '';
function printBugNumber (num)
{
return "foo";
}
function optionsClear() {
var x = printBugNumber().split(',');
}
function optionsReset() {
optionsClear();
}
var code = new Array();
code.push("evaluate");
var x0 = "\
printBugNumber(BUGNUMBER);\n\
function gen()\n\
{\n\
try {\n\
yield 0;\n\
} finally {\n\
}\n\
}\n\
var iter1 = gen( iter1=\"NaN\", new gen(gen)) ;\n\
gc();\n\
";
code.push(x0);
code.push("evaluate");
var files = new Array();
while (true) {
var file = code.shift();
if (file == "evaluate") {
loadFiles(files);
} else if (file == undefined) {
break;
} else {
files.push(file);
}
}
function loadFiles(x) {
for (i in x) {
try {
eval(x[i]);
} catch (e) {
}
}
optionsReset();
}

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

@ -0,0 +1,11 @@
function f(a, b) {
this.a = a;
assertEq(b, 'x');
}
for (var x = 0; x < RUNLOOP; ++x) {
f.prototype = {};
var obj = new f(x, 'x');
assertEq(obj.a, x);
assertEq(Object.getPrototypeOf(obj), f.prototype);
}

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

@ -0,0 +1,12 @@
function f(a, b, c) {
this.a = a;
assertEq(b, 'x');
assertEq(c, void 0);
}
for (var x = 0; x < RUNLOOP; ++x) {
f.prototype = {};
var obj = new f(x, 'x'); // fewer than f.length arguments
assertEq(obj.a, x);
assertEq(Object.getPrototypeOf(obj), f.prototype);
}

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

@ -0,0 +1,11 @@
function f(a) {
this.a = a;
assertEq(arguments[1], 'x');
}
for (var x = 0; x < RUNLOOP; ++x) {
f.prototype = {};
var obj = new f(x, 'x'); // more than f.length arguments
assertEq(obj.a, x);
assertEq(Object.getPrototypeOf(obj), f.prototype);
}

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function callee() {

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function callee() {

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function callee() {

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); });

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); });

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
this.__defineGetter__("someProperty", function () { evalInFrame(1, "x = 'success'"); });

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function nop(){}

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function nop(){}

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
setDebug(true);
function nop(){}

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

@ -0,0 +1,7 @@
var o = {};
for(var i=0; i<5; i++) {
o.p = 2;
o.watch("p", function() { });
o.p = 2;
delete o.p;
}

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

@ -1,3 +1,4 @@
// |jit-test| mjitalways
// vim: set ts=4 sw=4 tw=99 et:
function assertObjectsEqual(obj1, obj2) {

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

@ -955,11 +955,14 @@ JS_StringToVersion(const char *string);
#define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */
#define JSOPTION_PROFILING JS_BIT(15) /* Profiler to make tracer/methodjit choices. */
#define JSOPTION_METHODJIT_ALWAYS \
JS_BIT(16) /* Always whole-method JIT,
don't tune at run-time. */
/* Options which reflect compile-time properties of scripts. */
#define JSCOMPILEOPTION_MASK (JSOPTION_XML | JSOPTION_ANONFUNFIX)
#define JSRUNOPTION_MASK (JS_BITMASK(16) & ~JSCOMPILEOPTION_MASK)
#define JSRUNOPTION_MASK (JS_BITMASK(17) & ~JSCOMPILEOPTION_MASK)
#define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
extern JS_PUBLIC_API(uint32)

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

@ -188,8 +188,8 @@ struct ClosureVarInfo;
#define _JS_CTYPE_THIS _JS_CTYPE(JSObject *, _JS_PTR,"T", "", INFALLIBLE)
#define _JS_CTYPE_THIS_DOUBLE _JS_CTYPE(jsdouble, _JS_F64,"D", "", INFALLIBLE)
#define _JS_CTYPE_THIS_STRING _JS_CTYPE(JSString *, _JS_PTR,"S", "", INFALLIBLE)
#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f","", INFALLIBLE)
#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p","", INFALLIBLE)
#define _JS_CTYPE_CALLEE _JS_CTYPE(JSObject *, _JS_PTR,"f", "", INFALLIBLE)
#define _JS_CTYPE_CALLEE_PROTOTYPE _JS_CTYPE(JSObject *, _JS_PTR,"p", "", INFALLIBLE)
#define _JS_CTYPE_FUNCTION _JS_CTYPE(JSFunction *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_PC _JS_CTYPE(jsbytecode *, _JS_PTR,"P", "", INFALLIBLE)
#define _JS_CTYPE_VALUEPTR _JS_CTYPE(js::Value *, _JS_PTR, --, --, INFALLIBLE)
@ -233,7 +233,8 @@ struct ClosureVarInfo;
#define _JS_CTYPE_CVIPTR _JS_CTYPE(const ClosureVarInfo *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_FRAMEINFO _JS_CTYPE(FrameInfo *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_PICTABLE _JS_CTYPE(PICTable *, _JS_PTR, --, --, INFALLIBLE)
#define _JS_CTYPE_UINTN _JS_CTYPE(uintN, _JS_PTR, --, --, INFALLIBLE)
/*
* The "VALUE" type is used to indicate that a native takes a js::Value
* parameter by value. Unfortunately, for technical reasons, we can't simply

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

@ -327,7 +327,11 @@ StackSpace::getCallFrame(JSContext *cx, Value *firstUnused, uintN nactual,
uintN ncopy = 2 + nformal;
if (JS_UNLIKELY(!check(*this, cx, firstUnused, ncopy + nvals)))
return NULL;
memcpy(firstUnused, firstUnused - (2 + nactual), ncopy * sizeof(Value));
Value *dst = firstUnused;
Value *src = firstUnused - (2 + nactual);
PodCopy(dst, src, ncopy);
Debug_SetValueRangeToCrashOnTouch(src, ncopy);
return reinterpret_cast<JSStackFrame *>(firstUnused + ncopy);
}

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

@ -53,6 +53,10 @@
#include "jsgcinlines.h"
#if ENABLE_YARR_JIT
#include "assembler/jit/ExecutableAllocator.h"
#endif
using namespace js;
using namespace js::gc;
@ -151,10 +155,12 @@ JSCompartment::init()
return false;
#endif
#ifdef JS_METHODJIT
if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>())) {
if (!backEdgeTable.init())
return false;
#ifdef JS_METHODJIT
if (!(jaegerCompartment = js_new<mjit::JaegerCompartment>()))
return false;
}
return jaegerCompartment->Initialize();
#else
return true;
@ -174,7 +180,8 @@ JSCompartment::arenaListsAreEmpty()
static bool
IsCrossCompartmentWrapper(JSObject *wrapper)
{
return !!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT);
return wrapper->isWrapper() &&
!!(JSWrapper::wrapperHandler(wrapper)->flags() & JSWrapper::CROSS_COMPARTMENT);
}
bool
@ -584,3 +591,25 @@ JSCompartment::allocMathCache(JSContext *cx)
js_ReportOutOfMemory(cx);
return mathCache;
}
size_t
JSCompartment::backEdgeCount(jsbytecode *pc) const
{
if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
return p->value;
return 0;
}
size_t
JSCompartment::incBackEdgeCount(jsbytecode *pc)
{
if (BackEdgeMap::AddPtr p = backEdgeTable.lookupForAdd(pc)) {
p->value++;
return p->value;
} else {
backEdgeTable.add(p, pc, 1);
return 1;
}
}

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

@ -482,6 +482,13 @@ struct JS_FRIEND_API(JSCompartment) {
bool marked;
typedef js::HashMap<jsbytecode*,
size_t,
js::DefaultHasher<jsbytecode*>,
js::SystemAllocPolicy> BackEdgeMap;
BackEdgeMap backEdgeTable;
public:
js::MathCache *getMathCache(JSContext *cx) {
return mathCache ? mathCache : allocMathCache(cx);
@ -489,6 +496,9 @@ struct JS_FRIEND_API(JSCompartment) {
bool isMarked() { return marked; }
void clearMark() { marked = false; }
size_t backEdgeCount(jsbytecode *pc) const;
size_t incBackEdgeCount(jsbytecode *pc);
};
#define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC)

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

@ -565,7 +565,7 @@ JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
struct JSWatchPoint {
JSCList links;
JSObject *object; /* weak link, see js_FinalizeObject */
JSObject *object; /* weak link, see js_SweepWatchPoints */
const Shape *shape;
StrictPropertyOp setter;
JSWatchPointHandler handler;
@ -711,45 +711,110 @@ js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
&wp->links != &rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
const Shape *shape = wp->shape;
if (wp->object == obj && SHAPE_USERID(shape) == id &&
!(wp->flags & JSWP_HELD)) {
if (wp->object == obj && SHAPE_USERID(shape) == id && !(wp->flags & JSWP_HELD)) {
wp->flags |= JSWP_HELD;
DBG_UNLOCK(rt);
jsid propid = shape->id;
shape = obj->nativeLookup(propid);
JS_ASSERT(IsWatchedProperty(cx, shape));
jsid userid = SHAPE_USERID(shape);
/* NB: wp is held, so we can safely dereference it still. */
if (!wp->handler(cx, obj, propid,
obj->containsSlot(shape->slot)
? Jsvalify(obj->nativeGetSlot(shape->slot))
: JSVAL_VOID,
Jsvalify(vp), wp->closure)) {
DBG_LOCK(rt);
DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
return JS_FALSE;
/* Determine the property's old value. */
bool ok;
uint32 slot = shape->slot;
Value old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
const Shape *needMethodSlotWrite = NULL;
if (shape->isMethod()) {
/*
* We get here in two cases: (1) the existing watched property
* is a method; or (2) the watched property was deleted and is
* now in the middle of being re-added via JSOP_SETMETHOD. In
* both cases we must trip the method read barrier in order to
* avoid passing an uncloned function object to the handler.
*
* Case 2 is especially hairy. js_watch_set, uniquely, gets
* called in the middle of creating a method property, after
* shape is in obj but before the slot has been set. So in this
* case we must finish initializing the half-finished method
* property before triggering the method read barrier.
*
* Bonus weirdness: because this changes obj's shape,
* js_NativeSet (which is our caller) will not write to the
* slot, as it will appear the property was deleted and a new
* property added. We must write the slot ourselves -- however
* we must do it after calling the watchpoint handler. So set
* needMethodSlotWrite here and use it to write to the slot
* below, if the handler does not tinker with the property
* further.
*/
JS_ASSERT(!wp->setter);
Value method = ObjectValue(shape->methodObject());
if (old.isUndefined())
obj->nativeSetSlot(slot, method);
ok = obj->methodReadBarrier(cx, *shape, &method);
if (!ok)
goto out;
wp->shape = shape = needMethodSlotWrite = obj->nativeLookup(propid);
JS_ASSERT(shape->isDataDescriptor());
JS_ASSERT(!shape->isMethod());
if (old.isUndefined())
obj->nativeSetSlot(shape->slot, old);
else
old = method;
}
/* Handler could have redefined the shape; see bug 624050. */
shape = wp->shape;
{
Conditionally<AutoShapeRooter> tvr(needMethodSlotWrite, cx, needMethodSlotWrite);
/*
* Pass the output of the handler to the setter. Security wrappers
* prevent any funny business between watchpoints and setters.
*/
JSBool ok = !wp->setter ||
(shape->hasSetterValue()
/*
* Call the handler. This invalidates shape, so re-lookup the shape.
* NB: wp is held, so we can safely dereference it still.
*/
ok = wp->handler(cx, obj, propid, Jsvalify(old), Jsvalify(vp), wp->closure);
if (!ok)
goto out;
shape = obj->nativeLookup(propid);
JS_ASSERT_IF(!shape, !wp->setter);
if (!shape) {
ok = true;
} else if (wp->setter) {
/*
* Pass the output of the handler to the setter. Security wrappers
* prevent any funny business between watchpoints and setters.
*/
ok = shape->hasSetterValue()
? ExternalInvoke(cx, ObjectValue(*obj),
ObjectValue(*CastAsObject(wp->setter)),
1, vp, vp)
: CallJSPropertyOpSetter(cx, wp->setter, obj, userid, strict, vp));
: CallJSPropertyOpSetter(cx, wp->setter, obj, userid, strict, vp);
} else if (shape == needMethodSlotWrite) {
/* See comment above about needMethodSlotWrite. */
obj->nativeSetSlot(shape->slot, *vp);
ok = true;
} else {
/*
* A property with the default setter might be either a method
* or an ordinary function-valued data property subject to the
* method write barrier.
*
* It is not the setter's job to call methodWriteBarrier,
* but js_watch_set must do so, because the caller will be
* fooled into not doing it: shape does *not* have the
* default setter and therefore seems not to be a method.
*/
ok = obj->methodWriteBarrier(cx, *shape, *vp) != NULL;
}
}
out:
DBG_LOCK(rt);
return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
}
}
DBG_UNLOCK(rt);
return JS_TRUE;
return true;
}
static JSBool
@ -825,23 +890,23 @@ WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, StrictPropertyOp setter)
return CastAsStrictPropertyOp(FUN_OBJECT(wrapper));
}
static bool
UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const js::Shape *newShape)
static const Shape *
UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
{
JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id);
JS_ASSERT(!IsWatchedProperty(cx, newShape));
/* Create a watching setter we can substitute for the new shape's setter. */
js::StrictPropertyOp watchingSetter =
StrictPropertyOp watchingSetter =
WrapWatchedSetter(cx, newShape->id, newShape->attributes(), newShape->setter());
if (!watchingSetter)
return false;
return NULL;
/*
* Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will
* return a new shape, or mutate this one.
*/
js::StrictPropertyOp originalSetter = newShape->setter();
StrictPropertyOp originalSetter = newShape->setter();
/*
* Drop the watching setter into the object, in place of newShape. Note that a single
@ -849,21 +914,21 @@ UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const js::Shape *newShape
* wrap all (JSPropertyOp, not JSObject *) setters with js_watch_set, so shapes that
* differ only in their setter may all get wrapped to the same shape.
*/
const js::Shape *watchingShape =
const Shape *watchingShape =
js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(),
newShape->getter(), watchingSetter);
if (!watchingShape)
return false;
return NULL;
/* Update the watchpoint with the new shape and its original setter. */
wp->setter = originalSetter;
wp->shape = watchingShape;
return true;
return watchingShape;
}
bool
js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape)
const Shape *
js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const Shape *newShape)
{
/*
* The watchpoint code uses the normal property-modification functions to install its
@ -873,11 +938,11 @@ js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Sha
* proceed without interference.
*/
if (IsWatchedProperty(cx, newShape))
return true;
return newShape;
JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id);
if (!wp)
return true;
return newShape;
return UpdateWatchpointShape(cx, wp, newShape);
}
@ -1034,6 +1099,13 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
JS_APPEND_LINK(&wp->links, &rt->watchPointList);
++rt->debuggerMutations;
}
/*
* Ensure that an object with watchpoints never has the same shape as an
* object without them, even if the watched properties are deleted.
*/
obj->watchpointOwnShapeChange(cx);
wp->handler = handler;
wp->closure = reinterpret_cast<JSObject*>(closure);
DBG_UNLOCK(rt);

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

@ -46,19 +46,19 @@
#if defined(JS_HAS_OBJ_WATCHPOINT) && defined(__cplusplus)
extern bool
extern const js::Shape *
js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape);
/*
* Update any watchpoints on |obj| on |new_shape->id| to use |new_shape|. Property-manipulating
* Update any watchpoints on |obj| on |newShape->id| to use |newShape|. Property-manipulating
* functions must call this any time it takes on a new shape to represent a potentially
* watched property, or when it mutates a shape's attributes/setter/getter.
*/
static inline bool
static inline const js::Shape *
js_UpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape)
{
if (JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
return true;
return newShape;
return js_SlowPathUpdateWatchpointsForShape(cx, obj, newShape);
}

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

@ -554,10 +554,7 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
// sure any updates also get written back to the trace native stack.
// For simplicity, we just leave trace, since this is presumably not
// a common operation.
if (JS_ON_TRACE(cx)) {
DeepBail(cx);
return false;
}
LeaveTrace(cx);
#endif
if (!InstanceOf(cx, obj, &js_ArgumentsClass, NULL))
@ -1707,6 +1704,46 @@ fun_enumerate(JSContext *cx, JSObject *obj)
return true;
}
static JSObject *
ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj)
{
JSFunction *fun = obj->getFunctionPrivate();
JS_ASSERT(fun->isInterpreted());
JS_ASSERT(!fun->isFunctionPrototype());
/*
* Assert that fun is not a compiler-created function object, which
* must never leak to script or embedding code and then be mutated.
* Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
*/
JS_ASSERT(!IsInternalFunctionObject(obj));
JS_ASSERT(!obj->isBoundFunction());
/*
* Make the prototype object an instance of Object with the same parent
* as the function object itself.
*/
JSObject *parent = obj->getParent();
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
if (!proto)
return NULL;
/*
* ECMA (15.3.5.2) says that a user-defined function's .prototype property
* is non-configurable, non-enumerable, and (initially) writable. Hence
* JSPROP_PERMANENT below. By contrast, the built-in constructors, such as
* Object (15.2.3.1) and Function (15.3.3.1), have non-writable
* .prototype properties. Those are eagerly defined, with attributes
* JSPROP_PERMANENT | JSPROP_READONLY, in js_InitClass.
*/
if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT))
return NULL;
return proto;
}
static JSBool
fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp)
@ -1733,36 +1770,8 @@ fun_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
if (fun->isNative() || fun->isFunctionPrototype())
return true;
/*
* Assert that fun is not a compiler-created function object, which
* must never leak to script or embedding code and then be mutated.
* Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
*/
JS_ASSERT(!IsInternalFunctionObject(obj));
JS_ASSERT(!obj->isBoundFunction());
/*
* Make the prototype object an instance of Object with the same parent
* as the function object itself.
*/
JSObject *parent = obj->getParent();
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
if (!ResolveInterpretedFunctionPrototype(cx, obj))
return false;
proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
if (!proto)
return false;
/*
* ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
* user-defined functions, but DontEnum | ReadOnly | DontDelete for
* native "system" constructors such as Object or Function. So lazily
* set the former here in fun_resolve, but eagerly define the latter
* in js_InitClass, with the right attributes.
*/
if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT))
return false;
*objp = obj;
return true;
}
@ -2633,6 +2642,28 @@ IsBuiltinFunctionConstructor(JSFunction *fun)
return fun->maybeNative() == Function;
}
const Shape *
LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
{
JSFunction *fun = funobj->getFunctionPrivate();
JS_ASSERT(fun->isInterpreted());
JS_ASSERT(!fun->isFunctionPrototype());
JS_ASSERT(!funobj->isBoundFunction());
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
const Shape *shape = funobj->nativeLookup(id);
if (!shape) {
if (!ResolveInterpretedFunctionPrototype(cx, funobj))
return false;
shape = funobj->nativeLookup(id);
}
JS_ASSERT(!shape->configurable());
JS_ASSERT(shape->isDataDescriptor());
JS_ASSERT(shape->hasSlot());
JS_ASSERT(!shape->isMethod());
return shape;
}
}
static JSBool

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

@ -432,6 +432,21 @@ GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
extern JS_FRIEND_API(bool)
IsBuiltinFunctionConstructor(JSFunction *fun);
/*
* Preconditions: funobj->isInterpreted() && !funobj->isFunctionPrototype() &&
* !funobj->isBoundFunction(). This is sufficient to establish that funobj has
* a non-configurable non-method .prototype data property, thought it might not
* have been resolved yet, and its value could be anything.
*
* Return the shape of the .prototype property of funobj, resolving it if
* needed. On error, return NULL.
*
* This is not safe to call on trace because it defines properties, which can
* trigger lookups that could reenter.
*/
const Shape *
LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
} /* namespace js */
extern JSString *

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

@ -1850,7 +1850,7 @@ js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
while ((script = *listp) != NULL) {
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
js_DestroyScriptFromGC(cx, script);
js_DestroyCachedScript(cx, script);
}
}
}

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

@ -76,6 +76,7 @@
#include "jslibmath.h"
#include "jsvector.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/MethodJIT-inl.h"
#include "methodjit/Logging.h"
#include "jsatominlines.h"
@ -505,7 +506,6 @@ BoxThisForVp(JSContext *cx, Value *vp)
if (!vp[1].isObject())
return !!js_PrimitiveToObject(cx, &vp[1]);
JS_ASSERT(IsSaneThisObject(vp[1].toObject()));
return true;
}
@ -638,7 +638,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
JS_ASSERT(fp->script() == script);
#ifdef JS_METHODJIT
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, fp);
mjit::CompileStatus status =
mjit::CanMethodJIT(cx, script, fp, mjit::CompileRequest_Interpreter);
if (status == mjit::Compile_Error)
return JS_FALSE;
@ -741,10 +742,6 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
AbortRecording(cx, "attempt to reenter VM while recording");
#ifdef JS_METHODJIT
if (TRACE_PROFILER(cx))
AbortProfiling(cx);
#endif
LeaveTrace(cx);
#endif
@ -781,7 +778,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
#ifdef JS_METHODJIT
/* Hoist dynamic checks from RunScript. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp);
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp, mjit::CompileRequest_JIT);
if (status == mjit::Compile_Error)
return false;
if (status != mjit::Compile_Okay)
@ -843,7 +840,6 @@ ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
JSObject *thisp = args.thisv().toObject().thisObject(cx);
if (!thisp)
return false;
JS_ASSERT(IsSaneThisObject(*thisp));
args.thisv().setObject(*thisp);
}
@ -2331,6 +2327,35 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#define LOAD_DOUBLE(PCOFF, dbl) \
(dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
bool methodJitFailed = false;
#ifdef JS_METHODJIT
#define MONITOR_BRANCH_METHODJIT() \
JS_BEGIN_MACRO \
mjit::CompileStatus status = \
mjit::CanMethodJITAtBranch(cx, script, regs.fp, regs.pc); \
if (status == mjit::Compile_Error) \
goto error; \
if (status == mjit::Compile_Okay) { \
void *ncode = \
script->nativeCodeForPC(regs.fp->isConstructing(), regs.pc); \
interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode); \
if (inlineCallCount) \
goto jit_return; \
goto leave_on_safe_point; \
} \
if (status == mjit::Compile_Abort) { \
methodJitFailed = true; \
} \
JS_END_MACRO
#else
#define MONITOR_BRANCH_METHODJIT() ((void) 0)
#endif
#ifdef JS_TRACER
#ifdef MOZ_TRACEVIS
@ -2363,24 +2388,38 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#define MONITOR_BRANCH() \
JS_BEGIN_MACRO \
if (TRACING_ENABLED(cx)) { \
MonitorResult r = MonitorLoopEdge(cx, inlineCallCount); \
if (r == MONITOR_RECORDING) { \
JS_ASSERT(TRACE_RECORDER(cx)); \
JS_ASSERT(!TRACE_PROFILER(cx)); \
MONITOR_BRANCH_TRACEVIS; \
ENABLE_INTERRUPTS(); \
CLEAR_LEAVE_ON_TRACE_POINT(); \
if (!TRACE_RECORDER(cx) && \
!TRACE_PROFILER(cx) && \
interpMode == JSINTERP_NORMAL && \
cx->methodJitEnabled && \
!methodJitFailed) \
{ \
MONITOR_BRANCH_METHODJIT(); \
} else { \
MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
if (r == MONITOR_RECORDING) { \
JS_ASSERT(TRACE_RECORDER(cx)); \
JS_ASSERT(!TRACE_PROFILER(cx)); \
MONITOR_BRANCH_TRACEVIS; \
ENABLE_INTERRUPTS(); \
CLEAR_LEAVE_ON_TRACE_POINT(); \
} \
RESTORE_INTERP_VARS(); \
JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
if (r == MONITOR_ERROR) \
goto error; \
} \
RESTORE_INTERP_VARS(); \
JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR); \
if (r == MONITOR_ERROR) \
goto error; \
} else { \
MONITOR_BRANCH_METHODJIT(); \
} \
JS_END_MACRO
#else /* !JS_TRACER */
#define MONITOR_BRANCH() ((void) 0)
#define MONITOR_BRANCH() \
JS_BEGIN_MACRO \
MONITOR_BRANCH_METHODJIT(); \
JS_END_MACRO
#endif /* !JS_TRACER */
@ -2511,10 +2550,6 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
ENABLE_INTERRUPTS();
} else if (TRACE_RECORDER(cx)) {
AbortRecording(cx, "attempt to reenter interpreter while recording");
#ifdef JS_METHODJIT
} else if (TRACE_PROFILER(cx)) {
AbortProfiling(cx);
#endif
}
if (regs.fp->hasImacropc())
@ -2637,7 +2672,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#ifdef JS_TRACER
#ifdef JS_METHODJIT
if (LoopProfile *prof = TRACE_PROFILER(cx)) {
if (TRACE_PROFILER(cx) && interpMode == JSINTERP_PROFILE) {
LoopProfile *prof = TRACE_PROFILER(cx);
JS_ASSERT(!TRACE_RECORDER(cx));
LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
switch (act) {
@ -4674,7 +4710,6 @@ BEGIN_CASE(JSOP_FUNCALL)
newscript, &flags);
if (JS_UNLIKELY(!newfp))
goto error;
JS_ASSERT_IF(!vp[1].isPrimitive(), IsSaneThisObject(vp[1].toObject()));
/* Initialize frame, locals. */
newfp->initCallFrame(cx, *callee, newfun, argc, flags);
@ -4702,7 +4737,10 @@ BEGIN_CASE(JSOP_FUNCALL)
#ifdef JS_METHODJIT
/* Try to ensure methods are method JIT'd. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp);
mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
? mjit::CompileRequest_Interpreter
: mjit::CompileRequest_JIT;
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp, request);
if (status == mjit::Compile_Error)
goto error;
if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay) {
@ -6929,7 +6967,7 @@ END_CASE(JSOP_ARRAYPUSH)
* This path is used when it's guaranteed the method can be finished
* inside the JIT.
*/
#if defined(JS_TRACER) && defined(JS_METHODJIT)
#if defined(JS_METHODJIT)
leave_on_safe_point:
#endif
return interpReturnOK;

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

@ -246,9 +246,12 @@ JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
JS_ASSERT(othersp >= otherfp->slots());
JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
size_t nbytes = (othersp - othervp) * sizeof(js::Value);
memcpy(vp, othervp, nbytes);
JS_ASSERT(vp == actualArgs() - 2);
PodCopy(vp, othervp, othersp - othervp);
JS_ASSERT(vp == this->actualArgs() - 2);
/* Catch bad-touching of non-canonical args (e.g., generator_trace). */
if (otherfp->hasOverflowArgs())
Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
/*
* Repoint Call, Arguments, Block and With objects to the new live frame.
@ -374,7 +377,6 @@ JSStackFrame::computeThis(JSContext *cx)
}
if (!js::BoxThisForVp(cx, &thisv - 1))
return false;
JS_ASSERT(IsSaneThisObject(thisv.toObject()));
return true;
}
@ -585,8 +587,8 @@ InvokeSessionGuard::invoke(JSContext *cx) const
formals_[-2] = savedCallee_;
formals_[-1] = savedThis_;
void *code;
#ifdef JS_METHODJIT
void *code;
if (!optimized() || !(code = script_->getJIT(false /* !constructing */)->invokeEntry))
#else
if (!optimized())

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

@ -590,7 +590,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
JSObject *proto = obj->getProto();
if (last) {
NativeIterator *lastni = last->getNativeIterator();
if (!(lastni->flags & JSITER_ACTIVE) &&
if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
obj->isNative() &&
obj->shape() == lastni->shapes_array[0] &&
proto && proto->isNative() &&
@ -627,7 +627,7 @@ GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
if (iterobj) {
NativeIterator *ni = iterobj->getNativeIterator();
if (!(ni->flags & JSITER_ACTIVE) &&
if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
ni->shapes_key == key &&
ni->shapes_length == shapes.length() &&
Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
@ -882,6 +882,10 @@ SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, IdPredicate predicat
memmove(idp, idp + 1, (props_end - (idp + 1)) * sizeof(jsid));
ni->props_end = ni->end() - 1;
}
/* Don't reuse modified native iterators. */
ni->flags |= JSITER_UNREUSABLE;
if (predicate.matchesAtMostOne())
break;
}
@ -1080,9 +1084,17 @@ generator_trace(JSTracer *trc, JSObject *obj)
JSStackFrame *fp = gen->floatingFrame();
JS_ASSERT(gen->liveFrame() == fp);
MarkValueRange(trc, gen->floatingStack, fp->formalArgsEnd(), "generator slots");
/*
* Currently, generators are not mjitted. Still, (overflow) args can be
* pushed by the mjit and need to be conservatively marked. Technically, the
* formal args and generator slots are safe for exact marking, but since the
* plan is to eventually mjit generators, it makes sense to future-proof
* this code and save someone an hour later.
*/
MarkStackRangeConservatively(trc, gen->floatingStack, fp->formalArgsEnd());
js_TraceStackFrame(trc, fp);
MarkValueRange(trc, fp->slots(), gen->regs.sp, "generator slots");
MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp);
}
Class js_GeneratorClass = {

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

@ -63,7 +63,8 @@
* For cacheable native iterators, whether the iterator is currently active.
* Not serialized by XDR.
*/
#define JSITER_ACTIVE 0x1000
#define JSITER_ACTIVE 0x1000
#define JSITER_UNREUSABLE 0x2000
namespace js {

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

@ -1249,9 +1249,23 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
JSScript *script = NULL;
JSScript **bucket = EvalCacheHash(cx, linearStr);
if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame())
if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame()) {
script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket);
/*
* Although the eval cache keeps a script alive from the perspective of
* the JS engine, from a jsdbgapi user's perspective each eval()
* creates and destroys a script. This hides implementation details and
* allows jsdbgapi clients to avoid calling JS_GetScriptObject after a
* script has been returned to the eval cache, which is invalid since
* script->u.object aliases script->u.nextToGC.
*/
if (script) {
js_CallNewScriptHook(cx, script, NULL);
MUST_FLOW_THROUGH("destroy");
}
}
/*
* We can't have a callerFrame (down in js::Execute's terms) if we're in
* global code (or if we're an indirect eval).
@ -1280,6 +1294,9 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
cx->runtime->atomState.evalAtom) &&
Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
MUST_FLOW_LABEL(destroy);
js_CallDestroyScriptHook(cx, script);
script->u.nextToGC = *bucket;
*bucket = script;
#ifdef CHECK_SCRIPT_OWNER
@ -2979,59 +2996,37 @@ js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0,
nanojit::ACCSET_STORE_ANY)
JSObject* FASTCALL
js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor)
JSObject * FASTCALL
js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot)
{
JS_ASSERT(JS_ON_TRACE(cx));
#ifdef DEBUG
JS_ASSERT(ctor->isFunction());
if (!ctor->ensureClassReservedSlots(cx))
return NULL;
jsid classPrototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
const Shape *shape = ctor->nativeLookup(classPrototypeId);
Value pval = shape ? ctor->getSlot(shape->slot) : MagicValue(JS_GENERIC_MAGIC);
JS_ASSERT(ctor->getFunctionPrivate()->isInterpreted());
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
const Shape *shape = ctor->nativeLookup(id);
JS_ASSERT(shape->slot == protoSlot);
JS_ASSERT(!shape->configurable());
JS_ASSERT(!shape->isMethod());
#endif
JSObject *parent = ctor->getParent();
JSObject *proto;
if (pval.isObject()) {
/* An object in ctor.prototype, let's use it as the new instance's proto. */
proto = &pval.toObject();
const Value &protov = ctor->getSlotRef(protoSlot);
if (protov.isObject()) {
proto = &protov.toObject();
} else {
/* A hole or a primitive: either way, we need to get Object.prototype. */
/*
* GetInterpretedFunctionPrototype found that ctor.prototype is
* primitive. Use Object.prototype for proto, per ES5 13.2.2 step 7.
*/
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
if (pval.isMagic(JS_GENERIC_MAGIC)) {
/*
* No ctor.prototype was set, so we inline-expand and optimize
* fun_resolve's prototype creation code.
*/
proto = NewNativeClassInstance(cx, clasp, proto, parent);
if (!proto)
return NULL;
JSFunction *fun = ctor->getFunctionPrivate();
if (!fun->isNative() && !fun->isFunctionPrototype()) {
if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
return NULL;
}
} else {
/*
* A primitive value in .prototype means to use Object.prototype
* for proto. See ES5 13.2.2 step 7.
*/
}
}
/*
* FIXME: 561785 at least. Quasi-natives including XML objects prevent us
* from easily or unconditionally calling NewNativeClassInstance here.
*/
gc::FinalizeKind kind = NewObjectGCKind(cx, clasp);
return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind);
}
JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0,
JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0,
nanojit::ACCSET_STORE_ANY)
#else /* !JS_TRACER */
@ -6970,17 +6965,5 @@ js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
}
}
#ifdef DEBUG
bool
IsSaneThisObject(JSObject &obj)
{
Class *clasp = obj.getClass();
return clasp != &js_CallClass &&
clasp != &js_BlockClass &&
clasp != &js_DeclEnvClass &&
clasp != &js_WithClass;
}
#endif
#endif /* DEBUG */

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

@ -518,6 +518,7 @@ struct JSObject : js::gc::Cell {
void protoShapeChange(JSContext *cx);
void shadowingShapeChange(JSContext *cx, const js::Shape &shape);
bool globalObjectOwnShapeChange(JSContext *cx);
void watchpointOwnShapeChange(JSContext *cx) { generateOwnShape(cx); }
void extensibleShapeChange(JSContext *cx) {
/* This will do for now. */
@ -1893,7 +1894,6 @@ JS_FRIEND_API(void) js_DumpObject(JSObject *obj);
JS_FRIEND_API(void) js_DumpValue(const js::Value &val);
JS_FRIEND_API(void) js_DumpId(jsid id);
JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, JSStackFrame *start = NULL);
bool IsSaneThisObject(JSObject &obj);
#endif
extern uintN

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

@ -151,6 +151,10 @@ JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp
JS_ASSERT(nativeContains(shape));
JS_ASSERT(shape.isMethod());
JS_ASSERT(&shape.methodObject() == &vp->toObject());
JS_ASSERT(shape.writable());
JS_ASSERT(shape.slot != SHAPE_INVALID_SLOT);
JS_ASSERT(shape.hasDefaultSetter() || shape.setterOp() == js_watch_set);
JS_ASSERT(!isGlobal()); /* i.e. we are not changing the global shape */
JSObject *funobj = &vp->toObject();
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
@ -161,9 +165,19 @@ JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp
return false;
funobj->setMethodObj(*this);
/*
* Replace the method property with an ordinary data property. This is
* equivalent to this->setProperty(cx, shape.id, vp) except that any
* watchpoint on the property is not triggered.
*/
uint32 slot = shape.slot;
const js::Shape *newshape = methodShapeChange(cx, shape);
if (!newshape)
return NULL;
JS_ASSERT(!newshape->isMethod());
JS_ASSERT(newshape->slot == slot);
vp->setObject(*funobj);
if (!js_SetPropertyHelper(cx, this, shape.id, 0, vp, false))
return false;
nativeSetSlot(slot, *vp);
#ifdef DEBUG
if (cx->runtime->functionMeterFilename) {

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

@ -139,7 +139,7 @@ JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0};
# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
#else
# define METER(x) /* nothing */
# define METER(x) ((void) 0)
#endif
bool
@ -768,10 +768,9 @@ JSObject::addProperty(JSContext *cx, jsid id,
return NULL;
/* Update any watchpoints referring to this property. */
if (!js_UpdateWatchpointsForShape(cx, this, shape)) {
shape = js_UpdateWatchpointsForShape(cx, this, shape);
if (!shape)
METER(wrapWatchFails);
return NULL;
}
return shape;
}
@ -896,13 +895,14 @@ JSObject::putProperty(JSContext *cx, jsid id,
return NULL;
}
const Shape *new_shape =
const Shape *newShape =
addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
if (!js_UpdateWatchpointsForShape(cx, this, new_shape)) {
METER(wrapWatchFails);
if (!newShape)
return NULL;
}
return new_shape;
newShape = js_UpdateWatchpointsForShape(cx, this, newShape);
if (!newShape)
METER(wrapWatchFails);
return newShape;
}
/* Property exists: search must have returned a valid *spp. */
@ -1038,12 +1038,10 @@ JSObject::putProperty(JSContext *cx, jsid id,
CHECK_SHAPE_CONSISTENCY(this);
METER(puts);
if (!js_UpdateWatchpointsForShape(cx, this, shape)) {
const Shape *newShape = js_UpdateWatchpointsForShape(cx, this, shape);
if (!newShape)
METER(wrapWatchFails);
return NULL;
}
return shape;
return newShape;
}
const Shape *
@ -1109,11 +1107,12 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m
lastProp->shape = js_GenerateShape(cx);
clearOwnShape();
if (!js_UpdateWatchpointsForShape(cx, this, shape)) {
shape = js_UpdateWatchpointsForShape(cx, this, shape);
if (!shape) {
METER(wrapWatchFails);
return NULL;
}
JS_ASSERT(shape == mutableShape);
newShape = mutableShape;
} else if (shape == lastProp) {
Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);

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

@ -1561,7 +1561,7 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
}
}
JS_FRIEND_API(void)
void
js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
{
JSDestroyScriptHook hook;
@ -1569,6 +1569,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
hook = cx->debugHooks->destroyScriptHook;
if (hook)
hook(cx, script, cx->debugHooks->destroyScriptHookData);
JS_ClearScriptTraps(cx, script);
}
static void
@ -1581,9 +1582,6 @@ DestroyScript(JSContext *cx, JSScript *script)
JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
#endif
js_CallDestroyScriptHook(cx, script);
JS_ClearScriptTraps(cx, script);
if (script->principals)
JSPRINCIPALS_DROP(cx, script->principals);
@ -1646,11 +1644,20 @@ void
js_DestroyScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(!cx->runtime->gcRunning);
js_CallDestroyScriptHook(cx, script);
DestroyScript(cx, script);
}
void
js_DestroyScriptFromGC(JSContext *cx, JSScript *script)
{
JS_ASSERT(cx->runtime->gcRunning);
js_CallDestroyScriptHook(cx, script);
DestroyScript(cx, script);
}
void
js_DestroyCachedScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(cx->runtime->gcRunning);
DestroyScript(cx, script);

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

@ -373,6 +373,8 @@ struct JSScript {
private:
uint16 version; /* JS version under which script was compiled */
size_t callCount_; /* Number of times the script has been called. */
public:
uint16 nfixed; /* number of slots besides stack operands in
slot array */
@ -473,6 +475,9 @@ struct JSScript {
return constructing ? jitCtor : jitNormal;
}
size_t callCount() const { return callCount_; }
size_t incCallCount() { return ++callCount_; }
JITScriptStatus getJITStatus(bool constructing) {
void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
if (addr == NULL)
@ -649,7 +654,7 @@ js_SweepScriptFilenames(JSRuntime *rt);
extern JS_FRIEND_API(void)
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
extern JS_FRIEND_API(void)
extern void
js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
/*
@ -659,13 +664,18 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
extern void
js_DestroyScript(JSContext *cx, JSScript *script);
/*
* If data is not null, it indicates that the script could been accessed only
* from that thread.
*/
extern void
js_DestroyScriptFromGC(JSContext *cx, JSScript *script);
/*
* Script objects may be cached and reused, in which case their JSD-visible
* lifetimes may be shorter than their actual lifetimes. Destroy one such
* script for real as part of a GC pass. From JSD's point of view, the script
* is already dead.
*/
extern void
js_DestroyCachedScript(JSContext *cx, JSScript *script);
extern void
js_TraceScript(JSTracer *trc, JSScript *script);

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

@ -381,6 +381,9 @@ class Conditionally {
template <class T1>
Conditionally(bool b, const T1 &t1) { if (b) t.construct(t1); }
template <class T1, class T2>
Conditionally(bool b, const T1 &t1, const T2 &t2) { if (b) t.construct(t1, t2); }
};
template <class T>

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

@ -2314,6 +2314,11 @@ TraceRecorder::TraceRecorder(JSContext* cx, TraceMonitor *tm,
JS_ASSERT(globalObj->hasOwnShape());
JS_ASSERT(cx->regs->pc == (jsbytecode*)fragment->ip);
#ifdef JS_METHODJIT
if (TRACE_PROFILER(cx))
AbortProfiling(cx);
#endif
JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
@ -5628,7 +5633,6 @@ TraceRecorder::startRecorder(JSContext* cx, TraceMonitor *tm, VMSideExit* anchor
JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc,
bool speculate)
{
JS_ASSERT(!tm->profile);
JS_ASSERT(!tm->needFlush);
JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
@ -6494,6 +6498,11 @@ TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
prev = tm->tracerState;
tm->tracerState = this;
#ifdef JS_METH
if (TRACE_PROFILER(cx))
AbortProfiling(cx);
#endif
JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL ||
JS_THREAD_DATA(cx)->recordingCompartment == cx->compartment);
@ -7086,8 +7095,6 @@ RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
TraceVisStateObj tvso(cx, S_MONITOR);
#endif
JS_ASSERT(!tm->profile);
/* Is the recorder currently active? */
if (tm->recorder) {
tm->recorder->assertInsideLoop();
@ -7296,6 +7303,8 @@ TraceRecorder::monitorRecording(JSOp op)
{
JS_ASSERT(!addPropShapeBefore);
JS_ASSERT(traceMonitor == &cx->compartment->traceMonitor);
TraceMonitor &localtm = *traceMonitor;
debug_only_stmt( JSContext *localcx = cx; )
assertInsideLoop();
@ -10483,13 +10492,6 @@ TraceRecorder::record_EnterFrame()
RETURN_STOP_A("recursion started inlining");
}
if (fp->isConstructing()) {
LIns* args[] = { callee_ins, w.nameImmpNonGC(&js_ObjectClass), cx_ins };
LIns* tv_ins = w.call(&js_CreateThisFromTrace_ci, args);
guard(false, w.eqp0(tv_ins), OOM_EXIT);
set(&fp->thisValue(), tv_ins);
}
return ARECORD_CONTINUE;
}
@ -11440,7 +11442,8 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
Value* vp = &stackval(0 - (2 + argc));
JSObject* funobj = &vp[0].toObject();
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, funobj);
JSFunction* fun = funobj->getFunctionPrivate();
JS_ASSERT(fun->isNative());
Native native = fun->u.n.native;
switch (argc) {
@ -11616,39 +11619,24 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
clasp = &js_ObjectClass;
JS_ASSERT(((jsuword) clasp & 3) == 0);
// Abort on |new Function|. js_CreateThis would allocate a regular-
// sized JSObject, not a Function-sized one. (The Function ctor would
// deep-bail anyway but let's not go there.)
// Abort on |new Function|. (FIXME: This restriction might not
// unnecessary now that the constructor creates the new function object
// itself.)
if (clasp == &js_FunctionClass)
RETURN_STOP("new Function");
if (!clasp->isNative())
RETURN_STOP("new with non-native ops");
if (fun->isConstructor()) {
vp[1].setMagicWithObjectOrNullPayload(NULL);
newobj_ins = w.immpMagicNull();
// Don't trace |new Math.sin(0)|.
if (!fun->isConstructor())
RETURN_STOP("new with non-constructor native function");
/* Treat this as a regular call, the constructor will behave correctly. */
mode = JSOP_CALL;
} else {
args[0] = w.immpObjGC(funobj);
args[1] = w.immpNonGC(clasp);
args[2] = cx_ins;
newobj_ins = w.call(&js_CreateThisFromTrace_ci, args);
guard(false, w.eqp0(newobj_ins), OOM_EXIT);
vp[1].setMagicWithObjectOrNullPayload(NULL);
newobj_ins = w.immpMagicNull();
/*
* emitNativeCall may take a snapshot below. To avoid having a type
* mismatch (e.g., where get(&vp[1]) is an object and vp[1] is
* null), we make sure vp[1] is some object. The actual object
* doesn't matter; JSOP_NEW and InvokeConstructor both overwrite
* vp[1] without observing its value.
*
* N.B. tracing specializes for functions, so pick a non-function.
*/
vp[1].setObject(*globalObj);
}
/* Treat this as a regular call, the constructor will behave correctly. */
mode = JSOP_CALL;
this_ins = newobj_ins;
} else {
this_ins = get(&vp[1]);
@ -13741,6 +13729,42 @@ TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp)
return afp;
}
JS_REQUIRES_STACK RecordingStatus
TraceRecorder::createThis(JSObject& ctor, LIns* ctor_ins, LIns** thisobj_insp)
{
JS_ASSERT(ctor.getFunctionPrivate()->isInterpreted());
if (ctor.getFunctionPrivate()->isFunctionPrototype())
RETURN_STOP("new Function.prototype");
if (ctor.isBoundFunction())
RETURN_STOP("new applied to bound function");
// Given the above conditions, ctor.prototype is a non-configurable data
// property with a slot.
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
const Shape *shape = LookupInterpretedFunctionPrototype(cx, &ctor);
if (!shape)
RETURN_ERROR("new f: error resolving f.prototype");
// At run time ctor might be a different instance of the same function. Its
// .prototype property might not be resolved yet. Guard on the function
// object's shape to make sure .prototype is there.
//
// However, if ctor_ins is constant, which is usual, we don't need to
// guard: .prototype is non-configurable, and an object's non-configurable
// data properties always stay in the same slot for the life of the object.
if (!ctor_ins->isImmP())
guardShape(ctor_ins, &ctor, ctor.shape(), "ctor_shape", snapshot(MISMATCH_EXIT));
// Pass the slot of ctor.prototype to js_CreateThisFromTrace. We can only
// bake the slot into the trace, not the value, since .prototype is
// writable.
uintN protoSlot = shape->slot;
LIns* args[] = { w.nameImmw(protoSlot), ctor_ins, cx_ins };
*thisobj_insp = w.call(&js_CreateThisFromTrace_ci, args);
guard(false, w.eqp0(*thisobj_insp), OOM_EXIT);
return RECORD_CONTINUE;
}
JS_REQUIRES_STACK RecordingStatus
TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing)
{
@ -13752,14 +13776,10 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc,
*/
if (fun->script()->isEmpty()) {
LIns* rval_ins;
if (constructing) {
LIns* args[] = { get(&fval), w.nameImmpNonGC(&js_ObjectClass), cx_ins };
LIns* tv_ins = w.call(&js_CreateThisFromTrace_ci, args);
guard(false, w.eqp0(tv_ins), OOM_EXIT);
rval_ins = tv_ins;
} else {
if (constructing)
CHECK_STATUS(createThis(fval.toObject(), get(&fval), &rval_ins));
else
rval_ins = w.immiUndefined();
}
stack(-2 - argc, rval_ins);
return RECORD_CONTINUE;
}
@ -13769,6 +13789,12 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc,
JSStackFrame* const fp = cx->fp();
if (constructing) {
LIns* thisobj_ins;
CHECK_STATUS(createThis(fval.toObject(), get(&fval), &thisobj_ins));
stack(-argc - 1, thisobj_ins);
}
// Generate a type map for the outgoing frame and stash it in the LIR
unsigned stackSlots = NativeStackSlots(cx, 0 /* callDepth */);
FrameInfo* fi = (FrameInfo*)
@ -17003,7 +17029,6 @@ LookupLoopProfile(TraceMonitor *tm, jsbytecode *pc)
void
LoopProfile::stopProfiling(JSContext *cx)
{
JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
JS_THREAD_DATA(cx)->profilingCompartment = NULL;
@ -17022,10 +17047,14 @@ MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
*blacklist = false;
/*
* We may have re-entered Interpret while profiling. We don't profile
* the nested invocation.
* This is the only place where we check for re-entering the profiler.
* The assumption is that MonitorTracePoint is the only place where we
* start profiling. When we do so, we enter an interpreter frame with
* JSINTERP_PROFILE mode. All other entry points to the profiler check
* that the interpreter mode is JSINTERP_PROFILE. If it isn't, they
* don't profile.
*/
if (tm->profile)
if (TRACE_PROFILER(cx))
return TPA_Nothing;
jsbytecode* pc = cx->regs->pc;
@ -17055,7 +17084,6 @@ MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
tm->profile = prof;
JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL);
JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
JS_THREAD_DATA(cx)->profilingCompartment = cx->compartment;
@ -17092,6 +17120,9 @@ LoopProfile::profileOperation(JSContext* cx, JSOp op)
{
TraceMonitor* tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
JS_ASSERT(tm == traceMonitor);
JS_ASSERT(&entryScript->compartment->traceMonitor == tm);
if (profiled) {
stopProfiling(cx);
return ProfComplete;
@ -17448,10 +17479,10 @@ LoopProfile::decide(JSContext *cx)
}
JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, JSInterpMode interpMode)
{
TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
if (tm->profile)
if (interpMode == JSINTERP_PROFILE && tm->profile)
return tm->profile->profileLoopEdge(cx, inlineCallCount);
else
return RecordLoopEdge(cx, tm, inlineCallCount);
@ -17473,7 +17504,7 @@ AbortProfiling(JSContext *cx)
#else /* JS_METHODJIT */
JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, JSInterpMode interpMode)
{
TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
return RecordLoopEdge(cx, tm, inlineCallCount);

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

@ -1479,15 +1479,17 @@ class TraceRecorder
VMSideExit* exit);
JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj,
nanojit::LIns* obj_ins,
VMSideExit *exit);
VMSideExit* exit);
JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker();
JS_REQUIRES_STACK void putActivationObjects();
JS_REQUIRES_STACK RecordingStatus createThis(JSObject& ctor, nanojit::LIns* ctor_ins,
nanojit::LIns** thisobj_insp);
JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
unsigned *depthp);
JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins);
JS_REQUIRES_STACK void guardNotHole(nanojit::LIns *argsobj_ins, nanojit::LIns *ids_ins);
JS_REQUIRES_STACK void guardNotHole(nanojit::LIns* argsobj_ins, nanojit::LIns* ids_ins);
JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor,
nanojit::LIns*& proto_ins);
JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSProtoKey key,
@ -1690,10 +1692,7 @@ class TraceRecorder
#define TRACE_2(x,a,b) TRACE_ARGS(x, (a, b))
extern JS_REQUIRES_STACK MonitorResult
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount);
extern JS_REQUIRES_STACK MonitorResult
ProfileLoopEdge(JSContext* cx, uintN& inlineCallCount);
MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, JSInterpMode interpMode);
extern JS_REQUIRES_STACK TracePointAction
RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist);

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

@ -768,7 +768,7 @@ class TypedArrayTemplate
cx->destroy<ThisTypeArray>(tarray);
return false;
}
} else if (argv[0].isObject()) {
} else if (argc > 0 && argv[0].isObject()) {
int32_t byteOffset = -1;
int32_t length = -1;
@ -936,7 +936,7 @@ class TypedArrayTemplate
}
// first arg must be either a typed array or a JS array
if (!argv[0].isObject()) {
if (argc == 0 || !argv[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;

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

@ -1196,7 +1196,7 @@ SetValueRangeToUndefined(Value *beg, Value *end)
static JS_ALWAYS_INLINE void
SetValueRangeToUndefined(Value *vec, size_t len)
{
return SetValueRangeToUndefined(vec, vec + len);
SetValueRangeToUndefined(vec, vec + len);
}
static JS_ALWAYS_INLINE void
@ -1209,7 +1209,30 @@ SetValueRangeToNull(Value *beg, Value *end)
static JS_ALWAYS_INLINE void
SetValueRangeToNull(Value *vec, size_t len)
{
return SetValueRangeToNull(vec, vec + len);
SetValueRangeToNull(vec, vec + len);
}
/*
* To really poison a set of values, using 'magic' or 'undefined' isn't good
* enough since often these will just be ignored by buggy code (see bug 629974)
* in debug builds and crash in release builds. Instead, we use a safe-for-crash
* pointer.
*/
static JS_ALWAYS_INLINE void
Debug_SetValueRangeToCrashOnTouch(Value *beg, Value *end)
{
#ifdef DEBUG
for (Value *v = beg; v != end; ++v)
v->setObject(*reinterpret_cast<JSObject *>(0x42));
#endif
}
static JS_ALWAYS_INLINE void
Debug_SetValueRangeToCrashOnTouch(Value *vec, size_t len)
{
#ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
#endif
}
} /* namespace js */

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

@ -168,13 +168,13 @@ class LinkerHelper : public JSC::LinkBuffer
if (!ep)
return ep;
m_size = masm.size();
m_code = executableCopy(masm, ep);
if (!m_code) {
ep->release();
js_ReportOutOfMemory(cx);
return NULL;
}
m_size = masm.size(); // must come after the call to executableCopy()
return ep;
}

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

@ -645,7 +645,8 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
#ifdef JS_TRACER
jitTraceICs[i].loopCounterStart = GetHotloop(cx);
#endif
jitTraceICs[i].loopCounter = jitTraceICs[i].loopCounterStart;
jitTraceICs[i].loopCounter = jitTraceICs[i].loopCounterStart
- cx->compartment->backEdgeCount(traceICs[i].jumpTarget);
stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]);
}
@ -4032,7 +4033,8 @@ mjit::Compiler::iter(uintN flags)
/* Test for active iterator. */
Address flagsAddr(nireg, offsetof(NativeIterator, flags));
masm.load32(flagsAddr, T1);
Jump activeIterator = masm.branchTest32(Assembler::NonZero, T1, Imm32(JSITER_ACTIVE));
Jump activeIterator = masm.branchTest32(Assembler::NonZero, T1,
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE));
stubcc.linkExit(activeIterator, Uses(1));
/* Compare shape of object with iterator. */
@ -4224,9 +4226,8 @@ mjit::Compiler::iterEnd()
Address flagAddr(T1, offsetof(NativeIterator, flags));
masm.loadPtr(flagAddr, T2);
/* Test for (flags == ENUMERATE | ACTIVE). */
Jump notEnumerate = masm.branch32(Assembler::NotEqual, T2,
Imm32(JSITER_ENUMERATE | JSITER_ACTIVE));
/* Test for a normal enumerate iterator. */
Jump notEnumerate = masm.branchTest32(Assembler::Zero, T2, Imm32(JSITER_ENUMERATE));
stubcc.linkExit(notEnumerate, Uses(1));
/* Clear active bit. */

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

@ -68,6 +68,7 @@
#include "jscntxtinlines.h"
#include "jsatominlines.h"
#include "StubCalls-inl.h"
#include "MethodJIT-inl.h"
#include "jsautooplen.h"
@ -170,12 +171,7 @@ top:
}
/*
* Clean up a frame and return. popFrame indicates whether to additionally pop
* the frame and store the return value on the caller's stack. The frame will
* normally be popped by the caller on return from a call into JIT code,
* so must be popped here when that caller code will not execute. This can be
* either because of a call into an un-JITable script, or because the call is
* throwing an exception.
* Clean up a frame and return.
*/
static void
InlineReturn(VMFrame &f)
@ -311,8 +307,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
/*
* FixupArity/RemovePartialFrame expect to be called after the early
* prologue. Pass the existing value for ncode, it has already been set
* by the jit code calling into this stub.
* prologue.
*/
fp->initCallFrameEarlyPrologue(fun, nactual);
@ -333,7 +328,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
THROWV(NULL);
CompileStatus status = CanMethodJIT(cx, script, fp);
CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
if (status == Compile_Okay)
return script->getJIT(fp->isConstructing())->invokeEntry;
@ -348,7 +343,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
}
static inline bool
UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc)
{
JSContext *cx = f.cx;
Value *vp = f.regs.sp - (argc + 2);
@ -363,8 +358,6 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
f.entryfp, &f.stackLimit);
if (JS_UNLIKELY(!newfp))
return false;
JS_ASSERT_IF(!vp[1].isPrimitive() && !(flags & JSFRAME_CONSTRUCTING),
IsSaneThisObject(vp[1].toObject()));
/* Initialize frame, locals. */
newfp->initCallFrame(cx, callee, newfun, argc, flags);
@ -380,11 +373,14 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
/* Try to compile if not already compiled. */
if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
if (mjit::TryCompile(cx, newfp) == Compile_Error) {
CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter);
if (status == Compile_Error) {
/* A runtime exception was thrown, get out. */
InlineReturn(f);
return false;
}
if (status == Compile_Abort)
*unjittable = true;
}
/* If newscript was successfully compiled, run it. */
@ -420,7 +416,7 @@ stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
/* Try to do a fast inline call before the general Invoke path. */
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
ucr->callee = &vp->toObject();
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, argc))
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc))
THROW();
} else {
if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
@ -470,7 +466,7 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
if (ucr->fun->isInterpreted()) {
if (!UncachedInlineCall(f, 0, &ucr->codeAddr, argc))
if (!UncachedInlineCall(f, 0, &ucr->codeAddr, &ucr->unjittable, argc))
THROW();
return;
}

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

@ -38,7 +38,7 @@
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_logging_h__
#if !defined jsjaeger_logging_h__ && (defined JS_METHODJIT || defined JS_TRACER)
#define jsjaeger_logging_h__
#include "prmjtime.h"

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

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* David Mandelin <dmandelin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_methodjit_inl_h__ && defined JS_METHODJIT
#define jsjaeger_methodjit_inl_h__
namespace js {
namespace mjit {
enum CompileRequest
{
CompileRequest_Interpreter,
CompileRequest_JIT
};
/* Number of times a script must be called before we run it in the methodjit. */
static const size_t CALLS_BEFORE_COMPILE = 16;
/* Number of loop back-edges we execute in the interpreter before methodjitting. */
static const size_t BACKEDGES_BEFORE_COMPILE = 16;
static inline CompileStatus
CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp, CompileRequest request)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (request == CompileRequest_Interpreter &&
status == JITScript_None &&
!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
script->incCallCount() <= CALLS_BEFORE_COMPILE)
{
return Compile_Skipped;
}
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
/*
* Called from a backedge in the interpreter to decide if we should transition to the
* methodjit. If so, we compile the given function.
*/
static inline CompileStatus
CanMethodJITAtBranch(JSContext *cx, JSScript *script, JSStackFrame *fp, jsbytecode *pc)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (status == JITScript_None &&
!cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
cx->compartment->incBackEdgeCount(pc) <= BACKEDGES_BEFORE_COMPILE)
{
return Compile_Skipped;
}
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
}
}
#endif

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

@ -394,7 +394,8 @@ enum CompileStatus
{
Compile_Okay,
Compile_Abort,
Compile_Error
Compile_Error,
Compile_Skipped
};
void JS_FASTCALL
@ -406,19 +407,6 @@ TryCompile(JSContext *cx, JSStackFrame *fp);
void
ReleaseScriptCode(JSContext *cx, JSScript *script);
static inline CompileStatus
CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp)
{
if (!cx->methodJitEnabled)
return Compile_Abort;
JITScriptStatus status = script->getJITStatus(fp->isConstructing());
if (status == JITScript_Invalid)
return Compile_Abort;
if (status == JITScript_None)
return TryCompile(cx, fp);
return Compile_Okay;
}
struct CallSite
{
uint32 codeOffset;

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

@ -950,7 +950,8 @@ class CallCompiler : public BaseCompiler
// If the function cannot be jitted (generally unjittable or empty script),
// patch this site to go to a slow path always.
if (!ucr.codeAddr) {
disable(jit);
if (ucr.unjittable)
disable(jit);
return NULL;
}

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

@ -80,7 +80,7 @@ void JS_FASTCALL LeaveScript(VMFrame &f);
* These functions can have one of two results:
*
* (1) The function was executed in the interpreter. Then all fields
* are NULL.
* are NULL except unjittable.
*
* (2) The function was not executed, and the function has been compiled
* to JM native code. Then all fields are non-NULL.
@ -89,11 +89,13 @@ struct UncachedCallResult {
JSObject *callee; // callee object
JSFunction *fun; // callee function
void *codeAddr; // code address of compiled callee function
bool unjittable; // did we try to JIT and fail?
void init() {
callee = NULL;
fun = NULL;
codeAddr = NULL;
unjittable = false;
}
};

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

@ -582,6 +582,8 @@ usage(void)
" -i Enable interactive read-eval-print loop\n"
" -j Enable the TraceMonkey tracing JIT\n"
" -m Enable the JaegerMonkey method JIT\n"
" -a Always method JIT, ignore internal tuning\n"
" This only has effect with -m\n"
" -p Enable loop profiling for TraceMonkey\n"
" -d Enable debug mode\n"
" -b Print timing statistics\n"
@ -634,9 +636,10 @@ static const struct {
} js_options[] = {
{"anonfunfix", JSOPTION_ANONFUNFIX},
{"atline", JSOPTION_ATLINE},
{"jitprofiling", JSOPTION_PROFILING},
{"tracejit", JSOPTION_JIT},
{"methodjit", JSOPTION_METHODJIT},
{"jitprofiling", JSOPTION_PROFILING},
{"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
{"relimit", JSOPTION_RELIMIT},
{"strict", JSOPTION_STRICT},
{"werror", JSOPTION_WERROR},
@ -807,6 +810,10 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
break;
case 'a':
JS_ToggleOptions(cx, JSOPTION_METHODJIT_ALWAYS);
break;
case 'p':
enableProfiling = !enableProfiling;
JS_ToggleOptions(cx, JSOPTION_PROFILING);

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

@ -25,3 +25,11 @@ skip-if(!xulRuntime.shell) script clone-errors.js
skip-if(!xulRuntime.shell) script clone-forge.js
script set-property-non-extensible.js
script recursion.js
script regress-627984-1.js
script regress-627984-2.js
script regress-627984-3.js
script regress-627984-4.js
script regress-627984-5.js
script regress-627984-6.js
script regress-627984-7.js
script regress-631723.js

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

@ -0,0 +1,16 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// See bug 627984, comment 17, item 1.
var obj;
var methods = [];
for (var i = 0; i < 2; i++) {
obj = {m: function () { return this.x; }};
obj.watch("m", function (id, oldval, newval) { methods[i] = oldval; });
obj.m = 0;
}
assertEq(typeof methods[0], "function");
assertEq(typeof methods[1], "function");
assertEq(methods[0] !== methods[1], true);
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,15 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// See bug 627984, comment 17, item 2.
var obj = {};
var x;
obj.watch("m", function (id, oldval, newval) {
x = this.m;
return newval;
});
delete obj.m;
obj.m = function () { return this.method; };
obj.m = 2;
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,14 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Don't write string value to method slot.
// See bug 627984, comment 17, item 2.
var obj = {};
obj.watch("m", function (id, oldval, newval) {
return 'ok';
});
delete obj.m;
obj.m = function () { return this.x; };
assertEq(obj.m, 'ok');
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,15 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// See bug 627984, comment 17, item 3.
var obj = {};
obj.watch("m", function (id, oldval, newval) {
delete obj.m;
obj.m = function () {};
return newval;
});
delete obj.m;
obj.m = 1;
assertEq(obj.m, 1);
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,13 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Bug 627984 comment 11.
var o = ({});
o.p = function() {};
o.watch('p', function() { });
o.q = function() {}
delete o.p;
o.p = function() {};
assertEq(o.p, void 0);
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,15 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Bug 627984 description.
var o = Array;
o.p = function() {};
o.watch('p', function() { });
for(var x in o) {
o[x];
}
delete o.p;
o.p = function() {};
assertEq(o.p, void 0);
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,9 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// See bug 627984 comment 20.
var obj = {m: function () {}};
obj.watch("m", function () { throw 'FAIL'; });
var f = obj.m; // don't call the watchpoint
reportCompare(0, 0, 'ok');

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

@ -0,0 +1,10 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var o = {a:1, b:2};
o.watch("p", function() { return 13; });
delete o.p;
o.p = 0;
assertEq(o.p, 13);
reportCompare(0, 0, 'ok');

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

@ -650,7 +650,8 @@ function optionsClear() {
if (optionName &&
optionName != "methodjit" &&
optionName != "tracejit" &&
optionName != "jitprofiling")
optionName != "jitprofiling" &&
optionName != "methodjit_always")
{
options(optionName);
}

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

@ -33,5 +33,6 @@ user_pref("javascript.options.methodjit.chrome", false);
user_pref("javascript.options.methodjit.content", true);
user_pref("javascript.options.jitprofiling.chrome", false);
user_pref("javascript.options.jitprofiling.content", true);
user_pref("javascript.options.methodjit_always", false);
user_pref("javascript.options.strict", false);
user_pref("javascript.options.werror", false);

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

@ -1322,6 +1322,9 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
case 'm':
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
break;
case 'p':
JS_ToggleOptions(cx, JSOPTION_PROFILING);
break;
default:
return usage();
}

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

@ -83,6 +83,7 @@ _TEST_FILES = bug500931_helper.html \
test_bug629331.html \
test1_bug629331.html \
test2_bug629331.html \
test_bug618017.html \
$(NULL)
#test_bug484107.html \

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

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=618017
Parsing XML must not override the version.
-->
<head>
<title>Test for Bug 618017</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type='application/javascript;version=1.7'>
let x = 12;
function doLetEval() {
ok(eval('let x = 13; x') === 13, 'let statement is valid syntax in version 1.7');
}
</script>
<script type='application/javascript;version=1.5'>
ok(uneval(<test />) === '<test/>', 'xml can be parsed in any version');
doLetEval(); // Call to a function with a different version.
</script>
</body>
</html>

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

@ -184,11 +184,12 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj
if (!wn->GetClassInfo())
return DoubleWrap(cx, obj, flags);
JSAutoEnterCompartment ac;
if (!ac.enter(cx, obj))
return nsnull;
XPCCallContext ccx(JS_CALLER, cx, obj);
{
JSAutoEnterCompartment ac;
if (!ac.enter(cx, obj))
return nsnull;
XPCCallContext ccx(JS_CALLER, cx, obj);
if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
// We have a precreate hook. This object might enforce that we only
// ever create JS object for it.
@ -211,16 +212,12 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj
}
}
// The object we're looking at might allow us to create a new wrapped
// native in the new scope. Try it and continue wrapping on the
// possibly-new object.
JSAutoEnterCompartment ac;
if (!ac.enter(cx, scope))
return nsnull;
// NB: Passing a holder here inhibits slim wrappers under
// WrapNativeToJSVal.
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
// This public WrapNativeToJSVal API enters the compartment of 'scope'
// so we don't have to.
jsval v;
nsresult rv =
nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull,

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

@ -604,6 +604,7 @@ pref("javascript.options.methodjit.content", true);
pref("javascript.options.methodjit.chrome", false);
pref("javascript.options.jitprofiling.content", true);
pref("javascript.options.jitprofiling.chrome", false);
pref("javascript.options.methodjit_always", false);
// This preference limits the memory usage of javascript.
// If you want to change these values for your device,
// please find Bug 417052 comment 17 and Bug 456721