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: 24-Jun-2017 21:09:35 Branch: rpm-5_4 Handle: 2017062419093401 Modified files: (Branch: rpm-5_4) rpm/rpmio rpmjs17.cpp rpmjs185.cpp rpmjs24.cpp rpmjs31.cpp rpmjs38.cpp rpmjs45.cpp rpmjss.h Log: - rpmjss: return the script result. Summary: Revision Changes Path 1.1.2.6 +11 -6 rpm/rpmio/rpmjs17.cpp 1.1.2.6 +11 -6 rpm/rpmio/rpmjs185.cpp 1.1.2.7 +11 -6 rpm/rpmio/rpmjs24.cpp 1.1.2.7 +11 -6 rpm/rpmio/rpmjs31.cpp 1.1.2.7 +11 -6 rpm/rpmio/rpmjs38.cpp 1.1.2.8 +1528 -16 rpm/rpmio/rpmjs45.cpp 1.1.2.2 +2 -1 rpm/rpmio/rpmjss.h ____________________________________________________________________________ patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs17.cpp ============================================================================ $ cvs diff -u -r1.1.2.5 -r1.1.2.6 rpmjs17.cpp --- rpm/rpmio/rpmjs17.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.5 +++ rpm/rpmio/rpmjs17.cpp 24 Jun 2017 19:09:34 -0000 1.1.2.6 @@ -484,8 +484,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * filename, int lineno) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -500,15 +500,17 @@ JSAutoCompartment ac(I->cx, I->global); ok = JS_EvaluateScript(I->cx, I->global, - script, strlen(script), filename, lineno, + script, strlen(script), fn, ln, rval.address()); if (!ok) return 1; } JSString *str = rval.toString(); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -530,7 +532,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs185.cpp ============================================================================ $ cvs diff -u -r1.1.2.5 -r1.1.2.6 rpmjs185.cpp --- rpm/rpmio/rpmjs185.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.5 +++ rpm/rpmio/rpmjs185.cpp 24 Jun 2017 19:09:35 -0000 1.1.2.6 @@ -466,8 +466,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * filename, int lineno) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -488,15 +488,17 @@ #endif ok = JS_EvaluateScript(I->cx, I->global, - script, strlen(script), filename, lineno, + script, strlen(script), fn, ln, &rval); if (!ok) return 1; } JSString *str = JS_ValueToString(I->cx, rval); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -518,7 +520,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs24.cpp ============================================================================ $ cvs diff -u -r1.1.2.6 -r1.1.2.7 rpmjs24.cpp --- rpm/rpmio/rpmjs24.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.6 +++ rpm/rpmio/rpmjs24.cpp 24 Jun 2017 19:09:35 -0000 1.1.2.7 @@ -103,8 +103,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * filename, int lineno) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -133,15 +133,17 @@ JS_InitStandardClasses(I->cx, global); ok = JS_EvaluateScript(I->cx, global, - script, strlen(script), filename, lineno, + script, strlen(script), fn, ln, rval.address()); if (!ok) return 1; } JSString *str = rval.toString(); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -163,7 +165,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs31.cpp ============================================================================ $ cvs diff -u -r1.1.2.6 -r1.1.2.7 rpmjs31.cpp --- rpm/rpmio/rpmjs31.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.6 +++ rpm/rpmio/rpmjs31.cpp 24 Jun 2017 19:09:35 -0000 1.1.2.7 @@ -101,8 +101,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * filename, int lineno) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -130,15 +130,17 @@ JS_InitStandardClasses(I->cx, global); ok = JS_EvaluateScript(I->cx, global, - script, strlen(script), filename, lineno, + script, strlen(script), fn, ln, &rval); if (!ok) return 1; } JSString *str = rval.toString(); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -160,7 +162,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs38.cpp ============================================================================ $ cvs diff -u -r1.1.2.6 -r1.1.2.7 rpmjs38.cpp --- rpm/rpmio/rpmjs38.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.6 +++ rpm/rpmio/rpmjs38.cpp 24 Jun 2017 19:09:35 -0000 1.1.2.7 @@ -101,8 +101,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * filename, int lineno) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -130,15 +130,17 @@ JS_InitStandardClasses(I->cx, global); JS::CompileOptions opts(I->cx); - opts.setFileAndLine(filename, lineno); + opts.setFileAndLine(fn, ln); ok = JS::Evaluate(I->cx, global, opts, script, strlen(script), &rval); if (!ok) return 1; } JSString *str = rval.toString(); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -160,7 +162,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjs45.cpp ============================================================================ $ cvs diff -u -r1.1.2.7 -r1.1.2.8 rpmjs45.cpp --- rpm/rpmio/rpmjs45.cpp 23 Jun 2017 06:51:22 -0000 1.1.2.7 +++ rpm/rpmio/rpmjs45.cpp 24 Jun 2017 19:09:35 -0000 1.1.2.8 @@ -200,20 +200,1527 @@ } #endif /* NOTYET */ +static FILE* gErrFile = stderr; +static FILE* gOutFile = stdout; + +#ifdef NOTYET +static bool +Evaluate(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() < 1 || args.length() > 2) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, + "evaluate"); + return false; + } + + RootedString code(cx, nullptr); + RootedObject cacheEntry(cx, nullptr); + if (args[0].isString()) { + code = args[0].toString(); + } else if (args[0].isObject() && CacheEntry_isCacheEntry(&args[0].toObject())) { + cacheEntry = &args[0].toObject(); + code = CacheEntry_getSource(cacheEntry); + } + + if (!code || (args.length() == 2 && args[1].isPrimitive())) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate"); + return false; + } + + CompileOptions options(cx); + JSAutoByteString fileNameBytes; + bool newContext = false; + RootedString displayURL(cx); + RootedString sourceMapURL(cx); + RootedObject global(cx, nullptr); + bool catchTermination = false; + bool saveFrameChain = false; + bool loadBytecode = false; + bool saveBytecode = false; + bool assertEqBytecode = false; + RootedObject callerGlobal(cx, cx->global()); + + options.setIntroductionType("js shell evaluate") + .setFileAndLine("@evaluate", 1); + + global = JS_GetGlobalForObject(cx, &args.callee()); + if (!global) + return false; + + if (args.length() == 2) { + RootedObject opts(cx, &args[1].toObject()); + RootedValue v(cx); + + if (!ParseCompileOptions(cx, options, opts, fileNameBytes)) + return false; + + if (!JS_GetProperty(cx, opts, "newContext", &v)) + return false; + if (!v.isUndefined()) + newContext = ToBoolean(v); + + if (!JS_GetProperty(cx, opts, "displayURL", &v)) + return false; + if (!v.isUndefined()) { + displayURL = ToString(cx, v); + if (!displayURL) + return false; + } + + if (!JS_GetProperty(cx, opts, "sourceMapURL", &v)) + return false; + if (!v.isUndefined()) { + sourceMapURL = ToString(cx, v); + if (!sourceMapURL) + return false; + } + + if (!JS_GetProperty(cx, opts, "global", &v)) + return false; + if (!v.isUndefined()) { + if (v.isObject()) { + global = js::UncheckedUnwrap(&v.toObject()); + if (!global) + return false; + } + if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, + "\"global\" passed to evaluate()", "not a global object"); + return false; + } + } + + if (!JS_GetProperty(cx, opts, "catchTermination", &v)) + return false; + if (!v.isUndefined()) + catchTermination = ToBoolean(v); + + if (!JS_GetProperty(cx, opts, "saveFrameChain", &v)) + return false; + if (!v.isUndefined()) + saveFrameChain = ToBoolean(v); + + if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) + return false; + if (!v.isUndefined()) + loadBytecode = ToBoolean(v); + + if (!JS_GetProperty(cx, opts, "saveBytecode", &v)) + return false; + if (!v.isUndefined()) + saveBytecode = ToBoolean(v); + + if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) + return false; + if (!v.isUndefined()) + assertEqBytecode = ToBoolean(v); + + // We cannot load or save the bytecode if we have no object where the + // bytecode cache is stored. + if (loadBytecode || saveBytecode) { + if (!cacheEntry) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, + "evaluate"); + return false; + } + } + } + + AutoStableStringChars codeChars(cx); + if (!codeChars.initTwoByte(cx, code)) + return false; + + AutoNewContext ancx; + if (newContext) { + if (!ancx.enter(cx)) + return false; + cx = ancx.get(); + } + + uint32_t loadLength = 0; + uint8_t* loadBuffer = nullptr; + uint32_t saveLength = 0; + ScopedJSFreePtr<uint8_t> saveBuffer; + + if (loadBytecode) { + loadBuffer = CacheEntry_getBytecode(cacheEntry, &loadLength); + if (!loadBuffer) + return false; + } + + { + AutoSaveFrameChain asfc(cx); + if (saveFrameChain && !asfc.save()) + return false; + + JSAutoCompartment ac(cx, global); + RootedScript script(cx); + + { + if (saveBytecode) { + if (!JS::CompartmentOptionsRef(cx).cloneSingletons()) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_CACHE_SINGLETON_FAILED); + return false; + } + + // cloneSingletons implies that singletons are used as template objects. + MOZ_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); + } + + if (loadBytecode) { + script = JS_DecodeScript(cx, loadBuffer, loadLength); + } else { + mozilla::Range<const char16_t> chars = codeChars.twoByteRange(); + (void) JS::Compile(cx, options, chars.start().get(), chars.length(), &script); + } + + if (!script) + return false; + } + + if (displayURL && !script->scriptSource()->hasDisplayURL()) { + JSFlatString* flat = displayURL->ensureFlat(cx); + if (!flat) + return false; + + AutoStableStringChars chars(cx); + if (!chars.initTwoByte(cx, flat)) + return false; + + const char16_t* durl = chars.twoByteRange().start().get(); + if (!script->scriptSource()->setDisplayURL(cx, durl)) + return false; + } + if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) { + JSFlatString* flat = sourceMapURL->ensureFlat(cx); + if (!flat) + return false; + + AutoStableStringChars chars(cx); + if (!chars.initTwoByte(cx, flat)) + return false; + + const char16_t* smurl = chars.twoByteRange().start().get(); + if (!script->scriptSource()->setSourceMapURL(cx, smurl)) + return false; + } + if (!JS_ExecuteScript(cx, script, args.rval())) { + if (catchTermination && !JS_IsExceptionPending(cx)) { + JSAutoCompartment ac1(cx, callerGlobal); + JSString* str = JS_NewStringCopyZ(cx, "terminated"); + if (!str) + return false; + args.rval().setString(str); + return true; + } + return false; + } + + if (saveBytecode) { + saveBuffer = reinterpret_cast<uint8_t*>(JS_EncodeScript(cx, script, &saveLength)); + if (!saveBuffer) + return false; + } + } + + if (saveBytecode) { + // If we are both loading and saving, we assert that we are going to + // replace the current bytecode by the same stream of bytes. + if (loadBytecode && assertEqBytecode) { + if (saveLength != loadLength) { + char loadLengthStr[16]; + JS_snprintf(loadLengthStr, sizeof(loadLengthStr), "%u", loadLength); + char saveLengthStr[16]; + JS_snprintf(saveLengthStr, sizeof(saveLengthStr), "%u", saveLength); + + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_CACHE_EQ_SIZE_FAILED, + loadLengthStr, saveLengthStr); + return false; + } + + if (!PodEqual(loadBuffer, saveBuffer.get(), loadLength)) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, + JSSMSG_CACHE_EQ_CONTENT_FAILED); + return false; + } + } + + if (!CacheEntry_setBytecode(cx, cacheEntry, saveBuffer, saveLength)) + return false; + + saveBuffer.forget(); + } + + return JS_WrapValue(cx, args.rval()); +} +#endif /* NOTYET */ + +#ifdef NOTYET +JSString* +js::shell::FileAsString(JSContext* cx, const char* pathname) +{ + FILE* file; + RootedString str(cx); + size_t len, cc; + char* buf; + + file = fopen(pathname, "rb"); + if (!file) { + JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); + return nullptr; + } + AutoCloseFile autoClose(file); + + if (fseek(file, 0, SEEK_END) != 0) { + JS_ReportError(cx, "can't seek end of %s", pathname); + } else { + len = ftell(file); + if (fseek(file, 0, SEEK_SET) != 0) { + JS_ReportError(cx, "can't seek start of %s", pathname); + } else { + buf = (char*) JS_malloc(cx, len + 1); + if (buf) { + cc = fread(buf, 1, len, file); + if (cc != len) { + JS_ReportError(cx, "can't read %s: %s", pathname, + (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read"); + } else { + char16_t* ucbuf = + JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf, len), &len).get(); + if (!ucbuf) { + JS_ReportError(cx, "Invalid UTF-8 in file '%s'", pathname); + return nullptr; + } + str = JS_NewUCStringCopyN(cx, ucbuf, len); + free(ucbuf); + } + JS_free(cx, buf); + } + } + } + + return str; +} + +/* + * Function to run scripts and return compilation + execution time. Semantics + * are closely modelled after the equivalent function in WebKit, as this is used + * to produce benchmark timings by SunSpider. + */ +static bool +Run(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() != 1) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "run"); + return false; + } + + RootedString str(cx, JS::ToString(cx, args[0])); + if (!str) + return false; + args[0].setString(str); + JSAutoByteString filename(cx, str); + if (!filename) + return false; + + str = FileAsString(cx, filename.ptr()); + if (!str) + return false; + + AutoStableStringChars chars(cx); + if (!chars.initTwoByte(cx, str)) + return false; + + const char16_t* ucbuf = chars.twoByteRange().start().get(); + size_t buflen = str->length(); + + RootedScript script(cx); + int64_t startClock = PRMJ_Now(); + { + JS::CompileOptions options(cx); + options.setIntroductionType("js shell run") + .setFileAndLine(filename.ptr(), 1) + .setIsRunOnce(true) + .setNoScriptRval(true); + if (!JS_CompileUCScript(cx, ucbuf, buflen, options, &script)) + return false; + } + + if (!JS_ExecuteScript(cx, script)) + return false; + + int64_t endClock = PRMJ_Now(); + + args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)); + return true; +} +#endif /* NOTYET */ + +static bool +PutStr(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (args.length() != 0) { + RootedString str(cx, JS::ToString(cx, args[0])); + if (!str) + return false; + char* bytes = JS_EncodeStringToUTF8(cx, str); + if (!bytes) + return false; + fputs(bytes, gOutFile); + JS_free(cx, bytes); + fflush(gOutFile); + } + + args.rval().setUndefined(); + return true; +} + +#ifdef NOTYET +static bool +Now(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC); + args.rval().setDouble(now); + return true; +} +#endif + +static bool +PrintInternal(JSContext* cx, const CallArgs& args, FILE* file) +{ + for (unsigned i = 0; i < args.length(); i++) { + RootedString str(cx, JS::ToString(cx, args[i])); + if (!str) + return false; + char* bytes = JS_EncodeStringToUTF8(cx, str); + if (!bytes) + return false; + fprintf(file, "%s%s", i ? " " : "", bytes); + JS_free(cx, bytes); + } + + fputc('\n', file); + fflush(file); + + args.rval().setUndefined(); + return true; +} + +static bool +Print(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return PrintInternal(cx, args, gOutFile); +} + +static bool +PrintErr(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return PrintInternal(cx, args, gErrFile); +} + +#ifdef NOTYET +static bool +Help(JSContext* cx, unsigned argc, Value* vp); + +static bool +Quit(JSContext* cx, unsigned argc, Value* vp) +{ + ShellRuntime* sr = GetShellRuntime(cx); + +#ifdef JS_MORE_DETERMINISTIC + // Print a message to stderr in more-deterministic builds to help jsfunfuzz + // find uncatchable-exception bugs. + fprintf(stderr, "quit called\n"); +#endif + + CallArgs args = CallArgsFromVp(argc, vp); + int32_t code; + if (!ToInt32(cx, args.get(0), &code)) + return false; + + // The fuzzers check the shell's exit code and assume a value >= 128 means + // the process crashed (for instance, SIGSEGV will result in code 139). On + // POSIX platforms, the exit code is 8-bit and negative values can also + // result in an exit code >= 128. We restrict the value to range [0, 127] to + // avoid false positives. + if (code < 0 || code >= 128) { + JS_ReportError(cx, "quit exit code should be in range 0-127"); + return false; + } + + sr->exitCode = code; + sr->quitting = true; + return false; +} +#endif /* NOTYET */ + +#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; +} + +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 /* NOTYET */ + +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; +} + +static JSScript* +GetTopScript(JSContext* cx) +{ + NonBuiltinScriptFrameIter iter(cx); + return iter.done() ? nullptr : iter.script(); +} + +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; +} + +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; +} + +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; +} + +#ifdef DEBUG + +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); +} + +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"); + } +} + +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; +} + +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" }; + +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; +} + +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; +} + +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; +} + +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 */ + +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(); +} + +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; +} + +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; +} + +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; +} + +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 /* DEBUG */ + +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; +} + +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; +} + +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 /* NOTYET */ + +#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 /* NOTYET */ + +static bool +ThrowError(JSContext* cx, unsigned argc, Value* vp) +{ + JS_ReportError(cx, "This is an error"); + return false; +} + +#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 /* NOTYET */ + static const JSFunctionSpec global_functions[] = { - JS_FN("version", Version, 0,0), + JS_FN("version", Version, 0,0), #ifdef NOTYET - JS_FN("load", Load, 1,0), - JS_FN("evaluate", Evaluate, 1,0), - JS_FN("run", Run, 1,0), -#ifdef NOTYET - JS_FN("readline", ReadLine, 0,0), -#endif - JS_FN("print", Print, 0,0), - JS_FN("putstr", PutStr, 0,0), - JS_FN("dateNow", Now, 0,0), - JS_FS("require", Require, 0,0), + JS_FN("options", Options, 0,0), + JS_FN("load", Load, 1,0), + JS_FN("loadRelativeToScript", LoadScriptRelativeToScript, 1,0), + 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), +#ifdef NOTYET + JS_FN("dateNow", Now, 0,0), + JS_FN("help", Help, 0,0), + JS_FN("quit", Quit, 0,0), +#endif /* NOTYET */ + 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), #endif + JS_FN("sleep", Sleep_fn, 1,0), + JS_FN("compile", Compile, 1,0), + JS_FN("parseModule", ParseModule, 1,0), + JS_FN("setModuleResolveHook", SetModuleResolveHook, 1,0), + JS_FN("getModuleLoadPath", GetModuleLoadPath, 0,0), + JS_FN("parse", Parse, 1,0), + JS_FN("syntaxParse", SyntaxParse, 1,0), + JS_FN("offThreadCompileScript", OffThreadCompileScript, 1,0), + JS_FN("runOffThreadScript", runOffThreadScript, 0,0), + JS_FN("timeout", Timeout, 1,0), + JS_FN("interruptIf", InterruptIf, 1,0), + JS_FN("invokeInterruptCallback", InvokeInterruptCallbackWrapper, 0,0), + JS_FN("setInterruptCallback", SetInterruptCallback, 1,0), + JS_FN("enableLastWarning", EnableLastWarning, 0,0), + JS_FN("disableLastWarning", DisableLastWarning, 0,0), + JS_FN("getLastWarning", GetLastWarning, 0,0), + JS_FN("clearLastWarning", ClearLastWarning, 0,0), + JS_FN("elapsed", Elapsed, 0,0), + JS_FN("decompileFunction", DecompileFunction, 1,0), + JS_FN("decompileThis", DecompileThisScript, 0,0), + JS_FN("thisFilename", ThisFilename, 0,0), + JS_FN("newGlobal", NewGlobal, 1,0), + JS_FN("nukeCCW", NukeCCW, 1,0), + JS_FN("createMappedArrayBuffer", CreateMappedArrayBuffer, 1,0), + JS_FN("getMaxArgs", GetMaxArgs, 0,0), + JS_FN("objectEmulatingUndefined", ObjectEmulatingUndefined, 0,0), + JS_FN("isCachingEnabled", IsCachingEnabled, 0,0), + JS_FN("setCachingEnabled", SetCachingEnabled, 1,0), + JS_FN("cacheEntry", CacheEntry, 1,0), + JS_FN("printProfilerEvents", PrintProfilerEvents, 0,0), + JS_FN("enableSingleStepProfiling", EnableSingleStepProfiling, 0,0), + JS_FN("disableSingleStepProfiling", DisableSingleStepProfiling, 0,0), + JS_FN("isLatin1", IsLatin1, 1,0), + JS_FN("stackPointerInfo", StackPointerInfo, 0,0), + JS_FN("entryPoints", EntryPoints, 1,0), + + JS_FS("require", Require, 0,0), +#endif /* NOTYET */ JS_FS_END }; @@ -792,8 +2299,8 @@ return I; } -static int mozRun(rpmjss jss, - const char * script, const char * fn, int ln) +static int mozRun(rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln) { JSI_t I = (JSI_t) jss->I; @@ -814,8 +2321,10 @@ JSString *str = rval.toString(); char * t = JS_EncodeString(I->cx, str); - printf("%s\n", t); - t = _free(t); + if (resultp) + *resultp = t; + else + t = _free(t); return 0; } @@ -837,7 +2346,10 @@ { const char *script = "'hello'+'world, it is '+new Date()"; - rc = mozRun(jss, script, __FILE__, __LINE__); + const char * result = NULL; + rc = mozRun(jss, &result, script, __FILE__, __LINE__); + fprintf(stderr, "<== result |%s|\n", result); + result = _free(result); } mozFini(jss); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmjss.h ============================================================================ $ cvs diff -u -r1.1.2.1 -r1.1.2.2 rpmjss.h --- rpm/rpmio/rpmjss.h 23 Jun 2017 03:39:44 -0000 1.1.2.1 +++ rpm/rpmio/rpmjss.h 24 Jun 2017 19:09:35 -0000 1.1.2.2 @@ -54,7 +54,8 @@ struct JSIO_s { void (*mozFini) (rpmjss jss); void * (*mozInit) (rpmjss jss); - int (*mozRun) (rpmjss jss, const char * script, const char * filename, int lineno); + int (*mozRun) (rpmjss jss, const char ** resultp, + const char * script, const char * fn, unsigned ln); }; extern JSIO_t mozjs185; @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org