Bug 935203: Provide introductionType information for all sources of JS in SpiderMonkey. r=djvj
authorJim Blandy <jimb@mozilla.com>
Mon, 24 Feb 2014 15:31:47 -0800
changeset 170643 7bfc35e8d2b571a0c6a49368990495a578e4b3a3
parent 170642 9d1d368fdc10e65e8e151843a7804bb2056172bf
child 170644 684a53eafa313de2749dc9d3e3b4485936cfd527
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersdjvj
bugs935203
milestone30.0a1
Bug 935203: Provide introductionType information for all sources of JS in SpiderMonkey. r=djvj
js/src/jit-test/tests/debug/Source-introductionType-data
js/src/jit-test/tests/debug/Source-introductionType.js
js/src/jsapi.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/SelfHosting.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Source-introductionType-data
@@ -0,0 +1,1 @@
+debugger;
--- a/js/src/jit-test/tests/debug/Source-introductionType.js
+++ b/js/src/jit-test/tests/debug/Source-introductionType.js
@@ -1,38 +1,117 @@
 // Check that scripts' introduction types are properly marked.
 
 var g = newGlobal();
-var dbg = new Debugger(g);
+var dbg = new Debugger();
+var gDO = dbg.addDebuggee(g);
 var log;
 
+// (Indirect) eval.
 dbg.onDebuggerStatement = function (frame) {
   log += 'd';
   assertEq(frame.script.source.introductionType, 'eval');
 };
 log = '';
 g.eval('debugger;');
 assertEq(log, 'd');
 
+// Function constructor.
 dbg.onDebuggerStatement = function (frame) {
   log += 'd';
   assertEq(frame.script.source.introductionType, 'Function');
 };
 log = '';
 g.Function('debugger;')();
 assertEq(log, 'd');
 
+// GeneratorFunction constructor.
 dbg.onDebuggerStatement = function (frame) {
   log += 'd';
   assertEq(frame.script.source.introductionType, 'GeneratorFunction');
 };
 log = '';
 g.eval('(function*() {})').constructor('debugger;')().next();
 assertEq(log, 'd');
 
+// Shell 'evaluate' function
 dbg.onDebuggerStatement = function (frame) {
   log += 'd';
-  assertEq(frame.script.source.introductionType, undefined);
+  assertEq(frame.script.source.introductionType, "js shell evaluate");
 };
 log = '';
 g.evaluate('debugger;');
 assertEq(log, 'd');
 
+// Shell 'load' function
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  assertEq(frame.script.source.introductionType, "js shell load");
+};
+log = '';
+g.load(scriptdir + 'Source-introductionType-data');
+assertEq(log, 'd');
+
+// Shell 'run' function
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  assertEq(frame.script.source.introductionType, "js shell run");
+};
+log = '';
+g.run(scriptdir + 'Source-introductionType-data');
+assertEq(log, 'd');
+
+// Shell 'offThreadCompileScript' function.
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  assertEq(frame.script.source.introductionType, "js shell offThreadCompileScript");
+};
+log = '';
+g.offThreadCompileScript('debugger;');
+g.runOffThreadScript();
+assertEq(log, 'd');
+
+// Debugger.Frame.prototype.eval
+dbg.onDebuggerStatement = function (frame) {
+  log += 'o';
+  dbg.onDebuggerStatement = innerHandler;
+  frame.eval('debugger');
+  function innerHandler(frame) {
+    log += 'i';
+    assertEq(frame.script.source.introductionType, "debugger eval");
+  }
+};
+log = '';
+g.eval('debugger;');
+assertEq(log, 'oi');
+
+// Debugger.Frame.prototype.evalWithBindings
+dbg.onDebuggerStatement = function (frame) {
+  log += 'o';
+  dbg.onDebuggerStatement = innerHandler;
+  frame.evalWithBindings('debugger', { x: 42 });
+  function innerHandler(frame) {
+    log += 'i';
+    assertEq(frame.script.source.introductionType, "debugger eval");
+  }
+};
+log = '';
+g.eval('debugger;');
+assertEq(log, 'oi');
+
+// Debugger.Object.evalInGlobal
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  assertEq(frame.script.source.introductionType, "debugger eval");
+};
+log = '';
+gDO.evalInGlobal('debugger;');
+assertEq(log, 'd');
+
+// Debugger.Object.evalInGlobalWithBindings
+dbg.onDebuggerStatement = function (frame) {
+  log += 'd';
+  assertEq(frame.script.source.introductionType, "debugger eval");
+};
+log = '';
+gDO.evalInGlobalWithBindings('debugger;', { x: 42 });
+assertEq(log, 'd');
+
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3619,16 +3619,17 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions &setColumn(unsigned c) { column = c; return *this; }
     OwningCompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     OwningCompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    OwningCompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     bool setIntroductionInfo(JSContext *cx, const char *introducerFn, const char *intro,
                              unsigned line, uint32_t offset)
     {
         if (!setIntroducerFilename(cx, introducerFn))
             return false;
         introductionType = intro;
         introductionLineno = line;
         introductionOffset = offset;
@@ -3696,16 +3697,17 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions &setColumn(unsigned c) { column = c; return *this; }
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    CompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     CompileOptions &setIntroductionInfo(const char *introducerFn, const char *intro,
                                         unsigned line, uint32_t offset)
     {
         introducerFilename_ = introducerFn;
         introductionType = intro;
         introductionLineno = line;
         introductionOffset = offset;
         hasIntroductionInfo = true;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -387,17 +387,18 @@ RunFile(JSContext *cx, Handle<JSObject*>
     int64_t t1 = PRMJ_Now();
     RootedScript script(cx);
 
     {
         JS::AutoSaveContextOptions asco(cx);
         JS::ContextOptionsRef(cx).setNoScriptRval(true);
 
         CompileOptions options(cx);
-        options.setUTF8(true)
+        options.setIntroductionType("js shell file")
+               .setUTF8(true)
                .setFileAndLine(filename, 1)
                .setCompileAndGo(true);
 
         gGotError = false;
         script = JS::Compile(cx, obj, options, file);
         JS_ASSERT_IF(!script, gGotError);
     }
 
@@ -417,17 +418,18 @@ RunFile(JSContext *cx, Handle<JSObject*>
 }
 
 static bool
 EvalAndPrint(JSContext *cx, Handle<JSObject*> global, const char *bytes, size_t length,
              int lineno, bool compileOnly, FILE *out)
 {
     // Eval.
     JS::CompileOptions options(cx);
-    options.setUTF8(true)
+    options.setIntroductionType("js shell interactive")
+           .setUTF8(true)
            .setCompileAndGo(true)
            .setFileAndLine("typein", lineno);
     RootedScript script(cx);
     script = JS::Compile(cx, global, options, bytes, length);
     if (!script)
         return false;
     if (compileOnly)
         return true;
@@ -742,17 +744,20 @@ LoadScript(JSContext *cx, unsigned argc,
             JS_ReportError(cx, "unable to resolve path");
             return false;
         }
         JSAutoByteString filename(cx, str);
         if (!filename)
             return false;
         errno = 0;
         CompileOptions opts(cx);
-        opts.setUTF8(true).setCompileAndGo(true).setNoScriptRval(true);
+        opts.setIntroductionType("js shell load")
+            .setUTF8(true)
+            .setCompileAndGo(true)
+            .setNoScriptRval(true);
         if ((compileOnly && !Compile(cx, thisobj, opts, filename.ptr())) ||
             !Evaluate(cx, thisobj, opts, filename.ptr(), nullptr))
         {
             return false;
         }
     }
 
     args.rval().setUndefined();
@@ -1040,17 +1045,18 @@ Evaluate(JSContext *cx, unsigned argc, j
     RootedObject global(cx, nullptr);
     bool catchTermination = false;
     bool saveFrameChain = false;
     bool loadBytecode = false;
     bool saveBytecode = false;
     bool assertEqBytecode = false;
     RootedObject callerGlobal(cx, cx->global());
 
-    options.setFileAndLine("@evaluate", 1);
+    options.setIntroductionType("js shell evaluate")
+           .setFileAndLine("@evaluate", 1);
 
     global = JS_GetGlobalForObject(cx, &args.callee());
     if (!global)
         return false;
 
     if (args.length() == 2) {
         RootedObject opts(cx, &args[1].toObject());
         RootedValue v(cx);
@@ -1358,17 +1364,18 @@ Run(JSContext *cx, unsigned argc, jsval 
 
     RootedScript script(cx);
     int64_t startClock = PRMJ_Now();
     {
         JS::AutoSaveContextOptions asco(cx);
         JS::ContextOptionsRef(cx).setNoScriptRval(true);
 
         JS::CompileOptions options(cx);
-        options.setFileAndLine(filename.ptr(), 1)
+        options.setIntroductionType("js shell run")
+               .setFileAndLine(filename.ptr(), 1)
                .setCompileAndGo(true);
         script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, options);
         if (!script)
             return false;
     }
 
     if (!JS_ExecuteScript(cx, thisobj, script, nullptr))
         return false;
@@ -2197,17 +2204,18 @@ DisassFile(JSContext *cx, unsigned argc,
         return false;
     RootedScript script(cx);
 
     {
         JS::AutoSaveContextOptions asco(cx);
         JS::ContextOptionsRef(cx).setNoScriptRval(true);
 
         CompileOptions options(cx);
-        options.setUTF8(true)
+        options.setIntroductionType("js shell disFile")
+               .setUTF8(true)
                .setFileAndLine(filename.ptr(), 1)
                .setCompileAndGo(true);
 
         script = JS::Compile(cx, thisobj, options, filename.ptr());
         if (!script)
             return false;
     }
 
@@ -3377,17 +3385,18 @@ Compile(JSContext *cx, unsigned argc, js
         return false;
     }
 
     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     JSString *scriptContents = args[0].toString();
     JS::AutoSaveContextOptions asco(cx);
     JS::ContextOptionsRef(cx).setNoScriptRval(true);
     JS::CompileOptions options(cx);
-    options.setFileAndLine("<string>", 1)
+    options.setIntroductionType("js shell compile")
+           .setFileAndLine("<string>", 1)
            .setCompileAndGo(true);
     bool ok = JS_CompileUCScript(cx, global, JS_GetStringCharsZ(cx, scriptContents),
                                  JS_GetStringLength(scriptContents), options);
     args.rval().setUndefined();
     return ok;
 }
 
 static bool
@@ -3405,17 +3414,18 @@ Parse(JSContext *cx, unsigned argc, jsva
     if (!args[0].isString()) {
         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
         JS_ReportError(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
     JSString *scriptContents = args[0].toString();
     CompileOptions options(cx);
-    options.setFileAndLine("<string>", 1)
+    options.setIntroductionType("js shell parse")
+           .setFileAndLine("<string>", 1)
            .setCompileAndGo(false);
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options,
                                     JS_GetStringCharsZ(cx, scriptContents),
                                     JS_GetStringLength(scriptContents),
                                     /* foldConstants = */ true, nullptr, nullptr);
 
     ParseNode *pn = parser.parse(nullptr);
     if (!pn)
@@ -3443,17 +3453,18 @@ SyntaxParse(JSContext *cx, unsigned argc
     if (!args[0].isString()) {
         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
         JS_ReportError(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
     JSString *scriptContents = args[0].toString();
     CompileOptions options(cx);
-    options.setFileAndLine("<string>", 1)
+    options.setIntroductionType("js shell syntaxParse")
+           .setFileAndLine("<string>", 1)
            .setCompileAndGo(false);
 
     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
     size_t length = JS_GetStringLength(scriptContents);
     Parser<frontend::SyntaxParseHandler> parser(cx, &cx->tempLifoAlloc(),
                                                 options, chars, length, false, nullptr, nullptr);
 
     bool succeeded = parser.parse(nullptr);
@@ -3573,17 +3584,18 @@ OffThreadCompileScript(JSContext *cx, un
     if (!args[0].isString()) {
         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
         JS_ReportError(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
     JSAutoByteString fileNameBytes;
     CompileOptions options(cx);
-    options.setFileAndLine("<string>", 1);
+    options.setIntroductionType("js shell offThreadCompileScript")
+           .setFileAndLine("<string>", 1);
 
     if (args.length() >= 2) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
             return false;
         }
 
         RootedObject opts(cx, &args[1].toObject());
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4447,17 +4447,18 @@ js::EvaluateInEnv(JSContext *cx, Handle<
      * static level will suffice.
      */
     CompileOptions options(cx);
     options.setPrincipals(env->compartment()->principals)
            .setCompileAndGo(true)
            .setForEval(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
-           .setCanLazilyParse(false);
+           .setCanLazilyParse(false)
+           .setIntroductionType("debugger eval");
     RootedScript callerScript(cx, frame ? frame.script() : nullptr);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, callerScript,
                                                     options, chars.get(), length,
                                                     /* source = */ nullptr,
                                                     /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -779,16 +779,17 @@ js::FillSelfHostingCompileOptions(Compil
      * As that object is inaccessible to client code, the lookups are
      * guaranteed to return the original objects, ensuring safe implementation
      * of self-hosted builtins.
      *
      * Additionally, the special syntax callFunction(fun, receiver, ...args)
      * is supported, for which bytecode is emitted that invokes |fun| with
      * |receiver| as the this-object and ...args as the arguments.
      */
+    options.setIntroductionType("self-hosted");
     options.setFileAndLine("self-hosted", 1);
     options.setSelfHostingMode(true);
     options.setCanLazilyParse(false);
     options.setSourcePolicy(CompileOptions::NO_SOURCE);
     options.setVersion(JSVERSION_LATEST);
     options.werrorOption = true;
 
 #ifdef DEBUG