mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-29 21:25:35 +00:00
Bug 711273 - rm cruft: JS_REQUIRES_STACK, find-child.py, jsstack.js, fallibility of ClonedBlockObject::put/UnwindScope (r=waldo)
This commit is contained in:
parent
e159dd7c22
commit
6a7c05f4f2
@ -2015,7 +2015,7 @@ struct SortComparatorFunction {
|
||||
SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
|
||||
: cx(cx), fval(fval), ag(ag) { }
|
||||
|
||||
bool JS_REQUIRES_STACK operator()(const Value &a, const Value &b, bool *lessOrEqualp);
|
||||
bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -502,11 +502,8 @@ JSContext::ensureParseMapPool()
|
||||
return parseMapPool_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current frame, first lazily instantiating stack frames if needed.
|
||||
* (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
|
||||
*/
|
||||
static JS_FORCES_STACK JS_INLINE js::StackFrame *
|
||||
/* Get the current frame, first lazily instantiating stack frames if needed. */
|
||||
static inline js::StackFrame *
|
||||
js_GetTopStackFrame(JSContext *cx, FrameExpandKind expand)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
|
@ -2081,7 +2081,7 @@ MarkWeakReferences(GCMarker *gcmarker)
|
||||
JS_ASSERT(gcmarker->isMarkStackEmpty());
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK void
|
||||
void
|
||||
MarkRuntime(JSTracer *trc)
|
||||
{
|
||||
JSRuntime *rt = trc->runtime;
|
||||
|
@ -1368,13 +1368,13 @@ js_IsAddressableGCThing(JSRuntime *rt, jsuword w, js::gc::AllocKind *thingKind,
|
||||
|
||||
namespace js {
|
||||
|
||||
extern JS_REQUIRES_STACK void
|
||||
extern void
|
||||
MarkRuntime(JSTracer *trc);
|
||||
|
||||
extern void
|
||||
TraceRuntime(JSTracer *trc);
|
||||
|
||||
extern JS_REQUIRES_STACK JS_FRIEND_API(void)
|
||||
extern JS_FRIEND_API(void)
|
||||
MarkContext(JSTracer *trc, JSContext *acx);
|
||||
|
||||
/* Must be called with GC lock taken. */
|
||||
|
@ -420,7 +420,7 @@ js::OnUnknownMethod(JSContext *cx, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
static JSBool
|
||||
NoSuchMethod(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
InvokeArgsGuard args;
|
||||
@ -446,7 +446,7 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
#endif /* JS_HAS_NO_SUCH_METHOD */
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
bool
|
||||
js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp)
|
||||
{
|
||||
JS_ASSERT(script);
|
||||
@ -1114,12 +1114,9 @@ js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth)
|
||||
obj.asNestedScope().stackDepth() >= stackDepth;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unwind block and scope chains to match the given depth. The function sets
|
||||
* fp->sp on return to stackDepth.
|
||||
*/
|
||||
bool
|
||||
js::UnwindScope(JSContext *cx, uint32_t stackDepth, JSBool normalUnwind)
|
||||
/* Unwind block and scope chains to match the given depth. */
|
||||
void
|
||||
js::UnwindScope(JSContext *cx, uint32_t stackDepth)
|
||||
{
|
||||
JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp);
|
||||
|
||||
@ -1136,16 +1133,11 @@ js::UnwindScope(JSContext *cx, uint32_t stackDepth, JSBool normalUnwind)
|
||||
JSObject &scopeChain = fp->scopeChain();
|
||||
if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth))
|
||||
break;
|
||||
if (scopeChain.isClonedBlock()) {
|
||||
/* Don't fail until after we've updated all stacks. */
|
||||
normalUnwind &= scopeChain.asClonedBlock().put(cx, normalUnwind);
|
||||
} else {
|
||||
if (scopeChain.isClonedBlock())
|
||||
scopeChain.asClonedBlock().put(cx);
|
||||
else
|
||||
LeaveWith(cx);
|
||||
}
|
||||
}
|
||||
|
||||
cx->regs().sp = fp->base() + stackDepth;
|
||||
return normalUnwind;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5167,8 +5159,8 @@ BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
|
||||
* the stack into the clone, and pop it off the chain.
|
||||
*/
|
||||
JSObject &scope = regs.fp()->scopeChain();
|
||||
if (scope.getProto() == &blockObj && !scope.asClonedBlock().put(cx, JS_TRUE))
|
||||
goto error;
|
||||
if (scope.getProto() == &blockObj)
|
||||
scope.asClonedBlock().put(cx);
|
||||
|
||||
regs.fp()->setBlockChain(blockObj.enclosingBlock());
|
||||
|
||||
@ -5384,15 +5376,8 @@ END_CASE(JSOP_ARRAYPUSH)
|
||||
*/
|
||||
regs.pc = (script)->main() + tn->start + tn->length;
|
||||
|
||||
JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE);
|
||||
JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth);
|
||||
if (!ok) {
|
||||
/*
|
||||
* Restart the handler search with updated pc and stack depth
|
||||
* to properly notify the debugger.
|
||||
*/
|
||||
goto error;
|
||||
}
|
||||
UnwindScope(cx, tn->stackDepth);
|
||||
regs.sp = regs.fp()->base() + tn->stackDepth;
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
@ -5428,7 +5413,7 @@ END_CASE(JSOP_ARRAYPUSH)
|
||||
JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
|
||||
Value v = cx->getPendingException();
|
||||
cx->clearPendingException();
|
||||
ok = js_CloseIterator(cx, ®s.sp[-1].toObject());
|
||||
bool ok = js_CloseIterator(cx, ®s.sp[-1].toObject());
|
||||
regs.sp -= 1;
|
||||
if (!ok)
|
||||
goto error;
|
||||
@ -5454,15 +5439,8 @@ END_CASE(JSOP_ARRAYPUSH)
|
||||
}
|
||||
|
||||
forced_return:
|
||||
/*
|
||||
* Unwind the scope making sure that interpReturnOK stays false even when
|
||||
* UnwindScope returns true.
|
||||
*
|
||||
* When a trap handler returns JSTRAP_RETURN, we jump here with
|
||||
* interpReturnOK set to true bypassing any finally blocks.
|
||||
*/
|
||||
interpReturnOK &= (JSBool)UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
|
||||
JS_ASSERT(regs.sp == regs.fp()->base());
|
||||
UnwindScope(cx, 0);
|
||||
regs.sp = regs.fp()->base();
|
||||
|
||||
if (entryFrame != regs.fp())
|
||||
goto inline_return;
|
||||
|
@ -180,7 +180,7 @@ InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc
|
||||
* InvokeConstructor* implement a function call from a constructor context
|
||||
* (e.g. 'new') handling the the creation of the new 'this' object.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK bool
|
||||
extern bool
|
||||
InvokeConstructorKernel(JSContext *cx, const CallArgs &args);
|
||||
|
||||
/* See the InvokeArgsGuard overload of Invoke. */
|
||||
@ -201,7 +201,7 @@ InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Val
|
||||
* InvokeConstructorWithGivenThis directly calls the constructor with the given
|
||||
* 'this'; the caller must choose the semantically correct 'this'.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK bool
|
||||
extern bool
|
||||
InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
|
||||
uintN argc, Value *argv, Value *rval);
|
||||
|
||||
@ -233,10 +233,10 @@ enum InterpMode
|
||||
* Execute the caller-initialized frame for a user-defined script or function
|
||||
* pointed to by cx->fp until completion or error.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
|
||||
extern JS_NEVER_INLINE bool
|
||||
Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
|
||||
|
||||
extern JS_REQUIRES_STACK bool
|
||||
extern bool
|
||||
RunScript(JSContext *cx, JSScript *script, StackFrame *fp);
|
||||
|
||||
extern bool
|
||||
@ -323,8 +323,8 @@ class InterpreterFrames {
|
||||
* Unwind block and scope chains to match the given depth. The function sets
|
||||
* fp->sp on return to stackDepth.
|
||||
*/
|
||||
extern bool
|
||||
UnwindScope(JSContext *cx, uint32_t stackDepth, JSBool normalUnwind);
|
||||
extern void
|
||||
UnwindScope(JSContext *cx, uint32_t stackDepth);
|
||||
|
||||
extern bool
|
||||
OnUnknownMethod(JSContext *cx, js::Value *vp);
|
||||
|
@ -866,7 +866,7 @@ js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
static JSBool
|
||||
CloseGenerator(JSContext *cx, JSObject *genobj);
|
||||
#endif
|
||||
|
||||
@ -1270,7 +1270,7 @@ Class js::GeneratorClass = {
|
||||
* from the activation in fp, so we can steal away fp->callobj and fp->argsobj
|
||||
* if they are non-null.
|
||||
*/
|
||||
JS_REQUIRES_STACK JSObject *
|
||||
JSObject *
|
||||
js_NewGenerator(JSContext *cx)
|
||||
{
|
||||
FrameRegs &stackRegs = cx->regs();
|
||||
@ -1340,7 +1340,7 @@ typedef enum JSGeneratorOp {
|
||||
* Start newborn or restart yielding generator and perform the requested
|
||||
* operation inside its frame.
|
||||
*/
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
static JSBool
|
||||
SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
||||
JSGenerator *gen, const Value &arg)
|
||||
{
|
||||
@ -1446,7 +1446,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
static JSBool
|
||||
CloseGenerator(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->isGenerator());
|
||||
|
@ -3185,7 +3185,7 @@ js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType)
|
||||
* access is "object-detecting" in the sense used by web scripts, e.g., when
|
||||
* checking whether document.all is defined.
|
||||
*/
|
||||
JS_REQUIRES_STACK JSBool
|
||||
JSBool
|
||||
Detecting(JSContext *cx, jsbytecode *pc)
|
||||
{
|
||||
jsbytecode *endpc;
|
||||
|
@ -1789,7 +1789,7 @@ extern JS_FRIEND_API(JSBool)
|
||||
js_FindProperty(JSContext *cx, jsid id, bool global,
|
||||
JSObject **objp, JSObject **pobjp, JSProperty **propp);
|
||||
|
||||
extern JS_REQUIRES_STACK JSObject *
|
||||
extern JSObject *
|
||||
js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id);
|
||||
|
||||
extern JSObject *
|
||||
@ -1952,7 +1952,7 @@ eval(JSContext *cx, uintN argc, Value *vp);
|
||||
* currently-executing stack frame, which must be a script frame. On completion
|
||||
* the result is returned in args.rval.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK bool
|
||||
extern bool
|
||||
DirectEval(JSContext *cx, const CallArgs &args);
|
||||
|
||||
/*
|
||||
|
@ -46,7 +46,7 @@
|
||||
|
||||
using namespace js;
|
||||
|
||||
JS_REQUIRES_STACK PropertyCacheEntry *
|
||||
PropertyCacheEntry *
|
||||
PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj,
|
||||
const Shape *shape)
|
||||
{
|
||||
@ -176,7 +176,7 @@ GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs
|
||||
return atom;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JSAtom *
|
||||
JSAtom *
|
||||
PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
|
||||
PropertyCacheEntry *entry)
|
||||
{
|
||||
|
@ -180,8 +180,8 @@ class PropertyCache
|
||||
|
||||
static inline bool matchShape(JSContext *cx, JSObject *obj, uint32_t shape);
|
||||
|
||||
JS_REQUIRES_STACK JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||
JSObject **pobjp, PropertyCacheEntry *entry);
|
||||
JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||
JSObject **pobjp, PropertyCacheEntry *entry);
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertEmpty();
|
||||
@ -190,9 +190,9 @@ class PropertyCache
|
||||
#endif
|
||||
|
||||
public:
|
||||
JS_ALWAYS_INLINE JS_REQUIRES_STACK void test(JSContext *cx, jsbytecode *pc,
|
||||
JSObject *&obj, JSObject *&pobj,
|
||||
PropertyCacheEntry *&entry, JSAtom *&atom);
|
||||
JS_ALWAYS_INLINE void test(JSContext *cx, jsbytecode *pc,
|
||||
JSObject *&obj, JSObject *&pobj,
|
||||
PropertyCacheEntry *&entry, JSAtom *&atom);
|
||||
|
||||
/*
|
||||
* Test for cached information about a property set on *objp at pc.
|
||||
@ -213,8 +213,8 @@ class PropertyCache
|
||||
* Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was
|
||||
* not possible.
|
||||
*/
|
||||
JS_REQUIRES_STACK PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
|
||||
JSObject *pobj, const js::Shape *shape);
|
||||
PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
|
||||
JSObject *pobj, const js::Shape *shape);
|
||||
|
||||
void purge(JSContext *cx);
|
||||
|
||||
|
@ -1,375 +0,0 @@
|
||||
/*
|
||||
* Check that only JS_REQUIRES_STACK/JS_FORCES_STACK functions, and functions
|
||||
* that have called a JS_FORCES_STACK function, access cx->fp directly or
|
||||
* indirectly.
|
||||
*/
|
||||
|
||||
require({ after_gcc_pass: 'cfg' });
|
||||
include('gcc_util.js');
|
||||
include('unstable/adts.js');
|
||||
include('unstable/analysis.js');
|
||||
include('unstable/lazy_types.js');
|
||||
include('unstable/esp.js');
|
||||
|
||||
var Zero_NonZero = {};
|
||||
include('unstable/zero_nonzero.js', Zero_NonZero);
|
||||
|
||||
// Tell MapFactory we don't need multimaps (a speed optimization).
|
||||
MapFactory.use_injective = true;
|
||||
|
||||
/*
|
||||
* There are two regions in the program: RED and GREEN. Functions and member
|
||||
* variables may be declared RED in the C++ source. GREEN is the default.
|
||||
*
|
||||
* RED signals danger. A GREEN part of a function must not call a RED function
|
||||
* or access a RED member.
|
||||
*
|
||||
* The body of a RED function is all red. The body of a GREEN function is all
|
||||
* GREEN by default, but parts dominated by a call to a TURN_RED function are
|
||||
* red. This way GREEN functions can safely access RED stuff by calling a
|
||||
* TURN_RED function as preparation.
|
||||
*
|
||||
* The analysis does not attempt to prove anything about the body of a TURN_RED
|
||||
* function. (Both annotations are trusted; only unannotated code is checked
|
||||
* for errors.)
|
||||
*/
|
||||
const RED = 'JS_REQUIRES_STACK';
|
||||
const TURN_RED = 'JS_FORCES_STACK';
|
||||
const IGNORE_ERRORS = 'JS_IGNORE_STACK';
|
||||
|
||||
function attrs(tree) {
|
||||
let a = DECL_P(tree) ? DECL_ATTRIBUTES(tree) : TYPE_ATTRIBUTES(tree);
|
||||
return translate_attributes(a);
|
||||
}
|
||||
|
||||
function hasUserAttribute(tree, attrname) {
|
||||
let attributes = attrs(tree);
|
||||
if (attributes) {
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
let attr = attributes[i];
|
||||
if (attr.name == 'user' && attr.value.length == 1 && attr.value[0] == attrname)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function process_tree_type(d)
|
||||
{
|
||||
let t = dehydra_convert(d);
|
||||
if (t.isFunction)
|
||||
return;
|
||||
|
||||
if (t.typedef !== undefined)
|
||||
if (isRed(TYPE_NAME(d)))
|
||||
warning("Typedef declaration is annotated JS_REQUIRES_STACK: the annotation should be on the type itself", t.loc);
|
||||
|
||||
if (hasAttribute(t, RED)) {
|
||||
warning("Non-function is annotated JS_REQUIRES_STACK", t.loc);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let st = t; st !== undefined && st.isPointer; st = st.type) {
|
||||
if (hasAttribute(st, RED)) {
|
||||
warning("Non-function is annotated JS_REQUIRES_STACK", t.loc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (st.parameters)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function process_tree_decl(d)
|
||||
{
|
||||
// For VAR_DECLs, walk the DECL_INITIAL looking for bad assignments
|
||||
if (TREE_CODE(d) != VAR_DECL)
|
||||
return;
|
||||
|
||||
let i = DECL_INITIAL(d);
|
||||
if (!i)
|
||||
return;
|
||||
|
||||
assignCheck(i, TREE_TYPE(d), function() { return location_of(d); });
|
||||
|
||||
functionPointerWalk(i, d);
|
||||
}
|
||||
|
||||
/*
|
||||
* x is an expression or decl. These functions assume that
|
||||
*/
|
||||
function isRed(x) { return hasUserAttribute(x, RED); }
|
||||
function isTurnRed(x) { return hasUserAttribute(x, TURN_RED); }
|
||||
|
||||
function process_tree(fndecl)
|
||||
{
|
||||
if (hasUserAttribute(fndecl, IGNORE_ERRORS))
|
||||
return;
|
||||
|
||||
if (!(isRed(fndecl) || isTurnRed(fndecl))) {
|
||||
// Ordinarily a user of ESP runs the analysis, then generates output based
|
||||
// on the results. But in our case (a) we need sub-basic-block resolution,
|
||||
// which ESP doesn't keep; (b) it so happens that even though ESP can
|
||||
// iterate over blocks multiple times, in our case that won't cause
|
||||
// spurious output. (It could cause us to the same error message each time
|
||||
// through--but that's easily avoided.) Therefore we generate the output
|
||||
// while the ESP analysis is running.
|
||||
let a = new RedGreenCheck(fndecl, 0);
|
||||
if (a.hasRed)
|
||||
a.run();
|
||||
}
|
||||
|
||||
functionPointerCheck(fndecl);
|
||||
}
|
||||
|
||||
function RedGreenCheck(fndecl, trace) {
|
||||
//print("RedGreenCheck: " + fndecl.toCString());
|
||||
this._fndecl = fndecl;
|
||||
|
||||
// Tell ESP that fndecl is a "property variable". This makes ESP track it in
|
||||
// a flow-sensitive way. The variable will be 1 in RED regions and "don't
|
||||
// know" in GREEN regions. (We are technically lying to ESP about fndecl
|
||||
// being a variable--what we really want is a synthetic variable indicating
|
||||
// RED/GREEN state, but ESP operates on GCC decl nodes.)
|
||||
this._state_var_decl = fndecl;
|
||||
let state_var = new ESP.PropVarSpec(this._state_var_decl, true, undefined);
|
||||
|
||||
// Call base class constructor.
|
||||
let cfg = function_decl_cfg(fndecl);
|
||||
ESP.Analysis.apply(this, [cfg, [state_var], Zero_NonZero.meet, trace]);
|
||||
this.join = Zero_NonZero.join;
|
||||
|
||||
// Preprocess all instructions in the cfg to determine whether this analysis
|
||||
// is necessary and gather some information we'll use later.
|
||||
//
|
||||
// Each isn may include a function call, an assignment, and/or some reads.
|
||||
// Using walk_tree to walk the isns is a little crazy but robust.
|
||||
//
|
||||
this.hasRed = false;
|
||||
let self = this; // Allow our 'this' to be accessed inside closure
|
||||
for (let bb in cfg_bb_iterator(cfg)) {
|
||||
for (let isn in bb_isn_iterator(bb)) {
|
||||
// Treehydra objects don't support reading never-defined properties
|
||||
// as undefined, so we have to explicitly initialize anything we want
|
||||
// to check for later.
|
||||
isn.redInfo = undefined;
|
||||
walk_tree(isn, function(t, stack) {
|
||||
function getLocation(skiptop) {
|
||||
if (!skiptop) {
|
||||
let loc = location_of(t);
|
||||
if (loc !== undefined)
|
||||
return loc;
|
||||
}
|
||||
|
||||
for (let i = stack.length - 1; i >= 0; --i) {
|
||||
let loc = location_of(stack[i]);
|
||||
if (loc !== undefined)
|
||||
return loc;
|
||||
}
|
||||
return location_of(fndecl);
|
||||
}
|
||||
|
||||
switch (TREE_CODE(t)) {
|
||||
case FIELD_DECL:
|
||||
if (isRed(t)) {
|
||||
let varName = dehydra_convert(t).name;
|
||||
// location_of(t) is the location of the declaration.
|
||||
isn.redInfo = ["cannot access JS_REQUIRES_STACK variable " + varName,
|
||||
getLocation(true)];
|
||||
self.hasRed = true;
|
||||
}
|
||||
break;
|
||||
case GIMPLE_CALL:
|
||||
{
|
||||
let callee = gimple_call_fndecl(t);
|
||||
if (callee) {
|
||||
if (isRed(callee)) {
|
||||
let calleeName = dehydra_convert(callee).name;
|
||||
isn.redInfo = ["cannot call JS_REQUIRES_STACK function " + calleeName,
|
||||
getLocation(false)];
|
||||
self.hasRed = true;
|
||||
} else if (isTurnRed(callee)) {
|
||||
isn.turnRed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let fntype = TREE_CHECK(
|
||||
TREE_TYPE( // the function type
|
||||
TREE_TYPE( // the function pointer type
|
||||
gimple_call_fn(t)
|
||||
)
|
||||
),
|
||||
FUNCTION_TYPE, METHOD_TYPE);
|
||||
if (isRed(fntype)) {
|
||||
isn.redInfo = ["cannot call JS_REQUIRES_STACK function pointer",
|
||||
getLocation(false)];
|
||||
self.hasRed = true;
|
||||
}
|
||||
else if (isTurnRed(fntype)) {
|
||||
isn.turnRed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize mixin for infeasible-path elimination.
|
||||
this._zeroNonzero = new Zero_NonZero.Zero_NonZero();
|
||||
}
|
||||
|
||||
RedGreenCheck.prototype = new ESP.Analysis;
|
||||
|
||||
RedGreenCheck.prototype.flowStateCond = function(isn, truth, state) {
|
||||
// forward event to mixin
|
||||
this._zeroNonzero.flowStateCond(isn, truth, state);
|
||||
};
|
||||
|
||||
RedGreenCheck.prototype.flowState = function(isn, state) {
|
||||
// forward event to mixin
|
||||
//try { // The try/catch here is a workaround for some baffling bug in zero_nonzero.
|
||||
this._zeroNonzero.flowState(isn, state);
|
||||
//} catch (exc) {
|
||||
// warning(exc, location_of(isn));
|
||||
// warning("(Remove the workaround in jsstack.js and recompile to get a JS stack trace.)",
|
||||
// location_of(isn));
|
||||
//}
|
||||
let stackState = state.get(this._state_var_decl);
|
||||
let green = stackState != 1 && stackState != ESP.NOT_REACHED;
|
||||
let redInfo = isn.redInfo;
|
||||
if (green && redInfo) {
|
||||
warning(redInfo[0], redInfo[1]);
|
||||
isn.redInfo = undefined; // avoid duplicate messages about this instruction
|
||||
}
|
||||
|
||||
// If we call a TURNS_RED function, it doesn't take effect until after the
|
||||
// whole isn finishes executing (the most conservative rule).
|
||||
if (isn.turnRed)
|
||||
state.assignValue(this._state_var_decl, 1, isn);
|
||||
};
|
||||
|
||||
function followTypedefs(type)
|
||||
{
|
||||
while (type.typedef !== undefined)
|
||||
type = type.typedef;
|
||||
return type;
|
||||
}
|
||||
|
||||
function assignCheck(source, destType, locfunc)
|
||||
{
|
||||
if (TREE_CODE(destType) != POINTER_TYPE)
|
||||
return;
|
||||
|
||||
let destCode = TREE_CODE(TREE_TYPE(destType));
|
||||
if (destCode != FUNCTION_TYPE && destCode != METHOD_TYPE)
|
||||
return;
|
||||
|
||||
if (isRed(TREE_TYPE(destType)))
|
||||
return;
|
||||
|
||||
while (TREE_CODE(source) == NOP_EXPR)
|
||||
source = source.operands()[0];
|
||||
|
||||
// The destination is a green function pointer
|
||||
|
||||
if (TREE_CODE(source) == ADDR_EXPR) {
|
||||
let sourcefn = source.operands()[0];
|
||||
|
||||
// oddly enough, SpiderMonkey assigns the address of something that's not
|
||||
// a function to a function pointer as part of the API! See JS_TN
|
||||
if (TREE_CODE(sourcefn) != FUNCTION_DECL)
|
||||
return;
|
||||
|
||||
if (isRed(sourcefn))
|
||||
warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function " + dehydra_convert(sourcefn).name, locfunc());
|
||||
} else if (TREE_TYPE(source).tree_code() == POINTER_TYPE) {
|
||||
let sourceType = TREE_TYPE(TREE_TYPE(source));
|
||||
switch (TREE_CODE(sourceType)) {
|
||||
case FUNCTION_TYPE:
|
||||
case METHOD_TYPE:
|
||||
if (isRed(sourceType))
|
||||
warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function pointer", locfunc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type checker which verifies that a red function pointer is never converted
|
||||
* to a green function pointer.
|
||||
*/
|
||||
|
||||
function functionPointerWalk(t, baseloc)
|
||||
{
|
||||
walk_tree(t, function(t, stack) {
|
||||
function getLocation(skiptop) {
|
||||
if (!skiptop) {
|
||||
let loc = location_of(t);
|
||||
if (loc !== undefined)
|
||||
return loc;
|
||||
}
|
||||
|
||||
for (let i = stack.length - 1; i >= 0; --i) {
|
||||
let loc = location_of(stack[i]);
|
||||
if (loc !== undefined)
|
||||
return loc;
|
||||
}
|
||||
return location_of(baseloc);
|
||||
}
|
||||
|
||||
switch (TREE_CODE(t)) {
|
||||
case GIMPLE_ASSIGN: {
|
||||
let [dest, source] = t.operands();
|
||||
assignCheck(source, TREE_TYPE(dest), getLocation);
|
||||
break;
|
||||
}
|
||||
case CONSTRUCTOR: {
|
||||
let ttype = TREE_TYPE(t);
|
||||
switch (TREE_CODE(ttype)) {
|
||||
case RECORD_TYPE:
|
||||
case UNION_TYPE: {
|
||||
for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t)))
|
||||
assignCheck(ce.value, TREE_TYPE(ce.index), getLocation);
|
||||
break;
|
||||
}
|
||||
case ARRAY_TYPE: {
|
||||
let eltype = TREE_TYPE(ttype);
|
||||
for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t)))
|
||||
assignCheck(ce.value, eltype, getLocation);
|
||||
break;
|
||||
}
|
||||
case LANG_TYPE:
|
||||
// these can be safely ignored
|
||||
break;
|
||||
default:
|
||||
warning("Unexpected type in initializer: " + TREE_CODE(TREE_TYPE(t)), getLocation());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GIMPLE_CALL: {
|
||||
// Check that the arguments to a function and the declared types
|
||||
// of those arguments are compatible.
|
||||
let ops = t.operands();
|
||||
let funcType = TREE_TYPE( // function type
|
||||
TREE_TYPE(ops[1])); // function pointer type
|
||||
let argTypes = [t for (t in function_type_args(funcType))];
|
||||
for (let i = argTypes.length - 1; i >= 0; --i) {
|
||||
let destType = argTypes[i];
|
||||
let source = ops[i + 3];
|
||||
assignCheck(source, destType, getLocation);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function functionPointerCheck(fndecl)
|
||||
{
|
||||
let cfg = function_decl_cfg(fndecl);
|
||||
for (let bb in cfg_bb_iterator(cfg))
|
||||
for (let isn in bb_isn_iterator(bb))
|
||||
functionPointerWalk(isn, fndecl);
|
||||
}
|
@ -157,25 +157,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NS_STATIC_CHECKING
|
||||
/*
|
||||
* Attributes for static analysis. Functions declared with JS_REQUIRES_STACK
|
||||
* always have a valid cx->fp and can access it freely. Other functions can
|
||||
* access cx->fp only after calling a function that "forces" the stack
|
||||
* (i.e. lazily instantiates it as needed).
|
||||
*/
|
||||
# define JS_REQUIRES_STACK __attribute__((user("JS_REQUIRES_STACK")))
|
||||
# define JS_FORCES_STACK __attribute__((user("JS_FORCES_STACK")))
|
||||
/*
|
||||
* Skip the JS_REQUIRES_STACK analysis within functions with this annotation.
|
||||
*/
|
||||
# define JS_IGNORE_STACK __attribute__((user("JS_IGNORE_STACK")))
|
||||
#else
|
||||
# define JS_REQUIRES_STACK
|
||||
# define JS_FORCES_STACK
|
||||
# define JS_IGNORE_STACK
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
** MACROS: JS_BEGIN_MACRO
|
||||
** JS_END_MACRO
|
||||
|
@ -111,10 +111,11 @@ top:
|
||||
if (tn->stackDepth > cx->regs().sp - fp->base())
|
||||
continue;
|
||||
|
||||
UnwindScope(cx, tn->stackDepth);
|
||||
|
||||
jsbytecode *pc = script->main() + tn->start + tn->length;
|
||||
cx->regs().pc = pc;
|
||||
JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE);
|
||||
JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth);
|
||||
cx->regs().sp = fp->base() + tn->stackDepth;
|
||||
|
||||
switch (tn->kind) {
|
||||
case JSTRY_CATCH:
|
||||
@ -156,7 +157,7 @@ top:
|
||||
Value v = cx->getPendingException();
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
|
||||
cx->clearPendingException();
|
||||
ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject());
|
||||
bool ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject());
|
||||
cx->regs().sp -= 1;
|
||||
if (!ok)
|
||||
goto top;
|
||||
@ -579,7 +580,8 @@ js_InternalThrow(VMFrame &f)
|
||||
// and epilogues. RunTracer(), Interpret(), and Invoke() all
|
||||
// rely on this property.
|
||||
JS_ASSERT(!f.fp()->finishedInInterpreter());
|
||||
UnwindScope(cx, 0, cx->isExceptionPending());
|
||||
UnwindScope(cx, 0);
|
||||
f.regs.sp = f.fp()->base();
|
||||
|
||||
if (cx->compartment->debugMode())
|
||||
js::ScriptDebugEpilogue(cx, f.fp(), false);
|
||||
|
@ -1873,8 +1873,8 @@ stubs::LeaveBlock(VMFrame &f)
|
||||
* the stack into the clone, and pop it off the chain.
|
||||
*/
|
||||
JSObject &obj = fp->scopeChain();
|
||||
if (obj.getProto() == &blockObj && !obj.asClonedBlock().put(cx, JS_TRUE))
|
||||
THROW();
|
||||
if (obj.getProto() == &blockObj)
|
||||
obj.asClonedBlock().put(cx);
|
||||
|
||||
fp->setBlockChain(blockObj.enclosingBlock());
|
||||
}
|
||||
|
@ -472,8 +472,8 @@ ClonedBlockObject::create(JSContext *cx, StaticBlockObject &block, StackFrame *f
|
||||
return &obj->asClonedBlock();
|
||||
}
|
||||
|
||||
bool
|
||||
ClonedBlockObject::put(JSContext *cx, JSBool normalUnwind)
|
||||
void
|
||||
ClonedBlockObject::put(JSContext *cx)
|
||||
{
|
||||
StackFrame *fp = cx->fp();
|
||||
JS_ASSERT(maybeStackFrame() == js_FloatingFrameIfGenerator(cx, fp));
|
||||
@ -488,13 +488,11 @@ ClonedBlockObject::put(JSContext *cx, JSBool normalUnwind)
|
||||
/* See comments in CheckDestructuring in frontend/Parser.cpp. */
|
||||
JS_ASSERT(count >= 1);
|
||||
|
||||
if (normalUnwind)
|
||||
copySlotRange(RESERVED_SLOTS, fp->base() + depth, count);
|
||||
copySlotRange(RESERVED_SLOTS, fp->base() + depth, count);
|
||||
|
||||
/* We must clear the private slot even with errors. */
|
||||
setPrivate(NULL);
|
||||
fp->setScopeChainNoCallObj(enclosingScope());
|
||||
return normalUnwind;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -250,7 +250,7 @@ class ClonedBlockObject : public BlockObject
|
||||
* When this block's stack slots are about to be popped, 'put' must be
|
||||
* called to copy the slot values into this block's object slots.
|
||||
*/
|
||||
bool put(JSContext *cx, JSBool normalUnwind);
|
||||
void put(JSContext *cx);
|
||||
|
||||
/* Assuming 'put' has been called, return the value of the ith let var. */
|
||||
const Value &closedSlot(unsigned i);
|
||||
|
Loading…
Reference in New Issue
Block a user