mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 791850 - Add intrinsic function %_DecompileArg to self-hosting environment. r=jwalden
This commit is contained in:
parent
a39758fcb0
commit
9f2c624430
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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_),
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user