Skip to content

Instantly share code, notes, and snippets.

@jimblandy
Created February 14, 2018 16:47
Show Gist options
  • Save jimblandy/6b54d1b2d6e59c5eaba2cedf9b9687c1 to your computer and use it in GitHub Desktop.
Save jimblandy/6b54d1b2d6e59c5eaba2cedf9b9687c1 to your computer and use it in GitHub Desktop.
Patch implementing dumpFunctionSkeleton testing JS function in SpiderMonkey
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4948,24 +4948,184 @@ IsConstructor(JSContext* cx, unsigned ar
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1)
args.rval().setBoolean(false);
else
args.rval().setBoolean(IsConstructor(args[0]));
return true;
}
+static void DumpFunctionSkeleton(JSContext*, HandleFunction, int);
+
+static void
+DumpScriptSourceSkeleton(JSContext* cx, ScriptSource* scriptSource, int indent)
+{
+ if (!scriptSource) {
+ printf("%*s(no scriptSource set)\n", indent, "");
+ } else {
+ printf("%*sscriptSource filename=\"%s\" startColumn=%u\n",
+ indent, "",
+ scriptSource->filename(),
+ (unsigned) scriptSource->introductionOffset());
+ }
+}
+
+static void
+DumpLazyScriptSkeleton(JSContext* cx, LazyScript* lazy_script, int indent)
+{
+ if (!lazy_script) {
+ printf("%*sLazyScript nullptr\n", indent, "");
+ return;
+ }
+
+ printf("%*sLazyScript lineno=%u column=%u\n",
+ indent, "",
+ (unsigned) lazy_script->lineno(),
+ (unsigned) lazy_script->column());
+
+ ScriptSource *scriptSource =
+ lazy_script->sourceObject() ? lazy_script->scriptSource() : nullptr;
+ DumpScriptSourceSkeleton(cx, scriptSource, indent + 4);
+
+ printf("%*s begin=%u end=%u\n",
+ indent, "",
+ (unsigned) lazy_script->begin(),
+ (unsigned) lazy_script->end());
+ printf("%*s toStringStart=%u End=%u\n",
+ indent, "",
+ (unsigned) lazy_script->toStringStart(),
+ (unsigned) lazy_script->toStringEnd());
+
+ indent += 2;
+
+ size_t num_inner_functions = lazy_script->numInnerFunctions();
+ for (size_t i = 0; i < num_inner_functions; i++) {
+ RootedFunction inner(cx, lazy_script->innerFunctions()[i]);
+ DumpFunctionSkeleton(cx, inner, indent);
+ }
+}
+
+static void
+DumpScriptSkeleton(JSContext* cx, HandleScript script, int indent)
+{
+ printf("%*sscript lineno=%u column=%u\n",
+ indent, "",
+ (unsigned) script->lineno(),
+ (unsigned) script->column());
+
+ DumpScriptSourceSkeleton(cx, script->scriptSource(), indent + 4);
+
+ printf("%*s sourceStart=%u End=%u\n",
+ indent, "",
+ (unsigned) script->sourceStart(),
+ (unsigned) script->sourceEnd());
+ printf("%*s toStringStart=%u End=%u\n",
+ indent, "",
+ (unsigned) script->toStringStart(),
+ (unsigned) script->toStringEnd());
+
+ indent += 2;
+
+ if (script->hasObjects()) {
+ ObjectArray* objects = script->objects();
+
+ for (size_t i = 0; i < objects->length; i++) {
+ RootedObject obj(cx, objects->vector[i]);
+ if (obj->is<JSFunction>()) {
+ RootedFunction fun(cx, &obj->as<JSFunction>());
+ DumpFunctionSkeleton(cx, fun, indent);
+ }
+ }
+ }
+}
+
+static void
+DumpFunctionFlags(uint64_t flags)
+{
+ if (flags & JSFunction::Flags::INTERPRETED)
+ printf(" INTERPRETED");
+ if (flags & JSFunction::Flags::CONSTRUCTOR)
+ printf(" CONSTRUCTOR");
+ if (flags & JSFunction::Flags::EXTENDED)
+ printf(" EXTENDED");
+ if (flags & JSFunction::Flags::BOUND_FUN)
+ printf(" BOUND_FUN");
+ if (flags & JSFunction::Flags::HAS_GUESSED_ATOM)
+ printf(" HAS_GUESSED_ATOM");
+ if (flags & JSFunction::Flags::HAS_BOUND_FUNCTION_NAME_PREFIX)
+ printf(" HAS_BOUND_FUNCTION_NAME_PREFIX");
+ if (flags & JSFunction::Flags::LAMBDA)
+ printf(" LAMBDA");
+ if (flags & JSFunction::Flags::SELF_HOSTED)
+ printf(" SELF_HOSTED");
+ if (flags & JSFunction::Flags::HAS_COMPILE_TIME_NAME)
+ printf(" HAS_COMPILE_TIME_NAME");
+ if (flags & JSFunction::Flags::INTERPRETED_LAZY)
+ printf(" INTERPRETED_LAZY");
+ if (flags & JSFunction::Flags::RESOLVED_LENGTH)
+ printf(" RESOLVED_LENGTH");
+ if (flags & JSFunction::Flags::RESOLVED_NAME)
+ printf(" RESOLVED_NAME");
+
+ uint64_t kind = (flags & JSFunction::FUNCTION_KIND_MASK) >> JSFunction::FUNCTION_KIND_SHIFT;
+ const char* kind_name = "(unrecognized kind)";
+ switch (kind) {
+ case JSFunction::NormalFunction: kind_name = "NormalFunction"; break;
+ case JSFunction::Arrow: kind_name = "Arrow"; break;
+ case JSFunction::Method: kind_name = "Method"; break;
+ case JSFunction::ClassConstructor: kind_name = "ClassConstructor"; break;
+ case JSFunction::Getter: kind_name = "Getter"; break;
+ case JSFunction::Setter: kind_name = "Setter"; break;
+ case JSFunction::AsmJS: kind_name = "AsmJS"; break;
+ case JSFunction::FunctionKindLimit: kind_name = "FunctionKindLimit"; break;
+ }
+ printf(" %s", kind_name);
+}
+
+static void
+DumpFunctionSkeleton(JSContext* cx, HandleFunction fun, int indent)
+{
+ JSString* str = fun->displayAtom();
+ char* chars = str ? JS_EncodeString(cx, str) : nullptr;
+
+ printf("%*sfunction displayAtom=%s flags=", indent, "", chars ? chars : "(null)");
+ if (!chars)
+ cx->clearPendingException();
+ js_free(chars);
+
+ DumpFunctionFlags(fun->flags());
+ putchar('\n');
+
+ indent += 2;
+
+ if (fun->isInterpretedLazy()) {
+ DumpLazyScriptSkeleton(cx, fun->lazyScriptOrNull(), indent);
+ } else if (fun->hasScript()) {
+ RootedScript script(cx, fun->nonLazyScript());
+ DumpScriptSkeleton(cx, script, indent);
+ }
+}
+
static bool
-IsLegacyIterator(JSContext* cx, unsigned argc, Value* vp)
+DumpFunctionSkeleton(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- if (args.length() < 1)
- args.rval().setBoolean(false);
- else
- args.rval().setBoolean(IsPropertyIterator(args[0]));
+ if (!args.requireAtLeast(cx, "getErrorNotes", 1))
+ return false;
+
+ RootedObject obj(cx, ToObject(cx, args[0]));
+ if (!obj->is<JSFunction>()) {
+ ReportUsageErrorASCII(cx, obj, "argument must be a function");
+ return false;
+ }
+
+ RootedFunction fun(cx, &obj->as<JSFunction>());
+ DumpFunctionSkeleton(cx, fun, 0);
+
+ args.rval().setUndefined();
return true;
}
static bool
EnableExpressionClosures(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS::ContextOptionsRef(cx).setExpressionClosures(true);
@@ -5585,19 +5745,20 @@ gc::ZealModeHelpText),
"TimeSinceCreation()",
" Returns the time in milliseconds since process creation.\n"
" This uses a clock compatible with the profiler.\n"),
JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
"isConstructor(value)",
" Returns whether the value is considered IsConstructor.\n"),
- JS_FN_HELP("isLegacyIterator", IsLegacyIterator, 1, 0,
-"isLegacyIterator(value)",
-" Returns whether the value is considered is a legacy iterator.\n"),
+ JS_FN_HELP("dumpFunctionSkeleton", DumpFunctionSkeleton, 1, 0,
+"dumpFunctionSkeleton(fn)",
+" Traverse the tree of nested functions rooted at fn,\n"
+" showing scripts and lazy scripts."),
JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
"getTimeZone()",
" Get the current time zone.\n"),
JS_FN_HELP("enableExpressionClosures", EnableExpressionClosures, 0, 0,
"enableExpressionClosures()",
" Enables the deprecated, non-standard expression closures.\n"),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment