Bug 791850 - Add intrinsic function %_DecompileArg to self-hosting environment. r=jwalden

This commit is contained in:
Shu-yu Guo 2012-11-19 20:17:00 +01:00
parent a39758fcb0
commit 9f2c624430
5 changed files with 148 additions and 10 deletions

View File

@ -298,12 +298,18 @@ intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
char *errorArgs[3] = {NULL, NULL, NULL};
for (unsigned i = 1; i < 4 && i < args.length(); i++) {
RootedValue val(cx, args[i]);
if (val.isInt32() || val.isString()) {
if (val.isInt32()) {
JSString *str = ToString(cx, val);
if (!str)
return false;
errorArgs[i - 1] = JS_EncodeString(cx, str);
} else if (val.isString()) {
errorArgs[i - 1] = JS_EncodeString(cx, ToString(cx, val));
} else {
ptrdiff_t spIndex = cx->stack.spIndexOf(val.address());
errorArgs[i - 1] = DecompileValueGenerator(cx, spIndex, val, NullPtr(), 1);
errorArgs[i - 1] = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
}
if (!errorArgs[i - 1])
return false;
}
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
@ -313,6 +319,31 @@ intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
return false;
}
/*
* Used to decompile values in the nearest non-builtin stack frame, falling
* back to decompiling in the current frame. Helpful for printing higher-order
* function arguments.
*
* The user must supply the argument number of the value in question; it
* _cannot_ be automatically determined.
*/
static JSBool
intrinsic_DecompileArg(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(args.length() == 2);
RootedValue value(cx, args[1]);
ScopedFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value));
if (!str)
return false;
RootedAtom atom(cx, Atomize(cx, str, strlen(str)));
if (!atom)
return false;
args.rval().setString(atom);
return true;
}
static JSBool
intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp)
{
@ -331,6 +362,7 @@ JSFunctionSpec intrinsic_functions[] = {
JS_FN("IsCallable", intrinsic_IsCallable, 1,0),
JS_FN("ThrowError", intrinsic_ThrowError, 4,0),
JS_FN("_MakeConstructible", intrinsic_MakeConstructible, 1,0),
JS_FN("_DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FS_END
};
bool

View File

@ -6269,11 +6269,99 @@ js::DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
if (!fallback)
return NULL;
}
size_t length = fallback->length();
const jschar *chars = fallback->getChars(cx);
if (!chars)
Rooted<JSStableString *> stable(cx, fallback->ensureStable(cx));
if (!stable)
return NULL;
return DeflateString(cx, chars, length);
return DeflateString(cx, stable->chars().get(), stable->length());
}
static bool
DecompileArgumentFromStack(JSContext *cx, int formalIndex, char **res)
{
JS_ASSERT(formalIndex >= 0);
*res = NULL;
#ifdef JS_MORE_DETERMINISTIC
/* See note in DecompileExpressionFromStack. */
return true;
#endif
/*
* Settle on the nearest script frame, which should be the builtin that
* called the intrinsic.
*/
StackIter frameIter(cx);
while (!frameIter.done() && !frameIter.isScript())
++frameIter;
JS_ASSERT(!frameIter.done());
/*
* Get the second-to-top frame, the caller of the builtin that called the
* intrinsic.
*/
++frameIter;
/*
* If this frame isn't a script, we can't decompile. Even if it is a
* script but we popped a call frame during the last bump, assume that we
* just came from a frameless native and bail conservatively.
*/
if (frameIter.done() || frameIter.poppedCallDuringSettle() || !frameIter.isScript())
return true;
RootedScript script(cx, frameIter.script());
jsbytecode *current = frameIter.pc();
RootedFunction fun(cx, frameIter.isFunctionFrame()
? frameIter.callee()
: NULL);
JS_ASSERT(script->code <= current && current < script->code + script->length);
if (current < script->main())
return true;
PCStack pcStack;
if (!pcStack.init(cx, script, current))
return false;
if (formalIndex + 2 >= pcStack.depth())
return true;
ExpressionDecompiler ed(cx, script, fun);
if (!ed.init())
return false;
if (!ed.decompilePC(pcStack[formalIndex + 2]))
return false;
return ed.getOutput(res);
}
char *
js::DecompileArgument(JSContext *cx, int formalIndex, HandleValue v)
{
AssertCanGC();
{
char *result;
if (!DecompileArgumentFromStack(cx, formalIndex, &result))
return NULL;
if (result) {
if (strcmp(result, "(intermediate value)"))
return result;
js_free(result);
}
}
if (v.isUndefined())
return JS_strdup(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
RootedString fallback(cx, js_ValueToSource(cx, v));
if (!fallback)
return NULL;
Rooted<JSStableString *> stable(cx, fallback->ensureStable(cx));
if (!stable)
return NULL;
return DeflateString(cx, stable->chars().get(), stable->length());
}
static char *

View File

@ -362,6 +362,13 @@ char *
DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
HandleString fallback, int skipStackHits = 0);
/*
* Decompile the formal argument at formalIndex in the nearest non-builtin
* stack frame, falling back with converting v to source.
*/
char *
DecompileArgument(JSContext *cx, int formalIndex, HandleValue v);
/*
* Sprintf, but with unlimited and automatically allocated buffering.
*/

View File

@ -1302,6 +1302,9 @@ StackIter::settleOnNewState()
{
AutoAssertNoGC nogc;
/* Reset whether or we popped a call last time we settled. */
poppedCallDuringSettle_ = false;
/*
* There are elements of the calls_ and fp_ chains that we want to skip
* over so iterate until we settle on one or until there are no more.
@ -1399,6 +1402,7 @@ StackIter::settleOnNewState()
/* Pop the call and keep looking. */
popCall();
poppedCallDuringSettle_ = true;
}
}
@ -1406,7 +1410,8 @@ StackIter::StackIter(JSContext *cx, SavedOption savedOption)
: perThread_(&cx->runtime->mainThread),
maybecx_(cx),
savedOption_(savedOption),
script_(cx, NULL)
script_(cx, NULL),
poppedCallDuringSettle_(false)
#ifdef JS_ION
, ionActivations_(cx),
ionFrames_((uint8_t *)NULL),
@ -1431,7 +1436,8 @@ StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
: perThread_(&rt->mainThread),
maybecx_(NULL),
savedOption_(STOP_AT_SAVED),
script_(rt, NULL)
script_(rt, NULL),
poppedCallDuringSettle_(false)
#ifdef JS_ION
, ionActivations_(rt),
ionFrames_((uint8_t *)NULL),
@ -1457,7 +1463,8 @@ StackIter::StackIter(const StackIter &other)
seg_(other.seg_),
pc_(other.pc_),
script_(perThread_, other.script_),
args_(other.args_)
args_(other.args_),
poppedCallDuringSettle_(other.poppedCallDuringSettle_)
#ifdef JS_ION
, ionActivations_(other.ionActivations_),
ionFrames_(other.ionFrames_),

View File

@ -1731,6 +1731,8 @@ class StackIter
RootedScript script_;
CallArgs args_;
bool poppedCallDuringSettle_;
#ifdef JS_ION
ion::IonActivationIterator ionActivations_;
ion::IonFrameIterator ionFrames_;
@ -1760,6 +1762,8 @@ class StackIter
JSCompartment *compartment() const;
bool poppedCallDuringSettle() const { return poppedCallDuringSettle_; }
bool isScript() const {
JS_ASSERT(!done());
#ifdef JS_ION