Bug 1516697 - Remove the implementation of the asm.js caching mechanism. (APIs to invoke it still remain in place; they just don't do anything.) r=luke

This commit is contained in:
Jeff Walden 2019-01-17 12:39:02 -05:00
Родитель bdfbf55e8a
Коммит 6796de0f32
22 изменённых файлов: 50 добавлений и 873 удалений

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

@ -44,7 +44,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=929236
evalAsync(code);
break;
case 1:
ok(jsFuns.isAsmJSModuleLoadedFromCache(module), "module loaded from cache");
ok(jsFuns.isAsmJSModule(module), "module");
SimpleTest.finish();
break;
default:

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

@ -18,8 +18,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
var assertCacheHit = false;
// generate four slightly different big asm.js modules and compile them async
// so that we can hit the asm.js cache.
@ -36,7 +34,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
code2 += "return g" + i + ";\n";
code2 += "}\n";
code2 += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
code2 += "if (assertCacheHit) ok(jsFuns.isAsmJSModuleLoadedFromCache(f), 'cache hit');\n";
code2 += "var gX = f();\n";
code2 += "ok(jsFuns.isAsmJSFunction(gX), 'gX is an asm.js function')\n";
code2 += "ok(gX() === " + i + ", 'gX returns the correct result')\n";
@ -58,7 +55,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
if (finishedCount < 1 || finishedCount > 2 * N) {
throw "Huh?!";
} else if (finishedCount == N) {
assertCacheHit = true;
for (let i = 0; i < N; i++)
evalAsync(codes[i]);
} else if (finishedCount == 2 * N) {

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

@ -31,7 +31,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=941830
var workerBlob = new Blob([workerCode], {type: "application/javascript"});
var mainCode = asmjsCode;
mainCode += "ok(jsFuns.isAsmJSModuleLoadedFromCache(f), 'f is a cache hit')\n";
mainCode += "ok(jsFuns.isAsmJSModule(f), 'f is a module')\n";
mainCode += "var g42 = f();\n";
mainCode += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function');\n";
mainCode += "ok(g42() === 42, 'g42 returns the correct result');\n";

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

@ -5923,12 +5923,6 @@ gc::ZealModeHelpText),
" Returns whether the given value is a function containing \"use asm\" that has been\n"
" validated according to the asm.js spec."),
JS_FN_HELP("isAsmJSModuleLoadedFromCache", IsAsmJSModuleLoadedFromCache, 1, 0,
"isAsmJSModuleLoadedFromCache(fn)",
" Return whether the given asm.js module function has been loaded directly\n"
" from the cache. This function throws an error if fn is not a validated asm.js\n"
" module."),
JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
"isAsmJSFunction(fn)",
" Returns whether the given value is a nested function in an asm.js module that has been\n"

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

@ -26,20 +26,8 @@ function asmCompileCached()
if (!isAsmJSCompilationAvailable())
return Function.apply(null, arguments);
if (!isCachingEnabled()) {
var f = Function.apply(null, arguments);
assertEq(isAsmJSModule(f), true);
return f;
}
var quotedArgs = [];
for (var i = 0; i < arguments.length; i++)
quotedArgs.push("'" + arguments[i] + "'");
var code = "setCachingEnabled(true); var f = new Function(" + quotedArgs.join(',') + ");assertEq(isAsmJSModule(f), true);";
nestedShell("--js-cache", "--no-js-cache-per-process", "--execute=" + code);
var f = Function.apply(null, arguments);
assertEq(isAsmJSModuleLoadedFromCache(f), true);
assertEq(isAsmJSModule(f), true);
return f;
}

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

@ -56007,6 +56007,5 @@ function runBullet() {
// Let the caller decide what should have happened.
return {
asmJSValidated: isAsmJSModule(asmModule) && isAsmJSFunction(asm._main),
loadedFromCache: isAsmJSModule(asmModule) && isAsmJSModuleLoadedFromCache(asmModule)
}
}

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

@ -1,7 +1,6 @@
load(libdir + "asm.js");
setIonCheckGraphCoherency(false);
setCachingEnabled(false);
// constants
var buf = new ArrayBuffer(BUF_MIN);

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

@ -1,19 +1,14 @@
// |jit-test| skip-if: !isAsmJSCompilationAvailable()
// Test a big fat asm.js module. First load/compile/cache bullet.js in a
// separate process and then load it again in this process, which should be a
// cache hit.
setCachingEnabled(true);
// separate process and then load it again in this process.
// Note: if you get some failure in this test, it probably has to do with
// bullet.js and not the nestedShell() call, so try first commenting out
// nestedShell() (and the loadedFromCache assertion) to see if the error
// reproduces.
var code = "setIonCheckGraphCoherency(false); setCachingEnabled(true); load('" + libdir + "bullet.js'); runBullet()";
// nestedShell() to see if the error reproduces.
var code = "setIonCheckGraphCoherency(false); load('" + libdir + "bullet.js'); runBullet()";
nestedShell("--js-cache", "--no-js-cache-per-process", "--execute=" + code);
setIonCheckGraphCoherency(false);
load(libdir + 'bullet.js');
var results = runBullet();
assertEq(results.asmJSValidated, true);
assertEq(results.loadedFromCache, true);

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

@ -1,104 +0,0 @@
// |jit-test| skip-if: !isAsmJSCompilationAvailable()
load(libdir + "asm.js");
setCachingEnabled(true);
if (!isCachingEnabled())
quit();
var body1 = "'use asm'; function f() { return 42 } function ff() { return 43 } return f";
var m = new Function(body1);
assertEq(isAsmJSModule(m), true);
assertEq(m()(), 42);
var m = new Function(body1);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m()(), 42);
var body2 = body1 + "f";
var m = new Function(body2);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
assertEq(m()(), 43);
var evalStr1 = "(function() { " + body1 + "})";
var m = eval(evalStr1);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
assertEq(m()(), 42);
var m = eval(evalStr1);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m()(), 42);
var evalStr2 = "(function() { " + body2 + "})";
var m = eval(evalStr2);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
assertEq(m()(), 43);
var m = eval(evalStr2);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m()(), 43);
var evalStr3 = "(function(global) { 'use asm'; var sin=global.Math.sin; function g(d) { d=+d; return +sin(d) } return g })";
var m = eval(evalStr3);
assertEq(isAsmJSModule(m), true);
assertEq(m(this)(.3), Math.sin(.3));
var m = eval(evalStr3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m(this)(.3), Math.sin(.3));
var evalStr4 = "(function(gobal) { 'use asm'; var sin=global.Math.sin; function g(d) { d=+d; return +sin(d) } return g })";
var m = eval(evalStr4);
assertEq(isAsmJSModule(m), false);
var m = eval(evalStr3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var evalStr5 = "(function(global,foreign) { 'use asm'; var sin=global.Math.sin; function g(d) { d=+d; return +sin(d) } return g })";
var m = eval(evalStr5);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
var m = new Function(body1);
assertEq(isAsmJSModule(m), true);
var body3 = "'use asm'; var sin=global.Math.sin; function g(d) { d=+d; return +sin(d) } return g";
var m = new Function('global', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
assertEq(m(this)(.2), Math.sin(.2));
var m = new Function('gobal', body3);
assertEq(isAsmJSModule(m), false);
var m = new Function('global', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var m = new Function('global','foreign', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
var m = new Function('global','foreign', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var m = new Function('gobal','foreign', body3);
assertEq(isAsmJSModule(m), false);
var m = new Function('global','foreign', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var m = new Function('global','foregn', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
var m = new Function('global','foreign', 'buffer', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
var m = new Function('global','foreign', 'buffer', 'foopy', body3);
assertEq(isAsmJSModule(m), false);
var m = new Function('global','foreign', 'buffer', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var m = new Function('global','foreign', 'bffer', body3);
assertEq(isAsmJSModuleLoadedFromCache(m), false);
var m = new Function('global','foreign', 'foreign', body3);
assertEq(isAsmJSModule(m), false);
var body = "f() { 'use asm'; function g() {} return g }";
var evalStr6 = "(function " + body + ")";
var evalStr7 = "(function* " + body + ")";
var m = eval(evalStr6);
assertEq(isAsmJSModule(m), true);
var m = eval(evalStr6);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
var m = eval(evalStr7);
assertEq(isAsmJSModule(m), false);
// Test caching using a separate process (which, with ASLR, should mean a
// separate address space) to compile/cache the code. Ideally, every asmCompile
// would do this, but that makes jit-tests run 100x slower. Do it here, for one
// of each feature. asm.js/testBullet.js should pound on everything.
assertEq(asmLink(asmCompileCached(USE_ASM + "function f(i) { i=i|0; return +((i+1)|0) } function g(d) { d=+d; return +(d + +f(42) + 1.5) } return g"))(.2), .2+43+1.5);
assertEq(asmLink(asmCompileCached(USE_ASM + "function f1() { return 1 } function f2() { return 2 } function f(i) { i=i|0; return T[i&1]()|0 } var T=[f1,f2]; return f"))(1), 2);
assertEq(asmLink(asmCompileCached("g", USE_ASM + "var s=g.Math.sin; function f(d) { d=+d; return +s(d) } return f"), this)(.3), Math.sin(.3));
assertEq(asmLink(asmCompileCached("g","ffis", USE_ASM + "var ffi=ffis.ffi; function f(i) { i=i|0; return ffi(i|0)|0 } return f"), null, {ffi:function(i){return i+2}})(1), 3);
assertEq(asmLink(asmCompileCached("g","ffis", USE_ASM + "var x=ffis.x|0; function f() { return x|0 } return f"), null, {x:43})(), 43);
var i32 = new Int32Array(BUF_MIN/4);
i32[4] = 42;
assertEq(asmLink(asmCompileCached("g","ffis","buf", USE_ASM + "var i32=new g.Int32Array(buf); function f(i) { i=i|0; return i32[i>>2]|0 } return f"), this, null, i32.buffer)(4*4), 42);
assertEq(asmLink(asmCompileCached('glob', USE_ASM + 'var x=glob.Math.PI; function f() { return +x } return f'), this)(), Math.PI);

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

@ -1,7 +1,5 @@
load(libdir + "asm.js");
setCachingEnabled(true);
var code = asmCompile(USE_ASM + "function g() { return 42 } return g");
assertEq(asmLink(code)(), 42);
assertEq(asmLink(code)(), 42);

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

@ -15,8 +15,6 @@ var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i
var f = asmLink(code, this, null, new ArrayBuffer(BUF_MIN));
assertEq(f(0), 0);
setCachingEnabled(true);
// In order to allow following tests work on both big-endian and little-
// endian architectures we need to define least significant byte (lsb) and
// least significant word (lsw).
@ -49,8 +47,6 @@ assertEq(f(0x100),0);
assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + 'var i32=new stdlib.Int32Array(heap); function f(i) {i=i|0;var j=0x10000;return (i32[j>>2] = i)|0 } return f'), this, null, buf)(1), 1);
}
setCachingEnabled(false);
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u8[' + lsb + ']|0}; return f');
var f = asmLink(code, this, null, new ArrayBuffer(BUF_MIN));
assertEq(f(0),0);

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

@ -1,5 +1,3 @@
setCachingEnabled(true);
(function() {
/*
* NO ARGUMENT
@ -51,9 +49,9 @@ f0 = new Function(bodyOnly);
assertEq(f0.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
assertEq(f0.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var m = new Function(bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(isAsmJSModule(m), true);
assertEq(m.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
}
@ -110,9 +108,9 @@ f1 = new Function('glob', bodyOnly);
assertEq(f1.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
assertEq(f1.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var m = new Function('glob', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(isAsmJSModule(m), true);
assertEq(m.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
}
@ -170,9 +168,9 @@ f2 = new Function('glob', 'ffi', bodyOnly);
assertEq(f2.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
assertEq(f2.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var m = new Function('glob', 'ffi', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(isAsmJSModule(m), true);
assertEq(m.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
}
@ -230,9 +228,9 @@ f3 = new Function('glob', 'ffi', 'heap', bodyOnly);
assertEq(f3.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
assertEq(f3.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var m = new Function('glob', 'ffi', 'heap', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(isAsmJSModule(m), true);
assertEq(m.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
assertEq(m.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
}
@ -257,9 +255,9 @@ var expectedToSource = '(' + expectedToString + ')';
assertEq(f4.toString(), expectedToString);
assertEq(f4.toSource(), expectedToSource);
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var f5 = eval("\"use strict\";\n(" + funcSource + ")");
assertEq(isAsmJSModuleLoadedFromCache(f5), true);
assertEq(isAsmJSModule(f5), true);
assertEq(f5.toString(), expectedToString);
assertEq(f5.toSource(), expectedToSource);
}
@ -318,21 +316,21 @@ function checkFuncSrc(m) {
}
checkFuncSrc(moduleG);
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var g2 = new Function(funcBody);
assertEq(isAsmJSModuleLoadedFromCache(g2), true);
assertEq(isAsmJSModule(g2), true);
m = g2();
checkFuncSrc(m);
var moduleDecl = 'function g3() {' + funcBody + '}';
eval(moduleDecl);
m = g3();
assertEq(isAsmJSModuleLoadedFromCache(g3), false);
assertEq(isAsmJSModule(g3), true);
checkFuncSrc(m);
eval('var x = 42;' + moduleDecl);
m = g3();
assertEq(isAsmJSModuleLoadedFromCache(g3), true);
assertEq(isAsmJSModule(g3), true);
checkFuncSrc(m);
}
@ -358,9 +356,9 @@ var expectedToSource = expectedToString
assertEq(f5.toString(), expectedToString);
assertEq(f5.toSource(), expectedToSource);
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var mf5 = eval("\"use strict\";\n(" + moduleCode + ")");
assertEq(isAsmJSModuleLoadedFromCache(mf5), true);
assertEq(isAsmJSModule(mf5), true);
var f5 = mf5();
assertEq(f5.toString(), expectedToString);
assertEq(f5.toSource(), expectedToSource);
@ -387,9 +385,9 @@ var f6 = eval(useStrict + ";\n(" + moduleCode + "({Math:{}}))");
assertEq(f6.toString(), funcCode);
assertEq(f6.toSource(), funcCode);
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var mf6 = eval("\"use strict\";\n(" + moduleCode + ")");
assertEq(isAsmJSModuleLoadedFromCache(mf6), true);
assertEq(isAsmJSModule(mf6), true);
var f6 = mf6({Math:{}});
assertEq(f6.toString(), funcCode);
assertEq(f6.toSource(), funcCode);

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

@ -20,7 +20,6 @@ function dumpStack()
setJitCompilerOption("ion.warmup.trigger", 10);
setJitCompilerOption("baseline.warmup.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
setCachingEnabled(true);
var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");
@ -31,9 +30,9 @@ for (var i = 0; i < 15; i++) {
matchStack(stack, ['dumpStack', 'f']);
}
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
if (isAsmJSCompilationAvailable()) {
var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");
assertEq(isAsmJSModuleLoadedFromCache(callFFI), true);
assertEq(isAsmJSModule(callFFI), true);
stack = null;
f();
matchStack(stack, ['dumpStack', 'f']);

Двоичный файл не отображается.

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

@ -1 +0,0 @@
// |jit-test| skip-if: !isAsmJSCompilationAvailable()

Двоичный файл не отображается.

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

@ -1 +0,0 @@
// |jit-test| skip-if: !isAsmJSCompilationAvailable()

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

@ -1,35 +0,0 @@
// |jit-test| skip-if: !isAsmJSCompilationAvailable()
load(libdir + "asm.js");
setCachingEnabled(true);
if (!isCachingEnabled())
quit();
// Test Latin1 and TwoByte PropertyName serialization.
// Latin1
var body1 = "'use asm'; function funName() { return 42 } return funName";
var m = new Function(body1);
assertEq(isAsmJSModule(m), true);
assertEq(m()(), 42);
var m = new Function(body1);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m()(), 42);
var f = m();
assertEq(isLatin1(f.name), true);
assertEq(f.name, "funName");
// TwoByte
var body1 = "'use asm'; function funName\u1200() { return 42 } return funName\u1200";
var m = new Function(body1);
assertEq(isAsmJSModule(m), true);
assertEq(m()(), 42);
var m = new Function(body1);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
assertEq(m()(), 42);
var f = m();
assertEq(isLatin1(f.name), false);
assertEq(f.name, "funName\u1200");

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

@ -367,7 +367,8 @@ MSG_DEF(JSMSG_BAD_CODE_UNITS, 1, JSEXN_NOTE, "the code units comprising
// asm.js
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled asm.js code ({0})")
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled asm.js code (total compilation time {0}ms)")
MSG_DEF(JSMSG_USE_ASM_TYPE_OK_NO_TIME, 0, JSEXN_WARN, "Successfully compiled asm.js code ()")
// wasm
MSG_DEF(JSMSG_WASM_VERBOSE, 1, JSEXN_WARN, "WebAssembly verbose: {0}")

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

@ -523,7 +523,6 @@ 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);
@ -6499,24 +6498,6 @@ static bool WithSourceHook(JSContext* cx, unsigned argc, Value* vp) {
return result;
}
static bool IsCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(jsCachingEnabled && jsCacheAsmJSPath != nullptr);
return true;
}
static bool SetCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (GetShellContext(cx)->isWorker) {
JS_ReportErrorASCII(cx, "Caching is not supported in workers");
return false;
}
jsCachingEnabled = ToBoolean(args.get(0));
args.rval().setUndefined();
return true;
}
static void PrintProfilerEvents_Callback(const char* msg) {
fprintf(stderr, "PROFILER EVENT: %s\n", msg);
}
@ -8622,14 +8603,6 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
" This function implements the exact requirements of the $262.IsHTMLDDA\n"
" property in test262."),
JS_FN_HELP("isCachingEnabled", IsCachingEnabled, 0, 0,
"isCachingEnabled()",
" Return whether JS caching is enabled."),
JS_FN_HELP("setCachingEnabled", SetCachingEnabled, 1, 0,
"setCachingEnabled(b)",
" Enable or disable JS caching."),
JS_FN_HELP("cacheEntry", CacheEntry, 1, 0,
"cacheEntry(code)",
" Return a new opaque object which emulates a cache entry of a script. This\n"
@ -9657,59 +9630,7 @@ static const uint32_t asmJSCacheCookie = 0xabbadaba;
static bool ShellOpenAsmJSCacheEntryForRead(
HandleObject global, const char16_t* begin, const char16_t* limit,
size_t* serializedSizeOut, const uint8_t** memoryOut, intptr_t* handleOut) {
if (!jsCachingEnabled || !jsCacheAsmJSPath) {
return false;
}
ScopedFileDesc fd(open(jsCacheAsmJSPath, O_RDWR), ScopedFileDesc::READ_LOCK);
if (fd == -1) {
return false;
}
// Get the size and make sure we can dereference at least one uint32_t.
off_t off = lseek(fd, 0, SEEK_END);
if (off == -1 || off < (off_t)sizeof(uint32_t)) {
return false;
}
// Map the file into memory.
void* memory;
#ifdef XP_WIN
HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd);
HANDLE fileMapping =
CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (!fileMapping) {
return false;
}
memory = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
CloseHandle(fileMapping);
if (!memory) {
return false;
}
#else
memory = mmap(nullptr, off, PROT_READ, MAP_SHARED, fd, 0);
if (memory == MAP_FAILED) {
return false;
}
#endif
// Perform check described by asmJSCacheCookie comment.
if (*(uint32_t*)memory != asmJSCacheCookie) {
#ifdef XP_WIN
UnmapViewOfFile(memory);
#else
munmap(memory, off);
#endif
return false;
}
// The embedding added the cookie so strip it off of the buffer returned to
// the JS engine.
*serializedSizeOut = off - sizeof(uint32_t);
*memoryOut = (uint8_t*)memory + sizeof(uint32_t);
*handleOut = fd.forget();
return true;
return false;
}
static void ShellCloseAsmJSCacheEntryForRead(size_t serializedSize,
@ -9734,86 +9655,7 @@ static void ShellCloseAsmJSCacheEntryForRead(size_t serializedSize,
static JS::AsmJSCacheResult ShellOpenAsmJSCacheEntryForWrite(
HandleObject global, const char16_t* begin, const char16_t* end,
size_t serializedSize, uint8_t** memoryOut, intptr_t* handleOut) {
if (!jsCachingEnabled || !jsCacheAsmJSPath) {
return JS::AsmJSCache_Disabled_ShellFlags;
}
// Create the cache directory if it doesn't already exist.
struct stat dirStat;
if (stat(jsCacheDir, &dirStat) == 0) {
if (!(dirStat.st_mode & S_IFDIR)) {
return JS::AsmJSCache_InternalError;
}
} else {
#ifdef XP_WIN
if (mkdir(jsCacheDir) != 0) {
return JS::AsmJSCache_InternalError;
}
#else
if (mkdir(jsCacheDir, 0777) != 0) {
return JS::AsmJSCache_InternalError;
}
#endif
}
ScopedFileDesc fd(open(jsCacheAsmJSPath, O_CREAT | O_RDWR, 0660),
ScopedFileDesc::WRITE_LOCK);
if (fd == -1) {
return JS::AsmJSCache_InternalError;
}
// Include extra space for the asmJSCacheCookie.
serializedSize += sizeof(uint32_t);
// Resize the file to the appropriate size after zeroing their contents.
#ifdef XP_WIN
if (chsize(fd, 0)) {
return JS::AsmJSCache_InternalError;
}
if (chsize(fd, serializedSize)) {
return JS::AsmJSCache_InternalError;
}
#else
if (ftruncate(fd, 0)) {
return JS::AsmJSCache_InternalError;
}
if (ftruncate(fd, serializedSize)) {
return JS::AsmJSCache_InternalError;
}
#endif
// Map the file into memory.
void* memory;
#ifdef XP_WIN
HANDLE fdOsHandle = (HANDLE)_get_osfhandle(fd);
HANDLE fileMapping =
CreateFileMapping(fdOsHandle, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (!fileMapping) {
return JS::AsmJSCache_InternalError;
}
memory = MapViewOfFile(fileMapping, FILE_MAP_WRITE, 0, 0, 0);
CloseHandle(fileMapping);
if (!memory) {
return JS::AsmJSCache_InternalError;
}
MOZ_ASSERT(*(uint32_t*)memory == 0);
#else
memory = mmap(nullptr, serializedSize, PROT_READ, MAP_SHARED, fd, 0);
if (memory == MAP_FAILED) {
return JS::AsmJSCache_InternalError;
}
MOZ_ASSERT(*(uint32_t*)memory == 0);
if (mprotect(memory, serializedSize, PROT_READ | PROT_WRITE)) {
return JS::AsmJSCache_InternalError;
}
#endif
// The embedding added the cookie so strip it off of the buffer returned to
// the JS engine. The asmJSCacheCookie will be written on close, below.
*memoryOut = (uint8_t*)memory + sizeof(uint32_t);
*handleOut = fd.forget();
return JS::AsmJSCache_Success;
return JS::AsmJSCache_Disabled_ShellFlags;
}
static void ShellCloseAsmJSCacheEntryForWrite(size_t serializedSize,

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

@ -7064,409 +7064,6 @@ size_t AsmJSMetadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
bufferArgumentName.sizeOfExcludingThis(mallocSizeOf);
}
namespace {
// Assumptions captures ambient state that must be the same when compiling and
// deserializing a module for the compiled code to be valid. If it's not, then
// the module must be recompiled from scratch.
struct Assumptions {
uint32_t cpuId;
JS::BuildIdCharVector buildId;
Assumptions();
bool init();
bool operator==(const Assumptions& rhs) const;
bool operator!=(const Assumptions& rhs) const { return !(*this == rhs); }
size_t serializedSize() const;
uint8_t* serialize(uint8_t* cursor) const;
const uint8_t* deserialize(const uint8_t* cursor, size_t remain);
};
Assumptions::Assumptions() : cpuId(ObservedCPUFeatures()), buildId() {}
bool Assumptions::init() { return GetBuildId && GetBuildId(&buildId); }
bool Assumptions::operator==(const Assumptions& rhs) const {
return cpuId == rhs.cpuId && buildId.length() == rhs.buildId.length() &&
ArrayEqual(buildId.begin(), rhs.buildId.begin(), buildId.length());
}
size_t Assumptions::serializedSize() const {
return sizeof(uint32_t) + SerializedPodVectorSize(buildId);
}
uint8_t* Assumptions::serialize(uint8_t* cursor) const {
// The format of serialized Assumptions must never change in a way that
// would cause old cache files written with by an old build-id to match the
// assumptions of a different build-id.
cursor = WriteScalar<uint32_t>(cursor, cpuId);
cursor = SerializePodVector(cursor, buildId);
return cursor;
}
const uint8_t* Assumptions::deserialize(const uint8_t* cursor, size_t remain) {
(cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &cpuId)) &&
(cursor = DeserializePodVectorChecked(cursor, &remain, &buildId));
return cursor;
}
class ModuleChars {
protected:
uint32_t isFunCtor_;
Vector<CacheableChars, 0, SystemAllocPolicy> funCtorArgs_;
public:
static uint32_t beginOffset(AsmJSParser<char16_t>& parser) {
return parser.pc->functionBox()->functionNode->pn_pos.begin;
}
static uint32_t endOffset(AsmJSParser<char16_t>& parser) {
TokenPos pos(0, 0); // initialize to silence GCC warning
MOZ_ALWAYS_TRUE(
parser.tokenStream.peekTokenPos(&pos, TokenStreamShared::Operand));
return pos.end;
}
};
class ModuleCharsForStore : ModuleChars {
uint32_t uncompressedSize_;
uint32_t compressedSize_;
Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
public:
bool init(AsmJSParser<char16_t>& parser) {
MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
uncompressedSize_ =
(endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
if (maxCompressedSize < uncompressedSize_) {
return false;
}
if (!compressedBuffer_.resize(maxCompressedSize)) {
return false;
}
const char16_t* chars =
parser.tokenStream.codeUnitPtrAt(beginOffset(parser));
const char* source = reinterpret_cast<const char*>(chars);
size_t compressedSize =
LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
if (!compressedSize || compressedSize > UINT32_MAX) {
return false;
}
compressedSize_ = compressedSize;
// For a function statement or named function expression:
// function f(x,y,z) { abc }
// the range [beginOffset, endOffset) captures the source:
// f(x,y,z) { abc }
// An unnamed function expression captures the same thing, sans 'f'.
// Since asm.js modules do not contain any free variables, equality of
// [beginOffset, endOffset) is sufficient to guarantee identical code
// generation, modulo Assumptions.
//
// For functions created with 'new Function', function arguments are
// not present in the source so we must manually explicitly serialize
// and match the formals as a Vector of PropertyName.
isFunCtor_ = parser.pc->isStandaloneFunctionBody();
if (isFunCtor_) {
unsigned numArgs;
CodeNode* functionNode = parser.pc->functionBox()->functionNode;
ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
UniqueChars name =
StringToNewUTF8CharsZ(nullptr, *arg->as<NameNode>().name());
if (!name || !funCtorArgs_.append(std::move(name))) {
return false;
}
}
}
return true;
}
size_t serializedSize() const {
return sizeof(uint32_t) + sizeof(uint32_t) + compressedSize_ +
sizeof(uint32_t) +
(isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
}
uint8_t* serialize(uint8_t* cursor) const {
cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
if (isFunCtor_) {
cursor = SerializeVector(cursor, funCtorArgs_);
}
return cursor;
}
};
class ModuleCharsForLookup : ModuleChars {
Vector<char16_t, 0, SystemAllocPolicy> chars_;
public:
const uint8_t* deserialize(const uint8_t* cursor) {
uint32_t uncompressedSize;
cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
uint32_t compressedSize;
cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
if (!chars_.resize(uncompressedSize / sizeof(char16_t))) {
return nullptr;
}
const char* source = reinterpret_cast<const char*>(cursor);
char* dest = reinterpret_cast<char*>(chars_.begin());
if (!LZ4::decompress(source, dest, uncompressedSize)) {
return nullptr;
}
cursor += compressedSize;
cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
if (isFunCtor_) {
cursor = DeserializeVector(cursor, &funCtorArgs_);
}
return cursor;
}
bool match(AsmJSParser<char16_t>& parser) const {
const char16_t* parseBegin =
parser.tokenStream.codeUnitPtrAt(beginOffset(parser));
const char16_t* parseLimit = parser.tokenStream.rawLimit();
MOZ_ASSERT(parseLimit >= parseBegin);
if (uint32_t(parseLimit - parseBegin) < chars_.length()) {
return false;
}
if (!ArrayEqual(chars_.begin(), parseBegin, chars_.length())) {
return false;
}
if (isFunCtor_ != parser.pc->isStandaloneFunctionBody()) {
return false;
}
if (isFunCtor_) {
// For function statements, the closing } is included as the last
// character of the matched source. For Function constructor,
// parsing terminates with EOF which we must explicitly check. This
// prevents
// new Function('"use asm"; function f() {} return f')
// from incorrectly matching
// new Function('"use asm"; function f() {} return ff')
if (parseBegin + chars_.length() != parseLimit) {
return false;
}
unsigned numArgs;
CodeNode* functionNode = parser.pc->functionBox()->functionNode;
ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
if (funCtorArgs_.length() != numArgs) {
return false;
}
for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
UniqueChars name =
StringToNewUTF8CharsZ(nullptr, *arg->as<NameNode>().name());
if (!name || strcmp(funCtorArgs_[i].get(), name.get())) {
return false;
}
}
}
return true;
}
};
struct ScopedCacheEntryOpenedForWrite {
JSContext* cx;
const size_t serializedSize;
uint8_t* memory;
intptr_t handle;
ScopedCacheEntryOpenedForWrite(JSContext* cx, size_t serializedSize)
: cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1) {}
~ScopedCacheEntryOpenedForWrite() {
if (memory) {
cx->asmJSCacheOps().closeEntryForWrite(serializedSize, memory, handle);
}
}
};
struct ScopedCacheEntryOpenedForRead {
JSContext* cx;
size_t serializedSize;
const uint8_t* memory;
intptr_t handle;
explicit ScopedCacheEntryOpenedForRead(JSContext* cx)
: cx(cx), serializedSize(0), memory(nullptr), handle(0) {}
~ScopedCacheEntryOpenedForRead() {
if (memory) {
cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
}
}
};
} // unnamed namespace
static JS::AsmJSCacheResult StoreAsmJSModuleInCache(
AsmJSParser<char16_t>& parser, const Module& module,
const LinkData& linkData, JSContext* cx) {
ModuleCharsForStore moduleChars;
if (!moduleChars.init(parser)) {
return JS::AsmJSCache_InternalError;
}
size_t moduleSize = module.serializedSize(linkData);
MOZ_RELEASE_ASSERT(moduleSize <= UINT32_MAX);
Assumptions assumptions;
if (!assumptions.init()) {
return JS::AsmJSCache_InternalError;
}
size_t serializedSize = assumptions.serializedSize() + sizeof(uint32_t) +
moduleSize + moduleChars.serializedSize();
JS::OpenAsmJSCacheEntryForWriteOp open =
cx->asmJSCacheOps().openEntryForWrite;
if (!open) {
return JS::AsmJSCache_Disabled_Internal;
}
const char16_t* begin =
parser.tokenStream.codeUnitPtrAt(ModuleChars::beginOffset(parser));
const char16_t* end =
parser.tokenStream.codeUnitPtrAt(ModuleChars::endOffset(parser));
ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
JS::AsmJSCacheResult openResult = open(
cx->global(), begin, end, serializedSize, &entry.memory, &entry.handle);
if (openResult != JS::AsmJSCache_Success) {
return openResult;
}
uint8_t* cursor = entry.memory;
cursor = assumptions.serialize(cursor);
cursor = WriteScalar<uint32_t>(cursor, moduleSize);
module.serialize(linkData, cursor, moduleSize);
cursor += moduleSize;
cursor = moduleChars.serialize(cursor);
MOZ_RELEASE_ASSERT(cursor == entry.memory + serializedSize);
return JS::AsmJSCache_Success;
}
static bool LookupAsmJSModuleInCache(JSContext* cx,
AsmJSParser<char16_t>& parser,
bool* loadedFromCache,
SharedModule* module,
UniqueChars* compilationTimeReport) {
int64_t before = PRMJ_Now();
*loadedFromCache = false;
JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
if (!open) {
return true;
}
const char16_t* begin =
parser.tokenStream.codeUnitPtrAt(ModuleChars::beginOffset(parser));
const char16_t* limit = parser.tokenStream.rawLimit();
ScopedCacheEntryOpenedForRead entry(cx);
if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory,
&entry.handle)) {
return true;
}
const uint8_t* cursor = entry.memory;
Assumptions deserializedAssumptions;
cursor = deserializedAssumptions.deserialize(cursor, entry.serializedSize);
if (!cursor) {
return true;
}
Assumptions currentAssumptions;
if (!currentAssumptions.init() ||
currentAssumptions != deserializedAssumptions) {
return true;
}
uint32_t moduleSize;
cursor = ReadScalar<uint32_t>(cursor, &moduleSize);
if (!cursor) {
return true;
}
MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
if (!asmJSMetadata) {
return false;
}
*module = Module::deserialize(cursor, moduleSize, asmJSMetadata.get());
if (!*module) {
ReportOutOfMemory(cx);
return false;
}
cursor += moduleSize;
// Due to the hash comparison made by openEntryForRead, this should succeed
// with high probability.
ModuleCharsForLookup moduleChars;
cursor = moduleChars.deserialize(cursor);
if (!moduleChars.match(parser)) {
return true;
}
// Don't punish release users by crashing if there is a programmer error
// here, just gracefully return with a cache miss.
#ifdef NIGHTLY_BUILD
MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
#endif
if (cursor != entry.memory + entry.serializedSize) {
return true;
}
// See AsmJSMetadata comment as well as ModuleValidator::init().
asmJSMetadata->toStringStart = parser.pc->functionBox()->toStringStart;
asmJSMetadata->srcStart =
parser.pc->functionBox()->functionNode->body()->pn_pos.begin;
asmJSMetadata->strict =
parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
asmJSMetadata->scriptSource.reset(parser.ss);
if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly())) {
return false;
}
int64_t after = PRMJ_Now();
int ms = (after - before) / PRMJ_USEC_PER_MSEC;
*compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms);
if (!*compilationTimeReport) {
return false;
}
*loadedFromCache = true;
return true;
}
/*****************************************************************************/
// Top-level js::CompileAsmJS
@ -7475,8 +7072,19 @@ static bool NoExceptionPending(JSContext* cx) {
}
static bool SuccessfulValidation(frontend::ParserBase& parser,
UniqueChars str) {
return parser.warningNoOffset(JSMSG_USE_ASM_TYPE_OK, str.get());
unsigned compilationTime) {
constexpr unsigned errNum =
#ifdef JS_MORE_DETERMINISTIC
JSMSG_USE_ASM_TYPE_OK_NO_TIME
#else
JSMSG_USE_ASM_TYPE_OK
#endif
;
char timeChars[20];
SprintfLiteral(timeChars, "%u", compilationTime);
return parser.warningNoOffset(errNum, timeChars);
}
static bool TypeFailureWarning(frontend::ParserBase& parser, const char* str) {
@ -7529,57 +7137,6 @@ static bool EstablishPreconditions(JSContext* cx,
return true;
}
static UniqueChars BuildConsoleMessage(unsigned time,
JS::AsmJSCacheResult cacheResult) {
#ifndef JS_MORE_DETERMINISTIC
const char* cacheString = "";
switch (cacheResult) {
case JS::AsmJSCache_Success:
cacheString = "stored in cache";
break;
case JS::AsmJSCache_ModuleTooSmall:
cacheString = "not stored in cache (too small to benefit)";
break;
case JS::AsmJSCache_SynchronousScript:
cacheString =
"unable to cache asm.js in synchronous scripts; try loading "
"asm.js via <script async> or createElement('script')";
break;
case JS::AsmJSCache_QuotaExceeded:
cacheString = "not enough temporary storage quota to store in cache";
break;
case JS::AsmJSCache_StorageInitFailure:
cacheString = "storage initialization failed (consider filing a bug)";
break;
case JS::AsmJSCache_Disabled_Internal:
cacheString =
"caching disabled by internal configuration (consider filing a bug)";
break;
case JS::AsmJSCache_Disabled_ShellFlags:
cacheString = "caching disabled by missing command-line arguments";
break;
case JS::AsmJSCache_Disabled_JitInspector:
cacheString = "caching disabled by active JIT inspector";
break;
case JS::AsmJSCache_InternalError:
cacheString =
"unable to store in cache due to internal error (consider filing a "
"bug)";
break;
case JS::AsmJSCache_Disabled_PrivateBrowsing:
cacheString = "caching disabled by private browsing mode";
break;
case JS::AsmJSCache_LIMIT:
MOZ_CRASH("bad AsmJSCacheResult");
break;
}
return JS_smprintf("total compilation time %dms; %s", time, cacheString);
#else
return DuplicateString("");
#endif
}
bool js::CompileAsmJS(JSContext* cx, AsmJSParser<char16_t>& parser,
ParseNode* stmtList, bool* validated) {
*validated = false;
@ -7589,40 +7146,18 @@ bool js::CompileAsmJS(JSContext* cx, AsmJSParser<char16_t>& parser,
return NoExceptionPending(cx);
}
// Before spending any time parsing the module, try to look it up in the
// embedding's cache using the chars about to be parsed as the key.
bool loadedFromCache;
// Validate and generate code in a single linear pass over the chars of the
// asm.js module.
SharedModule module;
UniqueChars message;
if (!LookupAsmJSModuleInCache(cx, parser, &loadedFromCache, &module,
&message)) {
return false;
}
// If not present in the cache, parse, validate and generate code in a
// single linear pass over the chars of the asm.js module.
if (!loadedFromCache) {
unsigned time;
{
// "Checking" parses, validates and compiles, producing a fully compiled
// WasmModuleObject as result.
UniqueLinkData linkData;
unsigned time;
module = CheckModule(cx, parser, stmtList, &linkData, &time);
if (!module) {
return NoExceptionPending(cx);
}
// Try to store the AsmJSModule in the embedding's cache. The
// AsmJSModule must be stored before static linking since static linking
// specializes the AsmJSModule to the current process's address space
// and therefore must be executed after a cache hit.
JS::AsmJSCacheResult cacheResult =
StoreAsmJSModuleInCache(parser, *module, *linkData, cx);
// Build the string message to display in the developer console.
message = BuildConsoleMessage(time, cacheResult);
if (!message) {
return NoExceptionPending(cx);
}
}
// Hand over ownership to a GC object wrapper which can then be referenced
@ -7649,9 +7184,10 @@ bool js::CompileAsmJS(JSContext* cx, AsmJSParser<char16_t>& parser,
MOZ_ASSERT(funbox->function()->isInterpreted());
funbox->clobberFunction(moduleFun);
// Success! Write to the console with a "warning" message.
// Success! Write to the console with a "warning" message indicating
// total compilation time.
*validated = true;
SuccessfulValidation(parser, std::move(message));
SuccessfulValidation(parser, time);
return NoExceptionPending(cx);
}
@ -7737,26 +7273,6 @@ bool js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
bool js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSFunction* fun = MaybeWrappedNativeFunction(args.get(0));
if (!fun || !IsAsmJSModule(fun)) {
JS_ReportErrorNumberUTF8(
cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
"argument passed to isAsmJSModuleLoadedFromCache is not a "
"validated asm.js module");
return false;
}
bool loadedFromCache =
AsmJSModuleFunctionToModule(fun).metadata().asAsmJS().cacheResult ==
CacheResult::Hit;
args.rval().set(BooleanValue(loadedFromCache));
return true;
}
/*****************************************************************************/
// asm.js toString/toSource support

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

@ -84,9 +84,6 @@ extern bool IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc,
extern bool IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
extern bool IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc,
JS::Value* vp);
extern bool IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
// asm.js toString/toSource support: