Bug 944121: Add options argument to the JS shell's offThreadCompileScript function. For off-thread compilation, put off initializing some slots of ScriptSourceObject until after the compartment merge. r=bhackett

--HG--
rename : js/src/jit-test/tests/basic/offThreadCompileScript.js => js/src/jit-test/tests/basic/offThreadCompileScript-01.js
This commit is contained in:
Jim Blandy 2014-01-22 16:41:16 -08:00
parent 34f1d33ff0
commit 84f1ffacbe
7 changed files with 90 additions and 10 deletions

View File

@ -0,0 +1,17 @@
// Test offThreadCompileScript option handling.
offThreadCompileScript('Error()');
assertEq(!!runOffThreadScript().stack.match(/^@<string>:1\n/), true);
offThreadCompileScript('Error()',
{ fileName: "candelabra", lineNumber: 6502 });
assertEq(!!runOffThreadScript().stack.match(/^@candelabra:6502\n/), true);
var element = {};
offThreadCompileScript('Error()', { element: element }); // shouldn't crash
runOffThreadScript();
var elementAttribute = "molybdenum";
elementAttribute += elementAttribute + elementAttribute + elementAttribute;
offThreadCompileScript('Error()', { elementProperty: elementAttribute }); // shouldn't crash
runOffThreadScript();

View File

@ -990,6 +990,13 @@ ScriptSourceObject::element() const
return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
}
void
ScriptSourceObject::initElement(HandleObject element)
{
JS_ASSERT(getReservedSlot(ELEMENT_SLOT).isNull());
setReservedSlot(ELEMENT_SLOT, ObjectOrNullValue(element));
}
const Value &
ScriptSourceObject::elementProperty() const
{

View File

@ -497,6 +497,8 @@ class ScriptSourceObject : public JSObject
void setSource(ScriptSource *source);
JSObject *element() const;
void initElement(HandleObject element);
const Value &elementProperty() const;
private:

View File

@ -191,7 +191,7 @@ ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSC
JS::OffThreadCompileCallback callback, void *callbackData)
: cx(cx), options(initCx), chars(chars), length(length),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain),
exclusiveContextGlobal(initCx, exclusiveContextGlobal), callback(callback),
exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx), callback(callback),
callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false)
{
}
@ -199,7 +199,15 @@ ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSC
bool
ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
{
return this->options.copy(cx, options);
if (!this->options.copy(cx, options))
return false;
// Save those compilation options that the ScriptSourceObject can't
// point at while it's in the compilation's temporary compartment.
optionsElement = this->options.element();
this->options.setElement(nullptr);
return true;
}
void
@ -209,6 +217,17 @@ ParseTask::activate(JSRuntime *rt)
cx->enterCompartment(exclusiveContextGlobal->compartment());
}
void
ParseTask::finish()
{
if (script) {
// Initialize the ScriptSourceObject slots that we couldn't while the SSO
// was in the temporary compartment.
ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
sso.initElement(optionsElement);
}
}
ParseTask::~ParseTask()
{
// ParseTask takes over ownership of its input exclusive context.
@ -640,6 +659,7 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
// Move the parsed script and all its contents into the desired compartment.
gc::MergeCompartments(parseTask->cx->compartment(), parseTask->scopeChain->compartment());
parseTask->finish();
RootedScript script(rt, parseTask->script);

View File

@ -347,6 +347,13 @@ struct ParseTask
// Rooted pointer to the global object used by 'cx'.
PersistentRootedObject exclusiveContextGlobal;
// Saved GC-managed CompileOptions fields that will populate slots in
// the ScriptSourceObject. We create the ScriptSourceObject in the
// compilation's temporary compartment, so storing these values there
// at that point would create cross-compartment references. Instead we
// hold them here, and install them after merging the compartments.
PersistentRootedObject optionsElement;
// Callback invoked off the main thread when the parse finishes.
JS::OffThreadCompileCallback callback;
void *callbackData;
@ -367,6 +374,7 @@ struct ParseTask
bool init(JSContext *cx, const ReadOnlyCompileOptions &options);
void activate(JSRuntime *rt);
void finish();
~ParseTask();
};

View File

@ -3307,16 +3307,30 @@ OffThreadCompileScript(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JSString *scriptContents = args[0].toString();
JSAutoByteString fileNameBytes;
CompileOptions options(cx);
options.setFileAndLine("<string>", 1)
.setCompileAndGo(true)
options.setFileAndLine("<string>", 1);
if (args.length() >= 2) {
if (args[1].isPrimitive()) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
return false;
}
RootedObject opts(cx, &args[1].toObject());
if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
return false;
}
// These option settings must override whatever the caller requested.
options.setCompileAndGo(true)
.setSourcePolicy(CompileOptions::SAVE_SOURCE);
// We assume the caller wants caching if at all possible, ignoring
// heuristics that make sense for a real browser.
options.forceAsync = true;
JSString *scriptContents = args[0].toString();
const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
if (!chars)
return false;
@ -4050,8 +4064,8 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" catchTermination: if true, catch termination (failure without\n"
" an exception value, as for slow scripts or out-of-memory)\n"
" and return 'terminated'\n"
" element: if present with value |v|, convert |v| to an object |o| mark\n"
" the source as being attached to the DOM element |o|. If the\n"
" element: if present with value |v|, convert |v| to an object |o| and\n"
" mark the source as being attached to the DOM element |o|. If the\n"
" property is omitted or |v| is null, don't attribute the source to\n"
" any DOM element.\n"
" elementProperty: if present and not undefined, the name of property\n"
@ -4245,14 +4259,26 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
#ifdef JS_THREADSAFE
JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
"offThreadCompileScript(code)",
" Trigger an off thread parse/emit for the input string"),
"offThreadCompileScript(code[, options])",
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
" and run the code, call |runOffThreadScript|. If present, |options| may\n"
" have properties saying how the code should be compiled:\n"
" noScriptRval: use the no-script-rval compiler option (default: false)\n"
" fileName: filename for error messages and debug info\n"
" lineNumber: starting line number for error messages and debug info\n"
" element: if present with value |v|, convert |v| to an object |o| and\n"
" mark the source as being attached to the DOM element |o|. If the\n"
" property is omitted or |v| is null, don't attribute the source to\n"
" any DOM element.\n"
" elementProperty: if present and not undefined, the name of property\n"
" of 'element' that holds this code. This is what Debugger.Source\n"
" .prototype.elementProperty returns.\n"),
JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
"runOffThreadScript()",
" Wait for off-thread compilation to complete. If an error occurred,\n"
" throw the appropriate exception; otherwise, run the script and return\n"
" its value."),
" its value."),
#endif