Bug 1337465 - Create proper help strings for JS shell namespace objects, r=jonco

MozReview-Commit-ID: Ha6gkvgUIgZ

--HG--
extra : rebase_source : 9272fd8f06280b19a25250fa43d970cfa72389a6
This commit is contained in:
Steve Fink 2017-02-17 14:04:06 -08:00
Родитель cce5817930
Коммит 3c066c06e6
3 изменённых файлов: 84 добавлений и 32 удалений

Просмотреть файл

@ -1001,25 +1001,30 @@ DefineOS(JSContext* cx, HandleObject global,
// For backwards compatibility, expose various os.file.* functions as // For backwards compatibility, expose various os.file.* functions as
// direct methods on the global. // direct methods on the global.
RootedValue val(cx); struct Export {
struct {
const char* src; const char* src;
const char* dst; const char* dst;
} osfile_exports[] = { };
const Export osfile_exports[] = {
{ "readFile", "read" }, { "readFile", "read" },
{ "readFile", "snarf" }, { "readFile", "snarf" },
{ "readRelativeToScript", "readRelativeToScript" }, { "readRelativeToScript", "readRelativeToScript" },
{ "redirect", "redirect" },
{ "redirectErr", "redirectErr" }
}; };
for (auto pair : osfile_exports) { for (auto pair : osfile_exports) {
if (!JS_GetProperty(cx, osfile, pair.src, &val)) if (!CreateAlias(cx, pair.dst, osfile, pair.src))
return false; return false;
if (val.isObject()) { }
RootedObject function(cx, &val.toObject());
if (!JS_DefineProperty(cx, global, pair.dst, function, 0)) if (!fuzzingSafe) {
const Export unsafe_osfile_exports[] = {
{ "redirect", "redirect" },
{ "redirectErr", "redirectErr" }
};
for (auto pair : unsafe_osfile_exports) {
if (!CreateAlias(cx, pair.dst, osfile, pair.src))
return false; return false;
} }
} }

Просмотреть файл

@ -18,6 +18,18 @@ using namespace JS;
namespace js { namespace js {
namespace shell { namespace shell {
// Generate 'usage' and 'help' properties for the given object.
// JS_DefineFunctionsWithHelp will define individual function objects with both
// of those properties (eg getpid.usage = "getpid()" and getpid.help = "return
// the process id"). This function will generate strings for an "interface
// object", eg os.file, which contains some number of functions.
//
// .usage will be set to "<name> - interface object".
//
// .help will be set to a newline-separated list of functions that have either
// 'help' or 'usage' properties. Functions are described with their usage
// strings, if they have them, else with just their names.
//
bool bool
GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name) GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name)
{ {
@ -26,45 +38,68 @@ GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name)
return false; return false;
StringBuffer buf(cx); StringBuffer buf(cx);
if (!buf.append(' ')) if (!buf.append(name, strlen(name)) || !buf.append(" - interface object", 19))
return false; return false;
RootedString s(cx, buf.finishString());
if (!s || !JS_DefineProperty(cx, obj, "usage", s, 0))
return false;
buf.clear();
bool first = true;
for (size_t i = 0; i < idv.length(); i++) { for (size_t i = 0; i < idv.length(); i++) {
RootedValue v(cx);
RootedId id(cx, idv[i]); RootedId id(cx, idv[i]);
RootedValue v(cx);
if (!JS_GetPropertyById(cx, obj, id, &v)) if (!JS_GetPropertyById(cx, obj, id, &v))
return false; return false;
if (!v.isObject()) if (!v.isObject())
continue; continue;
bool hasHelp = false;
RootedObject prop(cx, &v.toObject()); RootedObject prop(cx, &v.toObject());
if (!JS_GetProperty(cx, prop, "usage", &v))
RootedValue usage(cx);
RootedValue help(cx);
if (!JS_GetProperty(cx, prop, "usage", &usage))
return false; return false;
if (v.isString()) if (!JS_GetProperty(cx, prop, "help", &help))
hasHelp = true; return false;
if (!JS_GetProperty(cx, prop, "help", &v)) if (!usage.isString() && !help.isString())
continue;
if (!first && !buf.append("\n"))
return false;
first = false;
if (!buf.append(" ", 2))
return false;
if (!buf.append(usage.isString() ? usage.toString() : JSID_TO_FLAT_STRING(id)))
return false; return false;
if (v.isString())
hasHelp = true;
if (hasHelp) {
if (!buf.append(' ') ||
!buf.append(name, strlen(name)) ||
!buf.append('.') ||
!buf.append(JSID_TO_FLAT_STRING(id)))
{
return false;
}
}
} }
RootedString s(cx, buf.finishString()); s = buf.finishString();
if (!s || !JS_DefineProperty(cx, obj, "help", s, 0)) if (!s || !JS_DefineProperty(cx, obj, "help", s, 0))
return false; return false;
if (!buf.append(name, strlen(name)) || !buf.append(" - interface object", 20)) return true;
}
bool
CreateAlias(JSContext* cx, const char* dstName, JS::HandleObject namespaceObj, const char* srcName)
{
RootedObject global(cx, JS_GetGlobalForObject(cx, namespaceObj));
if (!global)
return false; return false;
s = buf.finishString();
if (!s || !JS_DefineProperty(cx, obj, "usage", s, 0)) RootedValue val(cx);
if (!JS_GetProperty(cx, namespaceObj, srcName, &val))
return false;
if (!val.isObject()) {
JS_ReportErrorASCII(cx, "attempted to alias nonexistent function");
return false;
}
RootedObject function(cx, &val.toObject());
if (!JS_DefineProperty(cx, global, dstName, function, 0))
return false; return false;
return true; return true;

Просмотреть файл

@ -78,6 +78,18 @@ struct RCFile {
bool release(); bool release();
}; };
// Alias the global dstName to namespaceObj.srcName. For example, if dstName is
// "snarf", namespaceObj represents "os.file", and srcName is "readFile", then
// this is equivalent to the JS code:
//
// snarf = os.file.readFile;
//
// This provides a mechanism for namespacing the various JS shell helper
// functions without breaking backwards compatibility with things that use the
// global names.
bool
CreateAlias(JSContext* cx, const char* dstName, JS::HandleObject namespaceObj, const char* srcName);
} /* namespace shell */ } /* namespace shell */
} /* namespace js */ } /* namespace js */