/* -*- 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 "vm/GlobalObject.h" #include "jsinferinlines.h" using namespace js; using namespace js::frontend; /* * Compile a top-level script. */ BytecodeCompiler::BytecodeCompiler(JSContext *cx, JSPrincipals *prin, StackFrame *cfp) : parser(cx, prin, cfp), globalScope(NULL) {} JSScript * BytecodeCompiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, JSPrincipals *principals, uint32 tcflags, const jschar *chars, size_t length, const char *filename, uintN lineno, JSVersion version, JSString *source /* = NULL */, uintN staticLevel /* = 0 */) { TokenKind tt; ParseNode *pn; JSScript *script; bool inDirectivePrologue; JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_GLOBAL))); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); JS_ASSERT_IF(staticLevel != 0, callerFrame); BytecodeCompiler compiler(cx, principals, callerFrame); if (!compiler.init(chars, length, filename, lineno, version)) return NULL; Parser &parser = compiler.parser; TokenStream &tokenStream = parser.tokenStream; BytecodeEmitter bce(&parser, tokenStream.getLineno()); if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT)) 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->getGlobal() ? scopeChain->getGlobal() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); /* Null script early in case of error, to reduce our code footprint. */ script = NULL; GlobalScope globalScope(cx, globalObj, &bce); bce.flags |= tcflags; bce.setScopeChain(scopeChain); compiler.globalScope = &globalScope; if (!SetStaticLevel(&bce, staticLevel)) goto out; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) { bce.flags |= TCF_STRICT_MODE_CODE; tokenStream.setStrictMode(); } #ifdef DEBUG bool savedCallerFun; savedCallerFun = false; #endif if (tcflags & TCF_COMPILE_N_GO) { 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 bodyid; if (!GenerateBlockId(&bce, bodyid)) goto out; bce.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; JS_ASSERT(!bce.blockNode); if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) goto out; if (!FoldConstants(cx, pn, &bce)) goto out; if (!parser.analyzeFunctions(&bce)) goto out; bce.functionList = NULL; if (!EmitTree(cx, &bce, pn)) goto out; #if JS_HAS_XML_SUPPORT if (!pn->isKind(TOK_SEMI) || !pn->pn_kid || !TreeTypeIsXML(pn->pn_kid->getKind())) onlyXML = false; #endif bce.freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via