| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| // OSObject.h - os object for exposing posix system calls in the JS shell |
| |
| #include "shell/OSObject.h" |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #if defined(STARBOARD) |
| // Intentionally empty. |
| #elif defined(XP_WIN) |
| #include <direct.h> |
| #include <process.h> |
| #include <string.h> |
| #else |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #endif |
| |
| #include "jsapi.h" |
| // For JSFunctionSpecWithHelp |
| #include "jsfriendapi.h" |
| #include "jsobj.h" |
| #ifdef XP_WIN |
| # include "jswin.h" |
| #endif |
| #include "jswrapper.h" |
| |
| #include "js/Conversions.h" |
| #include "shell/jsshell.h" |
| #include "vm/StringBuffer.h" |
| #include "vm/TypedArrayObject.h" |
| |
| #include "jsobjinlines.h" |
| |
| #if defined(STARBOARD) |
| #elif defined(XP_WIN) |
| # define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR) |
| # define getcwd _getcwd |
| #else |
| # include <libgen.h> |
| #endif |
| |
| using namespace JS; |
| |
| namespace js { |
| namespace shell { |
| |
| #ifdef XP_WIN |
| const char PathSeparator = '\\'; |
| #else |
| const char PathSeparator = '/'; |
| #endif |
| |
| static bool |
| IsAbsolutePath(const JSAutoByteString& filename) |
| { |
| const char* pathname = filename.ptr(); |
| |
| if (pathname[0] == PathSeparator) |
| return true; |
| |
| #ifdef XP_WIN |
| // On Windows there are various forms of absolute paths (see |
| // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx |
| // for details): |
| // |
| // "\..." |
| // "\\..." |
| // "C:\..." |
| // |
| // The first two cases are handled by the test above so we only need a test |
| // for the last one here. |
| |
| if ((strlen(pathname) > 3 && |
| isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\')) |
| { |
| return true; |
| } |
| #endif |
| |
| return false; |
| } |
| |
| /* |
| * Resolve a (possibly) relative filename to an absolute path. If |
| * |scriptRelative| is true, then the result will be relative to the directory |
| * containing the currently-running script, or the current working directory if |
| * the currently-running script is "-e" (namely, you're using it from the |
| * command line.) Otherwise, it will be relative to the current working |
| * directory. |
| */ |
| JSString* |
| ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveMode) |
| { |
| #if defined(STARBOARD) |
| return nullptr; |
| #else // defined(STARBOARD) |
| JSAutoByteString filename(cx, filenameStr); |
| if (!filename) |
| return nullptr; |
| |
| if (IsAbsolutePath(filename)) |
| return filenameStr; |
| |
| /* Get the currently executing script's name. */ |
| JS::AutoFilename scriptFilename; |
| if (!DescribeScriptedCaller(cx, &scriptFilename)) |
| return nullptr; |
| |
| if (!scriptFilename.get()) |
| return nullptr; |
| |
| if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0) |
| resolveMode = RootRelative; |
| |
| static char buffer[PATH_MAX+1]; |
| if (resolveMode == ScriptRelative) { |
| #ifdef XP_WIN |
| // The docs say it can return EINVAL, but the compiler says it's void |
| _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr); |
| #else |
| strncpy(buffer, scriptFilename.get(), PATH_MAX+1); |
| if (buffer[PATH_MAX] != '\0') |
| return nullptr; |
| |
| // dirname(buffer) might return buffer, or it might return a |
| // statically-allocated string |
| memmove(buffer, dirname(buffer), strlen(buffer) + 1); |
| #endif |
| } else { |
| const char* cwd = getcwd(buffer, PATH_MAX); |
| if (!cwd) |
| return nullptr; |
| } |
| |
| size_t len = strlen(buffer); |
| buffer[len] = '/'; |
| strncpy(buffer + len + 1, filename.ptr(), sizeof(buffer) - (len+1)); |
| if (buffer[PATH_MAX] != '\0') |
| return nullptr; |
| |
| return JS_NewStringCopyZ(cx, buffer); |
| #endif // defined(STARBOARD) |
| } |
| |
| static JSObject* |
| FileAsTypedArray(JSContext* cx, const char* pathname) |
| { |
| FILE* file = fopen(pathname, "rb"); |
| if (!file) { |
| JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); |
| return nullptr; |
| } |
| AutoCloseFile autoClose(file); |
| |
| RootedObject obj(cx); |
| if (fseek(file, 0, SEEK_END) != 0) { |
| JS_ReportError(cx, "can't seek end of %s", pathname); |
| } else { |
| size_t len = ftell(file); |
| if (fseek(file, 0, SEEK_SET) != 0) { |
| JS_ReportError(cx, "can't seek start of %s", pathname); |
| } else { |
| obj = JS_NewUint8Array(cx, len); |
| if (!obj) |
| return nullptr; |
| js::TypedArrayObject& ta = obj->as<js::TypedArrayObject>(); |
| if (ta.isSharedMemory()) { |
| // Must opt in to use shared memory. For now, don't. |
| // |
| // (It is incorrect to read into the buffer without |
| // synchronization since that can create a race. A |
| // lock here won't fix it - both sides must |
| // participate. So what one must do is to create a |
| // temporary buffer, read into that, and use a |
| // race-safe primitive to copy memory into the |
| // buffer.) |
| JS_ReportError(cx, "can't read %s: shared memory buffer", pathname); |
| return nullptr; |
| } |
| char* buf = static_cast<char*>(ta.viewDataUnshared()); |
| size_t cc = fread(buf, 1, len, file); |
| if (cc != len) { |
| JS_ReportError(cx, "can't read %s: %s", pathname, |
| (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read"); |
| obj = nullptr; |
| } |
| } |
| } |
| return obj; |
| } |
| |
| static bool |
| ReadFile(JSContext* cx, unsigned argc, Value* vp, bool scriptRelative) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() < 1 || args.length() > 2) { |
| JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, |
| args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, |
| "snarf"); |
| return false; |
| } |
| |
| if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) { |
| JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf"); |
| return false; |
| } |
| |
| RootedString givenPath(cx, args[0].toString()); |
| RootedString str(cx, js::shell::ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative)); |
| if (!str) |
| return false; |
| |
| JSAutoByteString filename(cx, str); |
| if (!filename) |
| return false; |
| |
| if (args.length() > 1) { |
| JSString* opt = JS::ToString(cx, args[1]); |
| if (!opt) |
| return false; |
| bool match; |
| if (!JS_StringEqualsAscii(cx, opt, "binary", &match)) |
| return false; |
| if (match) { |
| JSObject* obj; |
| if (!(obj = FileAsTypedArray(cx, filename.ptr()))) |
| return false; |
| args.rval().setObject(*obj); |
| return true; |
| } |
| } |
| |
| if (!(str = FileAsString(cx, filename.ptr()))) |
| return false; |
| args.rval().setString(str); |
| return true; |
| } |
| |
| static bool |
| osfile_readFile(JSContext* cx, unsigned argc, Value* vp) |
| { |
| return ReadFile(cx, argc, vp, false); |
| } |
| |
| static bool |
| osfile_readRelativeToScript(JSContext* cx, unsigned argc, Value* vp) |
| { |
| return ReadFile(cx, argc, vp, true); |
| } |
| |
| static bool |
| osfile_writeTypedArrayToFile(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() != 2 || |
| !args[0].isString() || |
| !args[1].isObject() || |
| !args[1].toObject().is<TypedArrayObject>()) |
| { |
| JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, |
| JSSMSG_INVALID_ARGS, "writeTypedArrayToFile"); |
| return false; |
| } |
| |
| RootedString givenPath(cx, args[0].toString()); |
| RootedString str(cx, ResolvePath(cx, givenPath, RootRelative)); |
| if (!str) |
| return false; |
| |
| JSAutoByteString filename(cx, str); |
| if (!filename) |
| return false; |
| |
| FILE* file = fopen(filename.ptr(), "wb"); |
| if (!file) { |
| JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno)); |
| return false; |
| } |
| AutoCloseFile autoClose(file); |
| |
| TypedArrayObject* obj = &args[1].toObject().as<TypedArrayObject>(); |
| |
| if (obj->isSharedMemory()) { |
| // Must opt in to use shared memory. For now, don't. |
| // |
| // See further comments in FileAsTypedArray, above. |
| JS_ReportError(cx, "can't write %s: shared memory buffer", filename.ptr()); |
| return false; |
| } |
| void* buf = obj->viewDataUnshared(); |
| if (fwrite(buf, obj->bytesPerElement(), obj->length(), file) != obj->length() || |
| !autoClose.release()) |
| { |
| JS_ReportError(cx, "can't write %s", filename.ptr()); |
| return false; |
| } |
| |
| args.rval().setUndefined(); |
| return true; |
| } |
| |
| static bool |
| Redirect(JSContext* cx, FILE* fp, HandleString relFilename) |
| { |
| RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative)); |
| if (!filename) |
| return false; |
| JSAutoByteString filenameABS(cx, filename); |
| if (!filenameABS) |
| return false; |
| if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) { |
| JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno)); |
| return false; |
| } |
| return true; |
| } |
| |
| static bool |
| osfile_redirect(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"); |
| return false; |
| } |
| |
| if (args[0].isString()) { |
| RootedString stdoutPath(cx, args[0].toString()); |
| if (!stdoutPath) |
| return false; |
| if (!Redirect(cx, stdout, stdoutPath)) |
| return false; |
| } |
| |
| if (args.length() > 1 && args[1].isString()) { |
| RootedString stderrPath(cx, args[1].toString()); |
| if (!stderrPath) |
| return false; |
| if (!Redirect(cx, stderr, stderrPath)) |
| return false; |
| } |
| |
| args.rval().setUndefined(); |
| return true; |
| } |
| |
| static const JSFunctionSpecWithHelp osfile_functions[] = { |
| JS_FN_HELP("readFile", osfile_readFile, 1, 0, |
| "readFile(filename, [\"binary\"])", |
| " Read filename into returned string. Filename is relative to the current\n" |
| " working directory."), |
| |
| JS_FN_HELP("readRelativeToScript", osfile_readRelativeToScript, 1, 0, |
| "readRelativeToScript(filename, [\"binary\"])", |
| " Read filename into returned string. Filename is relative to the directory\n" |
| " containing the current script."), |
| |
| JS_FS_HELP_END |
| }; |
| |
| static const JSFunctionSpecWithHelp osfile_unsafe_functions[] = { |
| JS_FN_HELP("writeTypedArrayToFile", osfile_writeTypedArrayToFile, 2, 0, |
| "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. Filenames are relative to the current working directory."), |
| |
| JS_FS_HELP_END |
| }; |
| |
| static bool |
| ospath_isAbsolute(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() != 1 || !args[0].isString()) { |
| JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "isAbsolute"); |
| return false; |
| } |
| |
| JSAutoByteString path(cx, args[0].toString()); |
| if (!path) |
| return false; |
| |
| args.rval().setBoolean(IsAbsolutePath(path)); |
| return true; |
| } |
| |
| static bool |
| ospath_join(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() < 1) { |
| JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "join"); |
| return false; |
| } |
| |
| // This function doesn't take into account some aspects of Windows paths, |
| // e.g. the drive letter is always reset when an absolute path is appended. |
| |
| StringBuffer buffer(cx); |
| |
| for (unsigned i = 0; i < args.length(); i++) { |
| if (!args[i].isString()) { |
| JS_ReportError(cx, "join expects string arguments only"); |
| return false; |
| } |
| |
| JSAutoByteString path(cx, args[i].toString()); |
| if (!path) |
| return false; |
| |
| if (IsAbsolutePath(path)) { |
| MOZ_ALWAYS_TRUE(buffer.resize(0)); |
| } else if (i != 0) { |
| if (!buffer.append(PathSeparator)) |
| return false; |
| } |
| |
| if (!buffer.append(args[i].toString())) |
| return false; |
| } |
| |
| JSString* result = buffer.finishString(); |
| if (!result) |
| return false; |
| |
| args.rval().setString(result); |
| return true; |
| } |
| |
| static const JSFunctionSpecWithHelp ospath_functions[] = { |
| JS_FN_HELP("isAbsolute", ospath_isAbsolute, 1, 0, |
| "isAbsolute(path)", |
| " Return whether the given path is absolute."), |
| |
| JS_FN_HELP("join", ospath_join, 1, 0, |
| "join(paths...)", |
| " Join one or more path components in a platform independent way."), |
| |
| JS_FS_HELP_END |
| }; |
| |
| static bool |
| os_getenv(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| if (args.length() < 1) { |
| JS_ReportError(cx, "os.getenv requires 1 argument"); |
| return false; |
| } |
| RootedString key(cx, ToString(cx, args[0])); |
| if (!key) |
| return false; |
| JSAutoByteString keyBytes; |
| if (!keyBytes.encodeUtf8(cx, key)) |
| return false; |
| |
| if (const char* valueBytes = js_sb_getenv(keyBytes.ptr())) { |
| RootedString value(cx, JS_NewStringCopyZ(cx, valueBytes)); |
| if (!value) |
| return false; |
| args.rval().setString(value); |
| } else { |
| args.rval().setUndefined(); |
| } |
| return true; |
| } |
| |
| static bool |
| os_getpid(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| if (args.length() != 0) { |
| JS_ReportError(cx, "os.getpid takes no arguments"); |
| return false; |
| } |
| #if defined(STARBOARD) |
| args.rval().setInt32(0); |
| #else |
| args.rval().setInt32(getpid()); |
| #endif |
| return true; |
| } |
| |
| #if !defined(XP_WIN) |
| |
| // There are two possible definitions of strerror_r floating around. The GNU |
| // one returns a char* which may or may not be the buffer you passed in. The |
| // other one returns an integer status code, and always writes the result into |
| // the provided buffer. |
| |
| inline char* |
| strerror_message(int result, char* buffer) |
| { |
| return result == 0 ? buffer : nullptr; |
| } |
| |
| inline char* |
| strerror_message(char* result, char* buffer) |
| { |
| return result; |
| } |
| |
| #endif |
| |
| static void |
| ReportSysError(JSContext* cx, const char* prefix) |
| { |
| char buffer[200]; |
| |
| #if defined(STARBOARD) |
| SbSystemGetErrorString(errno, buffer, sizeof(buffer)); |
| const char* errstr = buffer; |
| #elif defined(XP_WIN) |
| strerror_s(buffer, sizeof(buffer), errno); |
| const char* errstr = buffer; |
| #else |
| const char* errstr = strerror_message(strerror_r(errno, buffer, sizeof(buffer)), buffer); |
| #endif |
| |
| if (!errstr) |
| errstr = "unknown error"; |
| |
| size_t nbytes = strlen(prefix) + strlen(errstr) + 3; |
| char* final = (char*) js_malloc(nbytes); |
| if (!final) { |
| JS_ReportOutOfMemory(cx); |
| return; |
| } |
| |
| #ifdef XP_WIN |
| _snprintf(final, nbytes, "%s: %s", prefix, errstr); |
| #else |
| snprintf(final, nbytes, "%s: %s", prefix, errstr); |
| #endif |
| |
| JS_ReportError(cx, final); |
| js_free(final); |
| } |
| |
| static bool |
| os_system(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() == 0) { |
| JS_ReportError(cx, "os.system requires 1 argument"); |
| return false; |
| } |
| |
| JSString* str = JS::ToString(cx, args[0]); |
| if (!str) |
| return false; |
| |
| JSAutoByteString command(cx, str); |
| if (!command) |
| return false; |
| |
| int result = system(command.ptr()); |
| if (result == -1) { |
| ReportSysError(cx, "system call failed"); |
| return false; |
| } |
| |
| args.rval().setInt32(result); |
| return true; |
| } |
| |
| #if !defined(XP_WIN) && !defined(STARBOARD) |
| static bool |
| os_spawn(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| if (args.length() == 0) { |
| JS_ReportError(cx, "os.spawn requires 1 argument"); |
| return false; |
| } |
| |
| JSString* str = JS::ToString(cx, args[0]); |
| if (!str) |
| return false; |
| |
| JSAutoByteString command(cx, str); |
| if (!command) |
| return false; |
| |
| int32_t childPid = fork(); |
| if (childPid == -1) { |
| ReportSysError(cx, "fork failed"); |
| return false; |
| } |
| |
| if (childPid) { |
| args.rval().setInt32(childPid); |
| return true; |
| } |
| |
| // We are in the child |
| |
| const char* cmd[] = {"sh", "-c", nullptr, nullptr}; |
| cmd[2] = command.ptr(); |
| |
| execvp("sh", (char * const*)cmd); |
| exit(1); |
| } |
| |
| static bool |
| os_kill(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| int32_t pid; |
| if (args.length() < 1) { |
| JS_ReportError(cx, "os.kill requires 1 argument"); |
| return false; |
| } |
| if (!JS::ToInt32(cx, args[0], &pid)) |
| return false; |
| |
| // It is too easy to kill yourself accidentally with os.kill("goose"). |
| if (pid == 0 && !args[0].isInt32()) { |
| JS_ReportError(cx, "os.kill requires numeric pid"); |
| return false; |
| } |
| |
| int signal = SIGINT; |
| if (args.length() > 1) { |
| if (!JS::ToInt32(cx, args[1], &signal)) |
| return false; |
| } |
| |
| int status = kill(pid, signal); |
| if (status == -1) { |
| ReportSysError(cx, "kill failed"); |
| return false; |
| } |
| |
| args.rval().setUndefined(); |
| return true; |
| } |
| |
| static bool |
| os_waitpid(JSContext* cx, unsigned argc, Value* vp) |
| { |
| CallArgs args = CallArgsFromVp(argc, vp); |
| |
| int32_t pid; |
| if (args.length() == 0) { |
| pid = -1; |
| } else { |
| if (!JS::ToInt32(cx, args[0], &pid)) |
| return false; |
| } |
| |
| bool nohang = false; |
| if (args.length() >= 2) |
| nohang = JS::ToBoolean(args[1]); |
| |
| int status = 0; |
| pid_t result = waitpid(pid, &status, nohang ? WNOHANG : 0); |
| if (result == -1) { |
| ReportSysError(cx, "os.waitpid failed"); |
| return false; |
| } |
| |
| RootedObject info(cx, JS_NewPlainObject(cx)); |
| if (!info) |
| return false; |
| |
| RootedValue v(cx); |
| if (result != 0) { |
| v.setInt32(result); |
| if (!JS_DefineProperty(cx, info, "pid", v, JSPROP_ENUMERATE)) |
| return false; |
| if (WIFEXITED(status)) { |
| v.setInt32(WEXITSTATUS(status)); |
| if (!JS_DefineProperty(cx, info, "exitStatus", v, JSPROP_ENUMERATE)) |
| return false; |
| } |
| } |
| |
| args.rval().setObject(*info); |
| return true; |
| } |
| #endif |
| |
| static const JSFunctionSpecWithHelp os_functions[] = { |
| JS_FN_HELP("getenv", os_getenv, 1, 0, |
| "getenv(variable)", |
| " Get the value of an environment variable."), |
| |
| JS_FN_HELP("getpid", os_getpid, 0, 0, |
| "getpid()", |
| " Return the current process id."), |
| |
| JS_FN_HELP("system", os_system, 1, 0, |
| "system(command)", |
| " Execute command on the current host, returning result code or throwing an\n" |
| " exception on failure."), |
| |
| #if !defined(XP_WIN) && !defined(STARBOARD) |
| JS_FN_HELP("spawn", os_spawn, 1, 0, |
| "spawn(command)", |
| " Start up a separate process running the given command. Returns the pid."), |
| |
| JS_FN_HELP("kill", os_kill, 1, 0, |
| "kill(pid[, signal])", |
| " Send a signal to the given pid. The default signal is SIGINT. The signal\n" |
| " passed in must be numeric, if given."), |
| |
| JS_FN_HELP("waitpid", os_waitpid, 1, 0, |
| "waitpid(pid[, nohang])", |
| " Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n" |
| " The return value is an object containing a 'pid' field, if a process was waitable\n" |
| " and an 'exitStatus' field if a pid exited."), |
| #endif |
| |
| JS_FS_HELP_END |
| }; |
| |
| bool |
| DefineOS(JSContext* cx, HandleObject global, bool fuzzingSafe) |
| { |
| RootedObject obj(cx, JS_NewPlainObject(cx)); |
| if (!obj || !JS_DefineProperty(cx, global, "os", obj, 0)) |
| return false; |
| |
| if (!fuzzingSafe) { |
| if (!JS_DefineFunctionsWithHelp(cx, obj, os_functions)) |
| return false; |
| } |
| |
| RootedObject osfile(cx, JS_NewPlainObject(cx)); |
| if (!osfile || |
| !JS_DefineFunctionsWithHelp(cx, osfile, osfile_functions) || |
| !JS_DefineProperty(cx, obj, "file", osfile, 0)) |
| { |
| return false; |
| } |
| |
| if (!fuzzingSafe) { |
| if (!JS_DefineFunctionsWithHelp(cx, osfile, osfile_unsafe_functions)) |
| return false; |
| } |
| |
| RootedObject ospath(cx, JS_NewPlainObject(cx)); |
| if (!ospath || |
| !JS_DefineFunctionsWithHelp(cx, ospath, ospath_functions) || |
| !JS_DefineProperty(cx, obj, "path", ospath, 0)) |
| { |
| return false; |
| } |
| |
| // For backwards compatibility, expose various os.file.* functions as |
| // direct methods on the global. |
| RootedValue val(cx); |
| |
| struct { |
| const char* src; |
| const char* dst; |
| } osfile_exports[] = { |
| { "readFile", "read" }, |
| { "readFile", "snarf" }, |
| { "readRelativeToScript", "readRelativeToScript" }, |
| { "redirect", "redirect" } |
| }; |
| |
| for (auto pair : osfile_exports) { |
| if (!JS_GetProperty(cx, osfile, pair.src, &val)) |
| return false; |
| if (val.isObject()) { |
| RootedObject function(cx, &val.toObject()); |
| if (!JS_DefineProperty(cx, global, pair.dst, function, 0)) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace shell |
| } // namespace js |