Merge tracemonkey to mozilla-central.

This commit is contained in:
Robert Sayre 2010-08-01 16:00:36 -07:00
Родитель d37a1b83f6 2b561f9f3e
Коммит 57905eb5c0
35 изменённых файлов: 2608 добавлений и 821 удалений

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

@ -72,7 +72,7 @@ var tokens = [
// Nonterminal tree node type codes.
"SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
"ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
"GROUP", "LIST", "LET_STM", "LET_EXP", "LET_DEF",
"GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL",
// Terminals.
"IDENTIFIER", "NUMBER", "STRING", "REGEXP",
@ -81,7 +81,7 @@ var tokens = [
"break",
"case", "catch", "const", "continue",
"debugger", "default", "delete", "do",
"else", "enum",
"else",
"false", "finally", "for", "function",
"if", "in", "instanceof",
"let",

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

@ -71,10 +71,27 @@ var global = {
x2.scope = x.scope;
ExecutionContext.current = x2;
try {
execute(parse(s), x2);
execute(parse(new VanillaBuilder, s), x2);
} catch (e if e == THROW) {
x.result = x2.result;
throw e;
} catch (e if e instanceof SyntaxError) {
x.result = e;
throw THROW;
} catch (e if e instanceof InternalError) {
/*
* If we get too much recursion during parsing we need to re-throw
* it as a narcissus THROW.
*
* See bug 152646.
*/
var re = /InternalError: (script stack space quota is exhausted|too much recursion)/;
if (re.test(e.toString())) {
x.result = e;
throw THROW;
} else {
throw e;
}
} finally {
ExecutionContext.current = x;
}
@ -106,8 +123,9 @@ var global = {
var t = new Tokenizer("anonymous(" + p + ") {" + b + "}");
// NB: Use the STATEMENT_FORM constant since we don't want to push this
// function onto the null compilation context.
var f = FunctionDefinition(t, null, false, STATEMENT_FORM);
// function onto the fake compilation context.
var x = { builder: new VanillaBuilder };
var f = FunctionDefinition(t, x, false, STATEMENT_FORM);
var s = {object: global, parent: null};
return newFunction(f,{scope:s});
},
@ -482,7 +500,7 @@ function execute(n, x) {
case ASSIGN:
r = execute(n[0], x);
t = n[0].assignOp;
t = n.assignOp;
if (t)
u = getValue(r);
v = getValue(execute(n[1], x));
@ -1007,7 +1025,7 @@ function evaluate(s, f, l) {
var x2 = new ExecutionContext(GLOBAL_CODE);
ExecutionContext.current = x2;
try {
execute(parse(s, f, l), x2);
execute(parse(new VanillaBuilder, s, f, l), x2);
} catch (e if e == THROW) {
if (x) {
x.result = x2.result;

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

@ -1,4 +1,4 @@
/* vim: set sw=4 ts=8 et tw=78: */
/* vim: set sw=4 ts=4 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -57,7 +57,9 @@ for (var op in opTypeNames) {
}
}
// file ptr, path to file, line number -> Tokenizer
/*
* Tokenizer :: (file ptr, path, line number) -> Tokenizer
*/
function Tokenizer(s, f, l) {
this.cursor = 0;
this.source = String(s);
@ -65,22 +67,23 @@ function Tokenizer(s, f, l) {
this.tokenIndex = 0;
this.lookahead = 0;
this.scanNewlines = false;
this.scanOperand = true;
this.filename = f || "";
this.lineno = l || 1;
}
Tokenizer.prototype = {
get done() {
return this.peek() == END;
// We need to set scanOperand to true here because the first thing
// might be a regexp.
return this.peek(true) == END;
},
get token() {
return this.tokens[this.tokenIndex];
},
match: function (tt) {
return this.get() == tt || this.unget();
match: function (tt, scanOperand) {
return this.get(scanOperand) == tt || this.unget();
},
mustMatch: function (tt) {
@ -89,7 +92,7 @@ Tokenizer.prototype = {
return this.token;
},
peek: function () {
peek: function (scanOperand) {
var tt, next;
if (this.lookahead) {
next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
@ -97,15 +100,15 @@ Tokenizer.prototype = {
? NEWLINE
: next.type;
} else {
tt = this.get();
tt = this.get(scanOperand);
this.unget();
}
return tt;
},
peekOnSameLine: function () {
peekOnSameLine: function (scanOperand) {
this.scanNewlines = true;
var tt = this.peek();
var tt = this.peek(scanOperand);
this.scanNewlines = false;
return tt;
},
@ -334,13 +337,6 @@ Tokenizer.prototype = {
op += '=';
} else {
token.type = tokenIds[opTypeNames[op]];
if (this.scanOperand) {
switch (token.type) {
case PLUS: token.type = UNARY_PLUS; break;
case MINUS: token.type = UNARY_MINUS; break;
}
}
token.assignOp = null;
}
@ -364,10 +360,13 @@ Tokenizer.prototype = {
token.value = id;
},
// void -> token type
// It consumes input *only* if there is no lookahead.
// Dispatch to the appropriate lexing function depending on the input.
get: function () {
/*
* Tokenizer.get :: void -> token type
*
* Consumes input *only* if there is no lookahead.
* Dispatch to the appropriate lexing function depending on the input.
*/
get: function (scanOperand) {
var token;
while (this.lookahead) {
--this.lookahead;
@ -395,7 +394,7 @@ Tokenizer.prototype = {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
ch === '$' || ch === '_') {
this.lexIdent(ch);
} else if (this.scanOperand && ch === '/') {
} else if (scanOperand && ch === '/') {
this.lexRegExp(ch);
} else if (ch in opTokens) {
this.lexOp(ch);
@ -419,8 +418,11 @@ Tokenizer.prototype = {
return token.type;
},
// void -> undefined
// match depends on unget returning undefined.
/*
* Tokenizer.unget :: void -> undefined
*
* Match depends on unget returning undefined.
*/
unget: function () {
if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
this.tokenIndex = (this.tokenIndex - 1) & 3;
@ -431,6 +433,25 @@ Tokenizer.prototype = {
e.source = this.source;
e.cursor = this.cursor;
return e;
},
save: function () {
return {
cursor: this.cursor,
tokenIndex: this.tokenIndex,
tokens: this.tokens.slice(),
lookahead: this.lookahead,
scanNewlines: this.scanNewlines,
lineno: this.lineno
};
},
rewind: function(point) {
this.cursor = point.cursor;
this.tokenIndex = point.tokenIndex;
this.tokens = point.tokens.slice();
this.lookahead = point.lookahead;
this.scanNewline = point.scanNewline;
this.lineno = point.lineno;
}
};

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

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

@ -546,7 +546,7 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
#endif
JSRuntime::JSRuntime()
: gcChunkAllocator(&defaultGCChunkAllocator)
: gcChunkAllocator(&defaultGCChunkAllocator)
{
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
JS_INIT_CLIST(&contextList);
@ -557,6 +557,16 @@ JSRuntime::JSRuntime()
bool
JSRuntime::init(uint32 maxbytes)
{
#ifdef DEBUG
functionMeterFilename = getenv("JS_FUNCTION_STATFILE");
if (functionMeterFilename) {
if (!methodReadBarrierCountMap.init())
return false;
if (!unjoinedFunctionCountMap.init())
return false;
}
#endif
if (!(defaultCompartment = new JSCompartment(this)) ||
!defaultCompartment->init()) {
return false;

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

@ -1778,8 +1778,8 @@ sort_compare_strings(void *arg, const void *a, const void *b, int *result)
return JS_TRUE;
}
static JSBool
array_sort(JSContext *cx, uintN argc, Value *vp)
JSBool
js::array_sort(JSContext *cx, uintN argc, Value *vp)
{
jsuint len, newlen, i, undefs;
size_t elemsize;

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

@ -190,6 +190,15 @@ extern bool
js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp,
void *arg, void *tmp, JSMergeSortElemType elemType);
/*
* The Array.prototype.sort fast-native entry point is exported for joined
* function optimization in js{interp,tracer}.cpp.
*/
namespace js {
extern JSBool
array_sort(JSContext *cx, uintN argc, js::Value *vp);
}
#ifdef DEBUG_ARRAYS
extern JSBool
js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv, js::Value *rval);

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

@ -959,76 +959,87 @@ private:
FILE *mFile;
};
#ifdef JS_EVAL_CACHE_METERING
static void
DumpEvalCacheMeter(JSContext *cx)
{
struct {
const char *name;
ptrdiff_t offset;
} table[] = {
if (const char *filename = getenv("JS_EVALCACHE_STATFILE")) {
struct {
const char *name;
ptrdiff_t offset;
} table[] = {
#define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
EVAL_CACHE_METER_LIST(frob)
EVAL_CACHE_METER_LIST(frob)
#undef frob
};
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
};
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
static JSAutoFile fp;
if (!fp) {
fp.open("/tmp/evalcache.stats", "w");
if (!fp)
static JSAutoFile fp;
if (!fp && !fp.open(filename, "w"))
return;
}
fprintf(fp, "eval cache meter (%p):\n",
fprintf(fp, "eval cache meter (%p):\n",
#ifdef JS_THREADSAFE
(void *) cx->thread
(void *) cx->thread
#else
(void *) cx->runtime
(void *) cx->runtime
#endif
);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
fprintf(fp, "%-8.8s %llu\n",
table[i].name,
(unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
fprintf(fp, "%-8.8s %llu\n",
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);
fflush(fp);
}
fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
fflush(fp);
}
# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
#endif
#ifdef JS_FUNCTION_METERING
static void
DumpFunctionCountMap(const char *title, JSRuntime::FunctionCountMap &map, FILE *fp)
{
fprintf(fp, "\n%s count map:\n", title);
for (JSRuntime::FunctionCountMap::Range r = map.all(); !r.empty(); r.popFront()) {
JSFunction *fun = r.front().key;
int32 count = r.front().value;
fprintf(fp, "%10d %s:%u\n", count, fun->u.i.script->filename, fun->u.i.script->lineno);
}
}
static void
DumpFunctionMeter(JSContext *cx)
{
struct {
const char *name;
ptrdiff_t offset;
} table[] = {
if (const char *filename = cx->runtime->functionMeterFilename) {
struct {
const char *name;
ptrdiff_t offset;
} table[] = {
#define frob(x) { #x, offsetof(JSFunctionMeter, x) }
FUNCTION_KIND_METER_LIST(frob)
FUNCTION_KIND_METER_LIST(frob)
#undef frob
};
JSFunctionMeter *fm = &cx->runtime->functionMeter;
};
JSFunctionMeter *fm = &cx->runtime->functionMeter;
static JSAutoFile fp;
if (!fp) {
fp.open("/tmp/function.stats", "a");
if (!fp)
static JSAutoFile fp;
if (!fp && !fp.open(filename, "w"))
return;
}
fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
fprintf(fp, "%-11.11s %d\n",
table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i)
fprintf(fp, "%-19.19s %d\n", table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
DumpFunctionCountMap("method read barrier", cx->runtime->methodReadBarrierCountMap, fp);
DumpFunctionCountMap("unjoined function", cx->runtime->unjoinedFunctionCountMap, fp);
putc('\n', fp);
fflush(fp);
}
fflush(fp);
}
# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
#endif
#endif /* DEBUG && XP_UNIX */

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

@ -919,18 +919,13 @@ struct TraceMonitor {
# define JS_ON_TRACE(cx) JS_FALSE
#endif
#ifdef DEBUG_brendan
# define JS_EVAL_CACHE_METERING 1
# define JS_FUNCTION_METERING 1
#endif
/* Number of potentially reusable scriptsToGC to search for the eval cache. */
#ifndef JS_EVAL_CACHE_SHIFT
# define JS_EVAL_CACHE_SHIFT 6
#endif
#define JS_EVAL_CACHE_SIZE JS_BIT(JS_EVAL_CACHE_SHIFT)
#ifdef JS_EVAL_CACHE_METERING
#ifdef DEBUG
# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope)
# define identity(x) x
@ -941,10 +936,14 @@ struct JSEvalCacheMeter {
# undef identity
#endif
#ifdef JS_FUNCTION_METERING
#ifdef DEBUG
# define FUNCTION_KIND_METER_LIST(_) \
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
_(display), _(flat), _(setupvar), _(badfunarg)
_(display), _(flat), _(setupvar), _(badfunarg), \
_(joinedsetmethod), _(joinedinitmethod), \
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \
_(unjoined)
# define identity(x) x
struct JSFunctionMeter {
@ -952,8 +951,13 @@ struct JSFunctionMeter {
};
# undef identity
# define JS_FUNCTION_METER(cx,x) JS_RUNTIME_METER((cx)->runtime, functionMeter.x)
#else
# define JS_FUNCTION_METER(cx,x) ((void)0)
#endif
#define NATIVE_ITER_CACHE_LOG2 8
#define NATIVE_ITER_CACHE_MASK JS_BITMASK(NATIVE_ITER_CACHE_LOG2)
#define NATIVE_ITER_CACHE_SIZE JS_BIT(NATIVE_ITER_CACHE_LOG2)
@ -999,7 +1003,7 @@ struct JSThreadData {
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
#ifdef JS_EVAL_CACHE_METERING
#ifdef DEBUG
JSEvalCacheMeter evalCacheMeter;
#endif
@ -1603,9 +1607,22 @@ struct JSRuntime {
JSGCStats gcStats;
#endif
#ifdef JS_FUNCTION_METERING
#ifdef DEBUG
/*
* If functionMeterFilename, set from an envariable in JSRuntime's ctor, is
* null, the remaining members in this ifdef'ed group are not initialized.
*/
const char *functionMeterFilename;
JSFunctionMeter functionMeter;
char lastScriptFilename[1024];
typedef js::HashMap<JSFunction *,
int32,
js::DefaultHasher<JSFunction *>,
js::SystemAllocPolicy> FunctionCountMap;
FunctionCountMap methodReadBarrierCountMap;
FunctionCountMap unjoinedFunctionCountMap;
#endif
JSWrapObjectCallback wrapObjectCallback;
@ -1646,7 +1663,7 @@ struct JSRuntime {
#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor)
#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC)
#ifdef JS_EVAL_CACHE_METERING
#ifdef DEBUG
# define EVAL_CACHE_METER(x) (JS_THREAD_DATA(cx)->evalCacheMeter.x++)
#else
# define EVAL_CACHE_METER(x) ((void) 0)
@ -2174,7 +2191,7 @@ JS_ALWAYS_INLINE jsbytecode *
JSStackFrame::pc(JSContext *cx) const
{
JS_ASSERT(cx->containingSegment(this) != NULL);
return cx->fp == this ? cx->regs->pc : savedPC;
return (cx->fp == this) ? cx->regs->pc : savedPC;
}
/*

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

@ -1270,6 +1270,17 @@ JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
return fp->callee();
}
JS_PUBLIC_API(JSBool)
JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
{
Value v;
if (!fp->getValidCalleeObject(cx, &v))
return false;
*vp = Jsvalify(v);
return true;
}
JS_PUBLIC_API(JSBool)
JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
{

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

@ -272,11 +272,42 @@ extern JS_PUBLIC_API(void)
JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval);
/**
* Return fp's callee function object (fp->callee) if it has one.
* Return fp's callee function object (fp->callee) if it has one. Note that
* this API cannot fail. A null return means "no callee": fp is a global or
* eval-from-global frame, not a call frame.
*
* This API began life as an infallible getter, but now it can return either:
*
* 1. An optimized closure that was compiled assuming the function could not
* escape and be called from sites the compiler could not see.
*
* 2. A "joined function object", an optimization whereby SpiderMonkey avoids
* creating fresh function objects for every evaluation of a function
* expression that is used only once by a consumer that either promises to
* clone later when asked for the value or that cannot leak the value.
*
* Because Mozilla's Gecko embedding of SpiderMonkey (and no doubt other
* embeddings) calls this API in potentially performance-sensitive ways (e.g.
* in nsContentUtils::GetDocumentFromCaller), we are leaving this API alone. It
* may now return an unwrapped non-escaping optimized closure, or a joined
* function object. Such optimized objects may work well if called from the
* correct context, never mutated or compared for identity, etc.
*
* However, if you really need to get the same callee object that JS code would
* see, which means undoing the optimizations, where an undo attempt can fail,
* then use JS_GetValidFrameCalleeObject.
*/
extern JS_PUBLIC_API(JSObject *)
JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp);
/**
* Return fp's callee function object after running the deferred closure
* cloning "method read barrier". This API can fail! If the frame has no
* callee, this API returns true with JSVAL_IS_VOID(*vp).
*/
extern JS_PUBLIC_API(JSBool)
JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp);
/************************************************************************/
extern JS_PUBLIC_API(const char *)

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

@ -6633,8 +6633,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
bool lambda = PN_OP(init) == JSOP_LAMBDA;
if (lambda)
++methodInits;
if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable())
{
if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) {
op = JSOP_INITMETHOD;
pn2->pn_op = uint8(op);
} else {

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

@ -1290,6 +1290,102 @@ JS_PUBLIC_DATA(Class) js_CallClass = {
JS_CLASS_TRACE(args_or_call_trace)
};
bool
JSStackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
{
if (!fun) {
*vp = argv ? argv[-2] : UndefinedValue();
return true;
}
/*
* See the equivalent condition in args_getProperty for ARGS_CALLEE, but
* note that here we do not want to throw, since this escape can happen via
* a foo.caller reference alone, without any debugger or indirect eval. And
* alas, it seems foo.caller is still used on the Web.
*/
if (fun->needsWrapper()) {
JSObject *wrapper = WrapEscapingClosure(cx, this, fun, fun);
if (!wrapper)
return false;
vp->setObject(*wrapper);
return true;
}
JSObject *funobj = &calleeObject();
vp->setObject(*funobj);
/*
* Check for an escape attempt by a joined function object, which must go
* through the frame's |this| object's method read barrier for the method
* atom by which it was uniquely associated with a property.
*/
if (thisv.isObject()) {
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
if (fun == funobj && fun->methodAtom()) {
JSObject *thisp = &thisv.toObject();
JS_ASSERT(thisp->canHaveMethodBarrier());
JSScope *scope = thisp->scope();
if (scope->hasMethodBarrier()) {
JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(fun->methodAtom()));
/*
* The method property might have been deleted while the method
* barrier scope flag stuck, so we must lookup and test here.
*
* Two cases follow: the method barrier was not crossed yet, so
* we cross it here; the method barrier *was* crossed, in which
* case we must fetch and validate the cloned (unjoined) funobj
* in the method property's slot.
*
* In either case we must allow for the method property to have
* been replaced, or its value to have been overwritten.
*/
if (sprop) {
if (sprop->isMethod() && &sprop->methodObject() == funobj) {
if (!scope->methodReadBarrier(cx, sprop, vp))
return false;
setCalleeObject(vp->toObject());
return true;
}
if (sprop->hasSlot()) {
Value v = thisp->getSlot(sprop->slot);
JSObject *clone;
if (IsFunctionObject(v, &clone) &&
GET_FUNCTION_PRIVATE(cx, clone) == fun &&
clone->hasMethodObj(*thisp)) {
JS_ASSERT(clone != funobj);
*vp = v;
setCalleeObject(*clone);
return true;
}
}
}
/*
* If control flows here, we can't find an already-existing
* clone (or force to exist a fresh clone) created via thisp's
* method read barrier, so we must clone fun and store it in
* fp's callee to avoid re-cloning upon repeated foo.caller
* access. It seems that there are no longer any properties
* referring to fun.
*/
funobj = CloneFunctionObject(cx, fun, fun->getParent());
if (!funobj)
return false;
funobj->setMethodObj(*thisp);
setCalleeObject(*funobj);
return true;
}
}
}
return true;
}
/* Generic function tinyids. */
enum {
FUN_ARGUMENTS = -1, /* predefined arguments local variable */
@ -1303,7 +1399,7 @@ static JSBool
fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!JSID_IS_INT(id))
return JS_TRUE;
return true;
jsint slot = JSID_TO_INT(id);
@ -1331,10 +1427,10 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
while (!(fun = (JSFunction *)
GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
if (slot != FUN_LENGTH)
return JS_TRUE;
return true;
obj = obj->getProto();
if (!obj)
return JS_TRUE;
return true;
}
/* Find fun's top-most activation record. */
@ -1353,11 +1449,11 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
js_GetErrorMessage, NULL,
JSMSG_DEPRECATED_USAGE,
js_arguments_str)) {
return JS_FALSE;
return false;
}
if (fp) {
if (!js_GetArgsValue(cx, fp, vp))
return JS_FALSE;
return false;
} else {
vp->setNull();
}
@ -1374,30 +1470,12 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
break;
case FUN_CALLER:
if (fp && fp->down && fp->down->fun) {
JSFunction *caller = fp->down->fun;
/*
* See equivalent condition in args_getProperty for ARGS_CALLEE,
* but here we do not want to throw, since this escape can happen
* via foo.caller alone, without any debugger or indirect eval. And
* it seems foo.caller is still used on the Web.
*/
if (caller->needsWrapper()) {
JSObject *wrapper = WrapEscapingClosure(cx, fp->down, FUN_OBJECT(caller), caller);
if (!wrapper)
return JS_FALSE;
vp->setObject(*wrapper);
return JS_TRUE;
}
vp->setNull();
if (fp && fp->down && !fp->down->getValidCalleeObject(cx, vp))
return false;
JS_ASSERT(fp->down->argv);
*vp = fp->down->calleeValue();
} else {
vp->setNull();
}
/* Censor the caller if it is from another compartment. */
if (vp->isObject()) {
/* Censor the caller if it is from another compartment. */
if (vp->toObject().getCompartment(cx) != cx->compartment)
vp->setNull();
}
@ -1410,7 +1488,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
break;
}
return JS_TRUE;
return true;
}
struct LazyFunctionProp {
@ -1842,7 +1920,8 @@ JSFunction::countInterpretedReservedSlots() const
*/
JS_PUBLIC_DATA(Class) js_FunctionClass = {
js_Function_str,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::FUN_FIXED_RESERVED_SLOTS) |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
@ -2182,8 +2261,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
* js_CheckContentSecurityPolicy is defined in jsobj.cpp
*/
if (!js_CheckContentSecurityPolicy(cx)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_CSP_BLOCKED_FUNCTION);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
return JS_FALSE;
}

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

@ -45,6 +45,8 @@
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsobj.h"
#include "jsatom.h"
#include "jsstr.h"
typedef struct JSLocalNameMap JSLocalNameMap;
@ -95,6 +97,10 @@ typedef union JSLocalNames {
* can move to u.i.script->flags. For now we use function flag bits to minimize
* pointer-chasing.
*/
#define JSFUN_JOINABLE 0x0001 /* function is null closure that does not
appear to call itself via its own name
or arguments.callee */
#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */
#define JSFUN_TRCINFO 0x2000 /* when set, u.n.trcinfo is non-null,
JSFunctionSpec::call points to a
@ -202,6 +208,44 @@ struct JSFunction : public JSObject
bool mightEscape() const {
return FUN_INTERPRETED(this) && (FUN_FLAT_CLOSURE(this) || u.i.nupvars == 0);
}
bool joinable() const {
return flags & JSFUN_JOINABLE;
}
private:
/*
* js_FunctionClass reserves two slots, which are free in JSObject::fslots
* without requiring dslots allocation. Null closures that can be joined to
* a compiler-created function object use the first one to hold a mutable
* methodAtom() state variable, needed for correct foo.caller handling.
*/
enum {
METHOD_ATOM_SLOT = JSSLOT_FUN_METHOD_ATOM
};
public:
void setJoinable() {
JS_ASSERT(FUN_INTERPRETED(this));
fslots[METHOD_ATOM_SLOT].setNull();
flags |= JSFUN_JOINABLE;
}
/*
* Method name imputed from property uniquely assigned to or initialized,
* where the function does not need to be cloned to carry a scope chain or
* flattened upvars.
*/
JSAtom *methodAtom() const {
return (joinable() && fslots[METHOD_ATOM_SLOT].isString())
? STRING_TO_ATOM(fslots[METHOD_ATOM_SLOT].toString())
: NULL;
}
void setMethodAtom(JSAtom *atom) {
JS_ASSERT(joinable());
fslots[METHOD_ATOM_SLOT].setString(ATOM_TO_STRING(atom));
}
};
JS_STATIC_ASSERT(sizeof(JSFunction) % JS_GCTHING_ALIGN == 0);

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

@ -2564,6 +2564,19 @@ js_TraceRuntime(JSTracer *trc)
*/
if (rt->gcExtraRootsTraceOp)
rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
#ifdef DEBUG
if (rt->functionMeterFilename) {
for (int k = 0; k < 2; k++) {
typedef JSRuntime::FunctionCountMap HM;
HM &h = (k == 0) ? rt->methodReadBarrierCountMap : rt->unjoinedFunctionCountMap;
for (HM::Range r = h.all(); !r.empty(); r.popFront()) {
JSFunction *fun = r.front().key;
JS_CALL_OBJECT_TRACER(trc, fun, "FunctionCountMap key");
}
}
}
#endif
}
void

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

@ -2684,15 +2684,15 @@ BEGIN_CASE(JSOP_STOP)
== JSOP_CALL_LENGTH);
TRACE_0(LeaveFrame);
if (!TRACE_RECORDER(cx) && recursive) {
if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE) {
if (regs.pc[JSOP_CALL_LENGTH] == JSOP_TRACE) {
regs.pc += JSOP_CALL_LENGTH;
MONITOR_BRANCH(Record_LeaveFrame);
op = (JSOp)*regs.pc;
DO_OP();
}
}
if (*(regs.pc + JSOP_CALL_LENGTH) == JSOP_TRACE ||
*(regs.pc + JSOP_CALL_LENGTH) == JSOP_NOP) {
if (regs.pc[JSOP_CALL_LENGTH] == JSOP_TRACE ||
regs.pc[JSOP_CALL_LENGTH] == JSOP_NOP) {
JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
regs.pc += JSOP_CALL_LENGTH;
len = JSOP_TRACE_LENGTH;
@ -4050,7 +4050,8 @@ BEGIN_CASE(JSOP_GETXPROP)
jsid id = ATOM_TO_JSID(atom);
if (JS_LIKELY(!aobj->getOps()->getProperty)
? !js_GetPropertyHelper(cx, obj, id,
fp->imacpc
(fp->imacpc ||
regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
&rval)
@ -5672,24 +5673,19 @@ BEGIN_CASE(JSOP_LAMBDA)
parent = fp->scopeChain;
if (obj->getParent() == parent) {
op = JSOp(regs.pc[JSOP_LAMBDA_LENGTH]);
jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH;
JSOp op2 = JSOp(*pc2);
/*
* Optimize ({method: function () { ... }, ...}) and
* this.method = function () { ... }; bytecode sequences.
* Optimize var obj = {method: function () { ... }, ...},
* this.method = function () { ... }; and other significant
* single-use-of-null-closure bytecode sequences.
*
* WARNING: code in TraceRecorder::record_JSOP_LAMBDA must
* match the optimization cases in the following code that
* break from the outer do-while(0).
*/
if (op == JSOP_SETMETHOD) {
#ifdef DEBUG
JSOp op2 = JSOp(regs.pc[JSOP_LAMBDA_LENGTH + JSOP_SETMETHOD_LENGTH]);
JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
#endif
const Value &lref = regs.sp[-1];
if (lref.isObject() &&
lref.toObject().getClass() == &js_ObjectClass) {
break;
}
} else if (op == JSOP_INITMETHOD) {
if (op2 == JSOP_INITMETHOD) {
#ifdef DEBUG
const Value &lref = regs.sp[-1];
JS_ASSERT(lref.isObject());
@ -5697,9 +5693,86 @@ BEGIN_CASE(JSOP_LAMBDA)
JS_ASSERT(obj2->getClass() == &js_ObjectClass);
JS_ASSERT(obj2->scope()->object == obj2);
#endif
fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
JS_FUNCTION_METER(cx, joinedinitmethod);
break;
}
if (op2 == JSOP_SETMETHOD) {
#ifdef DEBUG
op2 = JSOp(pc2[JSOP_SETMETHOD_LENGTH]);
JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
#endif
const Value &lref = regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH)));
JS_FUNCTION_METER(cx, joinedsetmethod);
break;
}
} else if (fun->joinable()) {
if (op2 == JSOP_CALL) {
/*
* Array.prototype.sort and String.prototype.replace are
* optimized as if they are special form. We know that they
* won't leak the joined function object in obj, therefore
* we don't need to clone that compiler- created function
* object for identity/mutation reasons.
*/
int iargc = GET_ARGC(pc2);
/*
* Note that we have not yet pushed obj as the final argument,
* so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
* is the callee for this JSOP_CALL.
*/
const Value &cref = regs.sp[1 - (iargc + 2)];
JSObject *callee;
if (IsFunctionObject(cref, &callee)) {
JSFunction *calleeFun = GET_FUNCTION_PRIVATE(cx, callee);
FastNative fastNative = FUN_FAST_NATIVE(calleeFun);
if (fastNative) {
if (iargc == 1 && fastNative == array_sort) {
JS_FUNCTION_METER(cx, joinedsort);
break;
}
if (iargc == 2 && fastNative == str_replace) {
JS_FUNCTION_METER(cx, joinedreplace);
break;
}
}
}
} else if (op2 == JSOP_NULL) {
pc2 += JSOP_NULL_LENGTH;
op2 = JSOp(*pc2);
if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) {
JS_FUNCTION_METER(cx, joinedmodulepat);
break;
}
}
}
}
#ifdef DEBUG
if (rt->functionMeterFilename) {
// No locking, this is mainly for js shell testing.
++rt->functionMeter.unjoined;
typedef JSRuntime::FunctionCountMap HM;
HM &h = rt->unjoinedFunctionCountMap;
HM::AddPtr p = h.lookupForAdd(fun);
if (!p) {
h.add(p, fun, 1);
} else {
JS_ASSERT(p->key == fun);
++p->value;
}
}
#endif
} else {
parent = js_GetScopeChain(cx, fp);
if (!parent)

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

@ -181,14 +181,33 @@ struct JSStackFrame
return argv[-2];
}
/* Infallible getter to return the callee object from this frame. */
JSObject &calleeObject() const {
JS_ASSERT(argv);
return argv[-2].toObject();
}
/*
* Fallible getter to compute the correct callee function object, which may
* require deferred cloning due to JSScope::methodReadBarrier. For a frame
* with null fun member, return true with *vp set from this->calleeValue(),
* which may not be an object (it could be undefined).
*/
bool getValidCalleeObject(JSContext *cx, js::Value *vp);
void setCalleeObject(JSObject &callable) {
JS_ASSERT(argv);
argv[-2].setObject(callable);
}
JSObject *callee() {
return argv ? &argv[-2].toObject() : NULL;
}
/*
* Get the object associated with the Execution Context's
* VariableEnvironment (ES5 10.3). The given CallStackSegment must contain
* this stack frame.
* Get the "variable object" (ES3 term) associated with the Execution
* Context's VariableEnvironment (ES5 10.3). The given CallStackSegment
* must contain this stack frame.
*/
JSObject *varobj(js::CallStackSegment *css) const;

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

@ -5079,13 +5079,13 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
* Check for Object class here to avoid defining a method on a class
* with magic resolve, addProperty, getProperty, etc. hooks.
*/
if ((defineHow & JSDNP_SET_METHOD) &&
obj->getClass() == &js_ObjectClass) {
if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
JS_ASSERT(IsFunctionObject(*vp));
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JSObject *funobj = &vp->toObject();
if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
if (fun == funobj) {
flags |= JSScopeProperty::METHOD;
getter = CastAsPropertyOp(funobj);
}
@ -5236,8 +5236,39 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
}
scope = obj->scope();
if (SPROP_HAS_VALID_SLOT(sprop, scope))
GC_POKE(cx, obj->lockedGetSlot(sprop->slot));
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
const Value &v = obj->lockedGetSlot(sprop->slot);
GC_POKE(cx, v);
/*
* Delete is rare enough that we can take the hit of checking for an
* active cloned method function object that must be homed to a callee
* slot on the active stack frame before this delete completes, in case
* someone saved the clone and checks it against foo.caller for a foo
* called from the active method.
*
* We do not check suspended frames. They can't be reached via caller,
* so the only way they could have the method's joined function object
* as callee is through an API abusage. We break any such edge case.
*/
if (scope->hasMethodBarrier()) {
JSObject *funobj;
if (IsFunctionObject(v, &funobj)) {
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
if (fun != funobj) {
for (JSStackFrame *fp = cx->fp; fp; fp = fp->down) {
if (fp->callee() == fun &&
fp->thisv.isObject() &&
&fp->thisv.toObject() == obj) {
fp->setCalleeObject(*funobj);
}
}
}
}
}
}
ok = scope->removeProperty(cx, id);
JS_UNLOCK_OBJ(cx, obj);
@ -6153,7 +6184,7 @@ dumpValue(const Value &v)
Class *clasp = obj->getClass();
fprintf(stderr, "<%s%s at %p>",
clasp->name,
clasp == &js_ObjectClass ? "" : " object",
(clasp == &js_ObjectClass) ? "" : " object",
(void *) obj);
} else if (v.isBoolean()) {
if (v.toBoolean())

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

@ -106,6 +106,12 @@ JSObject::getReservedSlot(uintN index) const
return (slot < numSlots()) ? getSlot(slot) : js::UndefinedValue();
}
inline bool
JSObject::canHaveMethodBarrier() const
{
return isObject() || isFunction() || isPrimitive() || isDate();
}
inline bool
JSObject::isPrimitive() const
{
@ -308,6 +314,19 @@ JSObject::setDateUTCTime(const js::Value &time)
fslots[JSSLOT_DATE_UTC_TIME] = time;
}
inline bool
JSObject::hasMethodObj(const JSObject& obj) const
{
return fslots[JSSLOT_FUN_METHOD_OBJ].isObject() &&
&fslots[JSSLOT_FUN_METHOD_OBJ].toObject() == &obj;
}
inline void
JSObject::setMethodObj(JSObject& obj)
{
fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj);
}
inline NativeIterator *
JSObject::getNativeIterator() const
{

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

@ -5495,10 +5495,6 @@ SimulateImacroCFG(JSContext *cx, JSScript *script,
#undef LOCAL_ASSERT
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
static intN
ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
jsbytecode **pcstack);
static intN
ReconstructImacroPCStack(JSContext *cx, JSScript *script,
jsbytecode *imacstart, jsbytecode *target,

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

@ -2130,11 +2130,7 @@ DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
void
Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
{
#ifdef JS_FUNCTION_METERING
# define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
#else
# define FUN_METER(x) ((void)0)
#endif
#define FUN_METER(x) JS_FUNCTION_METER(context, x)
for (;;) {
JSParseNode *fn = funbox->node;
@ -2344,6 +2340,9 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
}
}
if (funbox->joinable())
fun->setJoinable();
funbox = funbox->siblings;
if (!funbox)
break;

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

@ -1211,7 +1211,7 @@ JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop)
const Value &prev = object->lockedGetSlot(sprop->slot);
JS_ASSERT(&sprop->methodObject() == &prev.toObject());
JS_ASSERT(hasMethodBarrier());
JS_ASSERT(object->getClass() == &js_ObjectClass);
JS_ASSERT(object->canHaveMethodBarrier());
JS_ASSERT(!sprop->rawSetter || sprop->rawSetter == js_watch_set);
#endif

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

@ -611,7 +611,9 @@ struct JSScopeProperty {
union {
js::PropertyOp rawGetter; /* getter and setter hooks or objects */
JSObject *getterObj; /* user-defined callable "get" object or
null if sprop->hasGetterValue() */
null if sprop->hasGetterValue(); or
joined function object if METHOD flag
is set. */
JSScopeProperty *next; /* next node in freelist */
};

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

@ -122,17 +122,37 @@ JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, js::Value *vp)
JS_ASSERT(hasProperty(sprop));
JS_ASSERT(sprop->isMethod());
JS_ASSERT(&vp->toObject() == &sprop->methodObject());
JS_ASSERT(object->getClass() == &js_ObjectClass);
JS_ASSERT(object->canHaveMethodBarrier());
JSObject *funobj = &vp->toObject();
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
JS_ASSERT(FUN_OBJECT(fun) == funobj && FUN_NULL_CLOSURE(fun));
JS_ASSERT(fun == funobj && FUN_NULL_CLOSURE(fun));
funobj = CloneFunctionObject(cx, fun, funobj->getParent());
if (!funobj)
return false;
funobj->setMethodObj(*object);
vp->setObject(*funobj);
return !!js_SetPropertyHelper(cx, object, sprop->id, 0, vp);
if (!js_SetPropertyHelper(cx, object, sprop->id, 0, vp))
return false;
#ifdef DEBUG
if (cx->runtime->functionMeterFilename) {
JS_FUNCTION_METER(cx, mreadbarrier);
typedef JSRuntime::FunctionCountMap HM;
HM &h = cx->runtime->methodReadBarrierCountMap;
HM::AddPtr p = h.lookupForAdd(fun);
if (!p) {
h.add(p, fun, 1);
} else {
JS_ASSERT(p->key == fun);
++p->value;
}
}
#endif
return true;
}
static JS_ALWAYS_INLINE bool
@ -149,8 +169,10 @@ JSScope::methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop,
{
if (flags & (BRANDED | METHOD_BARRIER)) {
const js::Value &prev = object->lockedGetSlot(sprop->slot);
if (ChangesMethodValue(prev, v))
if (ChangesMethodValue(prev, v)) {
JS_FUNCTION_METER(cx, mwritebarrier);
return methodShapeChange(cx, sprop);
}
}
return true;
}
@ -160,8 +182,10 @@ JSScope::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
{
if (flags & (BRANDED | METHOD_BARRIER)) {
const js::Value &prev = object->lockedGetSlot(slot);
if (ChangesMethodValue(prev, v))
if (ChangesMethodValue(prev, v)) {
JS_FUNCTION_METER(cx, mwslotbarrier);
return methodShapeChange(cx, slot);
}
}
return true;
}

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

@ -617,12 +617,14 @@ SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
sfp->flags |= flags;
}
#ifdef JS_FUNCTION_METERING
size_t len = strlen(sfe->filename);
if (len >= sizeof rt->lastScriptFilename)
len = sizeof rt->lastScriptFilename - 1;
memcpy(rt->lastScriptFilename, sfe->filename, len);
rt->lastScriptFilename[len] = '\0';
#ifdef DEBUG
if (rt->functionMeterFilename) {
size_t len = strlen(sfe->filename);
if (len >= sizeof rt->lastScriptFilename)
len = sizeof rt->lastScriptFilename - 1;
memcpy(rt->lastScriptFilename, sfe->filename, len);
rt->lastScriptFilename[len] = '\0';
}
#endif
return sfe;

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

@ -2277,8 +2277,8 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr,
return true;
}
static JSBool
str_replace(JSContext *cx, uintN argc, Value *vp)
JSBool
js::str_replace(JSContext *cx, uintN argc, Value *vp)
{
ReplaceData rdata(cx);
NORMALIZE_THIS(cx, vp, rdata.str);

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

@ -1113,6 +1113,15 @@ extern JSBool
js_str_escape(JSContext *cx, JSObject *obj, uintN argc, js::Value *argv,
js::Value *rval);
/*
* The String.prototype.replace fast-native entry point is exported for joined
* function optimization in js{interp,tracer}.cpp.
*/
namespace js {
extern JSBool
str_replace(JSContext *cx, uintN argc, js::Value *vp);
}
extern JSBool
js_str_toString(JSContext *cx, uintN argc, js::Value *vp);

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

@ -14873,19 +14873,60 @@ TraceRecorder::record_JSOP_LAMBDA()
if (FUN_NULL_CLOSURE(fun)) {
if (FUN_OBJECT(fun)->getParent() != globalObj)
RETURN_STOP_A("Null closure function object parent must be global object");
JSOp op2 = JSOp(cx->regs->pc[JSOP_LAMBDA_LENGTH]);
jsbytecode *pc2 = cx->regs->pc + JSOP_LAMBDA_LENGTH;
JSOp op2 = JSOp(*pc2);
if (op2 == JSOP_INITMETHOD) {
stack(0, INS_CONSTOBJ(FUN_OBJECT(fun)));
return ARECORD_CONTINUE;
}
if (op2 == JSOP_SETMETHOD) {
Value lval = stackval(-1);
if (!lval.isPrimitive() &&
lval.toObject().getClass() == &js_ObjectClass) {
if (!lval.isPrimitive() && lval.toObject().canHaveMethodBarrier()) {
stack(0, INS_CONSTOBJ(FUN_OBJECT(fun)));
return ARECORD_CONTINUE;
}
} else if (op2 == JSOP_INITMETHOD) {
stack(0, INS_CONSTOBJ(FUN_OBJECT(fun)));
return ARECORD_CONTINUE;
} else if (fun->joinable()) {
if (op2 == JSOP_CALL) {
/*
* Array.prototype.sort and String.prototype.replace are
* optimized as if they are special form. We know that they
* won't leak the joined function object in obj, therefore
* we don't need to clone that compiler- created function
* object for identity/mutation reasons.
*/
int iargc = GET_ARGC(pc2);
/*
* Note that we have not yet pushed obj as the final argument,
* so regs.sp[1 - (iargc + 2)], and not regs.sp[-(iargc + 2)],
* is the callee for this JSOP_CALL.
*/
const Value &cref = cx->regs->sp[1 - (iargc + 2)];
JSObject *callee;
if (IsFunctionObject(cref, &callee)) {
JSFunction *calleeFun = GET_FUNCTION_PRIVATE(cx, callee);
FastNative fastNative = FUN_FAST_NATIVE(calleeFun);
if ((iargc == 1 && fastNative == array_sort) ||
(iargc == 2 && fastNative == str_replace)) {
stack(0, INS_CONSTOBJ(FUN_OBJECT(fun)));
return ARECORD_CONTINUE;
}
}
} else if (op2 == JSOP_NULL) {
pc2 += JSOP_NULL_LENGTH;
op2 = JSOp(*pc2);
if (op2 == JSOP_CALL && GET_ARGC(pc2) == 0) {
stack(0, INS_CONSTOBJ(FUN_OBJECT(fun)));
return ARECORD_CONTINUE;
}
}
}
LIns *proto_ins;

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

@ -598,7 +598,7 @@ class Value
*
* Private setters/getters allow the caller to read/write arbitrary types
* that fit in the 64-bit payload. It is the caller's responsibility, after
* storing to a value with setPrivateX to only read with getPrivateX.
* storing to a value with setPrivateX to read only using getPrivateX.
* Privates values are given a type type which ensures they are not marked.
*/

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

@ -170,7 +170,7 @@ skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild) script regress-341360.js # slow
script regress-343713.js
script regress-343966.js
script regress-344711-n.js
random-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT") asserts-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT",1) script regress-344804.js # bug 524732
skip script regress-344804.js # bug 524732
script regress-344959.js
script regress-346237.js
script regress-346801.js

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

@ -1,6 +1,6 @@
url-prefix ../../jsreftest.html?test=js1_5/Scope/
script regress-154693.js
random-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT") asserts-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT",1) script regress-181834.js # bug 524732
skip script regress-181834.js # bug 524732
script regress-184107.js
script regress-185485.js
script regress-191276.js

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

@ -24,4 +24,6 @@ script regress-566914.js
script regress-567152.js
script regress-569306.js
script regress-571014.js
script regress-577648-1.js
script regress-577648-2.js
script regress-583429.js

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

@ -0,0 +1,87 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var count = 0;
function testCaller(obj) {
switch (++count) {
case 1:
case 2:
/*
* The first two times, obj is objA. The first time, we reference
* arguments.callee.caller before obj.go, so the caller getter must
* force the joined function object in the stack frame to cross the
* method read barrier. The second time, obj.go has been cloned and
* it should match the new frame's callee from the get-go.
*/
assertEq(obj, objA);
break;
case 3: {
assertEq(obj, objB);
/*
* Store another clone of the joined function object before obj.go has
* been read, but after it has been invoked via objB.go(objB).
*
* In this case, arguments.callee.caller must not lie and return what
* is currently stored in objB.go, since that function object (objA.go)
* was cloned earlier, when count was 1, and it is not the function
* object that was truly invoked.
*
* But since the invocation of objB.go(objB) did not clone go, and the
* following assignment overwrote the invoked value, leaving the only
* reference to the joined function object for go in the stack frame's
* callee (argv[-2]) member, the arguments.callee.caller reference must
* clone a function object for the callee, store it as the callee, and
* return it here.
*
* It won't equal obj.go, but (implementation detail) it should have
* the same proto as obj.go
*/
obj.go = objA.go;
let caller = arguments.callee.caller;
let obj_go = obj.go;
return caller != obj_go && caller.__proto__ == obj_go.__proto__;
}
case 4: {
assertEq(obj, objC);
let save = obj.go;
delete obj.go;
return arguments.callee.caller == save;
}
case 5: {
assertEq(obj, objD);
let read = obj.go;
break;
}
}
return arguments.callee.caller == obj.go;
}
function make() {
return {
go: function(obj) {
return testCaller(obj);
}
};
}
var objA = make(),
objB = make(),
objC = make(),
objD = make();
reportCompare(true, objA.go(objA), "1");
reportCompare(true, objA.go(objA), "2");
reportCompare(true, objB.go(objB), "3");
reportCompare(true, objC.go(objC), "4");
reportCompare(true, objD.go(objD), "5");

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

@ -0,0 +1,12 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor: Jason Orendorff
*/
var o = { f: function() { return o.g(); }, g: function() { return arguments.callee.caller; } };
var c = o.f();
var i = 'f';
var d = o[i]();
reportCompare(true, c === o.f && d === o.f(), "");

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

@ -0,0 +1,5 @@
for (b = 0; b < 2; b++) {
/ /
(function(){})
}