зеркало из https://github.com/mozilla/gecko-dev.git
Bug 944164 - Implement proper redirection with ref counted underlying files, r=terrence
MozReview-Commit-ID: KVjZ0WXl9E6
This commit is contained in:
Родитель
094469068b
Коммит
c715a05b0c
|
@ -42,6 +42,11 @@
|
|||
# include <libgen.h>
|
||||
#endif
|
||||
|
||||
using js::shell::RCFile;
|
||||
|
||||
static RCFile** gErrFilePtr = nullptr;
|
||||
static RCFile** gOutFilePtr = nullptr;
|
||||
|
||||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
|
@ -306,53 +311,214 @@ osfile_writeTypedArrayToFile(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Redirect(JSContext* cx, FILE* fp, HandleString relFilename)
|
||||
/* static */ RCFile*
|
||||
RCFile::create(JSContext* cx, const char* filename, const char* mode)
|
||||
{
|
||||
FILE* fp = fopen(filename, mode);
|
||||
if (!fp)
|
||||
return nullptr;
|
||||
|
||||
RCFile* file = cx->new_<RCFile>(fp);
|
||||
if (!file) {
|
||||
fclose(fp);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
void
|
||||
RCFile::close()
|
||||
{
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
RCFile::release()
|
||||
{
|
||||
if (--numRefs)
|
||||
return false;
|
||||
this->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
class FileObject : public JSObject {
|
||||
enum : uint32_t {
|
||||
FILE_SLOT = 0,
|
||||
NUM_SLOTS
|
||||
};
|
||||
|
||||
public:
|
||||
static const js::Class class_;
|
||||
|
||||
static FileObject* create(JSContext* cx, RCFile* file) {
|
||||
JSObject* obj = js::NewObjectWithClassProto(cx, &class_, nullptr);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
FileObject* fileObj = &obj->as<FileObject>();
|
||||
fileObj->setRCFile(file);
|
||||
file->acquire();
|
||||
return fileObj;
|
||||
}
|
||||
|
||||
static void finalize(FreeOp* fop, JSObject* obj) {
|
||||
FileObject* fileObj = &obj->as<FileObject>();
|
||||
RCFile* file = fileObj->rcFile();
|
||||
if (file->release()) {
|
||||
fileObj->setRCFile(nullptr);
|
||||
fop->delete_(file);
|
||||
}
|
||||
}
|
||||
|
||||
bool isOpen() {
|
||||
RCFile* file = rcFile();
|
||||
return file && file->isOpen();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (!isOpen())
|
||||
return;
|
||||
rcFile()->close();
|
||||
}
|
||||
|
||||
RCFile* rcFile() {
|
||||
return reinterpret_cast<RCFile*>(js::GetReservedSlot(this, FILE_SLOT).toPrivate());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void setRCFile(RCFile* file) {
|
||||
js::SetReservedSlot(this, FILE_SLOT, PrivateValue(file));
|
||||
}
|
||||
};
|
||||
|
||||
const js::Class FileObject::class_ = {
|
||||
"File",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(FileObject::NUM_SLOTS),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
FileObject::finalize, /* finalize */
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
nullptr /* trace */
|
||||
};
|
||||
|
||||
static FileObject*
|
||||
redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
|
||||
{
|
||||
RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
|
||||
if (!filename)
|
||||
return false;
|
||||
return nullptr;
|
||||
JSAutoByteString filenameABS(cx, filename);
|
||||
if (!filenameABS)
|
||||
return false;
|
||||
if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
|
||||
return nullptr;
|
||||
RCFile* file = RCFile::create(cx, filenameABS.ptr(), "wb");
|
||||
if (!file) {
|
||||
JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Grant the global gOutFile ownership of the new file, release ownership
|
||||
// of its old file, and return a FileObject owning the old file.
|
||||
file->acquire(); // Global owner of new file
|
||||
|
||||
FileObject* fileObj = FileObject::create(cx, *globalFile); // Newly created owner of old file
|
||||
if (!fileObj) {
|
||||
file->release();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
(*globalFile)->release(); // Release (global) ownership of old file.
|
||||
*globalFile = file;
|
||||
|
||||
return fileObj;
|
||||
}
|
||||
|
||||
static bool
|
||||
Redirect(JSContext* cx, const CallArgs& args, RCFile** outFile)
|
||||
{
|
||||
if (args.length() > 1) {
|
||||
JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
|
||||
return false;
|
||||
}
|
||||
|
||||
RCFile* oldFile = *outFile;
|
||||
RootedObject oldFileObj(cx, FileObject::create(cx, oldFile));
|
||||
if (!oldFileObj)
|
||||
return false;
|
||||
|
||||
if (args.get(0).isUndefined()) {
|
||||
args.rval().setObject(*oldFileObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].isObject()) {
|
||||
RootedObject fileObj(cx, js::CheckedUnwrap(&args[0].toObject()));
|
||||
if (!fileObj)
|
||||
return false;
|
||||
|
||||
if (fileObj->is<FileObject>()) {
|
||||
// Passed in a FileObject. Create a FileObject for the previous
|
||||
// global file, and set the global file to the passed-in one.
|
||||
*outFile = fileObj->as<FileObject>().rcFile();
|
||||
(*outFile)->acquire();
|
||||
oldFile->release();
|
||||
|
||||
args.rval().setObject(*oldFileObj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
RootedString filename(cx, JS::ToString(cx, args[0]));
|
||||
if (!filename)
|
||||
return false;
|
||||
|
||||
if (!redirect(cx, filename, outFile))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*oldFileObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
osfile_redirect(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
osfile_redirectOutput(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return Redirect(cx, args, gOutFilePtr);
|
||||
}
|
||||
|
||||
static bool
|
||||
osfile_redirectError(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return Redirect(cx, args, gErrFilePtr);
|
||||
}
|
||||
|
||||
static bool
|
||||
osfile_close(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() < 1 || args.length() > 2) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
|
||||
Rooted<FileObject*> fileObj(cx);
|
||||
if (args.get(0).isObject()) {
|
||||
JSObject *obj = js::CheckedUnwrap(&args[0].toObject());
|
||||
if (obj->is<FileObject>())
|
||||
fileObj = &obj->as<FileObject>();
|
||||
}
|
||||
|
||||
if (!fileObj) {
|
||||
JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr,
|
||||
JSSMSG_INVALID_ARGS, "close");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args[0].isString() || args[0].isNull()) {
|
||||
RootedString stdoutPath(cx);
|
||||
if (!args[0].isNull()) {
|
||||
stdoutPath = args[0].toString();
|
||||
if (!stdoutPath)
|
||||
return false;
|
||||
}
|
||||
if (!Redirect(cx, stdout, stdoutPath))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length() > 1 && (args[1].isString() || args[1].isNull())) {
|
||||
RootedString stderrPath(cx);
|
||||
if (!args[1].isNull()) {
|
||||
stderrPath = args[1].toString();
|
||||
if (!stderrPath)
|
||||
return false;
|
||||
}
|
||||
if (!Redirect(cx, stderr, stderrPath))
|
||||
return false;
|
||||
}
|
||||
fileObj->close();
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
|
@ -377,11 +543,19 @@ static const JSFunctionSpecWithHelp osfile_unsafe_functions[] = {
|
|||
"writeTypedArrayToFile(filename, data)",
|
||||
" Write the contents of a typed array to the named file."),
|
||||
|
||||
JS_FN_HELP("redirect", osfile_redirect, 2, 0,
|
||||
"redirect(stdoutFilename[, stderrFilename])",
|
||||
" Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
|
||||
" redirecting, or null to discard the output. Filenames are relative to the\n"
|
||||
" current working directory."),
|
||||
JS_FN_HELP("redirect", osfile_redirectOutput, 1, 0,
|
||||
"redirect([path-or-object])",
|
||||
" Redirect print() output to the named file.\n"
|
||||
" Return an opaque object representing the previous destination, which\n"
|
||||
" may be passed into redirect() later to restore the output."),
|
||||
|
||||
JS_FN_HELP("redirectErr", osfile_redirectError, 1, 0,
|
||||
"redirectErr([path-or-object])",
|
||||
" Same as redirect(), but for printErr"),
|
||||
|
||||
JS_FN_HELP("close", osfile_close, 1, 0,
|
||||
"close(object)",
|
||||
" Close the file returned by an earlier redirect call."),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
@ -732,7 +906,9 @@ static const JSFunctionSpecWithHelp os_functions[] = {
|
|||
};
|
||||
|
||||
bool
|
||||
DefineOS(JSContext* cx, HandleObject global, bool fuzzingSafe)
|
||||
DefineOS(JSContext* cx, HandleObject global,
|
||||
bool fuzzingSafe,
|
||||
RCFile** shellOut, RCFile** shellErr)
|
||||
{
|
||||
RootedObject obj(cx, JS_NewPlainObject(cx));
|
||||
if (!obj || !JS_DefineProperty(cx, global, "os", obj, 0))
|
||||
|
@ -771,6 +947,9 @@ DefineOS(JSContext* cx, HandleObject global, bool fuzzingSafe)
|
|||
if (!GenerateInterfaceHelp(cx, obj, "os"))
|
||||
return false;
|
||||
|
||||
gOutFilePtr = shellOut;
|
||||
gErrFilePtr = shellErr;
|
||||
|
||||
// For backwards compatibility, expose various os.file.* functions as
|
||||
// direct methods on the global.
|
||||
RootedValue val(cx);
|
||||
|
@ -782,7 +961,8 @@ DefineOS(JSContext* cx, HandleObject global, bool fuzzingSafe)
|
|||
{ "readFile", "read" },
|
||||
{ "readFile", "snarf" },
|
||||
{ "readRelativeToScript", "readRelativeToScript" },
|
||||
{ "redirect", "redirect" }
|
||||
{ "redirect", "redirect" },
|
||||
{ "redirectErr", "redirectErr" }
|
||||
};
|
||||
|
||||
for (auto pair : osfile_exports) {
|
||||
|
|
|
@ -14,9 +14,13 @@
|
|||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
struct RCFile;
|
||||
|
||||
/* Define an os object on the given global object. */
|
||||
bool
|
||||
DefineOS(JSContext* cx, JS::HandleObject global, bool fuzzingSafe);
|
||||
DefineOS(JSContext* cx, JS::HandleObject global,
|
||||
bool fuzzingSafe,
|
||||
RCFile** shellOut, RCFile** shellErr);
|
||||
|
||||
enum PathResolutionMode {
|
||||
RootRelative,
|
||||
|
|
|
@ -183,8 +183,8 @@ static char gZealStr[128];
|
|||
static bool printTiming = false;
|
||||
static const char* jsCacheDir = nullptr;
|
||||
static const char* jsCacheAsmJSPath = nullptr;
|
||||
static FILE* gErrFile = nullptr;
|
||||
static FILE* gOutFile = nullptr;
|
||||
static RCFile* gErrFile = nullptr;
|
||||
static RCFile* gOutFile = nullptr;
|
||||
static bool reportWarnings = true;
|
||||
static bool compileOnly = false;
|
||||
static bool fuzzingSafe = false;
|
||||
|
@ -349,9 +349,9 @@ GetLine(FILE* file, const char * prompt)
|
|||
#endif
|
||||
|
||||
size_t len = 0;
|
||||
if (*prompt != '\0') {
|
||||
fprintf(gOutFile, "%s", prompt);
|
||||
fflush(gOutFile);
|
||||
if (*prompt != '\0' && gOutFile->isOpen()) {
|
||||
fprintf(gOutFile->fp, "%s", prompt);
|
||||
fflush(gOutFile->fp);
|
||||
}
|
||||
|
||||
size_t size = 80;
|
||||
|
@ -618,7 +618,7 @@ RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
|||
|
||||
static bool
|
||||
EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
|
||||
int lineno, bool compileOnly, FILE* out)
|
||||
int lineno, bool compileOnly)
|
||||
{
|
||||
// Eval.
|
||||
JS::CompileOptions options(cx);
|
||||
|
@ -635,7 +635,7 @@ EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
|
|||
if (!JS_ExecuteScript(cx, script, &result))
|
||||
return false;
|
||||
|
||||
if (!result.isUndefined()) {
|
||||
if (!result.isUndefined() && gOutFile->isOpen()) {
|
||||
// Print.
|
||||
RootedString str(cx);
|
||||
str = JS_ValueToSource(cx, result);
|
||||
|
@ -645,14 +645,14 @@ EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
|
|||
char* utf8chars = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!utf8chars)
|
||||
return false;
|
||||
fprintf(out, "%s\n", utf8chars);
|
||||
fprintf(gOutFile->fp, "%s\n", utf8chars);
|
||||
JS_free(cx, utf8chars);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
||||
ReadEvalPrintLoop(JSContext* cx, FILE* in, bool compileOnly)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
int lineno = 1;
|
||||
|
@ -697,9 +697,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
|||
if (hitEOF && buffer.empty())
|
||||
break;
|
||||
|
||||
if (!EvalAndPrint(cx, buffer.begin(), buffer.length(), startline, compileOnly,
|
||||
out))
|
||||
{
|
||||
if (!EvalAndPrint(cx, buffer.begin(), buffer.length(), startline, compileOnly)) {
|
||||
// Catch the error, report it, and keep going.
|
||||
JS_ReportPendingException(cx);
|
||||
}
|
||||
|
@ -707,7 +705,7 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
|||
// without further intervention. This call cleans up the global scope,
|
||||
// setting uninitialized lexicals to undefined so that they may still
|
||||
// be used. This behavior is _only_ acceptable in the context of the repl.
|
||||
if (JS::ForceLexicalInitialization(cx, globalLexical)) {
|
||||
if (JS::ForceLexicalInitialization(cx, globalLexical) && gErrFile->isOpen()) {
|
||||
fputs("Warning: According to the standard, after the above exception,\n"
|
||||
"Warning: the global bindings should be permanently uninitialized.\n"
|
||||
"Warning: We have non-standard-ly initialized them to `undefined`"
|
||||
|
@ -716,7 +714,8 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly)
|
|||
}
|
||||
} while (!hitEOF && !sr->quitting);
|
||||
|
||||
fprintf(out, "\n");
|
||||
if (gOutFile->isOpen())
|
||||
fprintf(gOutFile->fp, "\n");
|
||||
}
|
||||
|
||||
enum FileKind
|
||||
|
@ -750,7 +749,7 @@ Process(JSContext* cx, const char* filename, bool forceTTY, FileKind kind = File
|
|||
} else {
|
||||
// It's an interactive filehandle; drop into read-eval-print loop.
|
||||
MOZ_ASSERT(kind == FileScript);
|
||||
ReadEvalPrintLoop(cx, file, gOutFile, compileOnly);
|
||||
ReadEvalPrintLoop(cx, file, compileOnly);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1648,15 +1647,20 @@ PutStr(JSContext* cx, unsigned argc, Value* vp)
|
|||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 0) {
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedString str(cx, JS::ToString(cx, args[0]));
|
||||
if (!str)
|
||||
return false;
|
||||
char* bytes = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!bytes)
|
||||
return false;
|
||||
fputs(bytes, gOutFile);
|
||||
fputs(bytes, gOutFile->fp);
|
||||
JS_free(cx, bytes);
|
||||
fflush(gOutFile);
|
||||
fflush(gOutFile->fp);
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
@ -1673,8 +1677,13 @@ Now(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
static bool
|
||||
PrintInternal(JSContext* cx, const CallArgs& args, FILE* file)
|
||||
PrintInternal(JSContext* cx, const CallArgs& args, RCFile* file)
|
||||
{
|
||||
if (!file->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < args.length(); i++) {
|
||||
RootedString str(cx, JS::ToString(cx, args[i]));
|
||||
if (!str)
|
||||
|
@ -1682,12 +1691,12 @@ PrintInternal(JSContext* cx, const CallArgs& args, FILE* file)
|
|||
char* bytes = JS_EncodeStringToUTF8(cx, str);
|
||||
if (!bytes)
|
||||
return false;
|
||||
fprintf(file, "%s%s", i ? " " : "", bytes);
|
||||
fprintf(file->fp, "%s%s", i ? " " : "", bytes);
|
||||
JS_free(cx, bytes);
|
||||
}
|
||||
|
||||
fputc('\n', file);
|
||||
fflush(file);
|
||||
fputc('\n', file->fp);
|
||||
fflush(file->fp);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
|
@ -1776,8 +1785,8 @@ StopTimingMutator(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
double total_ms = mutator_ms + gc_ms;
|
||||
if (total_ms > 0) {
|
||||
fprintf(gOutFile, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
|
||||
if (total_ms > 0 && gOutFile->isOpen()) {
|
||||
fprintf(gOutFile->fp, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
|
||||
mutator_ms, mutator_ms / total_ms * 100.0, gc_ms, gc_ms / total_ms * 100.0);
|
||||
}
|
||||
|
||||
|
@ -2320,13 +2329,19 @@ static bool
|
|||
Disassemble(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sprinter sprinter(cx);
|
||||
if (!sprinter.init())
|
||||
return false;
|
||||
if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
|
||||
return false;
|
||||
|
||||
fprintf(stdout, "%s\n", sprinter.string());
|
||||
fprintf(gOutFile->fp, "%s\n", sprinter.string());
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
@ -2336,6 +2351,11 @@ DisassFile(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Support extra options at the start, just like Disassemble. */
|
||||
DisassembleOptionParser p(args.length(), args.array());
|
||||
if (!p.parse(cx))
|
||||
|
@ -2372,7 +2392,7 @@ DisassFile(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
bool ok = DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter);
|
||||
if (ok)
|
||||
fprintf(stdout, "%s\n", sprinter.string());
|
||||
fprintf(gOutFile->fp, "%s\n", sprinter.string());
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
|
@ -2385,6 +2405,11 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LINE_BUF_LEN 512
|
||||
unsigned len, line1, line2, bupline;
|
||||
FILE* file;
|
||||
|
@ -2466,7 +2491,7 @@ DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
|
|||
pc += len;
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\n", sprinter.string());
|
||||
fprintf(gOutFile->fp, "%s\n", sprinter.string());
|
||||
|
||||
bail:
|
||||
fclose(file);
|
||||
|
@ -2482,6 +2507,7 @@ static bool
|
|||
Intern(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSString* str = JS::ToString(cx, args.get(0));
|
||||
if (!str)
|
||||
return false;
|
||||
|
@ -3324,15 +3350,20 @@ StackDump(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showArgs = ToBoolean(args.get(0));
|
||||
bool showLocals = ToBoolean(args.get(1));
|
||||
bool showThisProps = ToBoolean(args.get(2));
|
||||
|
||||
char* buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
|
||||
if (!buf) {
|
||||
fputs("Failed to format JavaScript stack for dump\n", gOutFile);
|
||||
fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
|
||||
} else {
|
||||
fputs(buf, gOutFile);
|
||||
fputs(buf, gOutFile->fp);
|
||||
JS_smprintf_free(buf);
|
||||
}
|
||||
|
||||
|
@ -5612,6 +5643,7 @@ static bool
|
|||
PrintHelpString(JSContext* cx, Value v)
|
||||
{
|
||||
JSString* str = v.toString();
|
||||
MOZ_ASSERT(gOutFile->isOpen());
|
||||
|
||||
JSLinearString* linear = str->ensureLinear(cx);
|
||||
if (!linear)
|
||||
|
@ -5620,12 +5652,12 @@ PrintHelpString(JSContext* cx, Value v)
|
|||
JS::AutoCheckCannotGC nogc;
|
||||
if (linear->hasLatin1Chars()) {
|
||||
for (const Latin1Char* p = linear->latin1Chars(nogc); *p; p++)
|
||||
fprintf(gOutFile, "%c", char(*p));
|
||||
fprintf(gOutFile->fp, "%c", char(*p));
|
||||
} else {
|
||||
for (const char16_t* p = linear->twoByteChars(nogc); *p; p++)
|
||||
fprintf(gOutFile, "%c", char(*p));
|
||||
fprintf(gOutFile->fp, "%c", char(*p));
|
||||
}
|
||||
fprintf(gOutFile, "\n");
|
||||
fprintf(gOutFile->fp, "\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5671,11 +5703,16 @@ PrintEnumeratedHelp(JSContext* cx, HandleObject obj, bool brief)
|
|||
static bool
|
||||
Help(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
if (!gOutFile->isOpen()) {
|
||||
JS_ReportError(cx, "output file is closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedObject obj(cx);
|
||||
if (args.length() == 0) {
|
||||
fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
|
||||
fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion());
|
||||
|
||||
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
if (!PrintEnumeratedHelp(cx, global, false))
|
||||
|
@ -5749,6 +5786,16 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report)
|
|||
return true;
|
||||
}
|
||||
|
||||
static FILE*
|
||||
ErrorFilePointer()
|
||||
{
|
||||
if (gErrFile->isOpen())
|
||||
return gErrFile->fp;
|
||||
|
||||
fprintf(stderr, "error file is closed; falling back to stderr\n");
|
||||
return stderr;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintStackTrace(JSContext* cx, HandleValue exn)
|
||||
{
|
||||
|
@ -5779,8 +5826,9 @@ PrintStackTrace(JSContext* cx, HandleValue exn)
|
|||
if (!stack)
|
||||
return false;
|
||||
|
||||
fputs("Stack:\n", gErrFile);
|
||||
fputs(stack.get(), gErrFile);
|
||||
FILE* fp = ErrorFilePointer();
|
||||
fputs("Stack:\n", fp);
|
||||
fputs(stack.get(), fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5789,12 +5837,13 @@ void
|
|||
js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
FILE* fp = ErrorFilePointer();
|
||||
|
||||
if (report && JSREPORT_IS_WARNING(report->flags) && sr->lastWarningEnabled) {
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
if (!CreateLastWarningObject(cx, report)) {
|
||||
fputs("Unhandled error happened while creating last warning object.\n", gOutFile);
|
||||
fflush(gOutFile);
|
||||
fputs("Unhandled error happened while creating last warning object.\n", fp);
|
||||
fflush(fp);
|
||||
}
|
||||
savedExc.restore();
|
||||
}
|
||||
|
@ -5804,11 +5853,11 @@ js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* r
|
|||
if (JS_IsExceptionPending(cx))
|
||||
(void) JS_GetPendingException(cx, &exn);
|
||||
|
||||
sr->gotError = PrintError(cx, gErrFile, message, report, reportWarnings);
|
||||
sr->gotError = PrintError(cx, fp, message, report, reportWarnings);
|
||||
if (!exn.isUndefined()) {
|
||||
JS::AutoSaveExceptionState savedExc(cx);
|
||||
if (!PrintStackTrace(cx, exn))
|
||||
fputs("(Unable to print stack trace)\n", gOutFile);
|
||||
fputs("(Unable to print stack trace)\n", fp);
|
||||
savedExc.restore();
|
||||
}
|
||||
|
||||
|
@ -6404,7 +6453,7 @@ NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!DefineOS(cx, glob, fuzzingSafe))
|
||||
if (!DefineOS(cx, glob, fuzzingSafe, &gOutFile, &gErrFile))
|
||||
return nullptr;
|
||||
|
||||
RootedObject performanceObj(cx, JS_NewObject(cx, nullptr));
|
||||
|
@ -6903,13 +6952,18 @@ Shell(JSContext* cx, OptionParser* op, char** envp)
|
|||
}
|
||||
|
||||
static void
|
||||
MaybeOverrideOutFileFromEnv(const char* const envVar,
|
||||
FILE* defaultOut,
|
||||
FILE** outFile)
|
||||
SetOutputFile(const char* const envVar,
|
||||
FILE* defaultOut,
|
||||
RCFile** outFile)
|
||||
{
|
||||
const char* outPath = getenv(envVar);
|
||||
if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
|
||||
*outFile = defaultOut;
|
||||
FILE* newfp;
|
||||
if (outPath && *outPath && (newfp = fopen(outPath, "w"))) {
|
||||
*outFile = js_new<RCFile>(newfp);
|
||||
(*outFile)->acquire();
|
||||
} else {
|
||||
*outFile = js_new<RCFile>(defaultOut);
|
||||
(*outFile)->acquire();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6951,8 +7005,8 @@ main(int argc, char** argv, char** envp)
|
|||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
|
||||
MaybeOverrideOutFileFromEnv("JS_STDERR", stderr, &gErrFile);
|
||||
MaybeOverrideOutFileFromEnv("JS_STDOUT", stdout, &gOutFile);
|
||||
SetOutputFile("JS_STDERR", stderr, &gErrFile);
|
||||
SetOutputFile("JS_STDOUT", stdout, &gOutFile);
|
||||
|
||||
OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
|
||||
|
||||
|
|
|
@ -50,6 +50,24 @@ class AutoCloseFile
|
|||
}
|
||||
};
|
||||
|
||||
// Reference counted file.
|
||||
struct RCFile {
|
||||
FILE* fp;
|
||||
uint32_t numRefs;
|
||||
|
||||
RCFile() : fp(nullptr), numRefs(0) {}
|
||||
explicit RCFile(FILE* fp) : fp(fp), numRefs(0) {}
|
||||
|
||||
void acquire() { numRefs++; }
|
||||
|
||||
// Starts out with a ref count of zero.
|
||||
static RCFile* create(JSContext* cx, const char* filename, const char* mode);
|
||||
|
||||
void close();
|
||||
bool isOpen() const { return fp; }
|
||||
bool release();
|
||||
};
|
||||
|
||||
} /* namespace shell */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче