Skip to content
This repository has been archived by the owner on Jul 3, 2018. It is now read-only.

Commit

Permalink
Merge branch 'master' into update-electron
Browse files Browse the repository at this point in the history
  • Loading branch information
mykmelez committed Oct 6, 2016
2 parents a4d1dcd + 03cbf52 commit ffde810
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
[submodule "positron/spidernode"]
path = positron/spidernode
url = https://github.com/mozilla/positron-spidernode.git
branch = existing-js-runtime
branch = master
5 changes: 5 additions & 0 deletions js/src/builtin/Error.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ function ErrorToString()
/* Step 11. */
return name + ": " + msg;
}

function ErrorToStringWithTrailingNewline()
{
return FUN_APPLY(ErrorToString, this, []) + "\n";
}
98 changes: 98 additions & 0 deletions js/src/jsapi-tests/testSavedStacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,108 @@ BEGIN_TEST(testSavedStacks_RangeBasedForLoops)
}
CHECK(rf == nullptr);

// Stack string
const char* SpiderMonkeyStack = "[email protected]:4:14\n"
"[email protected]:3:22\n"
"[email protected]:2:20\n"
"@filename.js:1:11\n";
const char* V8Stack = " at three (filename.js:4:14)\n"
" at two (filename.js:3:22)\n"
" at one (filename.js:2:20)\n"
" at filename.js:1:11";
struct {
js::StackFormat format;
const char* expected;
} expectations[] = {
{js::StackFormat::Default, SpiderMonkeyStack},
{js::StackFormat::SpiderMonkey, SpiderMonkeyStack},
{js::StackFormat::V8, V8Stack}
};
auto CheckStacks = [&]() {
for (auto& expectation : expectations) {
JS::RootedString str(cx);
CHECK(JS::BuildStackString(cx, savedFrame, &str, 0, expectation.format));
JSLinearString* lin = str->ensureLinear(cx);
CHECK(lin);
CHECK(js::StringEqualsAscii(lin, expectation.expected));
}
return true;
};

CHECK(CheckStacks());

js::SetStackFormat(cx, js::StackFormat::V8);
expectations[0].expected = V8Stack;

CHECK(CheckStacks());

return true;
}
END_TEST(testSavedStacks_RangeBasedForLoops)

BEGIN_TEST(testSavedStacks_ErrorStackSpiderMonkey)
{
JS::RootedValue val(cx);
CHECK(evaluate("(function one() { \n" // 1
" return (function two() { \n" // 2
" return (function three() { \n" // 3
" return new Error('foo'); \n" // 4
" }()); \n" // 5
" }()); \n" // 6
"}()).stack \n", // 7
"filename.js",
1,
&val));

CHECK(val.isString());
JS::RootedString stack(cx, val.toString());

// Stack string
const char* SpiderMonkeyStack = "[email protected]:4:14\n"
"[email protected]:3:22\n"
"[email protected]:2:20\n"
"@filename.js:1:11\n";
JSLinearString* lin = stack->ensureLinear(cx);
CHECK(lin);
CHECK(js::StringEqualsAscii(lin, SpiderMonkeyStack));

return true;
}
END_TEST(testSavedStacks_ErrorStackSpiderMonkey)

BEGIN_TEST(testSavedStacks_ErrorStackV8)
{
js::SetStackFormat(cx, js::StackFormat::V8);

JS::RootedValue val(cx);
CHECK(evaluate("(function one() { \n" // 1
" return (function two() { \n" // 2
" return (function three() { \n" // 3
" return new Error('foo'); \n" // 4
" }()); \n" // 5
" }()); \n" // 6
"}()).stack \n", // 7
"filename.js",
1,
&val));

CHECK(val.isString());
JS::RootedString stack(cx, val.toString());

// Stack string
const char* V8Stack = "Error: foo\n"
" at three (filename.js:4:14)\n"
" at two (filename.js:3:22)\n"
" at one (filename.js:2:20)\n"
" at filename.js:1:11";
JSLinearString* lin = stack->ensureLinear(cx);
CHECK(lin);
CHECK(js::StringEqualsAscii(lin, V8Stack));

return true;
}
END_TEST(testSavedStacks_ErrorStackV8)

BEGIN_TEST(testSavedStacks_selfHostedFrames)
{
CHECK(js::DefineTestingFunctions(cx, global, false, false));
Expand Down
12 changes: 12 additions & 0 deletions js/src/jsapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6773,3 +6773,15 @@ JS::GCThingTraceKind(void* thing)
MOZ_ASSERT(thing);
return static_cast<js::gc::Cell*>(thing)->getTraceKind();
}

JS_PUBLIC_API(void)
js::SetStackFormat(JSContext* cx, js::StackFormat format)
{
cx->setStackFormat(format);
}

JS_PUBLIC_API(js::StackFormat)
js::GetStackFormat(JSContext* cx)
{
return cx->stackFormat();
}
22 changes: 21 additions & 1 deletion js/src/jsapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -5863,6 +5863,25 @@ JS_DecodeScript(JSContext* cx, const void* data, uint32_t length);
extern JS_PUBLIC_API(JSObject*)
JS_DecodeInterpretedFunction(JSContext* cx, const void* data, uint32_t length);

namespace js {

enum class StackFormat { SpiderMonkey, V8, Default };

/*
* Sets the format used for stringifying Error stacks.
*
* The default format is StackFormat::SpiderMonkey. Use StackFormat::V8
* in order to emulate V8's stack formatting. StackFormat::Default can't be
* used here.
*/
extern JS_PUBLIC_API(void)
SetStackFormat(JSContext* cx, StackFormat format);

extern JS_PUBLIC_API(StackFormat)
GetStackFormat(JSContext* cx);

}

namespace JS {

/*
Expand Down Expand Up @@ -6274,7 +6293,8 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
* each line.
*/
extern JS_PUBLIC_API(bool)
BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent = 0);
BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
size_t indent = 0, js::StackFormat stackFormat = js::StackFormat::Default);

/**
* Return true iff the given object is either a SavedFrame object or wrapper
Expand Down
1 change: 1 addition & 0 deletions js/src/vm/CommonPropertyNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
macro(enumerable, enumerable, "enumerable") \
macro(enumerate, enumerate, "enumerate") \
macro(era, era, "era") \
macro(ErrorToStringWithTrailingNewline, ErrorToStringWithTrailingNewline, "ErrorToStringWithTrailingNewline") \
macro(escape, escape, "escape") \
macro(eval, eval, "eval") \
macro(exec, exec, "exec") \
Expand Down
20 changes: 20 additions & 0 deletions js/src/vm/ErrorObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,26 @@ js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp)
RootedString stackString(cx);
if (!BuildStackString(cx, savedFrameObj, &stackString))
return false;

if (cx->stackFormat() == js::StackFormat::V8) {
// When emulating V8 stack frames, we also need to prepend the
// stringified Error to the stack string.
HandlePropertyName name = cx->names().ErrorToStringWithTrailingNewline;
RootedValue val(cx);
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), name, name, 0, &val))
return false;

RootedValue rval(cx);
if (!js::Call(cx, val, args.thisv(), &rval))
return false;

if (!rval.isString())
return false;

RootedString stringified(cx, rval.toString());
stackString = ConcatStrings<CanGC>(cx, stringified, stackString);
}

args.rval().setString(stackString);
return true;
}
Expand Down
4 changes: 3 additions & 1 deletion js/src/vm/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
debuggerMallocSizeOf(ReturnZeroSize),
lastAnimationTime(0),
performanceMonitoring(thisFromCtor()),
ionLazyLinkListSize_(0)
ionLazyLinkListSize_(0),
stackFormat_(parentRuntime ? js::StackFormat::Default
: js::StackFormat::SpiderMonkey)
{
setGCStoreBufferPtr(&gc.storeBuffer);

Expand Down
21 changes: 21 additions & 0 deletions js/src/vm/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,27 @@ struct JSRuntime : public JS::shadow::Runtime,

void ionLazyLinkListRemove(js::jit::IonBuilder* builder);
void ionLazyLinkListAdd(js::jit::IonBuilder* builder);

private:
/* The stack format for the current runtime. Only valid on non-child
* runtimes. */
js::StackFormat stackFormat_;

public:
js::StackFormat stackFormat() const {
const JSRuntime* rt = this;
while (rt->parentRuntime) {
MOZ_ASSERT(rt->stackFormat_ == js::StackFormat::Default);
rt = rt->parentRuntime;
}
MOZ_ASSERT(rt->stackFormat_ != js::StackFormat::Default);
return rt->stackFormat_;
}
void setStackFormat(js::StackFormat format) {
MOZ_ASSERT(!parentRuntime);
MOZ_ASSERT(format != js::StackFormat::Default);
stackFormat_ = format;
}
};

namespace js {
Expand Down
89 changes: 69 additions & 20 deletions js/src/vm/SavedStacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,15 +891,64 @@ GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject
return SavedFrameResult::Ok;
}

static bool
FormatSpiderMonkeyStackFrame(JSContext* cx, js::StringBuffer& sb,
HandleSavedFrame frame, size_t indent,
bool skippedAsync)
{
RootedString asyncCause(cx, frame->getAsyncCause());
if (!asyncCause && skippedAsync)
asyncCause.set(cx->names().Async);

js::RootedAtom name(cx, frame->getFunctionDisplayName());
return (!indent || sb.appendN(' ', indent))
&& (!asyncCause || (sb.append(asyncCause) && sb.append('*')))
&& (!name || sb.append(name))
&& sb.append('@')
&& sb.append(frame->getSource())
&& sb.append(':')
&& NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
&& sb.append(':')
&& NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
&& sb.append('\n');
}

static bool
FormatV8StackFrame(JSContext* cx, js::StringBuffer& sb,
HandleSavedFrame frame, size_t indent, bool lastFrame)
{
js::RootedAtom name(cx, frame->getFunctionDisplayName());
return sb.appendN(' ', indent + 4)
&& sb.append('a')
&& sb.append('t')
&& sb.append(' ')
&& (!name || (sb.append(name) &&
sb.append(' ') &&
sb.append('(')))
&& sb.append(frame->getSource())
&& sb.append(':')
&& NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
&& sb.append(':')
&& NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
&& (!name || sb.append(')'))
&& (lastFrame || sb.append('\n'));
}

JS_PUBLIC_API(bool)
BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent)
BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
size_t indent, js::StackFormat format)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
MOZ_RELEASE_ASSERT(cx->compartment());

js::StringBuffer sb(cx);

if (format == js::StackFormat::Default) {
format = cx->stackFormat();
}
MOZ_ASSERT(format != js::StackFormat::Default);

// Enter a new block to constrain the scope of possibly entering the stack's
// compartment. This ensures that when we finish the StringBuffer, we are
// back in the cx's original compartment, and fulfill our contract with
Expand All @@ -919,27 +968,27 @@ BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
MOZ_ASSERT(SavedFrameSubsumedByCaller(cx, frame));
MOZ_ASSERT(!frame->isSelfHosted(cx));

RootedString asyncCause(cx, frame->getAsyncCause());
if (!asyncCause && skippedAsync)
asyncCause.set(cx->names().Async);

js::RootedAtom name(cx, frame->getFunctionDisplayName());
if ((indent && !sb.appendN(' ', indent))
|| (asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
|| (name && !sb.append(name))
|| !sb.append('@')
|| !sb.append(frame->getSource())
|| !sb.append(':')
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
|| !sb.append(':')
|| !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
|| !sb.append('\n'))
{
return false;
parent = frame->getParent();
js::RootedSavedFrame nextFrame(cx, js::GetFirstSubsumedFrame(cx, parent,
SavedFrameSelfHosted::Exclude, skippedAsync));

switch (format) {
case js::StackFormat::SpiderMonkey:
if (!FormatSpiderMonkeyStackFrame(cx, sb, frame, indent, skippedAsync)) {
return false;
}
break;
case js::StackFormat::V8:
if (!FormatV8StackFrame(cx, sb, frame, indent, !nextFrame)) {
return false;
}
break;
case js::StackFormat::Default:
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected value");
break;
}

parent = frame->getParent();
frame = js::GetFirstSubsumedFrame(cx, parent, SavedFrameSelfHosted::Exclude, skippedAsync);
frame = nextFrame;
} while (frame);
}

Expand Down
2 changes: 1 addition & 1 deletion positron/configure.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

configure_cmd="./configure --engine=spidermonkey --enable-static"
configure_cmd="./configure --engine=spidermonkey --enable-static --external-spidermonkey-has-nspr"

# Determine whether or not to also generate a configuration for a debug build.
# Regardless of the value of this arg, Node's configuration script will always
Expand Down

0 comments on commit ffde810

Please sign in to comment.