/js/src/frontend/BytecodeCompiler.cpp

http://github.com/zpao/v8monkey · C++ · 425 lines · 259 code · 59 blank · 107 comment · 74 complexity · e41053f080b7590c10da649d7792301a MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=99:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998-2011
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "frontend/BytecodeCompiler.h"
  41. #include "jsprobes.h"
  42. #include "frontend/BytecodeEmitter.h"
  43. #include "frontend/FoldConstants.h"
  44. #include "frontend/SemanticAnalysis.h"
  45. #include "vm/GlobalObject.h"
  46. #include "jsinferinlines.h"
  47. using namespace js;
  48. using namespace js::frontend;
  49. bool
  50. DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
  51. {
  52. JSObject *globalObj = globalScope.globalObj;
  53. /* Define and update global properties. */
  54. for (size_t i = 0; i < globalScope.defs.length(); i++) {
  55. GlobalScope::GlobalDef &def = globalScope.defs[i];
  56. /* Names that could be resolved ahead of time can be skipped. */
  57. if (!def.atom)
  58. continue;
  59. jsid id = ATOM_TO_JSID(def.atom);
  60. Value rval;
  61. if (def.funbox) {
  62. JSFunction *fun = def.funbox->function();
  63. /*
  64. * No need to check for redeclarations or anything, global
  65. * optimizations only take place if the property is not defined.
  66. */
  67. rval.setObject(*fun);
  68. types::AddTypePropertyId(cx, globalObj, id, rval);
  69. } else {
  70. rval.setUndefined();
  71. }
  72. /*
  73. * Don't update the type information when defining the property for the
  74. * global object, per the consistency rules for type properties. If the
  75. * property is only undefined before it is ever written, we can check
  76. * the global directly during compilation and avoid having to emit type
  77. * checks every time it is accessed in the script.
  78. */
  79. const Shape *shape =
  80. DefineNativeProperty(cx, globalObj, id, rval, JS_PropertyStub, JS_StrictPropertyStub,
  81. JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE);
  82. if (!shape)
  83. return false;
  84. def.knownSlot = shape->slot();
  85. }
  86. Vector<JSScript *, 16> worklist(cx);
  87. if (!worklist.append(script))
  88. return false;
  89. /*
  90. * Recursively walk through all scripts we just compiled. For each script,
  91. * go through all global uses. Each global use indexes into globalScope->defs.
  92. * Use this information to repoint each use to the correct slot in the global
  93. * object.
  94. */
  95. while (worklist.length()) {
  96. JSScript *outer = worklist.back();
  97. worklist.popBack();
  98. if (JSScript::isValidOffset(outer->objectsOffset)) {
  99. JSObjectArray *arr = outer->objects();
  100. /*
  101. * If this is an eval script, don't treat the saved caller function
  102. * stored in the first object slot as an inner function.
  103. */
  104. size_t start = outer->savedCallerFun ? 1 : 0;
  105. for (size_t i = start; i < arr->length; i++) {
  106. JSObject *obj = arr->vector[i];
  107. if (!obj->isFunction())
  108. continue;
  109. JSFunction *fun = obj->toFunction();
  110. JS_ASSERT(fun->isInterpreted());
  111. JSScript *inner = fun->script();
  112. if (outer->function() && outer->function()->isHeavyweight()) {
  113. outer->isOuterFunction = true;
  114. inner->isInnerFunction = true;
  115. }
  116. if (!JSScript::isValidOffset(inner->globalsOffset) &&
  117. !JSScript::isValidOffset(inner->objectsOffset)) {
  118. continue;
  119. }
  120. if (!worklist.append(inner))
  121. return false;
  122. }
  123. }
  124. if (!JSScript::isValidOffset(outer->globalsOffset))
  125. continue;
  126. GlobalSlotArray *globalUses = outer->globals();
  127. uint32_t nGlobalUses = globalUses->length;
  128. for (uint32_t i = 0; i < nGlobalUses; i++) {
  129. uint32_t index = globalUses->vector[i].slot;
  130. JS_ASSERT(index < globalScope.defs.length());
  131. globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
  132. }
  133. }
  134. return true;
  135. }
  136. JSScript *
  137. frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame,
  138. JSPrincipals *principals, JSPrincipals *originPrincipals,
  139. uint32_t tcflags,
  140. const jschar *chars, size_t length,
  141. const char *filename, uintN lineno, JSVersion version,
  142. JSString *source /* = NULL */,
  143. uintN staticLevel /* = 0 */)
  144. {
  145. TokenKind tt;
  146. ParseNode *pn;
  147. JSScript *script;
  148. bool inDirectivePrologue;
  149. JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_COMPILE_FOR_EVAL
  150. | TCF_NEED_SCRIPT_GLOBAL)));
  151. /*
  152. * The scripted callerFrame can only be given for compile-and-go scripts
  153. * and non-zero static level requires callerFrame.
  154. */
  155. JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
  156. JS_ASSERT_IF(staticLevel != 0, callerFrame);
  157. Parser parser(cx, principals, originPrincipals, callerFrame);
  158. if (!parser.init(chars, length, filename, lineno, version))
  159. return NULL;
  160. TokenStream &tokenStream = parser.tokenStream;
  161. BytecodeEmitter bce(&parser, tokenStream.getLineno());
  162. if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT))
  163. return NULL;
  164. Probes::compileScriptBegin(cx, filename, lineno);
  165. MUST_FLOW_THROUGH("out");
  166. // We can specialize a bit for the given scope chain if that scope chain is the global object.
  167. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global()
  168. ? &scopeChain->global()
  169. : NULL;
  170. JS_ASSERT_IF(globalObj, globalObj->isNative());
  171. JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass()));
  172. /* Null script early in case of error, to reduce our code footprint. */
  173. script = NULL;
  174. GlobalScope globalScope(cx, globalObj, &bce);
  175. bce.flags |= tcflags;
  176. bce.setScopeChain(scopeChain);
  177. bce.globalScope = &globalScope;
  178. if (!SetStaticLevel(&bce, staticLevel))
  179. goto out;
  180. /* If this is a direct call to eval, inherit the caller's strictness. */
  181. if (callerFrame &&
  182. callerFrame->isScriptFrame() &&
  183. callerFrame->script()->strictModeCode) {
  184. bce.flags |= TCF_STRICT_MODE_CODE;
  185. tokenStream.setStrictMode();
  186. }
  187. #ifdef DEBUG
  188. bool savedCallerFun;
  189. savedCallerFun = false;
  190. #endif
  191. if (tcflags & TCF_COMPILE_N_GO) {
  192. if (source) {
  193. /*
  194. * Save eval program source in script->atoms[0] for the
  195. * eval cache (see EvalCacheLookup in jsobj.cpp).
  196. */
  197. JSAtom *atom = js_AtomizeString(cx, source);
  198. jsatomid _;
  199. if (!atom || !bce.makeAtomIndex(atom, &_))
  200. goto out;
  201. }
  202. if (callerFrame && callerFrame->isFunctionFrame()) {
  203. /*
  204. * An eval script in a caller frame needs to have its enclosing
  205. * function captured in case it refers to an upvar, and someone
  206. * wishes to decompile it while it's running.
  207. */
  208. ObjectBox *funbox = parser.newObjectBox(callerFrame->fun());
  209. if (!funbox)
  210. goto out;
  211. funbox->emitLink = bce.objectList.lastbox;
  212. bce.objectList.lastbox = funbox;
  213. bce.objectList.length++;
  214. #ifdef DEBUG
  215. savedCallerFun = true;
  216. #endif
  217. }
  218. }
  219. /*
  220. * Inline this->statements to emit as we go to save AST space. We must
  221. * generate our script-body blockid since we aren't calling Statements.
  222. */
  223. uint32_t bodyid;
  224. if (!GenerateBlockId(&bce, bodyid))
  225. goto out;
  226. bce.bodyid = bodyid;
  227. #if JS_HAS_XML_SUPPORT
  228. pn = NULL;
  229. bool onlyXML;
  230. onlyXML = true;
  231. #endif
  232. inDirectivePrologue = true;
  233. tokenStream.setOctalCharacterEscape(false);
  234. for (;;) {
  235. tt = tokenStream.peekToken(TSF_OPERAND);
  236. if (tt <= TOK_EOF) {
  237. if (tt == TOK_EOF)
  238. break;
  239. JS_ASSERT(tt == TOK_ERROR);
  240. goto out;
  241. }
  242. pn = parser.statement();
  243. if (!pn)
  244. goto out;
  245. JS_ASSERT(!bce.blockNode);
  246. if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
  247. goto out;
  248. if (!FoldConstants(cx, pn, &bce))
  249. goto out;
  250. if (!AnalyzeFunctions(&bce))
  251. goto out;
  252. bce.functionList = NULL;
  253. if (!EmitTree(cx, &bce, pn))
  254. goto out;
  255. #if JS_HAS_XML_SUPPORT
  256. if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem())
  257. onlyXML = false;
  258. #endif
  259. bce.freeTree(pn);
  260. }
  261. #if JS_HAS_XML_SUPPORT
  262. /*
  263. * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
  264. * For background, see:
  265. *
  266. * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
  267. */
  268. if (pn && onlyXML && !callerFrame) {
  269. parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
  270. goto out;
  271. }
  272. #endif
  273. /*
  274. * Nowadays the threaded interpreter needs a stop instruction, so we
  275. * do have to emit that here.
  276. */
  277. if (Emit1(cx, &bce, JSOP_STOP) < 0)
  278. goto out;
  279. JS_ASSERT(bce.version() == version);
  280. script = JSScript::NewScriptFromEmitter(cx, &bce);
  281. if (!script)
  282. goto out;
  283. JS_ASSERT(script->savedCallerFun == savedCallerFun);
  284. if (!DefineGlobals(cx, globalScope, script))
  285. script = NULL;
  286. out:
  287. Probes::compileScriptEnd(cx, script, filename, lineno);
  288. return script;
  289. }
  290. /*
  291. * Compile a JS function body, which might appear as the value of an event
  292. * handler attribute in an HTML <INPUT> tag.
  293. */
  294. bool
  295. frontend::CompileFunctionBody(JSContext *cx, JSFunction *fun,
  296. JSPrincipals *principals, JSPrincipals *originPrincipals,
  297. Bindings *bindings, const jschar *chars, size_t length,
  298. const char *filename, uintN lineno, JSVersion version)
  299. {
  300. Parser parser(cx, principals, originPrincipals);
  301. if (!parser.init(chars, length, filename, lineno, version))
  302. return false;
  303. TokenStream &tokenStream = parser.tokenStream;
  304. BytecodeEmitter funbce(&parser, tokenStream.getLineno());
  305. if (!funbce.init(cx, TreeContext::USED_AS_TREE_CONTEXT))
  306. return false;
  307. funbce.flags |= TCF_IN_FUNCTION;
  308. funbce.setFunction(fun);
  309. funbce.bindings.transfer(cx, bindings);
  310. fun->setArgCount(funbce.bindings.countArgs());
  311. if (!GenerateBlockId(&funbce, funbce.bodyid))
  312. return false;
  313. /* FIXME: make Function format the source for a function definition. */
  314. ParseNode *fn = FunctionNode::create(PNK_NAME, &funbce);
  315. if (fn) {
  316. fn->pn_body = NULL;
  317. fn->pn_cookie.makeFree();
  318. uintN nargs = fun->nargs;
  319. if (nargs) {
  320. /*
  321. * NB: do not use AutoLocalNameArray because it will release space
  322. * allocated from cx->tempLifoAlloc by DefineArg.
  323. */
  324. Vector<JSAtom *> names(cx);
  325. if (!funbce.bindings.getLocalNameArray(cx, &names)) {
  326. fn = NULL;
  327. } else {
  328. for (uintN i = 0; i < nargs; i++) {
  329. if (!DefineArg(fn, names[i], i, &funbce)) {
  330. fn = NULL;
  331. break;
  332. }
  333. }
  334. }
  335. }
  336. }
  337. /*
  338. * After we're done parsing, we must fold constants, analyze any nested
  339. * functions, and generate code for this function, including a stop opcode
  340. * at the end.
  341. */
  342. ParseNode *pn = fn ? parser.functionBody(Parser::StatementListBody) : NULL;
  343. if (pn) {
  344. if (!CheckStrictParameters(cx, &funbce)) {
  345. pn = NULL;
  346. } else if (!tokenStream.matchToken(TOK_EOF)) {
  347. parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
  348. pn = NULL;
  349. } else if (!FoldConstants(cx, pn, &funbce)) {
  350. /* FoldConstants reported the error already. */
  351. pn = NULL;
  352. } else if (!AnalyzeFunctions(&funbce)) {
  353. pn = NULL;
  354. } else {
  355. if (fn->pn_body) {
  356. JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
  357. fn->pn_body->append(pn);
  358. fn->pn_body->pn_pos = pn->pn_pos;
  359. pn = fn->pn_body;
  360. }
  361. if (!EmitFunctionScript(cx, &funbce, pn))
  362. pn = NULL;
  363. }
  364. }
  365. return pn != NULL;
  366. }