js/src/frontend/BytecodeCompiler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "frontend/BytecodeCompiler.h"
michael@0 8
michael@0 9 #include "jscntxt.h"
michael@0 10 #include "jsscript.h"
michael@0 11
michael@0 12 #include "frontend/BytecodeEmitter.h"
michael@0 13 #include "frontend/FoldConstants.h"
michael@0 14 #include "frontend/NameFunctions.h"
michael@0 15 #include "frontend/Parser.h"
michael@0 16 #include "jit/AsmJSLink.h"
michael@0 17 #include "vm/GlobalObject.h"
michael@0 18 #include "vm/TraceLogging.h"
michael@0 19
michael@0 20 #include "jsobjinlines.h"
michael@0 21 #include "jsscriptinlines.h"
michael@0 22
michael@0 23 #include "frontend/Parser-inl.h"
michael@0 24
michael@0 25 using namespace js;
michael@0 26 using namespace js::frontend;
michael@0 27 using mozilla::Maybe;
michael@0 28
michael@0 29 static bool
michael@0 30 CheckLength(ExclusiveContext *cx, SourceBufferHolder &srcBuf)
michael@0 31 {
michael@0 32 // Note this limit is simply so we can store sourceStart and sourceEnd in
michael@0 33 // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
michael@0 34 // is using size_t internally already.
michael@0 35 if (srcBuf.length() > UINT32_MAX) {
michael@0 36 if (cx->isJSContext())
michael@0 37 JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
michael@0 38 JSMSG_SOURCE_TOO_LONG);
michael@0 39 return false;
michael@0 40 }
michael@0 41 return true;
michael@0 42 }
michael@0 43
michael@0 44 static bool
michael@0 45 SetDisplayURL(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss)
michael@0 46 {
michael@0 47 if (tokenStream.hasDisplayURL()) {
michael@0 48 if (!ss->setDisplayURL(cx, tokenStream.displayURL()))
michael@0 49 return false;
michael@0 50 }
michael@0 51 return true;
michael@0 52 }
michael@0 53
michael@0 54 static bool
michael@0 55 SetSourceMap(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss)
michael@0 56 {
michael@0 57 if (tokenStream.hasSourceMapURL()) {
michael@0 58 if (!ss->setSourceMapURL(cx, tokenStream.sourceMapURL()))
michael@0 59 return false;
michael@0 60 }
michael@0 61 return true;
michael@0 62 }
michael@0 63
michael@0 64 static bool
michael@0 65 CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, HandleFunction fun)
michael@0 66 {
michael@0 67 if (fun->hasRest()) {
michael@0 68 // It's an error to use |arguments| in a function that has a rest
michael@0 69 // parameter.
michael@0 70 parser.report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);
michael@0 71 return false;
michael@0 72 }
michael@0 73
michael@0 74 // Force construction of arguments objects for functions that use
michael@0 75 // |arguments| within an eval.
michael@0 76 RootedScript script(cx, fun->getOrCreateScript(cx));
michael@0 77 if (!script)
michael@0 78 return false;
michael@0 79 if (script->argumentsHasVarBinding()) {
michael@0 80 if (!JSScript::argumentsOptimizationFailed(cx, script))
michael@0 81 return false;
michael@0 82 }
michael@0 83
michael@0 84 // It's an error to use |arguments| in a legacy generator expression.
michael@0 85 if (script->isGeneratorExp() && script->isLegacyGenerator()) {
michael@0 86 parser.report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
michael@0 87 return false;
michael@0 88 }
michael@0 89
michael@0 90 return true;
michael@0 91 }
michael@0 92
michael@0 93 static bool
michael@0 94 MaybeCheckEvalFreeVariables(ExclusiveContext *cxArg, HandleScript evalCaller, HandleObject scopeChain,
michael@0 95 Parser<FullParseHandler> &parser,
michael@0 96 ParseContext<FullParseHandler> &pc)
michael@0 97 {
michael@0 98 if (!evalCaller || !evalCaller->functionOrCallerFunction())
michael@0 99 return true;
michael@0 100
michael@0 101 // Eval scripts are only compiled on the main thread.
michael@0 102 JSContext *cx = cxArg->asJSContext();
michael@0 103
michael@0 104 // Watch for uses of 'arguments' within the evaluated script, both as
michael@0 105 // free variables and as variables redeclared with 'var'.
michael@0 106 RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
michael@0 107 HandlePropertyName arguments = cx->names().arguments;
michael@0 108 for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
michael@0 109 if (r.front().key() == arguments) {
michael@0 110 if (!CheckArgumentsWithinEval(cx, parser, fun))
michael@0 111 return false;
michael@0 112 }
michael@0 113 }
michael@0 114 for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
michael@0 115 if (r.front().key() == arguments) {
michael@0 116 if (!CheckArgumentsWithinEval(cx, parser, fun))
michael@0 117 return false;
michael@0 118 }
michael@0 119 }
michael@0 120
michael@0 121 // If the eval'ed script contains any debugger statement, force construction
michael@0 122 // of arguments objects for the caller script and any other scripts it is
michael@0 123 // transitively nested inside. The debugger can access any variable on the
michael@0 124 // scope chain.
michael@0 125 if (pc.sc->hasDebuggerStatement()) {
michael@0 126 RootedObject scope(cx, scopeChain);
michael@0 127 while (scope->is<ScopeObject>() || scope->is<DebugScopeObject>()) {
michael@0 128 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
michael@0 129 RootedScript script(cx, scope->as<CallObject>().callee().getOrCreateScript(cx));
michael@0 130 if (!script)
michael@0 131 return false;
michael@0 132 if (script->argumentsHasVarBinding()) {
michael@0 133 if (!JSScript::argumentsOptimizationFailed(cx, script))
michael@0 134 return false;
michael@0 135 }
michael@0 136 }
michael@0 137 scope = scope->enclosingScope();
michael@0 138 }
michael@0 139 }
michael@0 140
michael@0 141 return true;
michael@0 142 }
michael@0 143
michael@0 144 static inline bool
michael@0 145 CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
michael@0 146 {
michael@0 147 return options.canLazilyParse &&
michael@0 148 options.compileAndGo &&
michael@0 149 !cx->compartment()->options().discardSource() &&
michael@0 150 !options.sourceIsLazy &&
michael@0 151 !(cx->compartment()->debugMode() &&
michael@0 152 cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook);
michael@0 153 }
michael@0 154
michael@0 155 static void
michael@0 156 MarkFunctionsWithinEvalScript(JSScript *script)
michael@0 157 {
michael@0 158 // Mark top level functions in an eval script as being within an eval and,
michael@0 159 // if applicable, inside a with statement.
michael@0 160
michael@0 161 if (!script->hasObjects())
michael@0 162 return;
michael@0 163
michael@0 164 ObjectArray *objects = script->objects();
michael@0 165 size_t start = script->innerObjectsStart();
michael@0 166
michael@0 167 for (size_t i = start; i < objects->length; i++) {
michael@0 168 JSObject *obj = objects->vector[i];
michael@0 169 if (obj->is<JSFunction>()) {
michael@0 170 JSFunction *fun = &obj->as<JSFunction>();
michael@0 171 if (fun->hasScript())
michael@0 172 fun->nonLazyScript()->setDirectlyInsideEval();
michael@0 173 else if (fun->isInterpretedLazy())
michael@0 174 fun->lazyScript()->setDirectlyInsideEval();
michael@0 175 }
michael@0 176 }
michael@0 177 }
michael@0 178
michael@0 179 void
michael@0 180 frontend::MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options,
michael@0 181 SourceBufferHolder &srcBuf)
michael@0 182 {
michael@0 183 JSSourceHandler listener = cx->runtime()->debugHooks.sourceHandler;
michael@0 184 void *listenerData = cx->runtime()->debugHooks.sourceHandlerData;
michael@0 185
michael@0 186 if (listener) {
michael@0 187 void *listenerTSData;
michael@0 188 listener(options.filename(), options.lineno, srcBuf.get(), srcBuf.length(),
michael@0 189 &listenerTSData, listenerData);
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 ScriptSourceObject *
michael@0 194 frontend::CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
michael@0 195 {
michael@0 196 ScriptSource *ss = cx->new_<ScriptSource>();
michael@0 197 if (!ss)
michael@0 198 return nullptr;
michael@0 199 ScriptSourceHolder ssHolder(ss);
michael@0 200
michael@0 201 if (!ss->initFromOptions(cx, options))
michael@0 202 return nullptr;
michael@0 203
michael@0 204 return ScriptSourceObject::create(cx, ss, options);
michael@0 205 }
michael@0 206
michael@0 207 JSScript *
michael@0 208 frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain,
michael@0 209 HandleScript evalCaller,
michael@0 210 const ReadOnlyCompileOptions &options,
michael@0 211 SourceBufferHolder &srcBuf,
michael@0 212 JSString *source_ /* = nullptr */,
michael@0 213 unsigned staticLevel /* = 0 */,
michael@0 214 SourceCompressionTask *extraSct /* = nullptr */)
michael@0 215 {
michael@0 216 JS_ASSERT(srcBuf.get());
michael@0 217
michael@0 218 RootedString source(cx, source_);
michael@0 219
michael@0 220 js::TraceLogger *logger = nullptr;
michael@0 221 if (cx->isJSContext())
michael@0 222 logger = TraceLoggerForMainThread(cx->asJSContext()->runtime());
michael@0 223 else
michael@0 224 logger = TraceLoggerForCurrentThread();
michael@0 225 uint32_t logId = js::TraceLogCreateTextId(logger, options);
michael@0 226 js::AutoTraceLog scriptLogger(logger, logId);
michael@0 227 js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript);
michael@0 228
michael@0 229 if (cx->isJSContext())
michael@0 230 MaybeCallSourceHandler(cx->asJSContext(), options, srcBuf);
michael@0 231
michael@0 232 /*
michael@0 233 * The scripted callerFrame can only be given for compile-and-go scripts
michael@0 234 * and non-zero static level requires callerFrame.
michael@0 235 */
michael@0 236 JS_ASSERT_IF(evalCaller, options.compileAndGo);
michael@0 237 JS_ASSERT_IF(evalCaller, options.forEval);
michael@0 238 JS_ASSERT_IF(staticLevel != 0, evalCaller);
michael@0 239
michael@0 240 if (!CheckLength(cx, srcBuf))
michael@0 241 return nullptr;
michael@0 242 JS_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy);
michael@0 243
michael@0 244 RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
michael@0 245 if (!sourceObject)
michael@0 246 return nullptr;
michael@0 247
michael@0 248 ScriptSource *ss = sourceObject->source();
michael@0 249
michael@0 250 SourceCompressionTask mysct(cx);
michael@0 251 SourceCompressionTask *sct = extraSct ? extraSct : &mysct;
michael@0 252
michael@0 253 if (!cx->compartment()->options().discardSource()) {
michael@0 254 if (options.sourceIsLazy)
michael@0 255 ss->setSourceRetrievable();
michael@0 256 else if (!ss->setSourceCopy(cx, srcBuf, false, sct))
michael@0 257 return nullptr;
michael@0 258 }
michael@0 259
michael@0 260 bool canLazilyParse = CanLazilyParse(cx, options);
michael@0 261
michael@0 262 Maybe<Parser<SyntaxParseHandler> > syntaxParser;
michael@0 263 if (canLazilyParse) {
michael@0 264 syntaxParser.construct(cx, alloc, options, srcBuf.get(), srcBuf.length(),
michael@0 265 /* foldConstants = */ false,
michael@0 266 (Parser<SyntaxParseHandler> *) nullptr,
michael@0 267 (LazyScript *) nullptr);
michael@0 268 }
michael@0 269
michael@0 270 Parser<FullParseHandler> parser(cx, alloc, options, srcBuf.get(), srcBuf.length(),
michael@0 271 /* foldConstants = */ true,
michael@0 272 canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr);
michael@0 273 parser.sct = sct;
michael@0 274 parser.ss = ss;
michael@0 275
michael@0 276 Directives directives(options.strictOption);
michael@0 277 GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption);
michael@0 278
michael@0 279 bool savedCallerFun = options.compileAndGo &&
michael@0 280 evalCaller && evalCaller->functionOrCallerFunction();
michael@0 281 Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
michael@0 282 options, staticLevel, sourceObject, 0,
michael@0 283 srcBuf.length()));
michael@0 284 if (!script)
michael@0 285 return nullptr;
michael@0 286
michael@0 287 // We can specialize a bit for the given scope chain if that scope chain is the global object.
michael@0 288 JSObject *globalScope =
michael@0 289 scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr;
michael@0 290 JS_ASSERT_IF(globalScope, globalScope->isNative());
michael@0 291 JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
michael@0 292
michael@0 293 BytecodeEmitter::EmitterMode emitterMode =
michael@0 294 options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
michael@0 295 BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, options.forEval,
michael@0 296 evalCaller, !!globalScope, options.lineno, emitterMode);
michael@0 297 if (!bce.init())
michael@0 298 return nullptr;
michael@0 299
michael@0 300 // Syntax parsing may cause us to restart processing of top level
michael@0 301 // statements in the script. Use Maybe<> so that the parse context can be
michael@0 302 // reset when this occurs.
michael@0 303 Maybe<ParseContext<FullParseHandler> > pc;
michael@0 304
michael@0 305 pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc,
michael@0 306 (Directives *) nullptr, staticLevel, /* bodyid = */ 0,
michael@0 307 /* blockScopeDepth = */ 0);
michael@0 308 if (!pc.ref().init(parser.tokenStream))
michael@0 309 return nullptr;
michael@0 310
michael@0 311 /* If this is a direct call to eval, inherit the caller's strictness. */
michael@0 312 if (evalCaller && evalCaller->strict())
michael@0 313 globalsc.strict = true;
michael@0 314
michael@0 315 if (options.compileAndGo) {
michael@0 316 if (source) {
michael@0 317 /*
michael@0 318 * Save eval program source in script->atoms[0] for the
michael@0 319 * eval cache (see EvalCacheLookup in jsobj.cpp).
michael@0 320 */
michael@0 321 JSAtom *atom = AtomizeString(cx, source);
michael@0 322 jsatomid _;
michael@0 323 if (!atom || !bce.makeAtomIndex(atom, &_))
michael@0 324 return nullptr;
michael@0 325 }
michael@0 326
michael@0 327 if (evalCaller && evalCaller->functionOrCallerFunction()) {
michael@0 328 /*
michael@0 329 * An eval script in a caller frame needs to have its enclosing
michael@0 330 * function captured in case it refers to an upvar, and someone
michael@0 331 * wishes to decompile it while it's running.
michael@0 332 */
michael@0 333 JSFunction *fun = evalCaller->functionOrCallerFunction();
michael@0 334 Directives directives(/* strict = */ fun->strict());
michael@0 335 ObjectBox *funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.addr(),
michael@0 336 directives, fun->generatorKind());
michael@0 337 if (!funbox)
michael@0 338 return nullptr;
michael@0 339 bce.objectList.add(funbox);
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 bool canHaveDirectives = true;
michael@0 344 for (;;) {
michael@0 345 TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand);
michael@0 346 if (tt <= TOK_EOF) {
michael@0 347 if (tt == TOK_EOF)
michael@0 348 break;
michael@0 349 JS_ASSERT(tt == TOK_ERROR);
michael@0 350 return nullptr;
michael@0 351 }
michael@0 352
michael@0 353 TokenStream::Position pos(parser.keepAtoms);
michael@0 354 parser.tokenStream.tell(&pos);
michael@0 355
michael@0 356 ParseNode *pn = parser.statement(canHaveDirectives);
michael@0 357 if (!pn) {
michael@0 358 if (parser.hadAbortedSyntaxParse()) {
michael@0 359 // Parsing inner functions lazily may lead the parser into an
michael@0 360 // unrecoverable state and may require starting over on the top
michael@0 361 // level statement. Restart the parse; syntax parsing has
michael@0 362 // already been disabled for the parser and the result will not
michael@0 363 // be ambiguous.
michael@0 364 parser.clearAbortedSyntaxParse();
michael@0 365 parser.tokenStream.seek(pos);
michael@0 366
michael@0 367 // Destroying the parse context will destroy its free
michael@0 368 // variables, so check if any deoptimization is needed.
michael@0 369 if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
michael@0 370 return nullptr;
michael@0 371
michael@0 372 pc.destroy();
michael@0 373 pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr,
michael@0 374 &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0,
michael@0 375 script->bindings.numBlockScoped());
michael@0 376 if (!pc.ref().init(parser.tokenStream))
michael@0 377 return nullptr;
michael@0 378 JS_ASSERT(parser.pc == pc.addr());
michael@0 379
michael@0 380 pn = parser.statement();
michael@0 381 }
michael@0 382 if (!pn) {
michael@0 383 JS_ASSERT(!parser.hadAbortedSyntaxParse());
michael@0 384 return nullptr;
michael@0 385 }
michael@0 386 }
michael@0 387
michael@0 388 // Accumulate the maximum block scope depth, so that EmitTree can assert
michael@0 389 // when emitting JSOP_GETLOCAL that the local is indeed within the fixed
michael@0 390 // part of the stack frame.
michael@0 391 script->bindings.updateNumBlockScoped(pc.ref().blockScopeDepth);
michael@0 392
michael@0 393 if (canHaveDirectives) {
michael@0 394 if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives))
michael@0 395 return nullptr;
michael@0 396 }
michael@0 397
michael@0 398 if (!FoldConstants(cx, &pn, &parser))
michael@0 399 return nullptr;
michael@0 400
michael@0 401 if (!NameFunctions(cx, pn))
michael@0 402 return nullptr;
michael@0 403
michael@0 404 if (!EmitTree(cx, &bce, pn))
michael@0 405 return nullptr;
michael@0 406
michael@0 407 parser.handler.freeTree(pn);
michael@0 408 }
michael@0 409
michael@0 410 if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
michael@0 411 return nullptr;
michael@0 412
michael@0 413 if (!SetDisplayURL(cx, parser.tokenStream, ss))
michael@0 414 return nullptr;
michael@0 415
michael@0 416 if (!SetSourceMap(cx, parser.tokenStream, ss))
michael@0 417 return nullptr;
michael@0 418
michael@0 419 /*
michael@0 420 * Source map URLs passed as a compile option (usually via a HTTP source map
michael@0 421 * header) override any source map urls passed as comment pragmas.
michael@0 422 */
michael@0 423 if (options.sourceMapURL()) {
michael@0 424 if (!ss->setSourceMapURL(cx, options.sourceMapURL()))
michael@0 425 return nullptr;
michael@0 426 }
michael@0 427
michael@0 428 /*
michael@0 429 * Nowadays the threaded interpreter needs a last return instruction, so we
michael@0 430 * do have to emit that here.
michael@0 431 */
michael@0 432 if (Emit1(cx, &bce, JSOP_RETRVAL) < 0)
michael@0 433 return nullptr;
michael@0 434
michael@0 435 // Global/eval script bindings are always empty (all names are added to the
michael@0 436 // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped
michael@0 437 // locals, however, which are allocated to the fixed part of the stack
michael@0 438 // frame.
michael@0 439 InternalHandle<Bindings*> bindings(script, &script->bindings);
michael@0 440 if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, nullptr,
michael@0 441 pc.ref().blockScopeDepth))
michael@0 442 return nullptr;
michael@0 443
michael@0 444 if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
michael@0 445 return nullptr;
michael@0 446
michael@0 447 // Note that this marking must happen before we tell Debugger
michael@0 448 // about the new script, in case Debugger delazifies the script's
michael@0 449 // inner functions.
michael@0 450 if (options.forEval)
michael@0 451 MarkFunctionsWithinEvalScript(script);
michael@0 452
michael@0 453 bce.tellDebuggerAboutCompiledScript(cx);
michael@0 454
michael@0 455 if (sct && !extraSct && !sct->complete())
michael@0 456 return nullptr;
michael@0 457
michael@0 458 return script;
michael@0 459 }
michael@0 460
michael@0 461 bool
michael@0 462 frontend::CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const jschar *chars, size_t length)
michael@0 463 {
michael@0 464 JS_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
michael@0 465
michael@0 466 CompileOptions options(cx, lazy->version());
michael@0 467 options.setOriginPrincipals(lazy->originPrincipals())
michael@0 468 .setFileAndLine(lazy->source()->filename(), lazy->lineno())
michael@0 469 .setColumn(lazy->column())
michael@0 470 .setCompileAndGo(true)
michael@0 471 .setNoScriptRval(false)
michael@0 472 .setSelfHostingMode(false);
michael@0 473
michael@0 474 js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
michael@0 475 uint32_t logId = js::TraceLogCreateTextId(logger, options);
michael@0 476 js::AutoTraceLog scriptLogger(logger, logId);
michael@0 477 js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileLazy);
michael@0 478
michael@0 479 Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
michael@0 480 /* foldConstants = */ true, nullptr, lazy);
michael@0 481
michael@0 482 uint32_t staticLevel = lazy->staticLevel(cx);
michael@0 483
michael@0 484 Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
michael@0 485 JS_ASSERT(!lazy->isLegacyGenerator());
michael@0 486 ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
michael@0 487 lazy->generatorKind());
michael@0 488 if (!pn)
michael@0 489 return false;
michael@0 490
michael@0 491 if (!NameFunctions(cx, pn))
michael@0 492 return false;
michael@0 493
michael@0 494 RootedObject enclosingScope(cx, lazy->enclosingScope());
michael@0 495 RootedScriptSource sourceObject(cx, lazy->sourceObject());
michael@0 496 JS_ASSERT(sourceObject);
michael@0 497
michael@0 498 Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
michael@0 499 options, staticLevel,
michael@0 500 sourceObject, lazy->begin(), lazy->end()));
michael@0 501 if (!script)
michael@0 502 return false;
michael@0 503
michael@0 504 script->bindings = pn->pn_funbox->bindings;
michael@0 505
michael@0 506 if (lazy->directlyInsideEval())
michael@0 507 script->setDirectlyInsideEval();
michael@0 508 if (lazy->usesArgumentsAndApply())
michael@0 509 script->setUsesArgumentsAndApply();
michael@0 510 if (lazy->hasBeenCloned())
michael@0 511 script->setHasBeenCloned();
michael@0 512
michael@0 513 BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, options.forEval,
michael@0 514 /* evalCaller = */ NullPtr(), /* hasGlobalScope = */ true,
michael@0 515 options.lineno, BytecodeEmitter::LazyFunction);
michael@0 516 if (!bce.init())
michael@0 517 return false;
michael@0 518
michael@0 519 if (lazy->treatAsRunOnce())
michael@0 520 bce.lazyRunOnceLambda = true;
michael@0 521
michael@0 522 return EmitFunctionScript(cx, &bce, pn->pn_body);
michael@0 523 }
michael@0 524
michael@0 525 // Compile a JS function body, which might appear as the value of an event
michael@0 526 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
michael@0 527 static bool
michael@0 528 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
michael@0 529 const AutoNameVector &formals, SourceBufferHolder &srcBuf,
michael@0 530 GeneratorKind generatorKind)
michael@0 531 {
michael@0 532 js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
michael@0 533 uint32_t logId = js::TraceLogCreateTextId(logger, options);
michael@0 534 js::AutoTraceLog scriptLogger(logger, logId);
michael@0 535 js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction);
michael@0 536
michael@0 537 // FIXME: make Function pass in two strings and parse them as arguments and
michael@0 538 // ProgramElements respectively.
michael@0 539
michael@0 540 MaybeCallSourceHandler(cx, options, srcBuf);
michael@0 541
michael@0 542 if (!CheckLength(cx, srcBuf))
michael@0 543 return false;
michael@0 544
michael@0 545 RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
michael@0 546 if (!sourceObject)
michael@0 547 return nullptr;
michael@0 548 ScriptSource *ss = sourceObject->source();
michael@0 549
michael@0 550 SourceCompressionTask sct(cx);
michael@0 551 JS_ASSERT(!options.sourceIsLazy);
michael@0 552 if (!cx->compartment()->options().discardSource()) {
michael@0 553 if (!ss->setSourceCopy(cx, srcBuf, true, &sct))
michael@0 554 return false;
michael@0 555 }
michael@0 556
michael@0 557 bool canLazilyParse = CanLazilyParse(cx, options);
michael@0 558
michael@0 559 Maybe<Parser<SyntaxParseHandler> > syntaxParser;
michael@0 560 if (canLazilyParse) {
michael@0 561 syntaxParser.construct(cx, &cx->tempLifoAlloc(),
michael@0 562 options, srcBuf.get(), srcBuf.length(),
michael@0 563 /* foldConstants = */ false,
michael@0 564 (Parser<SyntaxParseHandler> *) nullptr,
michael@0 565 (LazyScript *) nullptr);
michael@0 566 }
michael@0 567
michael@0 568 JS_ASSERT(!options.forEval);
michael@0 569
michael@0 570 Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
michael@0 571 options, srcBuf.get(), srcBuf.length(),
michael@0 572 /* foldConstants = */ true,
michael@0 573 canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr);
michael@0 574 parser.sct = &sct;
michael@0 575 parser.ss = ss;
michael@0 576
michael@0 577 JS_ASSERT(fun);
michael@0 578 JS_ASSERT(fun->isTenured());
michael@0 579
michael@0 580 fun->setArgCount(formals.length());
michael@0 581
michael@0 582 // Speculatively parse using the default directives implied by the context.
michael@0 583 // If a directive is encountered (e.g., "use strict") that changes how the
michael@0 584 // function should have been parsed, we backup and reparse with the new set
michael@0 585 // of directives.
michael@0 586 Directives directives(options.strictOption);
michael@0 587
michael@0 588 TokenStream::Position start(parser.keepAtoms);
michael@0 589 parser.tokenStream.tell(&start);
michael@0 590
michael@0 591 ParseNode *fn;
michael@0 592 while (true) {
michael@0 593 Directives newDirectives = directives;
michael@0 594 fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives);
michael@0 595 if (fn)
michael@0 596 break;
michael@0 597
michael@0 598 if (parser.hadAbortedSyntaxParse()) {
michael@0 599 // Hit some unrecoverable ambiguity during an inner syntax parse.
michael@0 600 // Syntax parsing has now been disabled in the parser, so retry
michael@0 601 // the parse.
michael@0 602 parser.clearAbortedSyntaxParse();
michael@0 603 } else {
michael@0 604 if (parser.tokenStream.hadError() || directives == newDirectives)
michael@0 605 return false;
michael@0 606
michael@0 607 // Assignment must be monotonic to prevent reparsing iloops
michael@0 608 JS_ASSERT_IF(directives.strict(), newDirectives.strict());
michael@0 609 JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
michael@0 610 directives = newDirectives;
michael@0 611 }
michael@0 612
michael@0 613 parser.tokenStream.seek(start);
michael@0 614 }
michael@0 615
michael@0 616 if (!NameFunctions(cx, fn))
michael@0 617 return false;
michael@0 618
michael@0 619 if (fn->pn_funbox->function()->isInterpreted()) {
michael@0 620 JS_ASSERT(fun == fn->pn_funbox->function());
michael@0 621
michael@0 622 Rooted<JSScript*> script(cx, JSScript::Create(cx, js::NullPtr(), false, options,
michael@0 623 /* staticLevel = */ 0, sourceObject,
michael@0 624 /* sourceStart = */ 0, srcBuf.length()));
michael@0 625 if (!script)
michael@0 626 return false;
michael@0 627
michael@0 628 script->bindings = fn->pn_funbox->bindings;
michael@0 629
michael@0 630 /*
michael@0 631 * The reason for checking fun->environment() below is that certain
michael@0 632 * consumers of JS::CompileFunction, namely
michael@0 633 * EventListenerManager::CompileEventHandlerInternal, passes in a
michael@0 634 * nullptr environment. This compiled function is never used, but
michael@0 635 * instead is cloned immediately onto the right scope chain.
michael@0 636 */
michael@0 637 BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script,
michael@0 638 /* insideEval = */ false, /* evalCaller = */ js::NullPtr(),
michael@0 639 fun->environment() && fun->environment()->is<GlobalObject>(),
michael@0 640 options.lineno);
michael@0 641 if (!funbce.init())
michael@0 642 return false;
michael@0 643
michael@0 644 if (!EmitFunctionScript(cx, &funbce, fn->pn_body))
michael@0 645 return false;
michael@0 646 } else {
michael@0 647 fun.set(fn->pn_funbox->function());
michael@0 648 JS_ASSERT(IsAsmJSModuleNative(fun->native()));
michael@0 649 }
michael@0 650
michael@0 651 if (!SetDisplayURL(cx, parser.tokenStream, ss))
michael@0 652 return false;
michael@0 653
michael@0 654 if (!SetSourceMap(cx, parser.tokenStream, ss))
michael@0 655 return false;
michael@0 656
michael@0 657 if (!sct.complete())
michael@0 658 return false;
michael@0 659
michael@0 660 return true;
michael@0 661 }
michael@0 662
michael@0 663 bool
michael@0 664 frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun,
michael@0 665 const ReadOnlyCompileOptions &options,
michael@0 666 const AutoNameVector &formals, JS::SourceBufferHolder &srcBuf)
michael@0 667 {
michael@0 668 return CompileFunctionBody(cx, fun, options, formals, srcBuf, NotGenerator);
michael@0 669 }
michael@0 670
michael@0 671 bool
michael@0 672 frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun,
michael@0 673 const ReadOnlyCompileOptions &options, const AutoNameVector &formals,
michael@0 674 JS::SourceBufferHolder &srcBuf)
michael@0 675 {
michael@0 676 return CompileFunctionBody(cx, fun, options, formals, srcBuf, StarGenerator);
michael@0 677 }

mercurial