Bug 1659104 - Part 4 - Shell and testing functions for Stencil XDR. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D90513
This commit is contained in:
Kannan Vijayan 2020-09-24 22:21:21 +00:00
Родитель d775f14cc9
Коммит a404783ac7
2 изменённых файлов: 225 добавлений и 0 удалений

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

@ -43,6 +43,8 @@
#ifdef DEBUG
# include "frontend/TokenStream.h"
#endif
#include "frontend/BytecodeCompilation.h"
#include "frontend/CompilationInfo.h"
#include "gc/Allocator.h"
#include "gc/Zone.h"
#include "jit/BaselineJIT.h"
@ -100,6 +102,7 @@
#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
#include "vm/ProxyObject.h"
#include "vm/SavedStacks.h"
#include "vm/ScopeKind.h"
#include "vm/Stack.h"
#include "vm/StringType.h"
#include "vm/TraceLogging.h"
@ -4872,6 +4875,124 @@ static bool SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
static bool CompileStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "compileStencilXDR", 1)) {
return false;
}
RootedString src(cx, ToString<CanGC>(cx, args[0]));
if (!src) {
return false;
}
/* TODO: Retrieve these from an optional `config` object. */
const char* filename = "compileStencilXDR-DATA.js";
uint32_t lineno = 1;
/* Linearize the string to obtain a char16_t* range. */
AutoStableStringChars linearChars(cx);
if (!linearChars.initTwoByte(cx, src)) {
return false;
}
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, linearChars.twoByteChars(), src->length(),
JS::SourceOwnership::Borrowed)) {
return false;
}
/* Compile the script text to stencil. */
CompileOptions options(cx);
options.setFileAndLine(filename, lineno);
/* TODO: StencilXDR - Add option to select between full and syntax parse. */
options.setForceFullParse();
Rooted<frontend::CompilationInfo> compilationInfo(
cx, frontend::CompilationInfo(cx, options));
if (!compilationInfo.get().input.initForGlobal(cx)) {
return false;
}
if (!frontend::CompileGlobalScriptToStencil(cx, compilationInfo.get(), srcBuf,
ScopeKind::Global)) {
return false;
}
/* Serialize the stencil to XDR. */
JS::TranscodeBuffer xdrBytes;
if (!compilationInfo.get().serializeStencils(cx, xdrBytes)) {
return false;
}
/* Dump the bytes into a javascript ArrayBuffer and return a UInt8Array. */
RootedObject arrayBuf(cx, JS::NewArrayBuffer(cx, xdrBytes.length()));
if (!arrayBuf) {
return false;
}
{
JS::AutoAssertNoGC nogc;
bool isSharedMemory = false;
uint8_t* data = JS::GetArrayBufferData(arrayBuf, &isSharedMemory, nogc);
std::copy(xdrBytes.begin(), xdrBytes.end(), data);
}
args.rval().setObject(*arrayBuf);
return true;
}
static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "evalStencilXDR", 1)) {
return false;
}
/* Prepare the input byte array. */
if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
JS_ReportErrorASCII(cx, "evalStencilXDR: ArrayBuffer expected");
return false;
}
RootedArrayBufferObject src(cx, &args[0].toObject().as<ArrayBufferObject>());
const char* filename = "compileStencilXDR-DATA.js";
uint32_t lineno = 1;
/* Prepare the CompilationInfo for decoding. */
CompileOptions options(cx);
options.setFileAndLine(filename, lineno);
options.setForceFullParse();
Rooted<frontend::CompilationInfo> compilationInfo(
cx, frontend::CompilationInfo(cx, options));
if (!compilationInfo.get().input.initForGlobal(cx)) {
return false;
}
/* Deserialize the stencil from XDR. */
JS::TranscodeRange xdrRange(src->dataPointer(), src->byteLength());
if (!compilationInfo.get().deserializeStencils(cx, xdrRange)) {
return false;
}
/* Instantiate the stencil. */
frontend::CompilationGCOutput output(cx);
if (!compilationInfo.get().instantiateStencils(cx, output)) {
return false;
}
/* Obtain the JSScript and evaluate it. */
RootedScript script(cx, output.script);
RootedValue retVal(cx, UndefinedValue());
if (!JS_ExecuteScript(cx, script, &retVal)) {
return false;
}
args.rval().set(retVal);
return true;
}
static bool SetDiscardSource(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
@ -7188,6 +7309,16 @@ JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
" An empty string or undefined resets the runtime locale to its default value.\n"
" NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
JS_FN_HELP("compileStencilXDR", CompileStencilXDR, 1, 0,
"compileStencilXDR(string)",
" Parses the given string argument as js script, produces the stencil"
" for it, XDR-encodes the stencil, and returns an ArrayBuf of the contents."),
JS_FN_HELP("evalStencilXDR", EvalStencilXDR, 1, 0,
"evalStencilXDR(arrayBuf)",
" Reads the given buffer as an XDR-encoded stencil, and evaluates the"
" top-level script it defines."),
JS_FS_HELP_END
};
// clang-format on

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

@ -0,0 +1,94 @@
load(libdir + "asserts.js");
/*
* This exercises stencil XDR encoding and decoding using a broad
* smoke testing.
*
* A set of scripts exercising various codepaths are XDR-encoded,
* then decoded, and then executed. Their output is compared to
* the execution of the scripts through a normal path and the
* outputs checked.
*/
/*
* Exercises global scope access and object literals, as well as some
* simple object destructuring.
*/
const testGlobal0 = 13;
let testGlobal1 = undefined;
var testGlobal2 = undefined;
const SCRIPT_0 = `
testGlobal1 = 123456789012345678901234567890n;
testGlobal2 = {'foo':3, 'bar': [1, 2, 3],
'4': 'zing'};
var testGlobal3 = /NewlyDefinedGlobal/;
function testGlobal4(a, {b}) {
return a + b.foo + b['bar'].reduce((a,b) => (a+b), 0);
+ b[4].length +
+ testGlobal3.toString().length;
};
testGlobal4(Number(testGlobal1), {b:testGlobal2})
`;
/*
* Exercises function scopes, lexical scopes, var and let
* within them, and some longer identifiers, and array destructuring in
* arguments. Also contains some tiny atoms and globls access.
*/
const SCRIPT_1 = `
function foo(a, b, c) {
var q = a * (b + c);
let bix = function (d, e) {
let x = a + d;
var y = e * b;
const a0 = q + x + y;
for (let i = 0; i < 3; i++) {
y = a0 + Math.PI + y;
}
return y;
};
function bang(d, [e, f]) {
let reallyLongIdentifierName = a + d;
var y = e * b;
const z = reallyLongIdentifierName + f;
return z;
}
return bix(1, 2) + bang(3, [4, 5, 6]);
}
foo(1, 2, 3)
`;
/*
* Exercises eval and with scopes, object destructuring, function rest
* arguments.
*/
const SCRIPT_2 = `
function foo(with_obj, ...xs) {
const [x0, x1, ...xrest] = xs;
eval('var x2 = x0 + x1');
var sum = [];
with (with_obj) {
sum.push(x2 + xrest.length);
}
sum.push(x2 + xrest.length);
return sum;
}
foo({x2: 99}, 1, 2, 3, 4, 5, 6)
`;
function test_script(script_str) {
const eval_f = eval;
const bytes = compileStencilXDR(script_str);
const result = evalStencilXDR(bytes);
assertDeepEq(result, eval_f(script_str));
}
function tests() {
test_script(SCRIPT_0);
test_script(SCRIPT_1);
test_script(SCRIPT_2);
}
tests()