/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #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) { Root root(cx, &script); Vector worklist(cx); if (!worklist.append(script)) 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 */) { TokenKind tt; ParseNode *pn; bool inDirectivePrologue; /* * 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); bool foldConstants = true; Parser parser(cx, principals, originPrincipals, callerFrame, foldConstants, compileAndGo); if (!parser.init(chars, length, filename, lineno, version)) return NULL; TokenStream &tokenStream = parser.tokenStream; SharedContext sc(cx); TreeContext tc(&parser, &sc); if (!tc.init(cx)) return NULL; BytecodeEmitter bce(&parser, &sc, tokenStream.getLineno(), noScriptRval, needScriptGlobal); if (!bce.init()) return NULL; Probes::compileScriptBegin(cx, filename, lineno); MUST_FLOW_THROUGH("out"); // 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())); RootedVar script(cx); GlobalScope globalScope(cx, globalObj); bce.sc->setScopeChain(scopeChain); bce.globalScope = &globalScope; if (!SetStaticLevel(bce.sc, staticLevel)) goto out; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) { bce.sc->flags |= TCF_STRICT_MODE_CODE; tokenStream.setStrictMode(); } #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, &_)) goto out; } 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) goto out; 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)) goto out; bce.sc->bodyid = bodyid; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif inDirectivePrologue = true; tokenStream.setOctalCharacterEscape(false); for (;;) { tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); goto out; } pn = parser.statement(); if (!pn) goto out; if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) goto out; if (!FoldConstants(cx, pn, bce.parser)) goto out; if (!AnalyzeFunctions(bce.parser)) goto out; bce.sc->functionList = NULL; if (!EmitTree(cx, &bce, pn)) goto out; #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