Created
          February 14, 2018 16:47 
        
      - 
      
- 
        Save jimblandy/6b54d1b2d6e59c5eaba2cedf9b9687c1 to your computer and use it in GitHub Desktop. 
    Patch implementing dumpFunctionSkeleton testing JS function in SpiderMonkey
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | 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