/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=99: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "frontend/BytecodeCompiler.h" #include "jsprobes.h" #include "frontend/BytecodeEmitter.h" #include "frontend/FoldConstants.h" #include "frontend/SemanticAnalysis.h" #include "vm/GlobalObject.h" #include "jsinferinlines.h" #include "frontend/TreeContext-inl.h" using namespace js; using namespace js::frontend; bool MarkInnerAndOuterFunctions(JSContext *cx, JSScript* script_) { Rooted script(cx, script_); Vector worklist(cx); if (!worklist.append(script.reference())) return false; while (worklist.length()) { JSScript *outer = worklist.back(); worklist.popBack(); if (outer->hasObjects()) { ObjectArray *arr = outer->objects(); /* * If this is an eval script, don't treat the saved caller function * stored in the first object slot as an inner function. */ size_t start = outer->savedCallerFun ? 1 : 0; for (size_t i = start; i < arr->length; i++) { JSObject *obj = arr->vector[i]; if (!obj->isFunction()) continue; JSFunction *fun = obj->toFunction(); JS_ASSERT(fun->isInterpreted()); JSScript *inner = fun->script(); if (outer->function() && outer->function()->isHeavyweight()) { outer->isOuterFunction = true; inner->isInnerFunction = true; } if (!inner->hasObjects()) continue; if (!worklist.append(inner)) return false; } } } return true; } JSScript * frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval, bool needScriptGlobal, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source /* = NULL */, unsigned staticLevel /* = 0 */) { class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(filename, lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version, callerFrame, /* foldConstants = */ true, compileAndGo); if (!parser.init()) return NULL; SharedContext sc(cx, /* inFunction = */ false); TreeContext tc(&parser, &sc); if (!tc.init()) return NULL; BytecodeEmitter bce(&parser, &sc, lineno, noScriptRval, needScriptGlobal); if (!bce.init()) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() ? &scopeChain->global() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); GlobalScope globalScope(cx, globalObj); bce.sc->setScopeChain(scopeChain); bce.globalScope = &globalScope; if (!SetStaticLevel(bce.sc, staticLevel)) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) bce.sc->setInStrictMode(); #ifdef DEBUG bool savedCallerFun; savedCallerFun = false; #endif if (compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; #ifdef DEBUG savedCallerFun = true; #endif } } /* * Inline this->statements to emit as we go to save AST space. We must * generate our script-body blockid since we aren't calling Statements. */ uint32_t bodyid; if (!GenerateBlockId(bce.sc, bodyid)) return NULL; bce.sc->bodyid = bodyid; ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif bool inDirectivePrologue = true; TokenStream &tokenStream = parser.tokenStream; tokenStream.setOctalCharacterEscape(false); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) return NULL; if (!FoldConstants(cx, pn, bce.parser)) return NULL; if (!AnalyzeFunctions(bce.parser)) return NULL; bce.sc->functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif bce.parser->freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via