RPM Package Manager, CVS Repository http://rpm5.org/cvs/ ____________________________________________________________________________
Server: rpm5.org Name: Jeff Johnson Root: /v/rpm/cvs Email: j...@rpm5.org Module: rpm Date: 02-Jul-2017 14:34:30 Branch: rpm-5_4 Handle: 2017070212342900 Modified files: (Branch: rpm-5_4) rpm/rpmio Makefile.am rpmjs45.cpp rpmjss.inp Log: - r[pmjs45: WIP. Summary: Revision Changes Path 1.293.2.103 +1 -1 rpm/rpmio/Makefile.am 1.1.2.14 +4263 -183 rpm/rpmio/rpmjs45.cpp 1.1.2.5 +64 -2 rpm/rpmio/rpmjss.inp ____________________________________________________________________________ patch -p0 <<'@@ .' Index: rpm/rpmio/Makefile.am ============================================================================ $ cvs diff -u -r1.293.2.102 -r1.293.2.103 Makefile.am --- rpm/rpmio/Makefile.am 28 Jun 2017 09:54:15 -0000 1.293.2.102 +++ rpm/rpmio/Makefile.am 2 Jul 2017 12:34:29 -0000 1.293.2.103 @@ -575,7 +575,7 @@ rpmjs38_LDADD = -L/usr/lib64 -lmozjs-38 $(RPMIO_LDADD_COMMON) rpmjs45_SOURCES = rpmjs45.cpp -rpmjs45_CPPFLAGS = -include /usr/include/mozjs-45/js/RequiredDefines.h -I/usr/include/mozjs-45 -I/F/mozjs45/firefox-45.9.0esr/js/src -fPIC -DRPMJSS_SELF_TEST +rpmjs45_CPPFLAGS = -include /usr/include/mozjs-45/js/RequiredDefines.h -I/usr/include/mozjs-45 -I./js45/src/shell -I./js45/src -DXP_UNIX -fPIC -DRPMJSS_SELF_TEST rpmjs45_LDADD = -L/usr/lib64 -lmozjs-45 $(RPMIO_LDADD_COMMON) mozjs: rpmjs185 rpmjs17 rpmjs24 rpmjs31 rpmjs38 rpmjs45 @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs45.cpp ============================================================================ $ cvs diff -u -r1.1.2.13 -r1.1.2.14 rpmjs45.cpp --- rpm/rpmio/rpmjs45.cpp 1 Jul 2017 04:00:07 -0000 1.1.2.13 +++ rpm/rpmio/rpmjs45.cpp 2 Jul 2017 12:34:29 -0000 1.1.2.14 @@ -46,84 +46,6 @@ #include "debug.h" -/*==============================================================*/ -typedef struct JSI_s * JSI_t; -struct JSI_s { - JSRuntime *rt; - JSContext *cx; - JSObject *global; -}; - -// Shell state set once at startup. -static bool enableCodeCoverage = false; -static bool enableDisassemblyDumps = false; -static bool offthreadCompilation = false; -static bool enableBaseline = false; -static bool enableIon = false; -static bool enableAsmJS = false; -static bool enableNativeRegExp = false; -static bool enableUnboxedArrays = false; -#ifdef JS_GC_ZEAL -static char gZealStr[128]; -#endif -static bool printTiming = false; -static const char* jsCacheDir = nullptr; -static const char* jsCacheAsmJSPath = nullptr; -static bool reportWarnings = true; -static bool compileOnly = false; -static bool fuzzingSafe = false; -static bool disableOOMFunctions = false; -static const char* moduleLoadPath = "."; - -#ifdef DEBUG -static bool dumpEntrainedVariables = false; -static bool OOM_printAllocationCount = false; -#endif - -/*==============================================================*/ -static bool -global_enumerate(JSContext* cx, HandleObject obj) -{ -#ifdef LAZY_STANDARD_CLASSES - return JS_EnumerateStandardClasses(cx, obj); -#else - return true; -#endif -} - -static bool -global_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) -{ -#ifdef LAZY_STANDARD_CLASSES - if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) - return false; -#endif - return true; -} - -static bool -global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj) -{ - return JS_MayResolveStandardClass(names, id, maybeObj); -} - -static JSClass global_class = { - "global", - JSCLASS_GLOBAL_FLAGS, - nullptr, - nullptr, - nullptr, - nullptr, - global_enumerate, - global_resolve, - global_mayResolve, - nullptr, - nullptr, - nullptr, - nullptr, - JS_GlobalObjectTraceHook -}; - static int rpmjss_nopens; static int _rpmjss45_debug; @@ -131,51 +53,13 @@ if (_rpmjss45_debug) \ fprintf(stderr, _fmt, __VA_ARGS__) -/*==============================================================*/ -static void -rpmjssReportError(JSContext *cx, const char *message, JSErrorReport *report) -{ - fprintf(stderr, "%s:%u:%s\n", - report->filename ? report->filename : "<no filename=\"filename\">", - (unsigned int) report->lineno, message); -} - -static void -rpmjssOOMCallback(JSContext* cx, void* data) -{ -#ifdef NOTYET - // If a script is running, the engine is about to throw the string "out of - // memory", which may or may not be caught. Otherwise the engine will just - // unwind and return null/false, with no exception set. - if (!JS_IsRunning(cx)) - GetShellRuntime(cx)->gotError = true; -#endif -} - -enum JSShellErrNum { -#define MSG_DEF(name, count, exception, format) \ - name, -#include "jsshell.msg" -#undef MSG_DEF - JSShellErr_Limit -}; - -static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = { -#define MSG_DEF(name, count, exception, format) \ - { format, count, JSEXN_ERR } , -#include "jsshell.msg" -#undef MSG_DEF +typedef struct JSI_s * JSI_t; +struct JSI_s { + JSRuntime *rt; + JSContext *cx; + JSObject *global; }; -const JSErrorFormatString* -my_GetErrorMessage(void* userRef, const unsigned errorNumber) -{ - if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) - return nullptr; - - return &jsShell_ErrorFormatString[errorNumber]; -} - /*==============================================================*/ enum PathResolutionMode { /* XXX shell/OSObject.h */ RootRelative, @@ -353,7 +237,7 @@ obj = JS_NewUint8Array(cx, len); if (!obj) { #if !defined(FIXME) - if (file) (void) fclose(file); + if (file && fileno(file) > 2) (void) fclose(file); #endif return nullptr; } @@ -1036,13 +920,76 @@ #endif /*==============================================================*/ +static void +rpmjssReportError(JSContext *cx, const char *message, JSErrorReport *report) +{ + fprintf(stderr, "%s:%u:%s\n", + report->filename ? report->filename : "<no filename=\"filename\">", + (unsigned int) report->lineno, message); +} + +static void +rpmjssOOMCallback(JSContext* cx, void* data) +{ +#ifdef NOTYET + // If a script is running, the engine is about to throw the string "out of + // memory", which may or may not be caught. Otherwise the engine will just + // unwind and return null/false, with no exception set. + if (!JS_IsRunning(cx)) + GetShellRuntime(cx)->gotError = true; +#endif +} + +enum JSShellErrNum { +#define MSG_DEF(name, count, exception, format) \ + name, +#include "jsshell.msg" +#undef MSG_DEF + JSShellErr_Limit +}; + +static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = { +#define MSG_DEF(name, count, exception, format) \ + { format, count, JSEXN_ERR } , +#include "jsshell.msg" +#undef MSG_DEF +}; + +const JSErrorFormatString* +my_GetErrorMessage(void* userRef, const unsigned errorNumber) +{ + if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) + return nullptr; + + return &jsShell_ErrorFormatString[errorNumber]; +} + +/*==============================================================*/ enum JSShellExitCode { - EXITCODE_RUNTIME_ERROR = 3, - EXITCODE_FILE_NOT_FOUND = 4, - EXITCODE_OUT_OF_MEMORY = 5, - EXITCODE_TIMEOUT = 6 + EXITCODE_RUNTIME_ERROR = 3, + EXITCODE_FILE_NOT_FOUND = 4, + EXITCODE_OUT_OF_MEMORY = 5, + EXITCODE_TIMEOUT = 6 }; +static const size_t gStackChunkSize = 8192; + +/* + * Note: This limit should match the stack limit set by the browser in + * js/xpconnect/src/XPCJSRuntime.cpp + */ +#if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN)) +static const size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024; +#else +static const size_t gMaxStackSize = 128 * sizeof(size_t) * 1024; +#endif + +/* + * Limit the timeout to 30 minutes to prevent an overflow on platfoms + * that represent the time internally in microseconds using 32-bit int. + */ +static const double MAX_TIMEOUT_INTERVAL = 1800.0; + // Per-runtime shell state. #ifdef NOTYET struct ShellRuntime @@ -1073,6 +1020,127 @@ }; #endif +// Shell state set once at startup. +static bool enableCodeCoverage = false; +static bool enableDisassemblyDumps = false; +static bool offthreadCompilation = false; +static bool enableBaseline = false; +static bool enableIon = false; +static bool enableAsmJS = false; +static bool enableNativeRegExp = false; +static bool enableUnboxedArrays = false; +#ifdef JS_GC_ZEAL +static char gZealStr[128]; +#endif +static bool printTiming = false; +static const char* jsCacheDir = nullptr; +static const char* jsCacheAsmJSPath = nullptr; +static FILE* gErrFile = nullptr; +static FILE* gOutFile = nullptr; +static bool reportWarnings = true; +static bool compileOnly = false; +static bool fuzzingSafe = false; +static bool disableOOMFunctions = false; +static const char* moduleLoadPath = "."; + +#ifdef DEBUG +static bool dumpEntrainedVariables = false; +static bool OOM_printAllocationCount = false; +#endif + +// Shell state this is only accessed on the main thread. +bool jsCachingEnabled = false; +mozilla::Atomic<bool> jsCacheOpened(false); + +static bool +SetTimeoutValue(JSContext* cx, double t); + +static bool +InitWatchdog(JSRuntime* rt); + +static void +KillWatchdog(JSRuntime *rt); + +static bool +ScheduleWatchdog(JSRuntime* rt, double t); + +static void +CancelExecution(JSRuntime* rt); + +static JSContext* +NewContext(JSRuntime* rt); + +static void +DestroyContext(JSContext* cx, bool withGC); + +static JSObject* +NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options, + JSPrincipals* principals); + +/* + * A toy principals type for the shell. + * + * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the + * set bits in P are a superset of those in Q. Thus, the principal 0 is + * subsumed by everything, and the principal ~0 subsumes everything. + * + * As a special case, a null pointer as a principal is treated like 0xffff. + * + * The 'newGlobal' function takes an option indicating which principal the + * new global should have; 'evaluate' does for the new code. + */ +class ShellPrincipals final : public JSPrincipals { + uint32_t bits; + + static uint32_t getBits(JSPrincipals* p) { + if (!p) + return 0xffff; + return static_cast<ShellPrincipals*>(p)->bits; + } + + public: + explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) { + this->refcount = refcount; + } + + bool write(JSContext* cx, JSStructuredCloneWriter* writer) override { + MOZ_ASSERT(false, "not implemented"); + return false; + } + + static void destroy(JSPrincipals* principals) { + MOZ_ASSERT(principals != &fullyTrusted); + MOZ_ASSERT(principals->refcount == 0); + js_delete(static_cast<const ShellPrincipals*>(principals)); + } + + static bool subsumes(JSPrincipals* first, JSPrincipals* second) { + uint32_t firstBits = getBits(first); + uint32_t secondBits = getBits(second); + return (firstBits | secondBits) == firstBits; + } + + static JSSecurityCallbacks securityCallbacks; + + // Fully-trusted principals singleton. + static ShellPrincipals fullyTrusted; +}; + +JSSecurityCallbacks ShellPrincipals::securityCallbacks = { + nullptr, // contentSecurityPolicyAllows + subsumes +}; + +// The fully-trusted principal subsumes all other principals. +ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1); + +#ifdef EDITLINE +extern "C" { +extern JS_EXPORT_API(char*) readline(const char* prompt); +extern JS_EXPORT_API(void) add_history(char* line); +} // extern "C" +#endif + #ifdef NOTYET ShellRuntime::ShellRuntime() : isWorker(false), @@ -1089,7 +1157,9 @@ quitting(false), gotError(false) {} +#endif +#ifdef NOTYET static ShellRuntime* GetShellRuntime(JSRuntime *rt) { @@ -1099,12 +1169,485 @@ } #endif -static bool -Version(JSContext* cx, unsigned argc, Value* vp) +#ifdef NOTYET +static ShellRuntime* +GetShellRuntime(JSContext* cx) { - CallArgs args = CallArgsFromVp(argc, vp); - JSVersion origVersion = JS_GetVersion(cx); - if (args.length() == 0 || args[0].isUndefined()) { + return GetShellRuntime(cx->runtime()); +} +#endif + +static char* +GetLine(FILE* file, const char * prompt) +{ +#ifdef EDITLINE + /* + * Use readline only if file is stdin, because there's no way to specify + * another handle. Are other filehandles interactive? + */ + if (file == stdin) { + char* linep = readline(prompt); + /* + * We set it to zero to avoid complaining about inappropriate ioctl + * for device in the case of EOF. Looks like errno == 251 if line is + * finished with EOF and errno == 25 (EINVAL on Mac) if there is + * nothing left to read. + */ + if (errno == 251 || errno == 25 || errno == EINVAL) + errno = 0; + if (!linep) + return nullptr; + if (linep[0] != '\0') + add_history(linep); + return linep; + } +#endif + + size_t len = 0; + if (*prompt != '\0') { + fprintf(gOutFile, "%s", prompt); + fflush(gOutFile); + } + + size_t size = 80; + char* buffer = static_cast<char*>(malloc(size)); + if (!buffer) + return nullptr; + + char* current = buffer; + while (true) { + while (true) { + if (fgets(current, size - len, file)) + break; + if (errno != EINTR) { + free(buffer); + return nullptr; + } + } + + len += strlen(current); + char* t = buffer + len - 1; + if (*t == '\n') { + /* Line was read. We remove '\n' and exit. */ + *t = '\0'; + return buffer; + } + + if (len + 1 == size) { + size = size * 2; + char* tmp = static_cast<char*>(realloc(buffer, size)); + if (!tmp) { + free(buffer); + return nullptr; + } + buffer = tmp; + } + current = buffer + len; + } + + if (len && !ferror(file)) + return buffer; + free(buffer); + return nullptr; +} + +/* State to store as JSContext private. */ +struct JSShellContextData { + /* Creation timestamp, used by the elapsed() shell builtin. */ + int64_t startTime; +}; + +static JSShellContextData* +NewContextData() +{ + JSShellContextData* data = (JSShellContextData*) + js_calloc(sizeof(JSShellContextData), 1); + if (!data) + return nullptr; +#ifdef FIXME + data->startTime = PRMJ_Now(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + data->startTime = 1000 * 1000 * tv.tv_sec + tv.tv_usec; +#endif + return data; +} + +static inline JSShellContextData* +GetContextData(JSContext* cx) +{ + JSShellContextData* data = (JSShellContextData*) JS_GetContextPrivate(cx); + + MOZ_ASSERT(data); + return data; +} + +#ifdef NOTYET +static bool +ShellInterruptCallback(JSContext* cx) +{ + ShellRuntime* sr = GetShellRuntime(cx); + if (!sr->serviceInterrupt) + return true; + + // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to + // true to distinguish watchdog or user triggered interrupts. + // Do this first to prevent other interrupts that may occur while the + // user-supplied callback is executing from re-entering the handler. + sr->serviceInterrupt = false; + + bool result; + RootedValue interruptFunc(cx, sr->interruptFunc); + if (!interruptFunc.isNull()) { + JS::AutoSaveExceptionState savedExc(cx); + JSAutoCompartment ac(cx, &interruptFunc.toObject()); + RootedValue rval(cx); + if (!JS_CallFunctionValue(cx, nullptr, interruptFunc, + JS::HandleValueArray::empty(), &rval)) + { + return false; + } + if (rval.isBoolean()) + result = rval.toBoolean(); + else + result = false; + } else { + result = false; + } + + if (!result && sr->exitCode == 0) + sr->exitCode = EXITCODE_TIMEOUT; + + return result; +} +#endif + +/* + * Some UTF-8 files, notably those written using Notepad, have a Unicode + * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order + * is meaningless for UTF-8) but causes a syntax error unless we skip it. + */ +static void +SkipUTF8BOM(FILE* file) +{ + int ch1 = fgetc(file); + int ch2 = fgetc(file); + int ch3 = fgetc(file); + + // Skip the BOM + if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) + return; + + // No BOM - revert + if (ch3 != EOF) + ungetc(ch3, file); + if (ch2 != EOF) + ungetc(ch2, file); + if (ch1 != EOF) + ungetc(ch1, file); +} + +#ifdef NOTYET +static void +RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly) +{ + ShellRuntime* sr = GetShellRuntime(cx); + + SkipUTF8BOM(file); + + // To support the UNIX #! shell hack, gobble the first line if it starts + // with '#'. + int ch = fgetc(file); + if (ch == '#') { + while ((ch = fgetc(file)) != EOF) { + if (ch == '\n' || ch == '\r') + break; + } + } + ungetc(ch, file); + +#ifdef FIXME + int64_t t1 = PRMJ_Now(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t t1 = 1000 * 1000 * tv.tv_sec + tv.tv_usec; +#endif + RootedScript script(cx); + + { + CompileOptions options(cx); + options.setIntroductionType("js shell file") + .setUTF8(true) + .setFileAndLine(filename, 1) + .setIsRunOnce(true) + .setNoScriptRval(true); + + sr->gotError = false; + (void) JS::Compile(cx, options, file, &script); + MOZ_ASSERT_IF(!script, sr->gotError); + } + + #ifdef DEBUG + if (dumpEntrainedVariables) + AnalyzeEntrainedVariables(cx, script); + #endif + if (script && !compileOnly) { + if (!JS_ExecuteScript(cx, script)) { + if (!sr->quitting && sr->exitCode != EXITCODE_TIMEOUT) + sr->exitCode = EXITCODE_RUNTIME_ERROR; + } +#ifdef FIXME + int64_t t2 = PRMJ_Now() - t1; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t t2 = 1000 * 1000 * tv.tv_sec + tv.tv_usec; + t2 -= t1; +#endif + if (printTiming) + printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC); + } +} +#endif + +#ifdef NOTYET +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)); + if (!src || !DecompressString(moduleloader::compressedSources, moduleloader::GetCompressedSize(), + reinterpret_cast<unsigned char*>(src.get()), srcLen)) + { + return false; + } + + CompileOptions options(cx); + options.setIntroductionType("shell module loader"); + options.setFileAndLine("shell/ModuleLoader.js", 1); + options.setSelfHostingMode(false); + options.setCanLazilyParse(false); + options.setVersion(JSVERSION_LATEST); + options.werrorOption = true; + options.strictOption = true; + + RootedValue rv(cx); + return Evaluate(cx, options, src, srcLen, &rv); +} +#endif + +#ifdef NOTYET +static bool +GetLoaderObject(JSContext* cx, MutableHandleObject resultOut) +{ + // Look up the |Reflect.Loader| object that has been defined by the module + // loader. + + RootedObject object(cx, cx->global()); + RootedValue value(cx); + if (!JS_GetProperty(cx, object, "Reflect", &value) || !value.isObject()) + return false; + + object = &value.toObject(); + if (!JS_GetProperty(cx, object, "Loader", &value) || !value.isObject()) + return false; + + resultOut.set(&value.toObject()); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetImportMethod(JSContext* cx, HandleObject loader, MutableHandleFunction resultOut) +{ + // Look up the module loader's |import| method. + + RootedValue value(cx); + if (!JS_GetProperty(cx, loader, "import", &value) || !value.isObject()) + return false; + + RootedObject object(cx, &value.toObject()); + if (!object->is<JSFunction>()) + return false; + + resultOut.set(&object->as<JSFunction>()); + return true; +} +#endif + +#ifdef NOTYET +static void +RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly) +{ + // Exectute a module by calling |Reflect.Loader.import(filename)|. + + ShellRuntime* sr = GetShellRuntime(cx); + + RootedObject loaderObj(cx); + MOZ_ALWAYS_TRUE(GetLoaderObject(cx, &loaderObj)); + + RootedFunction importFun(cx); + MOZ_ALWAYS_TRUE(GetImportMethod(cx, loaderObj, &importFun)); + + AutoValueArray<2> args(cx); + args[0].setString(JS_NewStringCopyZ(cx, filename)); + args[1].setUndefined(); + + RootedValue value(cx); + if (!JS_CallFunction(cx, loaderObj, importFun, args, &value)) { + sr->exitCode = EXITCODE_RUNTIME_ERROR; + return; + } +} +#endif + +static bool +EvalAndPrint(JSContext* cx, const char* bytes, size_t length, + int lineno, bool compileOnly, FILE* out) +{ + // Eval. + JS::CompileOptions options(cx); + options.setIntroductionType("js shell interactive") + .setUTF8(true) + .setIsRunOnce(true) + .setFileAndLine("typein", lineno); + RootedScript script(cx); + if (!JS::Compile(cx, options, bytes, length, &script)) + return false; + if (compileOnly) + return true; + RootedValue result(cx); + if (!JS_ExecuteScript(cx, script, &result)) + return false; + + if (!result.isUndefined()) { + // Print. + RootedString str(cx); + str = JS_ValueToSource(cx, result); + if (!str) + return false; + + char* utf8chars = JS_EncodeStringToUTF8(cx, str); + if (!utf8chars) + return false; + fprintf(out, "%s\n", utf8chars); + JS_free(cx, utf8chars); + } + return true; +} + +#ifdef NOTYET +static void +ReadEvalPrintLoop(JSContext* cx, FILE* in, FILE* out, bool compileOnly) +{ + ShellRuntime* sr = GetShellRuntime(cx); + int lineno = 1; + bool hitEOF = false; + + do { + /* + * Accumulate lines until we get a 'compilable unit' - one that either + * generates an error (before running out of source) or that compiles + * cleanly. This should be whenever we get a complete statement that + * coincides with the end of a line. + */ + int startline = lineno; + typedef Vector<char, 32> CharBuffer; + CharBuffer buffer(cx); + do { + ScheduleWatchdog(cx->runtime(), -1); + sr->serviceInterrupt = false; + errno = 0; + + char* line = GetLine(in, startline == lineno ? "js> " : ""); + if (!line) { + if (errno) { + JS_ReportError(cx, strerror(errno)); + return; + } + hitEOF = true; + break; + } + + if (!buffer.append(line, strlen(line)) || !buffer.append('\n')) + return; + + lineno++; + if (!ScheduleWatchdog(cx->runtime(), sr->timeoutInterval)) { + hitEOF = true; + break; + } + } while (!JS_BufferIsCompilableUnit(cx, cx->global(), buffer.begin(), buffer.length())); + + if (hitEOF && buffer.empty()) + break; + + if (!EvalAndPrint(cx, buffer.begin(), buffer.length(), startline, compileOnly, + out)) + { + // Catch the error, report it, and keep going. + JS_ReportPendingException(cx); + } + } while (!hitEOF && !sr->quitting); + + fprintf(out, "\n"); +} +#endif + +enum FileKind +{ + FileScript, + FileModule +}; + +#ifdef NOTYET +static void +Process(JSContext* cx, const char* filename, bool forceTTY, FileKind kind = FileScript) +{ + FILE* file; + if (forceTTY || !filename || strcmp(filename, "-") == 0) { + file = stdin; + } else { + file = fopen(filename, "r"); + if (!file) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_CANT_OPEN, filename, strerror(errno)); + return; + } + } +#ifdef FIXME + AutoCloseFile autoClose(file); +#endif + + if (!forceTTY && !isatty(fileno(file))) { + // It's not interactive - just execute it. + if (kind == FileScript) + RunFile(cx, filename, file, compileOnly); + else + RunModule(cx, filename, file, compileOnly); + } else { + // It's an interactive filehandle; drop into read-eval-print loop. + MOZ_ASSERT(kind == FileScript); + ReadEvalPrintLoop(cx, file, gOutFile, compileOnly); + } +#if !defined(FIXME) + if (file && fileno(file) > 2) (void) fclose(file); +#endif +} +#endif + +static bool +Version(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + JSVersion origVersion = JS_GetVersion(cx); + if (args.length() == 0 || args[0].isUndefined()) { /* Get version. */ args.rval().setInt32(origVersion); } else { @@ -1276,7 +1819,6 @@ return true; } -#ifndef NOTYET static bool LoadScript(JSContext* cx, unsigned argc, Value* vp, bool scriptRelative) { @@ -1315,28 +1857,22 @@ args.rval().setUndefined(); return true; } -#endif -#ifndef NOTYET static bool Load(JSContext* cx, unsigned argc, Value* vp) { return LoadScript(cx, argc, vp, false); } -#endif -#ifndef NOTYET static bool LoadScriptRelativeToScript(JSContext* cx, unsigned argc, Value* vp) { return LoadScript(cx, argc, vp, true); } -#endif // Populate |options| with the options given by |opts|'s properties. If we // need to convert a filename to a C string, let fileNameBytes own the // bytes. -#ifdef NOTYET static bool ParseCompileOptions(JSContext* cx, CompileOptions& options, HandleObject opts, JSAutoByteString& fileNameBytes) @@ -1407,7 +1943,6 @@ return true; } -#endif #ifdef NOTYET class AutoNewContext @@ -1480,7 +2015,6 @@ "CacheEntryObject", JSCLASS_HAS_RESERVED_SLOTS(2) }; -#ifndef NOTYET static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) { @@ -1500,17 +2034,13 @@ args.rval().setObject(*obj); return true; } -#endif -#ifndef NOTYET static bool CacheEntry_isCacheEntry(JSObject* cache) { return JS_GetClass(cache) == &CacheEntry_class; } -#endif -#ifndef NOTYET static JSString* CacheEntry_getSource(HandleObject cache) { @@ -1521,7 +2051,6 @@ return v.toString(); } -#endif #ifdef NOTYET static uint8_t* @@ -1874,7 +2403,7 @@ if (!ucbuf) { JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname); #if !defined(FIXME) - if (file) fclose(file); + if (file && fileno(file) > 2) (void) fclose(file); #endif return nullptr; } @@ -2154,43 +2683,3316 @@ return false; } -static const JSFunctionSpec global_functions[] = { - JS_FN("version", Version, 0,0), - JS_FN("options", Options, 0,0), - JS_FN("load", Load, 1,0), - JS_FN("loadRelativeToScript", LoadScriptRelativeToScript, 1,0), -#ifdef NOTYET - JS_FN("evaluate", Evaluate, 2,0), - JS_FN("run", Run, 1,0), - JS_FN("readline", ReadLine, 0,0), -#endif /* NOTYET */ - JS_FN("print", Print, 0,0), - JS_FN("printErr", PrintErr, 0,0), - JS_FN("putstr", PutStr, 0,0), - JS_FN("dateNow", Now, 0,0), -#ifdef NOTYET - JS_FN("help", Help, 0,0), -#endif /* NOTYET */ - JS_FN("quit", Quit, 0,0), -#ifdef NOTYET - JS_FN("assertEq", AssertEq, 2,0), - JS_FN("startTimingMutator", StartTimingMutator, 0,0), - JS_FN("stopTimingMutator", StopTimingMutator, 0,0), - JS_FN("throwError", ThrowError, 0,0), -#ifdef DEBUG - JS_FN("disassemble", DisassembleToString, 1,0), - JS_FN("dis", Disassemble, 1,0), - JS_FN("disfile", DisassFile, 1,0), - JS_FN("dissrc", DisassWithSrc, 1,0), - JS_FN("notes", Notes, 1,0), - JS_FN("stackDump", StackDump, 3,0), -#endif /* DEBUG */ - JS_FN("intern", Intern, 1,0), - JS_FN("getslx", GetSLX, 1,0), - JS_FN("evalcx", EvalInContext, 1,0), - JS_FN("evalInWorker", EvalInWorker, 1,0), - JS_FN("getSharedArrayBuffer", GetSharedArrayBuffer, 0,0), - JS_FN("setSharedArrayBuffer", SetSharedArrayBuffer, 0,0), +#ifdef NOTYET +static bool +StartTimingMutator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() > 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_TOO_MANY_ARGS, "startTimingMutator"); + return false; + } + + if (!cx->runtime()->gc.stats.startTimingMutator()) { + JS_ReportError(cx, "StartTimingMutator should only be called from outside of GC"); + return false; + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +StopTimingMutator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() > 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_TOO_MANY_ARGS, "stopTimingMutator"); + return false; + } + + double mutator_ms, gc_ms; + if (!cx->runtime()->gc.stats.stopTimingMutator(mutator_ms, gc_ms)) { + JS_ReportError(cx, "stopTimingMutator called when not timing the mutator"); + return false; + } + double total_ms = mutator_ms + gc_ms; + if (total_ms > 0) { + fprintf(gOutFile, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n", + mutator_ms, mutator_ms / total_ms * 100.0, gc_ms, gc_ms / total_ms * 100.0); + } + + args.rval().setUndefined(); + return true; +} +#endif + +static const char* +ToSource(JSContext* cx, MutableHandleValue vp, JSAutoByteString* bytes) +{ + JSString* str = JS_ValueToSource(cx, vp); + if (str) { + vp.setString(str); + if (bytes->encodeLatin1(cx, str)) + return bytes->ptr(); + } + JS_ClearPendingException(cx); + return "<<error converting value to string>>"; +} + +static bool +AssertEq(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + (args.length() < 2) + ? JSSMSG_NOT_ENOUGH_ARGS + : (args.length() == 3) + ? JSSMSG_INVALID_ARGS + : JSSMSG_TOO_MANY_ARGS, + "assertEq"); + return false; + } + + bool same; + if (!JS_SameValue(cx, args[0], args[1], &same)) + return false; + if (!same) { + JSAutoByteString bytes0, bytes1; + const char* actual = ToSource(cx, args[0], &bytes0); + const char* expected = ToSource(cx, args[1], &bytes1); + if (args.length() == 2) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED, + actual, expected); + } else { + JSAutoByteString bytes2(cx, args[2].toString()); + if (!bytes2) + return false; + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED_MSG, + actual, expected, bytes2.ptr()); + } + return false; + } + args.rval().setUndefined(); + return true; +} + +#ifdef NOTYET +static JSScript* +ValueToScript(JSContext* cx, Value vArg, JSFunction** funp = nullptr) +{ + RootedValue v(cx, vArg); + + if (v.isString()) { + // To convert a string to a script, compile it. Parse it as an ES6 Program. + RootedLinearString linearStr(cx, StringToLinearString(cx, v.toString())); + if (!linearStr) + return nullptr; + size_t len = GetLinearStringLength(linearStr); + AutoStableStringChars linearChars(cx); + if (!linearChars.initTwoByte(cx, linearStr)) + return nullptr; + const char16_t* chars = linearChars.twoByteRange().start().get(); + + RootedScript script(cx); + CompileOptions options(cx); + if (!JS::Compile(cx, options, chars, len, &script)) + return nullptr; + return script; + } + + RootedFunction fun(cx, JS_ValueToFunction(cx, v)); + if (!fun) + return nullptr; + + // Unwrap bound functions. + while (fun->isBoundFunction()) { + JSObject* target = fun->getBoundFunctionTarget(); + if (target && target->is<JSFunction>()) + fun = &target->as<JSFunction>(); + else + break; + } + + if (!fun->isInterpreted()) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY); + return nullptr; + } + + JSScript* script = fun->getOrCreateScript(cx); + if (!script) + return nullptr; + + if (fun && funp) + *funp = fun; + + return script; +} +#endif + +#ifdef NOTYET +static JSScript* +GetTopScript(JSContext* cx) +{ + NonBuiltinScriptFrameIter iter(cx); + return iter.done() ? nullptr : iter.script(); +} +#endif + +#ifdef NOTYET +static bool +GetScriptAndPCArgs(JSContext* cx, unsigned argc, Value* argv, MutableHandleScript scriptp, + int32_t* ip) +{ + RootedScript script(cx, GetTopScript(cx)); + *ip = 0; + if (argc != 0) { + Value v = argv[0]; + unsigned intarg = 0; + if (v.isObject() && + JS_GetClass(&v.toObject()) == Jsvalify(&JSFunction::class_)) { + script = ValueToScript(cx, v); + if (!script) + return false; + intarg++; + } + if (argc > intarg) { + if (!JS::ToInt32(cx, HandleValue::fromMarkedLocation(&argv[intarg]), ip)) + return false; + if ((uint32_t)*ip >= script->length()) { + JS_ReportError(cx, "Invalid PC"); + return false; + } + } + } + + scriptp.set(script); + + return true; +} +#endif + +#ifdef NOTYET +static bool +LineToPC(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() == 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_LINE2PC_USAGE); + return false; + } + + RootedScript script(cx, GetTopScript(cx)); + int32_t lineArg = 0; + if (args[0].isObject() && args[0].toObject().is<JSFunction>()) { + script = ValueToScript(cx, args[0]); + if (!script) + return false; + lineArg++; + } + + uint32_t lineno; + if (!ToUint32(cx, args.get(lineArg), &lineno)) + return false; + + jsbytecode* pc = LineNumberToPC(script, lineno); + if (!pc) + return false; + args.rval().setInt32(script->pcToOffset(pc)); + return true; +} +#endif + +#ifdef NOTYET +static bool +PCToLine(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedScript script(cx); + int32_t i; + unsigned lineno; + + if (!GetScriptAndPCArgs(cx, args.length(), args.array(), &script, &i)) + return false; + lineno = PCToLineNumber(script, script->offsetToPC(i)); + if (!lineno) + return false; + args.rval().setInt32(lineno); + return true; +} +#endif + +#ifdef DEBUG + +#ifdef NOTYET +static void +UpdateSwitchTableBounds(JSContext* cx, HandleScript script, unsigned offset, + unsigned* start, unsigned* end) +{ + jsbytecode* pc; + JSOp op; + ptrdiff_t jmplen; + int32_t low, high, n; + + pc = script->offsetToPC(offset); + op = JSOp(*pc); + switch (op) { + case JSOP_TABLESWITCH: + jmplen = JUMP_OFFSET_LEN; + pc += jmplen; + low = GET_JUMP_OFFSET(pc); + pc += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc); + pc += JUMP_OFFSET_LEN; + n = high - low + 1; + break; + + default: + /* [condswitch] switch does not have any jump or lookup tables. */ + MOZ_ASSERT(op == JSOP_CONDSWITCH); + return; + } + + *start = script->pcToOffset(pc); + *end = *start + (unsigned)(n * jmplen); +} +#endif + +#ifdef NOTYET +static void +SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp) +{ + Sprint(sp, "\nSource notes:\n"); + Sprint(sp, "%4s %4s %5s %6s %-8s %s\n", + "ofs", "line", "pc", "delta", "desc", "args"); + Sprint(sp, "---- ---- ----- ------ -------- ------\n"); + unsigned offset = 0; + unsigned colspan = 0; + unsigned lineno = script->lineno(); + jssrcnote* notes = script->notes(); + unsigned switchTableEnd = 0, switchTableStart = 0; + for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + unsigned delta = SN_DELTA(sn); + offset += delta; + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); + const char* name = js_SrcNoteSpec[type].name; + Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name); + switch (type) { + case SRC_NULL: + case SRC_IF: + case SRC_CONTINUE: + case SRC_BREAK: + case SRC_BREAK2LABEL: + case SRC_SWITCHBREAK: + case SRC_ASSIGNOP: + case SRC_XDELTA: + break; + + case SRC_COLSPAN: + colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0)); + Sprint(sp, "%d", colspan); + break; + + case SRC_SETLINE: + lineno = GetSrcNoteOffset(sn, 0); + Sprint(sp, " lineno %u", lineno); + break; + + case SRC_NEWLINE: + ++lineno; + break; + + case SRC_FOR: + Sprint(sp, " cond %u update %u tail %u", + unsigned(GetSrcNoteOffset(sn, 0)), + unsigned(GetSrcNoteOffset(sn, 1)), + unsigned(GetSrcNoteOffset(sn, 2))); + break; + + case SRC_IF_ELSE: + Sprint(sp, " else %u", unsigned(GetSrcNoteOffset(sn, 0))); + break; + + case SRC_FOR_IN: + case SRC_FOR_OF: + Sprint(sp, " closingjump %u", unsigned(GetSrcNoteOffset(sn, 0))); + break; + + case SRC_COND: + case SRC_WHILE: + case SRC_NEXTCASE: + Sprint(sp, " offset %u", unsigned(GetSrcNoteOffset(sn, 0))); + break; + + case SRC_TABLESWITCH: { + JSOp op = JSOp(script->code()[offset]); + MOZ_ASSERT(op == JSOP_TABLESWITCH); + Sprint(sp, " length %u", unsigned(GetSrcNoteOffset(sn, 0))); + UpdateSwitchTableBounds(cx, script, offset, + &switchTableStart, &switchTableEnd); + break; + } + case SRC_CONDSWITCH: { + JSOp op = JSOp(script->code()[offset]); + MOZ_ASSERT(op == JSOP_CONDSWITCH); + Sprint(sp, " length %u", unsigned(GetSrcNoteOffset(sn, 0))); + unsigned caseOff = (unsigned) GetSrcNoteOffset(sn, 1); + if (caseOff) + Sprint(sp, " first case offset %u", caseOff); + UpdateSwitchTableBounds(cx, script, offset, + &switchTableStart, &switchTableEnd); + break; + } + + case SRC_TRY: + MOZ_ASSERT(JSOp(script->code()[offset]) == JSOP_TRY); + Sprint(sp, " offset to jump %u", unsigned(GetSrcNoteOffset(sn, 0))); + break; + + default: + MOZ_ASSERT(0); + break; + } + Sprint(sp, "\n"); + } +} +#endif + +#ifdef NOTYET +static bool +Notes(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; + + for (unsigned i = 0; i < args.length(); i++) { + RootedScript script (cx, ValueToScript(cx, args[i])); + if (!script) + return false; + + SrcNotes(cx, script, &sprinter); + } + + JSString* str = JS_NewStringCopyZ(cx, sprinter.string()); + if (!str) + return false; + args.rval().setString(str); + return true; +} +#endif + +JS_STATIC_ASSERT(JSTRY_CATCH == 0); +JS_STATIC_ASSERT(JSTRY_FINALLY == 1); +JS_STATIC_ASSERT(JSTRY_FOR_IN == 2); + +static const char* const TryNoteNames[] = { "catch", "finally", "for-in", "for-of", "loop" }; + +#ifdef NOTYET +static bool +TryNotes(JSContext* cx, HandleScript script, Sprinter* sp) +{ + if (!script->hasTrynotes()) + return true; + + JSTryNote* tn = script->trynotes()->vector; + JSTryNote* tnlimit = tn + script->trynotes()->length; + Sprint(sp, "\nException table:\nkind stack start end\n"); + do { + MOZ_ASSERT(tn->kind < ArrayLength(TryNoteNames)); + Sprint(sp, " %-7s %6u %8u %8u\n", + TryNoteNames[tn->kind], tn->stackDepth, + tn->start, tn->start + tn->length); + } while (++tn != tnlimit); + return true; +} +#endif + +#ifdef NOTYET +static bool +BlockNotes(JSContext* cx, HandleScript script, Sprinter* sp) +{ + if (!script->hasBlockScopes()) + return true; + + Sprint(sp, "\nBlock table:\n index parent start end\n"); + + BlockScopeArray* scopes = script->blockScopes(); + for (uint32_t i = 0; i < scopes->length; i++) { + const BlockScopeNote* note = &scopes->vector[i]; + if (note->index == BlockScopeNote::NoBlockScopeIndex) + Sprint(sp, "%8s ", "(none)"); + else + Sprint(sp, "%8u ", note->index); + if (note->parent == BlockScopeNote::NoBlockScopeIndex) + Sprint(sp, "%8s ", "(none)"); + else + Sprint(sp, "%8u ", note->parent); + Sprint(sp, "%8u %8u\n", note->start, note->start + note->length); + } + return true; +} +#endif + +#ifdef NOTYET +static bool +DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun, + bool lines, bool recursive, bool sourceNotes, Sprinter* sp) +{ + if (fun) { + Sprint(sp, "flags:"); + if (fun->isLambda()) + Sprint(sp, " LAMBDA"); + if (fun->needsCallObject()) + Sprint(sp, " NEEDS_CALLOBJECT"); + if (fun->isConstructor()) + Sprint(sp, " CONSTRUCTOR"); + if (fun->isExprBody()) + Sprint(sp, " EXPRESSION_CLOSURE"); + if (fun->isFunctionPrototype()) + Sprint(sp, " Function.prototype"); + if (fun->isSelfHostedBuiltin()) + Sprint(sp, " SELF_HOSTED"); + if (fun->isArrow()) + Sprint(sp, " ARROW"); + Sprint(sp, "\n"); + } + + if (!Disassemble(cx, script, lines, sp)) + return false; + if (sourceNotes) + SrcNotes(cx, script, sp); + TryNotes(cx, script, sp); + BlockNotes(cx, script, sp); + + if (recursive && script->hasObjects()) { + ObjectArray* objects = script->objects(); + for (unsigned i = 0; i != objects->length; ++i) { + JSObject* obj = objects->vector[i]; + if (obj->is<JSFunction>()) { + Sprint(sp, "\n"); + RootedFunction fun(cx, &obj->as<JSFunction>()); + if (fun->isInterpreted()) { + RootedScript script(cx, fun->getOrCreateScript(cx)); + if (script) { + if (!DisassembleScript(cx, script, fun, lines, recursive, sourceNotes, sp)) + return false; + } + } else { + Sprint(sp, "[native code]\n"); + } + } + } + } + return true; +} +#endif + +#ifdef NOTYET +namespace { + +struct DisassembleOptionParser { + unsigned argc; + Value* argv; + bool lines; + bool recursive; + bool sourceNotes; + + DisassembleOptionParser(unsigned argc, Value* argv) + : argc(argc), argv(argv), lines(false), recursive(false), sourceNotes(true) {} + + bool parse(JSContext* cx) { + /* Read options off early arguments */ + while (argc > 0 && argv[0].isString()) { + JSString* str = argv[0].toString(); + JSFlatString* flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return false; + if (JS_FlatStringEqualsAscii(flatStr, "-l")) + lines = true; + else if (JS_FlatStringEqualsAscii(flatStr, "-r")) + recursive = true; + else if (JS_FlatStringEqualsAscii(flatStr, "-S")) + sourceNotes = false; + else + break; + argv++, argc--; + } + return true; + } +}; + +} /* anonymous namespace */ +#endif + +#ifdef NOTYET +static bool +DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, Sprinter* sprinter) +{ + CallArgs args = CallArgsFromVp(argc, vp); + DisassembleOptionParser p(args.length(), args.array()); + if (!p.parse(cx)) + return false; + + if (p.argc == 0) { + /* Without arguments, disassemble the current script. */ + RootedScript script(cx, GetTopScript(cx)); + if (script) { + JSAutoCompartment ac(cx, script); + if (!Disassemble(cx, script, p.lines, sprinter)) + return false; + SrcNotes(cx, script, sprinter); + TryNotes(cx, script, sprinter); + BlockNotes(cx, script, sprinter); + } + } else { + for (unsigned i = 0; i < p.argc; i++) { + RootedFunction fun(cx); + RootedScript script(cx); + RootedValue value(cx, p.argv[i]); + if (value.isObject() && value.toObject().is<ModuleObject>()) + script = value.toObject().as<ModuleObject>().script(); + else + script = ValueToScript(cx, value, fun.address()); + if (!script) + return false; + if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, p.sourceNotes, sprinter)) + return false; + } + } + + return !sprinter->hadOutOfMemory(); +} +#endif + +#ifdef NOTYET +static bool +DisassembleToString(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; + if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) + return false; + + JSString* str = JS_NewStringCopyZ(cx, sprinter.string()); + if (!str) + return false; + args.rval().setString(str); + return true; +} +#endif + +#ifdef NOTYET +static bool +Disassemble(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; + if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) + return false; + + fprintf(stdout, "%s\n", sprinter.string()); + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +DisassFile(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + /* Support extra options at the start, just like Disassemble. */ + DisassembleOptionParser p(args.length(), args.array()); + if (!p.parse(cx)) + return false; + + if (!p.argc) { + args.rval().setUndefined(); + return true; + } + + // We should change DisassembleOptionParser to store CallArgs. + JSString* str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0])); + if (!str) + return false; + JSAutoByteString filename(cx, str); + if (!filename) + return false; + RootedScript script(cx); + + { + CompileOptions options(cx); + options.setIntroductionType("js shell disFile") + .setUTF8(true) + .setFileAndLine(filename.ptr(), 1) + .setIsRunOnce(true) + .setNoScriptRval(true); + + if (!JS::Compile(cx, options, filename.ptr(), &script)) + return false; + } + + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; + bool ok = DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter); + if (ok) + fprintf(stdout, "%s\n", sprinter.string()); + if (!ok) + return false; + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + +#define LINE_BUF_LEN 512 + unsigned len, line1, line2, bupline; + FILE* file; + char linebuf[LINE_BUF_LEN]; + static const char sep[] = ";-------------------------"; + + bool ok = true; + RootedScript script(cx); + for (unsigned i = 0; ok && i < args.length(); i++) { + script = ValueToScript(cx, args[i]); + if (!script) + return false; + + if (!script->filename()) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_FILE_SCRIPTS_ONLY); + return false; + } + + file = fopen(script->filename(), "r"); + if (!file) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_CANT_OPEN, script->filename(), + strerror(errno)); + return false; + } + + jsbytecode* pc = script->code(); + jsbytecode* end = script->codeEnd(); + + Sprinter sprinter(cx); + if (!sprinter.init()) { + ok = false; + goto bail; + } + + /* burn the leading lines */ + line2 = PCToLineNumber(script, pc); + for (line1 = 0; line1 < line2 - 1; line1++) { + char* tmp = fgets(linebuf, LINE_BUF_LEN, file); + if (!tmp) { + JS_ReportError(cx, "failed to read %s fully", script->filename()); + ok = false; + goto bail; + } + } + + bupline = 0; + while (pc < end) { + line2 = PCToLineNumber(script, pc); + + if (line2 < line1) { + if (bupline != line2) { + bupline = line2; + Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2); + } + } else { + if (bupline && line1 == line2) + Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2); + bupline = 0; + while (line1 < line2) { + if (!fgets(linebuf, LINE_BUF_LEN, file)) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_UNEXPECTED_EOF, + script->filename()); + ok = false; + goto bail; + } + line1++; + Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf); + } + } + + len = Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter); + if (!len) { + ok = false; + goto bail; + } + pc += len; + } + + fprintf(stdout, "%s\n", sprinter.string()); + + bail: + fclose(file); + } + args.rval().setUndefined(); + return ok; +#undef LINE_BUF_LEN +} +#endif + +#endif /* DEBUG */ + +#ifdef NOTYET +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; + + AutoStableStringChars strChars(cx); + if (!strChars.initTwoByte(cx, str)) + return false; + + mozilla::Range<const char16_t> chars = strChars.twoByteRange(); + + if (!JS_AtomizeAndPinUCStringN(cx, chars.start().get(), chars.length())) + return false; + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +Clone(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject parent(cx); + RootedObject funobj(cx); + + if (!args.length()) { + JS_ReportError(cx, "Invalid arguments to clone"); + return false; + } + + { + Maybe<JSAutoCompartment> ac; + RootedObject obj(cx, args[0].isPrimitive() ? nullptr : &args[0].toObject()); + + if (obj && obj->is<CrossCompartmentWrapperObject>()) { + obj = UncheckedUnwrap(obj); + ac.emplace(cx, obj); + args[0].setObject(*obj); + } + if (obj && obj->is<JSFunction>()) { + funobj = obj; + } else { + JSFunction* fun = JS_ValueToFunction(cx, args[0]); + if (!fun) + return false; + funobj = JS_GetFunctionObject(fun); + } + } + + if (args.length() > 1) { + if (!JS_ValueToObject(cx, args[1], &parent)) + return false; + } else { + parent = js::GetGlobalForObjectCrossCompartment(&args.callee()); + } + + // Should it worry us that we might be getting with wrappers + // around with wrappers here? + JS::AutoObjectVector scopeChain(cx); + if (!parent->is<GlobalObject>() && !scopeChain.append(parent)) + return false; + JSObject* clone = JS::CloneFunctionObject(cx, funobj, scopeChain); + if (!clone) + return false; + args.rval().setObject(*clone); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetSLX(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedScript script(cx); + + script = ValueToScript(cx, args.get(0)); + if (!script) + return false; + args.rval().setInt32(GetScriptLineExtent(script)); + return true; +} +#endif + +static bool +ThrowError(JSContext* cx, unsigned argc, Value* vp) +{ + JS_ReportError(cx, "This is an error"); + return false; +} + +#define LAZY_STANDARD_CLASSES + +/* A class for easily testing the inner/outer object callbacks. */ +typedef struct ComplexObject { + bool isInner; + bool frozen; + JSObject* inner; + JSObject* outer; +} ComplexObject; + +static bool +sandbox_enumerate(JSContext* cx, HandleObject obj) +{ + RootedValue v(cx); + + if (!JS_GetProperty(cx, obj, "lazy", &v)) + return false; + + if (!ToBoolean(v)) + return true; + + return JS_EnumerateStandardClasses(cx, obj); +} + +static bool +sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) +{ + RootedValue v(cx); + if (!JS_GetProperty(cx, obj, "lazy", &v)) + return false; + + if (ToBoolean(v)) + return JS_ResolveStandardClass(cx, obj, id, resolvedp); + return true; +} + +static const JSClass sandbox_class = { + "sandbox", + JSCLASS_GLOBAL_FLAGS, + nullptr, nullptr, nullptr, nullptr, + sandbox_enumerate, sandbox_resolve, + nullptr, nullptr, + nullptr, nullptr, nullptr, + JS_GlobalObjectTraceHook +}; + +#ifdef NOTYET +static JSObject* +NewSandbox(JSContext* cx, bool lazy) +{ + RootedObject obj(cx, JS_NewGlobalObject(cx, &sandbox_class, nullptr, + JS::DontFireOnNewGlobalHook)); + if (!obj) + return nullptr; + + { + JSAutoCompartment ac(cx, obj); + if (!lazy && !JS_InitStandardClasses(cx, obj)) + return nullptr; + + RootedValue value(cx, BooleanValue(lazy)); + if (!JS_SetProperty(cx, obj, "lazy", value)) + return nullptr; + } + + JS_FireOnNewGlobalObject(cx, obj); + + if (!cx->compartment()->wrap(cx, &obj)) + return nullptr; + return obj; +} +#endif + +#ifdef NOTYET +static bool +EvalInContext(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "evalcx", 1)) + return false; + + RootedString str(cx, ToString(cx, args[0])); + if (!str) + return false; + + RootedObject sobj(cx); + if (args.hasDefined(1)) { + sobj = ToObject(cx, args[1]); + if (!sobj) + return false; + } + + AutoStableStringChars strChars(cx); + if (!strChars.initTwoByte(cx, str)) + return false; + + mozilla::Range<const char16_t> chars = strChars.twoByteRange(); + size_t srclen = chars.length(); + const char16_t* src = chars.start().get(); + + bool lazy = false; + if (srclen == 4) { + if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { + lazy = true; + srclen = 0; + } + } + + if (!sobj) { + sobj = NewSandbox(cx, lazy); + if (!sobj) + return false; + } + + if (srclen == 0) { + args.rval().setObject(*sobj); + return true; + } + + JS::AutoFilename filename; + unsigned lineno; + + DescribeScriptedCaller(cx, &filename, &lineno); + { + Maybe<JSAutoCompartment> ac; + unsigned flags; + JSObject* unwrapped = UncheckedUnwrap(sobj, true, &flags); + if (flags & Wrapper::CROSS_COMPARTMENT) { + sobj = unwrapped; + ac.emplace(cx, sobj); + } + + sobj = ToWindowIfWindowProxy(sobj); + + if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) { + JS_ReportError(cx, "Invalid scope argument to evalcx"); + return false; + } + JS::CompileOptions opts(cx); + opts.setFileAndLine(filename.get(), lineno); + if (!JS::Evaluate(cx, opts, src, srclen, args.rval())) { + return false; + } + } + + if (!cx->compartment()->wrap(cx, args.rval())) + return false; + + return true; +} +#endif + +#ifdef NOTYET +struct WorkerInput +{ + JSRuntime* runtime; + char16_t* chars; + size_t length; + + WorkerInput(JSRuntime* runtime, char16_t* chars, size_t length) + : runtime(runtime), chars(chars), length(length) + {} + + ~WorkerInput() { + js_free(chars); + } +}; +#endif + +static void SetWorkerRuntimeOptions(JSRuntime* rt); + +#ifdef NOTYET +static void +WorkerMain(void* arg) +{ + WorkerInput* input = (WorkerInput*) arg; + + JSRuntime* rt = JS_NewRuntime(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->runtime); + if (!rt) { + js_delete(input); + return; + } + + mozilla::UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>(); + if (!sr) { + JS_DestroyRuntime(rt); + js_delete(input); + return; + } + + sr->isWorker = true; + JS_SetRuntimePrivate(rt, sr.get()); + JS_SetErrorReporter(rt, my_ErrorReporter); + SetWorkerRuntimeOptions(rt); + + if (!InitWatchdog(rt)) { + JS_DestroyRuntime(rt); + js_delete(input); + return; + } + + JSContext* cx = NewContext(rt); + if (!cx) { + JS_DestroyRuntime(rt); + js_delete(input); + return; + } + + JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx); + + do { + JSAutoRequest ar(cx); + + JS::CompartmentOptions compartmentOptions; + compartmentOptions.setVersion(JSVERSION_DEFAULT); + RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr)); + if (!global) + break; + + JSAutoCompartment ac(cx, global); + + JS::CompileOptions options(cx); + options.setFileAndLine("<string>", 1) + .setIsRunOnce(true); + + RootedScript script(cx); + if (!JS::Compile(cx, options, input->chars, input->length, &script)) + break; + RootedValue result(cx); + JS_ExecuteScript(cx, script, &result); + } while (0); + + JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr); + + DestroyContext(cx, false); + JS_DestroyRuntime(rt); + + js_delete(input); +} +#endif + +#ifdef NOTYET +Vector<PRThread*, 0, SystemAllocPolicy> workerThreads; +#endif + +#ifdef NOTYET +static bool +EvalInWorker(JSContext* cx, unsigned argc, Value* vp) +{ + if (!CanUseExtraThreads()) { + JS_ReportError(cx, "Can't create worker threads with --no-threads"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.get(0).isString()) { + JS_ReportError(cx, "Invalid arguments to evalInWorker"); + return false; + } + + if (!args[0].toString()->ensureLinear(cx)) + return false; + + JSLinearString* str = &args[0].toString()->asLinear(); + + char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t)); + if (!chars) + return false; + CopyChars(chars, *str); + + WorkerInput* input = js_new<WorkerInput>(cx->runtime(), chars, str->length()); + if (!input) + return false; + + PRThread* thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, + gMaxStackSize + 128 * 1024); + if (!thread || !workerThreads.append(thread)) + return false; + + return true; +} +#endif + +#ifdef NOTYET +static bool +ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.get(0).isObject()) { + JS_ReportError(cx, "shapeOf: object expected"); + return false; + } + JSObject* obj = &args[0].toObject(); + args.rval().set(JS_NumberValue(double(uintptr_t(obj->maybeShape()) >> 3))); + return true; +} +#endif + +/* + * Check that t1 comes strictly before t2. The function correctly deals with + * wrap-around between t2 and t1 assuming that t2 and t1 stays within INT32_MAX + * from each other. We use MAX_TIMEOUT_INTERVAL to enforce this restriction. + */ +#ifdef NOTYET +static bool +IsBefore(int64_t t1, int64_t t2) +{ + return int32_t(t1 - t2) < 0; +} +#endif + +#ifdef NOTYET +static bool +Sleep_fn(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + int64_t t_ticks; + + if (args.length() == 0) { + t_ticks = 0; + } else { + double t_secs; + + if (!ToNumber(cx, args[0], &t_secs)) + return false; + + /* NB: The next condition also filter out NaNs. */ + if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) { + JS_ReportError(cx, "Excessive sleep interval"); + return false; + } + t_ticks = (t_secs <= 0.0) + ? 0 + : int64_t(PRMJ_USEC_PER_SEC * t_secs); + } + PR_Lock(sr->watchdogLock); +#ifdef FIXME + int64_t to_wakeup = PRMJ_Now() + t_ticks; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t to_wakeup = 1000 * 1000 * tv.tv_sec + tv.tv_usec; + to_wakeup += t_ticks; +#endif + for (;;) { + PR_WaitCondVar(sr->sleepWakeup, PR_MillisecondsToInterval(t_ticks / 1000)); + if (sr->serviceInterrupt) + break; +#ifdef FIXME + int64_t now = PRMJ_Now(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t now = 1000 * 1000 * tv.tv_sec + tv.tv_usec; +#endif + if (!IsBefore(now, to_wakeup)) + break; + t_ticks = to_wakeup - now; + } + PR_Unlock(sr->watchdogLock); + args.rval().setUndefined(); + return !sr->serviceInterrupt; +} +#endif + +#ifdef NOTYET +static bool +InitWatchdog(JSRuntime* rt) +{ + ShellRuntime* sr = GetShellRuntime(rt); + MOZ_ASSERT(!sr->watchdogThread); + sr->watchdogLock = PR_NewLock(); + if (sr->watchdogLock) { + sr->watchdogWakeup = PR_NewCondVar(sr->watchdogLock); + if (sr->watchdogWakeup) { + sr->sleepWakeup = PR_NewCondVar(sr->watchdogLock); + if (sr->sleepWakeup) + return true; + PR_DestroyCondVar(sr->watchdogWakeup); + } + PR_DestroyLock(sr->watchdogLock); + } + return false; +} +#endif + +#ifdef NOTYET +static void +KillWatchdog(JSRuntime* rt) +{ + ShellRuntime* sr = GetShellRuntime(rt); + PRThread* thread; + + PR_Lock(sr->watchdogLock); + thread = sr->watchdogThread; + if (thread) { + /* + * The watchdog thread is running, tell it to terminate waking it up + * if necessary. + */ + sr->watchdogThread = nullptr; + PR_NotifyCondVar(sr->watchdogWakeup); + } + PR_Unlock(sr->watchdogLock); + if (thread) + PR_JoinThread(thread); + PR_DestroyCondVar(sr->sleepWakeup); + PR_DestroyCondVar(sr->watchdogWakeup); + PR_DestroyLock(sr->watchdogLock); +} +#endif + +#ifdef NOTYET +static void +WatchdogMain(void* arg) +{ + PR_SetCurrentThreadName("JS Watchdog"); + + JSRuntime* rt = (JSRuntime*) arg; + ShellRuntime* sr = GetShellRuntime(rt); + + PR_Lock(sr->watchdogLock); + while (sr->watchdogThread) { +#ifdef FIXME + int64_t now = PRMJ_Now(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t now = 1000 * 1000 * tv.tv_sec + tv.tv_usec; +#endif + if (sr->watchdogHasTimeout && !IsBefore(now, sr->watchdogTimeout)) { + /* + * The timeout has just expired. Request an interrupt callback + * outside the lock. + */ + sr->watchdogHasTimeout = false; + PR_Unlock(sr->watchdogLock); + CancelExecution(rt); + PR_Lock(sr->watchdogLock); + + /* Wake up any threads doing sleep. */ + PR_NotifyAllCondVar(sr->sleepWakeup); + } else { + if (sr->watchdogHasTimeout) { + /* + * Time hasn't expired yet. Simulate an interrupt callback + * which doesn't abort execution. + */ + JS_RequestInterruptCallback(rt); + } + + uint64_t sleepDuration = PR_INTERVAL_NO_TIMEOUT; + if (sr->watchdogHasTimeout) + sleepDuration = PR_TicksPerSecond() / 10; + mozilla::DebugOnly<PRStatus> status = + PR_WaitCondVar(sr->watchdogWakeup, sleepDuration); + MOZ_ASSERT(status == PR_SUCCESS); + } + } + PR_Unlock(sr->watchdogLock); +} +#endif + +#ifdef NOTYET +static bool +ScheduleWatchdog(JSRuntime* rt, double t) +{ + ShellRuntime* sr = GetShellRuntime(rt); + + if (t <= 0) { + PR_Lock(sr->watchdogLock); + sr->watchdogHasTimeout = false; + PR_Unlock(sr->watchdogLock); + return true; + } + + int64_t interval = int64_t(ceil(t * PRMJ_USEC_PER_SEC)); +#ifdef FIXME + int64_t timeout = PRMJ_Now() + interval; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t timeout = 1000 * 1000 * tv.tv_sec + tv.tv_usec; + timeout += interval; +#endif + PR_Lock(sr->watchdogLock); + if (!sr->watchdogThread) { + MOZ_ASSERT(!sr->watchdogHasTimeout); + sr->watchdogThread = PR_CreateThread(PR_USER_THREAD, + WatchdogMain, + rt, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + if (!sr->watchdogThread) { + PR_Unlock(sr->watchdogLock); + return false; + } + } else if (!sr->watchdogHasTimeout || IsBefore(timeout, sr->watchdogTimeout)) { + PR_NotifyCondVar(sr->watchdogWakeup); + } + sr->watchdogHasTimeout = true; + sr->watchdogTimeout = timeout; + PR_Unlock(sr->watchdogLock); + return true; +} +#endif + +#ifdef NOTYET +static void +CancelExecution(JSRuntime* rt) +{ + ShellRuntime* sr = GetShellRuntime(rt); + sr->serviceInterrupt = true; + JS_RequestInterruptCallback(rt); + + if (!sr->interruptFunc.isNull()) { + static const char msg[] = "Script runs for too long, terminating.\n"; + fputs(msg, stderr); + } +} +#endif + +#ifdef NOTYET +static bool +SetTimeoutValue(JSContext* cx, double t) +{ + /* NB: The next condition also filter out NaNs. */ + if (!(t <= MAX_TIMEOUT_INTERVAL)) { + JS_ReportError(cx, "Excessive timeout value"); + return false; + } + GetShellRuntime(cx)->timeoutInterval = t; + if (!ScheduleWatchdog(cx->runtime(), t)) { + JS_ReportError(cx, "Failed to create the watchdog"); + return false; + } + return true; +} +#endif + +#ifdef NOTYET +static bool +Timeout(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() == 0) { + args.rval().setNumber(sr->timeoutInterval); + return true; + } + + if (args.length() > 2) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + double t; + if (!ToNumber(cx, args[0], &t)) + return false; + + if (args.length() > 1) { + RootedValue value(cx, args[1]); + if (!value.isObject() || !value.toObject().is<JSFunction>()) { + JS_ReportError(cx, "Second argument must be a timeout function"); + return false; + } + sr->interruptFunc = value; + } + + args.rval().setUndefined(); + return SetTimeoutValue(cx, t); +} +#endif + +#ifdef NOTYET +static bool +InterruptIf(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + if (ToBoolean(args[0])) { + GetShellRuntime(cx)->serviceInterrupt = true; + JS_RequestInterruptCallback(cx->runtime()); + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() != 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + GetShellRuntime(cx)->serviceInterrupt = true; + JS_RequestInterruptCallback(cx->runtime()); + bool interruptRv = CheckForInterrupt(cx); + + // The interrupt handler could have set a pending exception. Since we call + // back into JS, don't have it see the pending exception. If we have an + // uncatchable exception that's not propagating a debug mode forced + // return, return. + if (!interruptRv && !cx->isExceptionPending() && !cx->isPropagatingForcedReturn()) + return false; + + JS::AutoSaveExceptionState savedExc(cx); + Value argv[1] = { BooleanValue(interruptRv) }; + RootedValue rv(cx); + if (!Invoke(cx, UndefinedValue(), args[0], 1, argv, &rv)) + return false; + + args.rval().setUndefined(); + return interruptRv; +} +#endif + +#ifdef NOTYET +static bool +SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + RootedValue value(cx, args[0]); + if (!value.isObject() || !value.toObject().is<JSFunction>()) { + JS_ReportError(cx, "Argument must be a function"); + return false; + } + GetShellRuntime(cx)->interruptFunc = value; + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +EnableLastWarning(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + + sr->lastWarningEnabled = true; + sr->lastWarning.setNull(); + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +DisableLastWarning(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + + sr->lastWarningEnabled = false; + sr->lastWarning.setNull(); + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetLastWarning(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + + if (!sr->lastWarningEnabled) { + JS_ReportError(cx, "Call enableLastWarning first."); + return false; + } + + if (!JS_WrapValue(cx, &sr->lastWarning)) + return false; + + args.rval().set(sr->lastWarning); + return true; +} +#endif + +#ifdef NOTYET +static bool +ClearLastWarning(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + CallArgs args = CallArgsFromVp(argc, vp); + + if (!sr->lastWarningEnabled) { + JS_ReportError(cx, "Call enableLastWarning first."); + return false; + } + + sr->lastWarning.setNull(); + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef DEBUG +static bool +StackDump(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + 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); + } else { + fputs(buf, gOutFile); + JS_smprintf_free(buf); + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +StackPointerInfo(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Copy the truncated stack pointer to the result. This value is not used + // as a pointer but as a way to measure frame-size from JS. + args.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args) & 0xfffffff)); + return true; +} +#endif + + +#ifdef NOTYET +static bool +Elapsed(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() == 0) { + double d = 0.0; + JSShellContextData* data = GetContextData(cx); + if (data) { +#ifdef FIXME + d = PRMJ_Now() - data->startTime; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + d = 1000 * 1000 * tv.tv_sec + tv.tv_usec; + d -= data->startTime; +#endif + } + args.rval().setDouble(d); + return true; + } + JS_ReportError(cx, "Wrong number of arguments"); + return false; +} +#endif + +#ifdef NOTYET +static bool +Compile(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "compile", "0", "s"); + return false; + } + if (!args[0].isString()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected string to compile, got %s", typeName); + return false; + } + + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx); + if (!scriptContents) + return false; + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, scriptContents)) + return false; + + JS::CompileOptions options(cx); + options.setIntroductionType("js shell compile") + .setFileAndLine("<string>", 1) + .setIsRunOnce(true) + .setNoScriptRval(true); + RootedScript script(cx); + const char16_t* chars = stableChars.twoByteRange().start().get(); + bool ok = JS_CompileUCScript(cx, chars, scriptContents->length(), options, &script); + args.rval().setUndefined(); + return ok; +} +#endif + +#ifdef NOTYET +static bool +ParseModule(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() == 0) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_MORE_ARGS_NEEDED, "parseModule", "0", "s"); + return false; + } + + if (!args[0].isString()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected string to compile, got %s", typeName); + return false; + } + + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx); + if (!scriptContents) + return false; + + mozilla::UniquePtr<char, JS::FreePolicy> filename; + CompileOptions options(cx); + if (args.length() > 1) { + if (!args[1].isString()) { + const char* typeName = InformalValueTypeName(args[1]); + JS_ReportError(cx, "expected filename string, got %s", typeName); + return false; + } + + RootedString str(cx, args[1].toString()); + filename.reset(JS_EncodeString(cx, str)); + if (!filename) + return false; + + options.setFileAndLine(filename.get(), 1); + } else { + options.setFileAndLine("<string>", 1); + } + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, scriptContents)) + return false; + + const char16_t* chars = stableChars.twoByteRange().start().get(); + SourceBufferHolder srcBuf(chars, scriptContents->length(), + SourceBufferHolder::NoOwnership); + + RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf)); + if (!module) + return false; + + args.rval().setObject(*module); + return true; +} +#endif + +#ifdef NOTYET +static bool +SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() != 1) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_MORE_ARGS_NEEDED, "setModuleResolveHook", "0", "s"); + return false; + } + + if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected hook function, got %s", typeName); + return false; + } + + RootedFunction hook(cx, &args[0].toObject().as<JSFunction>()); + Rooted<GlobalObject*> global(cx, cx->global()); + global->setModuleResolveHook(hook); + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setString(JS_NewStringCopyZ(cx, moduleLoadPath)); + return true; +} +#endif + +#ifdef NOTYET +static bool +Parse(JSContext* cx, unsigned argc, Value* vp) +{ + using namespace js::frontend; + + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "parse", "0", "s"); + return false; + } + if (!args[0].isString()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected string to parse, got %s", typeName); + return false; + } + + JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx); + if (!scriptContents) + return false; + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, scriptContents)) + return false; + + size_t length = scriptContents->length(); + const char16_t* chars = stableChars.twoByteRange().start().get(); + + CompileOptions options(cx); + options.setIntroductionType("js shell parse") + .setFileAndLine("<string>", 1); + Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, + /* foldConstants = */ true, nullptr, nullptr); + if (!parser.checkOptions()) + return false; + + ParseNode* pn = parser.parse(); + if (!pn) + return false; +#ifdef DEBUG + DumpParseTree(pn); + fputc('\n', stderr); +#endif + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +SyntaxParse(JSContext* cx, unsigned argc, Value* vp) +{ + using namespace js::frontend; + + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "parse", "0", "s"); + return false; + } + if (!args[0].isString()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected string to parse, got %s", typeName); + return false; + } + + JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx); + if (!scriptContents) + return false; + CompileOptions options(cx); + options.setIntroductionType("js shell syntaxParse") + .setFileAndLine("<string>", 1); + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, scriptContents)) + return false; + + const char16_t* chars = stableChars.twoByteRange().start().get(); + size_t length = scriptContents->length(); + Parser<frontend::SyntaxParseHandler> parser(cx, &cx->tempLifoAlloc(), + options, chars, length, false, nullptr, nullptr); + if (!parser.checkOptions()) + return false; + + bool succeeded = parser.parse(); + if (cx->isExceptionPending()) + return false; + + if (!succeeded && !parser.hadAbortedSyntaxParse()) { + // If no exception is posted, either there was an OOM or a language + // feature unhandled by the syntax parser was encountered. + MOZ_ASSERT(cx->runtime()->hadOutOfMemory); + return false; + } + + args.rval().setBoolean(succeeded); + return true; +} +#endif + +#ifdef NOTYET +class OffThreadState { + public: + enum State { + IDLE, /* ready to work; no token, no source */ + COMPILING, /* working; no token, have source */ + DONE /* compilation done: have token and source */ + }; + + OffThreadState() : monitor(), state(IDLE), token(), source(nullptr) { } + bool init() { return monitor.init(); } + + bool startIfIdle(JSContext* cx, ScopedJSFreePtr<char16_t>& newSource) { + AutoLockMonitor alm(monitor); + if (state != IDLE) + return false; + + MOZ_ASSERT(!token); + + source = newSource.forget(); + + state = COMPILING; + return true; + } + + void abandon(JSContext* cx) { + AutoLockMonitor alm(monitor); + MOZ_ASSERT(state == COMPILING); + MOZ_ASSERT(!token); + MOZ_ASSERT(source); + + js_free(source); + source = nullptr; + + state = IDLE; + } + + void markDone(void* newToken) { + AutoLockMonitor alm(monitor); + MOZ_ASSERT(state == COMPILING); + MOZ_ASSERT(!token); + MOZ_ASSERT(source); + MOZ_ASSERT(newToken); + + token = newToken; + state = DONE; + alm.notify(); + } + + void* waitUntilDone(JSContext* cx) { + AutoLockMonitor alm(monitor); + if (state == IDLE) + return nullptr; + + if (state == COMPILING) { + while (state != DONE) + alm.wait(); + } + + MOZ_ASSERT(source); + js_free(source); + source = nullptr; + + MOZ_ASSERT(token); + void* holdToken = token; + token = nullptr; + state = IDLE; + return holdToken; + } + + private: + Monitor monitor; + State state; + void* token; + char16_t* source; +}; +#endif + +#ifdef NOTYET +static OffThreadState offThreadState; +#endif + +#ifdef NOTYET +static void +OffThreadCompileScriptCallback(void* token, void* callbackData) +{ + offThreadState.markDone(token); +} +#endif + +#ifdef NOTYET +static bool +OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp) +{ + if (!CanUseExtraThreads()) { + JS_ReportError(cx, "Can't use offThreadCompileScript with --no-threads"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "offThreadCompileScript", "0", "s"); + return false; + } + if (!args[0].isString()) { + const char* typeName = InformalValueTypeName(args[0]); + JS_ReportError(cx, "expected string to parse, got %s", typeName); + return false; + } + + JSAutoByteString fileNameBytes; + CompileOptions options(cx); + 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()); + if (!ParseCompileOptions(cx, options, opts, fileNameBytes)) + return false; + } + + // These option settings must override whatever the caller requested. + options.setIsRunOnce(true) + .setSourceIsLazy(false); + + // We assume the caller wants caching if at all possible, ignoring + // heuristics that make sense for a real browser. + options.forceAsync = true; + + JSString* scriptContents = args[0].toString(); + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, scriptContents)) + return false; + + size_t length = scriptContents->length(); + const char16_t* chars = stableChars.twoByteRange().start().get(); + + // Make sure we own the string's chars, so that they are not freed before + // the compilation is finished. + ScopedJSFreePtr<char16_t> ownedChars; + if (stableChars.maybeGiveOwnershipToCaller()) { + ownedChars = const_cast<char16_t*>(chars); + } else { + char16_t* copy = cx->pod_malloc<char16_t>(length); + if (!copy) + return false; + + mozilla::PodCopy(copy, chars, length); + ownedChars = copy; + chars = copy; + } + + if (!JS::CanCompileOffThread(cx, options, length)) { + JS_ReportError(cx, "cannot compile code on worker thread"); + return false; + } + + if (!offThreadState.startIfIdle(cx, ownedChars)) { + JS_ReportError(cx, "called offThreadCompileScript without calling runOffThreadScript" + " to receive prior off-thread compilation"); + return false; + } + + if (!JS::CompileOffThread(cx, options, chars, length, + OffThreadCompileScriptCallback, nullptr)) + { + offThreadState.abandon(cx); + return false; + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +runOffThreadScript(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + JSRuntime* rt = cx->runtime(); + if (OffThreadParsingMustWaitForGC(rt)) + gc::AutoFinishGC finishgc(rt); + + void* token = offThreadState.waitUntilDone(cx); + if (!token) { + JS_ReportError(cx, "called runOffThreadScript when no compilation is pending"); + return false; + } + + RootedScript script(cx, JS::FinishOffThreadScript(cx, rt, token)); + if (!script) + return false; + + return JS_ExecuteScript(cx, script, args.rval()); +} +#endif + +#ifdef NOTYET +struct MOZ_RAII FreeOnReturn +{ + JSContext* cx; + const char* ptr; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + + explicit FreeOnReturn(JSContext* cx, const char* ptr = nullptr + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), ptr(ptr) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + void init(const char* ptr) { + MOZ_ASSERT(!this->ptr); + this->ptr = ptr; + } + + ~FreeOnReturn() { + JS_free(cx, (void*)ptr); + } +}; +#endif + +static int sArgc; +static char** sArgv; + +#ifdef NOTYET +class AutoCStringVector +{ + Vector<char*> argv_; + public: + explicit AutoCStringVector(JSContext* cx) : argv_(cx) {} + ~AutoCStringVector() { + for (size_t i = 0; i < argv_.length(); i++) + js_free(argv_[i]); + } + bool append(char* arg) { + if (!argv_.append(arg)) { + js_free(arg); + return false; + } + return true; + } + char* const* get() const { + return argv_.begin(); + } + size_t length() const { + return argv_.length(); + } + char* operator[](size_t i) const { + return argv_[i]; + } + void replace(size_t i, char* arg) { + js_free(argv_[i]); + argv_[i] = arg; + } + char* back() const { + return argv_.back(); + } + void replaceBack(char* arg) { + js_free(argv_.back()); + argv_.back() = arg; + } +}; +#endif + +#if defined(XP_WIN) +static bool +EscapeForShell(AutoCStringVector& argv) +{ + // Windows will break arguments in argv by various spaces, so we wrap each + // argument in quotes and escape quotes within. Even with quotes, \ will be + // treated like an escape character, so inflate each \ to \\. + + for (size_t i = 0; i < argv.length(); i++) { + if (!argv[i]) + continue; + + size_t newLen = 3; // quotes before and after and null-terminator + for (char* p = argv[i]; *p; p++) { + newLen++; + if (*p == '\"' || *p == '\\') + newLen++; + } + + char* escaped = (char*)js_malloc(newLen); + if (!escaped) + return false; + + char* src = argv[i]; + char* dst = escaped; + *dst++ = '\"'; + while (*src) { + if (*src == '\"' || *src == '\\') + *dst++ = '\\'; + *dst++ = *src++; + } + *dst++ = '\"'; + *dst++ = '\0'; + MOZ_ASSERT(escaped + newLen == dst); + + argv.replace(i, escaped); + } + return true; +} +#endif + +#ifdef NOTYET +static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags; +#endif + +#ifdef NOTYET +static bool +PropagateFlagToNestedShells(const char* flag) +{ + return sPropagatedFlags.append(flag); +} +#endif + +#ifdef NOTYET +static bool +NestedShell(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + AutoCStringVector argv(cx); + + // The first argument to the shell is its path, which we assume is our own + // argv[0]. + if (sArgc < 1) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); + return false; + } + if (!argv.append(strdup(sArgv[0]))) + return false; + + // Propagate selected flags from the current shell + for (unsigned i = 0; i < sPropagatedFlags.length(); i++) { + char* cstr = strdup(sPropagatedFlags[i]); + if (!cstr || !argv.append(cstr)) + return false; + } + + // The arguments to nestedShell are stringified and append to argv. + RootedString str(cx); + for (unsigned i = 0; i < args.length(); i++) { + str = ToString(cx, args[i]); + if (!str || !argv.append(JS_EncodeString(cx, str))) + return false; + + // As a special case, if the caller passes "--js-cache", replace that + // with "--js-cache=$(jsCacheDir)" + if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) { + char* newArg = JS_smprintf("--js-cache=%s", jsCacheDir); + if (!newArg) + return false; + argv.replaceBack(newArg); + } + } + + // execv assumes argv is null-terminated + if (!argv.append(nullptr)) + return false; + + int status = 0; +#if defined(XP_WIN) + if (!EscapeForShell(argv)) + return false; + status = _spawnv(_P_WAIT, sArgv[0], argv.get()); +#else + pid_t pid = fork(); + switch (pid) { + case -1: + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); + return false; + case 0: + (void)execv(sArgv[0], argv.get()); + exit(-1); + default: { + while (waitpid(pid, &status, 0) < 0 && errno == EINTR) + continue; + break; + } + } +#endif + + if (status != 0) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL); + return false; + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +DecompileFunction(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1 || !args[0].isObject() || !args[0].toObject().is<JSFunction>()) { + args.rval().setUndefined(); + return true; + } + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); + JSString* result = JS_DecompileFunction(cx, fun, 0); + if (!result) + return false; + args.rval().setString(result); + return true; +} +#endif + +#ifdef NOTYET +static bool +DecompileThisScript(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + NonBuiltinScriptFrameIter iter(cx); + if (iter.done()) { + args.rval().setString(cx->runtime()->emptyString); + return true; + } + + { + JSAutoCompartment ac(cx, iter.script()); + + RootedScript script(cx, iter.script()); + JSString* result = JS_DecompileScript(cx, script, "test", 0); + if (!result) + return false; + + args.rval().setString(result); + } + + return JS_WrapValue(cx, args.rval()); +} +#endif + +#ifdef NOTYET +static bool +ThisFilename(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + JS::AutoFilename filename; + if (!DescribeScriptedCaller(cx, &filename) || !filename.get()) { + args.rval().setString(cx->runtime()->emptyString); + return true; + } + + JSString* str = JS_NewStringCopyZ(cx, filename.get()); + if (!str) + return false; + + args.rval().setString(str); + return true; +} +#endif + +#ifdef NOTYET +static bool +WrapWithProto(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + Value obj = UndefinedValue(), proto = UndefinedValue(); + if (args.length() == 2) { + obj = args[0]; + proto = args[1]; + } + if (!obj.isObject() || !proto.isObjectOrNull()) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, + "wrapWithProto"); + return false; + } + + WrapperOptions options(cx); + options.setProto(proto.toObjectOrNull()); + JSObject* wrapped = Wrapper::New(cx, &obj.toObject(), + &Wrapper::singletonWithPrototype, options); + if (!wrapped) + return false; + + args.rval().setObject(*wrapped); + return true; +} +#endif + +#ifdef NOTYET +static bool +NewGlobal(JSContext* cx, unsigned argc, Value* vp) +{ + JSPrincipals* principals = nullptr; + JS::CompartmentOptions options; + options.setVersion(JSVERSION_DEFAULT); + + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() == 1 && args[0].isObject()) { + RootedObject opts(cx, &args[0].toObject()); + RootedValue v(cx); + + if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) + return false; + if (v.isObject()) + options.setSameZoneAs(UncheckedUnwrap(&v.toObject())); + + if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) + return false; + if (v.isBoolean()) + options.setInvisibleToDebugger(v.toBoolean()); + + if (!JS_GetProperty(cx, opts, "cloneSingletons", &v)) + return false; + if (v.isBoolean()) + options.setCloneSingletons(v.toBoolean()); + + if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v)) + return false; + if (v.isBoolean()) + options.setDisableLazyParsing(v.toBoolean()); + + if (!JS_GetProperty(cx, opts, "principal", &v)) + return false; + if (!v.isUndefined()) { + uint32_t bits; + if (!ToUint32(cx, v, &bits)) + return false; + principals = cx->new_<ShellPrincipals>(bits); + if (!principals) + return false; + JS_HoldPrincipals(principals); + } + } + + RootedObject global(cx, NewGlobalObject(cx, options, principals)); + if (principals) + JS_DropPrincipals(cx->runtime(), principals); + if (!global) + return false; + + if (!JS_WrapObject(cx, &global)) + return false; + + args.rval().setObject(*global); + return true; +} +#endif + +#ifdef NOTYET +static bool +NukeCCW(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1 || !args[0].isObject() || + !IsCrossCompartmentWrapper(&args[0].toObject())) + { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, + "nukeCCW"); + return false; + } + + NukeCrossCompartmentWrapper(cx, &args[0].toObject()); + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setInt32(ARGS_LENGTH_MAX); + return true; +} +#endif + +#ifdef NOTYET +static bool +ObjectEmulatingUndefined(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + static const JSClass cls = { + "ObjectEmulatingUndefined", + JSCLASS_EMULATES_UNDEFINED + }; + + RootedObject obj(cx, JS_NewObject(cx, &cls)); + if (!obj) + return false; + args.rval().setObject(*obj); + return true; +} +#endif + +#ifdef NOTYET +static bool +GetSelfHostedValue(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, + "getSelfHostedValue"); + return false; + } + RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args[0])); + if (!srcAtom) + return false; + RootedPropertyName srcName(cx, srcAtom->asPropertyName()); + return cx->runtime()->cloneSelfHostedValue(cx, srcName, args.rval()); +} +#endif + +#ifdef NOTYET +class ShellSourceHook: public SourceHook { + // The function we should call to lazily retrieve source code. + PersistentRootedFunction fun; + + public: + ShellSourceHook(JSContext* cx, JSFunction& fun) : fun(cx, &fun) {} + + bool load(JSContext* cx, const char* filename, char16_t** src, size_t* length) { + RootedString str(cx, JS_NewStringCopyZ(cx, filename)); + if (!str) + return false; + RootedValue filenameValue(cx, StringValue(str)); + + RootedValue result(cx); + if (!Call(cx, UndefinedHandleValue, fun, HandleValueArray(filenameValue), &result)) + return false; + + str = JS::ToString(cx, result); + if (!str) + return false; + + *length = JS_GetStringLength(str); + *src = cx->pod_malloc<char16_t>(*length); + if (!*src) + return false; + + JSLinearString* linear = str->ensureLinear(cx); + if (!linear) + return false; + + CopyChars(*src, *linear); + return true; + } +}; +#endif + +#ifdef NOTYET +static bool +WithSourceHook(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() != 2) { + ReportUsageError(cx, callee, "Wrong number of arguments."); + return false; + } + + if (!args[0].isObject() || !args[0].toObject().is<JSFunction>() + || !args[1].isObject() || !args[1].toObject().is<JSFunction>()) { + ReportUsageError(cx, callee, "First and second arguments must be functions."); + return false; + } + + UniquePtr<ShellSourceHook> hook = + MakeUnique<ShellSourceHook>(cx, args[0].toObject().as<JSFunction>()); + if (!hook) + return false; + + UniquePtr<SourceHook> savedHook = js::ForgetSourceHook(cx->runtime()); + js::SetSourceHook(cx->runtime(), Move(hook)); + + RootedObject fun(cx, &args[1].toObject()); + bool result = Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(), args.rval()); + js::SetSourceHook(cx->runtime(), Move(savedHook)); + return result; +} +#endif + +static bool +IsCachingEnabled(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setBoolean(jsCachingEnabled && jsCacheAsmJSPath != nullptr); + return true; +} + +#ifdef NOTYET +static bool +SetCachingEnabled(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (GetShellRuntime(cx)->isWorker) { + JS_ReportError(cx, "Caching is not supported in workers"); + return false; + } + + jsCachingEnabled = ToBoolean(args.get(0)); + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static void +PrintProfilerEvents_Callback(const char* msg) +{ + fprintf(stderr, "PROFILER EVENT: %s\n", msg); +} +#endif + +#ifdef NOTYET +static bool +PrintProfilerEvents(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (cx->runtime()->spsProfiler.enabled()) + js::RegisterRuntimeProfilingEventMarker(cx->runtime(), &PrintProfilerEvents_Callback); + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS64) +typedef Vector<char16_t, 0, SystemAllocPolicy> StackChars; +Vector<StackChars, 0, SystemAllocPolicy> stacks; + +static void +SingleStepCallback(void* arg, jit::Simulator* sim, void* pc) +{ + JSRuntime* rt = reinterpret_cast<JSRuntime*>(arg); + + // If profiling is not enabled, don't do anything. + if (!rt->spsProfiler.enabled()) + return; + + JS::ProfilingFrameIterator::RegisterState state; + state.pc = pc; +#if defined(JS_SIMULATOR_ARM) + state.sp = (void*)sim->get_register(jit::Simulator::sp); + state.lr = (void*)sim->get_register(jit::Simulator::lr); +#elif defined(JS_SIMULATOR_MIPS64) + state.sp = (void*)sim->getRegister(jit::Simulator::sp); + state.lr = (void*)sim->getRegister(jit::Simulator::ra); +#endif + + mozilla::DebugOnly<void*> lastStackAddress = nullptr; + StackChars stack; + uint32_t frameNo = 0; + for (JS::ProfilingFrameIterator i(rt, state); !i.done(); ++i) { + MOZ_ASSERT(i.stackAddress() != nullptr); + MOZ_ASSERT(lastStackAddress <= i.stackAddress()); + lastStackAddress = i.stackAddress(); + JS::ProfilingFrameIterator::Frame frames[16]; + uint32_t nframes = i.extractStack(frames, 0, 16); + for (uint32_t i = 0; i < nframes; i++) { + if (frameNo > 0) + stack.append(",", 1); + stack.append(frames[i].label, strlen(frames[i].label)); + frameNo++; + } + } + + // Only append the stack if it differs from the last stack. + if (stacks.empty() || + stacks.back().length() != stack.length() || + !PodEqual(stacks.back().begin(), stack.begin(), stack.length())) + { + stacks.append(Move(stack)); + } +} +#endif +#endif /* NOTYET */ + +#ifdef NOTYET +static bool +EnableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp) +{ +#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS64) + CallArgs args = CallArgsFromVp(argc, vp); + + jit::Simulator* sim = cx->runtime()->simulator(); + sim->enable_single_stepping(SingleStepCallback, cx->runtime()); + + args.rval().setUndefined(); + return true; +#else + JS_ReportError(cx, "single-step profiling not enabled on this platform"); + return false; +#endif +} +#endif + +#ifdef NOTYET +static bool +DisableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp) +{ +#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS64) + CallArgs args = CallArgsFromVp(argc, vp); + + jit::Simulator* sim = cx->runtime()->simulator(); + sim->disable_single_stepping(); + + AutoValueVector elems(cx); + for (size_t i = 0; i < stacks.length(); i++) { + JSString* stack = JS_NewUCStringCopyN(cx, stacks[i].begin(), stacks[i].length()); + if (!stack) + return false; + if (!elems.append(StringValue(stack))) + return false; + } + + JSObject* array = JS_NewArrayObject(cx, elems); + if (!array) + return false; + + stacks.clear(); + args.rval().setObject(*array); + return true; +#else + JS_ReportError(cx, "single-step profiling not enabled on this platform"); + return false; +#endif +} +#endif + +#ifdef NOTYET +static bool +IsLatin1(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + bool isLatin1 = args.get(0).isString() && args[0].toString()->hasLatin1Chars(); + args.rval().setBoolean(isLatin1); + return true; +} +#endif + +// Global mailbox that is used to communicate a SharedArrayBuffer +// value from one worker to another. +// +// For simplicity we store only the SharedArrayRawBuffer; retaining +// the SAB object would require per-runtime storage, and would have no +// real benefits. +// +// Invariant: when a SARB is in the mailbox its reference count is at +// least 1, accounting for the reference from the mailbox. +// +// The lock guards the mailbox variable and prevents a race where two +// workers try to set the mailbox at the same time to replace a SARB +// that is only referenced from the mailbox: the workers will both +// decrement the reference count on the old SARB, and one of those +// decrements will be on a garbage object. We could implement this +// with atomics and a CAS loop but it's not worth the bother. + +#ifdef NOTYET +static PRLock* sharedArrayBufferMailboxLock; +static SharedArrayRawBuffer* sharedArrayBufferMailbox; +#endif + +#ifdef NOTYET +static bool +InitSharedArrayBufferMailbox() +{ + sharedArrayBufferMailboxLock = PR_NewLock(); + return sharedArrayBufferMailboxLock != nullptr; +} +#endif + +#ifdef NOTYET +static void +DestructSharedArrayBufferMailbox() +{ + // All workers need to have terminated at this point. + if (sharedArrayBufferMailbox) + sharedArrayBufferMailbox->dropReference(); + PR_DestroyLock(sharedArrayBufferMailboxLock); +} +#endif + +#ifdef NOTYET +static bool +GetSharedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + JSObject* newObj = nullptr; + bool rval = true; + + PR_Lock(sharedArrayBufferMailboxLock); + SharedArrayRawBuffer* buf = sharedArrayBufferMailbox; + if (buf) { + buf->addReference(); + newObj = SharedArrayBufferObject::New(cx, buf); + if (!newObj) { + buf->dropReference(); + rval = false; + } + } + PR_Unlock(sharedArrayBufferMailboxLock); + + args.rval().setObjectOrNull(newObj); + return rval; +} +#endif + +#ifdef NOTYET +static bool +SetSharedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + SharedArrayRawBuffer* newBuffer = nullptr; + + if (argc == 0 || args.get(0).isNullOrUndefined()) { + // Clear out the mailbox + } + else if (args.get(0).isObject() && args[0].toObject().is<SharedArrayBufferObject>()) { + newBuffer = args[0].toObject().as<SharedArrayBufferObject>().rawBufferObject(); + newBuffer->addReference(); + } else { + JS_ReportError(cx, "Only a SharedArrayBuffer can be installed in the global mailbox"); + return false; + } + + PR_Lock(sharedArrayBufferMailboxLock); + SharedArrayRawBuffer* oldBuffer = sharedArrayBufferMailbox; + if (oldBuffer) + oldBuffer->dropReference(); + sharedArrayBufferMailbox = newBuffer; + PR_Unlock(sharedArrayBufferMailboxLock); + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +class SprintOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp +{ + Sprinter* sp; + bool startedTypes_; + + public: + explicit SprintOptimizationTypeInfoOp(Sprinter* sp) + : sp(sp), + startedTypes_(false) + { } + + void readType(const char* keyedBy, const char* name, + const char* location, Maybe<unsigned> lineno) override + { + if (!startedTypes_) { + startedTypes_ = true; + Sprint(sp, "{\"typeset\": ["); + } + Sprint(sp, "{\"keyedBy\":\"%s\"", keyedBy); + if (name) + Sprint(sp, ",\"name\":\"%s\"", name); + if (location) { + char buf[512]; + PutEscapedString(buf, mozilla::ArrayLength(buf), location, strlen(location), '"'); + Sprint(sp, ",\"location\":%s", buf); + } + if (lineno.isSome()) + Sprint(sp, ",\"line\":%u", *lineno); + Sprint(sp, "},"); + } + + void operator()(JS::TrackedTypeSite site, const char* mirType) override { + if (startedTypes_) { + // Clear trailing , + if ((*sp)[sp->getOffset() - 1] == ',') + (*sp)[sp->getOffset() - 1] = ' '; + Sprint(sp, "],"); + startedTypes_ = false; + } else { + Sprint(sp, "{"); + } + + Sprint(sp, "\"site\":\"%s\",\"mirType\":\"%s\"},", + TrackedTypeSiteString(site), mirType); + } +}; +#endif + +#ifdef NOTYET +class SprintOptimizationAttemptsOp : public JS::ForEachTrackedOptimizationAttemptOp +{ + Sprinter* sp; + + public: + explicit SprintOptimizationAttemptsOp(Sprinter* sp) + : sp(sp) + { } + + void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override { + Sprint(sp, "{\"strategy\":\"%s\",\"outcome\":\"%s\"},", + TrackedStrategyString(strategy), TrackedOutcomeString(outcome)); + } +}; +#endif + +#ifdef NOTYET +static bool +ReflectTrackedOptimizations(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + JSRuntime* rt = cx->runtime(); + + if (!rt->hasJitRuntime() || !rt->jitRuntime()->isOptimizationTrackingEnabled(rt)) { + JS_ReportError(cx, "Optimization tracking is off."); + return false; + } + + if (args.length() != 1) { + ReportUsageError(cx, callee, "Wrong number of arguments"); + return false; + } + + if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { + ReportUsageError(cx, callee, "Argument must be a function"); + return false; + } + + RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); + if (!fun->hasScript() || !fun->nonLazyScript()->hasIonScript()) { + args.rval().setNull(); + return true; + } + + // Suppress GC for the unrooted JitcodeGlobalEntry below. + gc::AutoSuppressGC suppress(cx); + + jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); + jit::JitcodeGlobalEntry entry; + jit::IonScript* ion = fun->nonLazyScript()->ionScript(); + table->lookupInfallible(ion->method()->raw(), &entry, rt); + + if (!entry.hasTrackedOptimizations()) { + JSObject* obj = JS_NewPlainObject(cx); + if (!obj) + return false; + args.rval().setObject(*obj); + return true; + } + + Sprinter sp(cx); + if (!sp.init()) + return false; + + const jit::IonTrackedOptimizationsRegionTable* regions = + entry.ionEntry().trackedOptimizationsRegionTable(); + + Sprint(&sp, "{\"regions\": ["); + for (uint32_t i = 0; i < regions->numEntries(); i++) { + jit::IonTrackedOptimizationsRegion region = regions->entry(i); + jit::IonTrackedOptimizationsRegion::RangeIterator iter = region.ranges(); + while (iter.more()) { + uint32_t startOffset, endOffset; + uint8_t index; + iter.readNext(&startOffset, &endOffset, &index); + JSScript* script; + jsbytecode* pc; + // Use endOffset, as startOffset may be associated with a + // previous, adjacent region ending exactly at startOffset. That + // is, suppose we have two regions [0, startOffset], [startOffset, + // endOffset]. Since we are not querying a return address, we want + // the second region and not the first. + uint8_t* addr = ion->method()->raw() + endOffset; + entry.youngestFrameLocationAtAddr(rt, addr, &script, &pc); + Sprint(&sp, "{\"location\":\"%s:%u\",\"offset\":%u,\"index\":%u}%s", + script->filename(), script->lineno(), script->pcToOffset(pc), index, + iter.more() ? "," : ""); + } + } + Sprint(&sp, "],"); + + Sprint(&sp, "\"opts\": ["); + for (uint8_t i = 0; i < entry.ionEntry().numOptimizationAttempts(); i++) { + Sprint(&sp, "%s{\"typeinfo\":[", i == 0 ? "" : ","); + SprintOptimizationTypeInfoOp top(&sp); + jit::IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(top); + entry.trackedOptimizationTypeInfo(i).forEach(adapter, entry.allTrackedTypes()); + // Clear the trailing , + if (sp[sp.getOffset() - 1] == ',') + sp[sp.getOffset() - 1] = ' '; + Sprint(&sp, "],\"attempts\":["); + SprintOptimizationAttemptsOp aop(&sp); + entry.trackedOptimizationAttempts(i).forEach(aop); + // Clear the trailing , + if (sp[sp.getOffset() - 1] == ',') + sp[sp.getOffset() - 1] = ' '; + Sprint(&sp, "]}"); + } + Sprint(&sp, "]}"); + + if (sp.hadOutOfMemory()) + return false; + + RootedString str(cx, JS_NewStringCopyZ(cx, sp.string())); + if (!str) + return false; + RootedValue jsonVal(cx); + if (!JS_ParseJSON(cx, str, &jsonVal)) + return false; + + args.rval().set(jsonVal); + return true; +} +#endif + +#ifdef NOTYET +#ifdef DEBUG +static bool +DumpStaticScopeChain(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() != 1) { + ReportUsageError(cx, callee, "Wrong number of arguments"); + return false; + } + + if (!args[0].isObject() || + !(args[0].toObject().is<JSFunction>() || args[0].toObject().is<ModuleObject>())) + { + ReportUsageError(cx, callee, "Argument must be an interpreted function or a module"); + return false; + } + + RootedObject obj(cx, &args[0].toObject()); + RootedScript script(cx); + + if (obj->is<JSFunction>()) { + RootedFunction fun(cx, &obj->as<JSFunction>()); + if (!fun->isInterpreted()) { + ReportUsageError(cx, callee, "Argument must be an interpreted function"); + return false; + } + script = fun->getOrCreateScript(cx); + } else { + script = obj->as<ModuleObject>().script(); + } + + js::DumpStaticScopeChain(script); + + args.rval().setUndefined(); + return true; +} +#endif +#endif /* NOTYET */ + +#ifdef NOTYET +namespace js { +namespace shell { + +class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor { + Vector<UniqueChars, 1, js::SystemAllocPolicy> log; + bool oom; + bool enteredWithoutExit; + + public: + explicit ShellAutoEntryMonitor(JSContext *cx) + : AutoEntryMonitor(cx), + oom(false), + enteredWithoutExit(false) + { } + + ~ShellAutoEntryMonitor() { + MOZ_ASSERT(!enteredWithoutExit); + } + + void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack, + JS::HandleString asyncCause) override { + MOZ_ASSERT(!enteredWithoutExit); + enteredWithoutExit = true; + + RootedString displayId(cx, JS_GetFunctionDisplayId(function)); + if (displayId) { + UniqueChars displayIdStr(JS_EncodeStringToUTF8(cx, displayId)); + oom = !displayIdStr || !log.append(mozilla::Move(displayIdStr)); + return; + } + + oom = !log.append(make_string_copy("anonymous")); + } + + void Entry(JSContext* cx, JSScript* script, JS::HandleValue asyncStack, + JS::HandleString asyncCause) override { + MOZ_ASSERT(!enteredWithoutExit); + enteredWithoutExit = true; + + UniqueChars label(JS_smprintf("eval:%s", JS_GetScriptFilename(script))); + oom = !label || !log.append(mozilla::Move(label)); + } + + void Exit(JSContext* cx) override { + MOZ_ASSERT(enteredWithoutExit); + enteredWithoutExit = false; + } + + bool buildResult(JSContext *cx, MutableHandleValue resultValue) { + if (oom) { + JS_ReportOutOfMemory(cx); + return false; + } + + RootedObject result(cx, JS_NewArrayObject(cx, log.length())); + if (!result) + return false; + + for (size_t i = 0; i < log.length(); i++) { + char *name = log[i].get(); + RootedString string(cx, Atomize(cx, name, strlen(name))); + if (!string) + return false; + RootedValue value(cx, StringValue(string)); + if (!JS_SetElement(cx, result, i, value)) + return false; + } + + resultValue.setObject(*result.get()); + return true; + } +}; + +} // namespace shell +} // namespace js +#endif + +#ifdef NOTYET +static bool +EntryPoints(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 1) { + JS_ReportError(cx, "Wrong number of arguments"); + return false; + } + + RootedObject opts(cx, ToObject(cx, args[0])); + if (!opts) + return false; + + // { function: f } --- Call f. + { + RootedValue fun(cx), dummy(cx); + + if (!JS_GetProperty(cx, opts, "function", &fun)) + return false; + if (!fun.isUndefined()) { + js::shell::ShellAutoEntryMonitor sarep(cx); + if (!Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(), &dummy)) + return false; + return sarep.buildResult(cx, args.rval()); + } + } + + // { object: o, property: p, value: v } --- Fetch o[p], or if + // v is present, assign o[p] = v. + { + RootedValue objectv(cx), propv(cx), valuev(cx); + + if (!JS_GetProperty(cx, opts, "object", &objectv) || + !JS_GetProperty(cx, opts, "property", &propv)) + return false; + if (!objectv.isUndefined() && !propv.isUndefined()) { + RootedObject object(cx, ToObject(cx, objectv)); + if (!object) + return false; + + RootedString string(cx, ToString(cx, propv)); + if (!string) + return false; + RootedId id(cx); + if (!JS_StringToId(cx, string, &id)) + return false; + + if (!JS_GetProperty(cx, opts, "value", &valuev)) + return false; + + js::shell::ShellAutoEntryMonitor sarep(cx); + + if (!valuev.isUndefined()) { + if (!JS_SetPropertyById(cx, object, id, valuev)) + return false; + } else { + if (!JS_GetPropertyById(cx, object, id, &valuev)) + return false; + } + + return sarep.buildResult(cx, args.rval()); + } + } + + // { ToString: v } --- Apply JS::ToString to v. + { + RootedValue v(cx); + + if (!JS_GetProperty(cx, opts, "ToString", &v)) + return false; + if (!v.isUndefined()) { + js::shell::ShellAutoEntryMonitor sarep(cx); + if (!JS::ToString(cx, v)) + return false; + return sarep.buildResult(cx, args.rval()); + } + } + + // { ToNumber: v } --- Apply JS::ToNumber to v. + { + RootedValue v(cx); + double dummy; + + if (!JS_GetProperty(cx, opts, "ToNumber", &v)) + return false; + if (!v.isUndefined()) { + js::shell::ShellAutoEntryMonitor sarep(cx); + if (!JS::ToNumber(cx, v, &dummy)) + return false; + return sarep.buildResult(cx, args.rval()); + } + } + + // { eval: code } --- Apply ToString and then Evaluate to code. + { + RootedValue code(cx), dummy(cx); + + if (!JS_GetProperty(cx, opts, "eval", &code)) + return false; + if (!code.isUndefined()) { + RootedString codeString(cx, ToString(cx, code)); + if (!codeString || !codeString->ensureFlat(cx)) + return false; + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, codeString)) + return false; + const char16_t* chars = stableChars.twoByteRange().start().get(); + size_t length = codeString->length(); + + CompileOptions options(cx); + options.setIntroductionType("entryPoint eval") + .setFileAndLine("entryPoint eval", 1); + + js::shell::ShellAutoEntryMonitor sarep(cx); + if (!JS::Evaluate(cx, options, chars, length, &dummy)) + return false; + return sarep.buildResult(cx, args.rval()); + } + } + + JS_ReportError(cx, "bad 'params' object"); + return false; +} +#endif + + +static const JSFunctionSpec global_functions[] = { + JS_FN("version", Version, 0,0), + JS_FN("options", Options, 0,0), + JS_FN("load", Load, 1,0), + JS_FN("loadRelativeToScript", LoadScriptRelativeToScript, 1,0), +#ifdef NOTYET + JS_FN("evaluate", Evaluate, 2,0), + JS_FN("run", Run, 1,0), + JS_FN("readline", ReadLine, 0,0), +#endif /* NOTYET */ + JS_FN("print", Print, 0,0), + JS_FN("printErr", PrintErr, 0,0), + JS_FN("putstr", PutStr, 0,0), + JS_FN("dateNow", Now, 0,0), +#ifdef NOTYET + JS_FN("help", Help, 0,0), +#endif /* NOTYET */ + JS_FN("quit", Quit, 0,0), + JS_FN("assertEq", AssertEq, 2,0), +#ifdef NOTYET + JS_FN("startTimingMutator", StartTimingMutator, 0,0), + JS_FN("stopTimingMutator", StopTimingMutator, 0,0), +#endif /* NOTYET */ + JS_FN("throwError", ThrowError, 0,0), +#ifdef NOTYET +#ifdef DEBUG + JS_FN("disassemble", DisassembleToString, 1,0), + JS_FN("dis", Disassemble, 1,0), + JS_FN("disfile", DisassFile, 1,0), + JS_FN("dissrc", DisassWithSrc, 1,0), + JS_FN("notes", Notes, 1,0), + JS_FN("stackDump", StackDump, 3,0), +#endif /* DEBUG */ + JS_FN("intern", Intern, 1,0), + JS_FN("getslx", GetSLX, 1,0), + JS_FN("evalcx", EvalInContext, 1,0), + JS_FN("evalInWorker", EvalInWorker, 1,0), + JS_FN("getSharedArrayBuffer", GetSharedArrayBuffer, 0,0), + JS_FN("setSharedArrayBuffer", SetSharedArrayBuffer, 0,0), JS_FN("shapeOf", ShapeOf, 1,0), #ifdef DEBUG JS_FN("arrayInfo", ArrayInfo, 1,0), @@ -2221,9 +6023,13 @@ JS_FN("createMappedArrayBuffer", CreateMappedArrayBuffer, 1,0), JS_FN("getMaxArgs", GetMaxArgs, 0,0), JS_FN("objectEmulatingUndefined", ObjectEmulatingUndefined, 0,0), +#endif /* NOTYET */ JS_FN("isCachingEnabled", IsCachingEnabled, 0,0), +#ifdef NOTYET JS_FN("setCachingEnabled", SetCachingEnabled, 1,0), +#endif /* NOTYET */ JS_FN("cacheEntry", CacheEntry, 1,0), +#ifdef NOTYET JS_FN("printProfilerEvents", PrintProfilerEvents, 0,0), JS_FN("enableSingleStepProfiling", EnableSingleStepProfiling, 0,0), JS_FN("disableSingleStepProfiling", DisableSingleStepProfiling, 0,0), @@ -2630,6 +6436,280 @@ #endif /* NOTYET */ /*==============================================================*/ +#ifdef NOTYET +static const JSFunctionSpecWithHelp console_functions[] = { + JS_FN_HELP("log", Print, 0, 0, +"log([exp ...])", +" Evaluate and print expressions to stdout.\n" +" This function is an alias of the print() function."), + JS_FS_HELP_END +}; +#endif + +#ifdef NOTYET +bool +DefineConsole(JSContext* cx, HandleObject global) +{ + RootedObject obj(cx, JS_NewPlainObject(cx)); + return obj && + JS_DefineFunctionsWithHelp(cx, obj, console_functions) && + JS_DefineProperty(cx, global, "console", obj, 0); +} +#endif + +#ifdef NOTYET +#ifdef MOZ_PROFILING +# define PROFILING_FUNCTION_COUNT 5 +# ifdef MOZ_CALLGRIND +# define CALLGRIND_FUNCTION_COUNT 3 +# else +# define CALLGRIND_FUNCTION_COUNT 0 +# endif +# ifdef MOZ_VTUNE +# define VTUNE_FUNCTION_COUNT 4 +# else +# define VTUNE_FUNCTION_COUNT 0 +# endif +# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT) +#else +# define EXTERNAL_FUNCTION_COUNT 0 +#endif + +#undef PROFILING_FUNCTION_COUNT +#undef CALLGRIND_FUNCTION_COUNT +#undef VTUNE_FUNCTION_COUNT +#undef EXTERNAL_FUNCTION_COUNT +#endif /* NOTYET */ + +#ifdef NOTYET +static bool +PrintHelpString(JSContext* cx, Value v) +{ + JSString* str = v.toString(); + + JSLinearString* linear = str->ensureLinear(cx); + if (!linear) + return false; + + JS::AutoCheckCannotGC nogc; + if (linear->hasLatin1Chars()) { + for (const Latin1Char* p = linear->latin1Chars(nogc); *p; p++) + fprintf(gOutFile, "%c", char(*p)); + } else { + for (const char16_t* p = linear->twoByteChars(nogc); *p; p++) + fprintf(gOutFile, "%c", char(*p)); + } + fprintf(gOutFile, "\n"); + + return true; +} +#endif + +#ifdef NOTYET +static bool +PrintHelp(JSContext* cx, HandleObject obj) +{ + RootedValue usage(cx); + if (!JS_GetProperty(cx, obj, "usage", &usage)) + return false; + RootedValue help(cx); + if (!JS_GetProperty(cx, obj, "help", &help)) + return false; + + if (usage.isUndefined() || help.isUndefined()) + return true; + + return PrintHelpString(cx, usage) && PrintHelpString(cx, help); +} +#endif + +#ifdef NOTYET +static bool +Help(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); + + RootedObject obj(cx); + if (args.length() == 0) { + RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + Rooted<IdVector> ida(cx, IdVector(cx)); + if (!JS_Enumerate(cx, global, &ida)) + return false; + + for (size_t i = 0; i < ida.length(); i++) { + RootedValue v(cx); + RootedId id(cx, ida[i]); + if (!JS_GetPropertyById(cx, global, id, &v)) + return false; + if (v.isPrimitive()) { + JS_ReportError(cx, "primitive arg"); + return false; + } + obj = v.toObjectOrNull(); + if (!PrintHelp(cx, obj)) + return false; + } + } else { + for (unsigned i = 0; i < args.length(); i++) { + if (args[i].isPrimitive()) { + JS_ReportError(cx, "primitive arg"); + return false; + } + obj = args[i].toObjectOrNull(); + if (!PrintHelp(cx, obj)) + return false; + } + } + + args.rval().setUndefined(); + return true; +} +#endif + +#ifdef NOTYET +static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = { +#define MSG_DEF(name, count, exception, format) \ + { format, count, JSEXN_ERR } , +#include "jsshell.msg" +#undef MSG_DEF +}; +#endif + +#ifdef NOTYET +const JSErrorFormatString* +js::shell::my_GetErrorMessage(void* userRef, const unsigned errorNumber) +{ + if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) + return nullptr; + + return &jsShell_ErrorFormatString[errorNumber]; +} +#endif + +#ifdef NOTYET +static bool +CreateLastWarningObject(JSContext* cx, JSErrorReport* report) +{ + RootedObject warningObj(cx, JS_NewObject(cx, nullptr)); + if (!warningObj) + return false; + + RootedString nameStr(cx); + if (report->exnType == JSEXN_NONE) + nameStr = JS_NewStringCopyZ(cx, "None"); + else + nameStr = GetErrorTypeName(cx->runtime(), report->exnType); + if (!nameStr) + return false; + RootedValue nameVal(cx, StringValue(nameStr)); + if (!DefineProperty(cx, warningObj, cx->names().name, nameVal)) + return false; + + RootedString messageStr(cx, JS_NewUCStringCopyZ(cx, report->ucmessage)); + if (!messageStr) + return false; + RootedValue messageVal(cx, StringValue(messageStr)); + if (!DefineProperty(cx, warningObj, cx->names().message, messageVal)) + return false; + + RootedValue linenoVal(cx, Int32Value(report->lineno)); + if (!DefineProperty(cx, warningObj, cx->names().lineNumber, linenoVal)) + return false; + + RootedValue columnVal(cx, Int32Value(report->column)); + if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal)) + return false; + + GetShellRuntime(cx)->lastWarning.setObject(*warningObj); + return true; +} +#endif + +#ifdef NOTYET +static bool +PrintStackTrace(JSContext* cx, HandleValue exn) +{ + if (!exn.isObject()) + return false; + + Maybe<JSAutoCompartment> ac; + RootedObject exnObj(cx, &exn.toObject()); + if (IsCrossCompartmentWrapper(exnObj)) { + exnObj = UncheckedUnwrap(exnObj); + ac.emplace(cx, exnObj); + } + + // Ignore non-ErrorObject thrown by |throw| statement. + if (!exnObj->is<ErrorObject>()) + return true; + + // Exceptions thrown while compiling top-level script have no stack. + RootedObject stackObj(cx, exnObj->as<ErrorObject>().stack()); + if (!stackObj) + return true; + + RootedString stackStr(cx); + if (!BuildStackString(cx, stackObj, &stackStr, 2)) + return false; + + UniquePtr<char[], JS::FreePolicy> stack(JS_EncodeStringToUTF8(cx, stackStr)); + if (!stack) + return false; + + fputs("Stack:\n", gErrFile); + fputs(stack.get(), gErrFile); + + return true; +} +#endif + + +/*==============================================================*/ +static bool +global_enumerate(JSContext* cx, HandleObject obj) +{ +#ifdef LAZY_STANDARD_CLASSES + return JS_EnumerateStandardClasses(cx, obj); +#else + return true; +#endif +} + +static bool +global_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) +{ +#ifdef LAZY_STANDARD_CLASSES + if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) + return false; +#endif + return true; +} + +static bool +global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj) +{ + return JS_MayResolveStandardClass(names, id, maybeObj); +} + +static JSClass global_class = { + "global", + JSCLASS_GLOBAL_FLAGS, + nullptr, + nullptr, + nullptr, + nullptr, + global_enumerate, + global_resolve, + global_mayResolve, + nullptr, + nullptr, + nullptr, + nullptr, + JS_GlobalObjectTraceHook +}; + +/*==============================================================*/ static JSObject* NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options, JSPrincipals* principals) @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjss.inp ============================================================================ $ cvs diff -u -r1.1.2.4 -r1.1.2.5 rpmjss.inp --- rpm/rpmio/rpmjss.inp 28 Jun 2017 09:54:15 -0000 1.1.2.4 +++ rpm/rpmio/rpmjss.inp 2 Jul 2017 12:34:29 -0000 1.1.2.5 @@ -1,3 +1,4 @@ +'use strict' 'hello'+'world, it is '+new Date() Math.random() "string" @@ -10,9 +11,70 @@ version() options("strict") options() -dateNow() +load("js/hello.js") +loadRelativeToScript("js/hello.js") +//evaluate("evaluate") +run("js/hello.js") +//var foo = readline() print("foo") var foo = "bar" print(foo) var foo = "print"; print(foo) -load("js/hello.js") +var foo = "printErr"; printErr(foo) +var foo = "putstr"; putstr(foo) +dateNow() +//help +var foo = 0; assertEq(foo, foo) +//startTimingMutator +//stopTimingMutator +throwError +//disassemble +//dis +//disfile +//dissrc +//notes +//stackDump +//intern +//getslx +//evalcx +//evalInWorker +//getSharedArrayBuffer +//setSharedArrayBuffer +//shapeOf +//arrayInfo +//sleep +//compile +//parseModule +//setModuleResolveHook +//getModuleLoadPath +//parse +//syntaxParse +//offThreadCompileScript +//runOffThreadScript +//timeout +//interruptIf +//invokeInterruptCallback +//setInterruptCallback +//enableLastWarnong +//disableLastWarnong +//clearLastWarnong +//elapsed +//decompileFunction +//decompileThis +//thisFilename +//newGlobal +//nukeCCW +createMappedArrayBuffer("js/createMappedArrayBuffer") +//getMaxArgs +//objectEmulatingUndefined +isCachingEnabled() +//setCachingEnabled +cacheEntry("cacheEntry") +//printProfilerEvents +//enableSingleStepProfiling +//disableSingleStepProfiling +isLatin1("isLatin1") +//stackPointerInfo +//entryPoints +//require +quit @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org