Bug 1409815 - Part 2: Allow running BinAST files from shell CLI. (r=Waldo)
authorEric Faust <efaustbmo@gmail.com>
Mon, 30 Apr 2018 17:04:04 -0700
changeset 472499 f7ad9a51e45bb8d1e3e3f84de2fbe028901cd8e6
parent 472498 1c19a65b35f71599033a2805f15c8018ed924862
child 472500 3ee25bda924ea97cbda2d8602ede83b30c61eb19
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1409815
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1409815 - Part 2: Allow running BinAST files from shell CLI. (r=Waldo)
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/shell/js.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -575,35 +575,35 @@ frontend::CompileGlobalScript(JSContext*
     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     return compiler.compileGlobalScript(scopeKind);
 }
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 frontend::CompileGlobalBinASTScript(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
-                                    js::Vector<uint8_t> &src)
+                                    const uint8_t* src, size_t len)
 {
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return nullptr;
 
     RootedObject sourceObj(cx, CreateScriptSourceObject(cx, options));
 
     if (!sourceObj)
         return nullptr;
 
-    RootedScript script(cx, JSScript::Create(cx, options, sourceObj, 0, src.length(), 0, src.length()));
+    RootedScript script(cx, JSScript::Create(cx, options, sourceObj, 0, len, 0, len));
 
     if (!script)
         return nullptr;
 
     frontend::BinASTParser<BinTokenReaderMultipart> parser(cx, alloc, usedNames, options);
 
-    auto parsed = parser.parse(src);
+    auto parsed = parser.parse(src, len);
 
     if (parsed.isErr())
         return nullptr;
 
     Directives dir(false);
     GlobalSharedContext sc(cx, ScopeKind::Global, dir, false);
     BytecodeEmitter bce(nullptr, &parser, &sc, script, nullptr, 0);
 
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -36,17 +36,17 @@ CompileGlobalScript(JSContext* cx, LifoA
                     SourceBufferHolder& srcBuf,
                     ScriptSourceObject** sourceObjectOut = nullptr);
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 CompileGlobalBinASTScript(JSContext *cx, LifoAlloc& alloc,
                           const ReadOnlyCompileOptions& options,
-                          Vector<uint8_t> &src);
+                          const uint8_t* src, size_t len);
 
 #endif // JS_BUILD_BINAST
 
 JSScript*
 CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
                   HandleObject scopeChain, HandleScope enclosingScope,
                   const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3850,17 +3850,17 @@ JS_DefineFunctionById(JSContext* cx, Han
 #if defined(HAVE_GETC_UNLOCKED)
 # define fast_getc getc_unlocked
 #elif defined(HAVE__GETC_NOLOCK)
 # define fast_getc _getc_nolock
 #else
 # define fast_getc getc
 #endif
 
-typedef Vector<char, 8, TempAllocPolicy> FileContents;
+using FileContents = Vector<uint8_t, 8, TempAllocPolicy>;
 
 static bool
 ReadCompleteFile(JSContext* cx, FILE* fp, FileContents& buffer)
 {
     /* Get the complete length of the file, if possible. */
     struct stat st;
     int ok = fstat(fileno(fp), &st);
     if (ok != 0)
@@ -4124,17 +4124,17 @@ Compile(JSContext* cx, const ReadOnlyCom
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         FILE* fp, MutableHandleScript script)
 {
     FileContents buffer(cx);
     if (!ReadCompleteFile(cx, fp, buffer))
         return false;
 
-    return ::Compile(cx, options, buffer.begin(), buffer.length(), script);
+    return ::Compile(cx, options, reinterpret_cast<const char*>(buffer.begin()), buffer.length(), script);
 }
 
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
         const char* filename, MutableHandleScript script)
 {
     AutoFile file;
     if (!file.open(cx, filename))
@@ -4220,16 +4220,41 @@ bool
 JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
                                 const char* filename, JS::MutableHandleScript script)
 {
     CompileOptions options(cx, optionsArg);
     options.setNonSyntacticScope(true);
     return ::Compile(cx, options, filename, script);
 }
 
+#if defined(JS_BUILD_BINAST)
+
+JSScript*
+JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
+                 const uint8_t* buf, size_t length)
+{
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    AssertHeapIsIdle();
+    CHECK_REQUEST(cx);
+
+    return frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options, buf, length);
+}
+
+JSScript*
+JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file)
+{
+    FileContents fileContents(cx);
+    if (!ReadCompleteFile(cx, file, fileContents))
+        return nullptr;
+
+    return DecodeBinAST(cx, options, fileContents.begin(), fileContents.length());
+}
+
+#endif /* JS_BUILD_BINAST */
+
 enum class OffThread {
     Compile, Decode,
 };
 
 static bool
 CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length, OffThread what)
 {
     static const size_t TINY_LENGTH = 5 * 1000;
@@ -4846,17 +4871,17 @@ Evaluate(JSContext* cx, const ReadOnlyCo
     {
         AutoFile file;
         if (!file.open(cx, filename) || !file.readAll(cx, buffer))
             return false;
     }
 
     CompileOptions options(cx, optionsArg);
     options.setFileAndLine(filename, 1);
-    return Evaluate(cx, options, buffer.begin(), buffer.length(), rval);
+    return Evaluate(cx, options, reinterpret_cast<const char*>(buffer.begin()), buffer.length(), rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
              SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
     return ::Evaluate(cx, ScopeKind::Global, globalLexical, optionsArg, srcBuf, rval);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4224,16 +4224,32 @@ extern JS_PUBLIC_API(void)
 GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
                             uint32_t* lineNumber, uint32_t* columnNumber);
 
 extern JS_PUBLIC_API(JSScript*)
 GetModuleScript(JS::HandleObject moduleRecord);
 
 } /* namespace JS */
 
+#if defined(JS_BUILD_BINAST)
+
+namespace JS {
+
+extern JS_PUBLIC_API(JSScript*)
+DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
+             FILE* file);
+
+extern JS_PUBLIC_API(JSScript*)
+DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
+             const uint8_t* buf, size_t length);
+
+} /* namespace JS */
+
+#endif /* JS_BUILD_BINAST */
+
 extern JS_PUBLIC_API(bool)
 JS_CheckForInterrupt(JSContext* cx);
 
 /*
  * These functions allow setting an interrupt callback that will be called
  * from the JS thread some time after any thread triggered the callback using
  * JS_RequestInterruptCallback(cx).
  *
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -837,16 +837,39 @@ RunFile(JSContext* cx, const char* filen
             return false;
         int64_t t2 = PRMJ_Now() - t1;
         if (printTiming)
             printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
     }
     return true;
 }
 
+#if defined(JS_BUILD_BINAST)
+
+static MOZ_MUST_USE bool
+RunBinAST(JSContext* cx, const char* filename, FILE* file)
+{
+    RootedScript script(cx);
+
+    {
+        CompileOptions options(cx);
+        options.setFileAndLine(filename, 0)
+               .setIsRunOnce(true)
+               .setNoScriptRval(true);
+
+        script = JS::DecodeBinAST(cx, options, file);
+        if (!script)
+            return false;
+    }
+
+    return JS_ExecuteScript(cx, script);
+}
+
+#endif // JS_BUILD_BINAST
+
 static bool
 InitModuleLoader(JSContext* cx)
 {
     // Decompress and evaluate the embedded module loader source to initialize
     // the module loader for the current compartment.
 
     uint32_t srcLen = moduleloader::GetRawScriptsSize();
     ScopedJSFreePtr<char> src(cx->pod_malloc<char>(srcLen));
@@ -1234,17 +1257,18 @@ ReadEvalPrintLoop(JSContext* cx, FILE* i
         fprintf(gOutFile->fp, "\n");
 
     return true;
 }
 
 enum FileKind
 {
     FileScript,
-    FileModule
+    FileModule,
+    FileBinAST
 };
 
 static void
 ReportCantOpenErrorUnknownEncoding(JSContext* cx, const char* filename)
 {
     /*
      * Filenames are in some random system encoding.  *Probably* it's UTF-8,
      * but no guarantees.
@@ -1271,22 +1295,33 @@ Process(JSContext* cx, const char* filen
             ReportCantOpenErrorUnknownEncoding(cx, filename);
             return false;
         }
     }
     AutoCloseFile autoClose(file);
 
     if (!forceTTY && !isatty(fileno(file))) {
         // It's not interactive - just execute it.
-        if (kind == FileScript) {
+        switch (kind) {
+          case FileScript:
             if (!RunFile(cx, filename, file, compileOnly))
                 return false;
-        } else {
+            break;
+          case FileModule:
             if (!RunModule(cx, filename, file, compileOnly))
                 return false;
+            break;
+#if defined(JS_BUILD_BINAST)
+          case FileBinAST:
+            if (!RunBinAST(cx, filename, file))
+                return false;
+            break;
+#endif // JS_BUILD_BINAST
+          default:
+            MOZ_CRASH("Impossible FileKind!");
         }
     } else {
         // It's an interactive filehandle; drop into read-eval-print loop.
         MOZ_ASSERT(kind == FileScript);
         if (!ReadEvalPrintLoop(cx, file, compileOnly))
             return false;
     }
     return true;
@@ -8292,20 +8327,25 @@ ProcessArgs(JSContext* cx, OptionParser*
 
     /* |scriptArgs| gets bound on the global before any code is run. */
     if (!BindScriptArgs(cx, op))
         return false;
 
     MultiStringRange filePaths = op->getMultiStringOption('f');
     MultiStringRange codeChunks = op->getMultiStringOption('e');
     MultiStringRange modulePaths = op->getMultiStringOption('m');
+    MultiStringRange binASTPaths(nullptr, nullptr);
+#if defined(JS_BUILD_BINAST)
+    binASTPaths = op->getMultiStringOption('B');
+#endif // JS_BUILD_BINAST
 
     if (filePaths.empty() &&
         codeChunks.empty() &&
         modulePaths.empty() &&
+        binASTPaths.empty() &&
         !op->getStringArg("script"))
     {
         return Process(cx, nullptr, true); /* Interactive. */
     }
 
     if (const char* path = op->getStringOption("module-load-path")) {
         RootedString jspath(cx, JS_NewStringCopyZ(cx, path));
         if (!jspath)
@@ -8321,38 +8361,44 @@ ProcessArgs(JSContext* cx, OptionParser*
     }
 
     if (!sc->moduleLoadPath)
         return false;
 
     if (!modulePaths.empty() && !InitModuleLoader(cx))
         return false;
 
-    while (!filePaths.empty() || !codeChunks.empty() || !modulePaths.empty()) {
+    while (!filePaths.empty() || !codeChunks.empty() || !modulePaths.empty() || !binASTPaths.empty()) {
         size_t fpArgno = filePaths.empty() ? SIZE_MAX : filePaths.argno();
         size_t ccArgno = codeChunks.empty() ? SIZE_MAX : codeChunks.argno();
         size_t mpArgno = modulePaths.empty() ? SIZE_MAX : modulePaths.argno();
-
-        if (fpArgno < ccArgno && fpArgno < mpArgno) {
+        size_t baArgno = binASTPaths.empty() ? SIZE_MAX : binASTPaths.argno();
+
+        if (fpArgno < ccArgno && fpArgno < mpArgno && fpArgno < baArgno) {
             char* path = filePaths.front();
             if (!Process(cx, path, false, FileScript))
                 return false;
             filePaths.popFront();
-        } else if (ccArgno < fpArgno && ccArgno < mpArgno) {
+        } else if (ccArgno < fpArgno && ccArgno < mpArgno && ccArgno < baArgno) {
             const char* code = codeChunks.front();
             RootedValue rval(cx);
             JS::CompileOptions opts(cx);
             opts.setFileAndLine("-e", 1);
             if (!JS::Evaluate(cx, opts, code, strlen(code), &rval))
                 return false;
             codeChunks.popFront();
             if (sc->quitting)
                 break;
+        } else if (baArgno < fpArgno && baArgno < ccArgno && baArgno < mpArgno) {
+            char* path = binASTPaths.front();
+            if (!Process(cx, path, false, FileBinAST))
+                return false;
+            binASTPaths.popFront();
         } else {
-            MOZ_ASSERT(mpArgno < fpArgno && mpArgno < ccArgno);
+            MOZ_ASSERT(mpArgno < fpArgno && mpArgno < ccArgno && mpArgno < baArgno);
             char* path = modulePaths.front();
             if (!Process(cx, path, false, FileModule))
                 return false;
             modulePaths.popFront();
         }
     }
 
     if (sc->quitting)
@@ -8898,16 +8944,19 @@ main(int argc, char** argv, char** envp)
         "all options have been processed. Just-In-Time compilation modes may be enabled via "
         "command line options.");
     op.setDescriptionWidth(72);
     op.setHelpWidth(80);
     op.setVersion(JS_GetImplementationVersion());
 
     if (!op.addMultiStringOption('f', "file", "PATH", "File path to run")
         || !op.addMultiStringOption('m', "module", "PATH", "Module path to run")
+#if defined(JS_BUILD_BINAST)
+        || !op.addMultiStringOption('B', "binast", "PATH", "BinAST path to run")
+#endif // JS_BUILD_BINAST
         || !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run")
         || !op.addBoolOption('i', "shell", "Enter prompt after running code")
         || !op.addBoolOption('c', "compileonly", "Only compile, don't run (syntax checking mode)")
         || !op.addBoolOption('w', "warnings", "Emit warnings")
         || !op.addBoolOption('W', "nowarnings", "Don't emit warnings")
         || !op.addBoolOption('s', "strict", "Check strictness")
         || !op.addBoolOption('D', "dump-bytecode", "Dump bytecode with exec count for all scripts")
         || !op.addBoolOption('b', "print-timing", "Print sub-ms runtime for each file that's run")