PageRenderTime 53ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsinterp.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2138 lines | 1569 code | 225 blank | 344 comment | 373 complexity | 00b5af12134531fe470209df5d04e58b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=79:
  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
  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 of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or 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. /*
  41. * JavaScript bytecode interpreter.
  42. */
  43. #include "jsstddef.h"
  44. #include <stdio.h>
  45. #include <string.h>
  46. #include <math.h>
  47. #include "jstypes.h"
  48. #include "jsarena.h" /* Added by JSIFY */
  49. #include "jsutil.h" /* Added by JSIFY */
  50. #include "jsprf.h"
  51. #include "jsapi.h"
  52. #include "jsarray.h"
  53. #include "jsatom.h"
  54. #include "jsbool.h"
  55. #include "jscntxt.h"
  56. #include "jsversion.h"
  57. #include "jsdbgapi.h"
  58. #include "jsfun.h"
  59. #include "jsgc.h"
  60. #include "jsinterp.h"
  61. #include "jsiter.h"
  62. #include "jslock.h"
  63. #include "jsnum.h"
  64. #include "jsobj.h"
  65. #include "jsopcode.h"
  66. #include "jsscan.h"
  67. #include "jsscope.h"
  68. #include "jsscript.h"
  69. #include "jsstr.h"
  70. #include "jsstaticcheck.h"
  71. #include "jstracer.h"
  72. #ifdef INCLUDE_MOZILLA_DTRACE
  73. #include "jsdtracef.h"
  74. #endif
  75. #if JS_HAS_XML_SUPPORT
  76. #include "jsxml.h"
  77. #endif
  78. #include "jsautooplen.h"
  79. /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
  80. #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
  81. uint32
  82. js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop)
  83. {
  84. JSRuntime *rt;
  85. uint32 shape;
  86. JSTempValueRooter tvr;
  87. rt = cx->runtime;
  88. shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
  89. JS_ASSERT(shape != 0);
  90. if (shape & SHAPE_OVERFLOW_BIT) {
  91. rt->gcPoke = JS_TRUE;
  92. if (sprop)
  93. JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
  94. js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);
  95. if (sprop)
  96. JS_POP_TEMP_ROOT(cx, &tvr);
  97. shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
  98. JS_ASSERT(shape != 0);
  99. JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,
  100. JS_PROPERTY_CACHE(cx).disabled);
  101. }
  102. return shape;
  103. }
  104. void
  105. js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
  106. uintN scopeIndex, uintN protoIndex,
  107. JSObject *pobj, JSScopeProperty *sprop,
  108. JSPropCacheEntry **entryp)
  109. {
  110. JSPropertyCache *cache;
  111. jsbytecode *pc;
  112. JSScope *scope;
  113. JSOp op;
  114. const JSCodeSpec *cs;
  115. jsuword vword;
  116. ptrdiff_t pcoff;
  117. jsuword khash;
  118. JSAtom *atom;
  119. JSPropCacheEntry *entry;
  120. JS_ASSERT(!cx->runtime->gcRunning);
  121. cache = &JS_PROPERTY_CACHE(cx);
  122. pc = cx->fp->regs->pc;
  123. if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {
  124. PCMETER(cache->disfills++);
  125. *entryp = NULL;
  126. return;
  127. }
  128. /*
  129. * Check for fill from js_SetPropertyHelper where the setter removed sprop
  130. * from pobj's scope (via unwatch or delete, e.g.).
  131. */
  132. scope = OBJ_SCOPE(pobj);
  133. JS_ASSERT(scope->object == pobj);
  134. if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
  135. PCMETER(cache->oddfills++);
  136. *entryp = NULL;
  137. return;
  138. }
  139. /*
  140. * Check for overdeep scope and prototype chain. Because resolve, getter,
  141. * and setter hooks can change the prototype chain using JS_SetPrototype
  142. * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
  143. * we have to validate protoIndex if it is non-zero. If it is zero, then
  144. * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact
  145. * that obj == pobj, that protoIndex is invariant.
  146. *
  147. * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
  148. * before any running script might consult a parent-linked scope chain. If
  149. * this requirement is not satisfied, the fill in progress will never hit,
  150. * but vcap vs. scope shape tests ensure nothing malfunctions.
  151. */
  152. JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
  153. if (protoIndex != 0) {
  154. JSObject *tmp;
  155. JS_ASSERT(pobj != obj);
  156. protoIndex = 1;
  157. tmp = obj;
  158. for (;;) {
  159. tmp = OBJ_GET_PROTO(cx, tmp);
  160. if (!tmp) {
  161. PCMETER(cache->noprotos++);
  162. *entryp = NULL;
  163. return;
  164. }
  165. if (tmp == pobj)
  166. break;
  167. ++protoIndex;
  168. }
  169. }
  170. if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
  171. PCMETER(cache->longchains++);
  172. *entryp = NULL;
  173. return;
  174. }
  175. /*
  176. * Optimize the cached vword based on our parameters and the current pc's
  177. * opcode format flags.
  178. */
  179. op = (JSOp) *pc;
  180. cs = &js_CodeSpec[op];
  181. do {
  182. /*
  183. * Check for a prototype "plain old method" callee computation. What
  184. * is a plain old method? It's a function-valued property with stub
  185. * getter and setter, so get of a function is idempotent and set is
  186. * transparent.
  187. */
  188. if (cs->format & JOF_CALLOP) {
  189. if (SPROP_HAS_STUB_GETTER(sprop) &&
  190. SPROP_HAS_VALID_SLOT(sprop, scope)) {
  191. jsval v;
  192. v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
  193. if (VALUE_IS_FUNCTION(cx, v)) {
  194. /*
  195. * Great, we have a function-valued prototype property
  196. * where the getter is JS_PropertyStub. The type id in
  197. * pobj's scope does not evolve with changes to property
  198. * values, however.
  199. *
  200. * So here, on first cache fill for this method, we brand
  201. * the scope with a new shape and set the SCOPE_BRANDED
  202. * flag. Once this scope flag is set, any write that adds
  203. * or deletes a function-valued plain old property in
  204. * scope->object will result in shape being regenerated.
  205. */
  206. if (!SCOPE_IS_BRANDED(scope)) {
  207. PCMETER(cache->brandfills++);
  208. #ifdef DEBUG_notme
  209. fprintf(stderr,
  210. "branding %p (%s) for funobj %p (%s), kshape %lu\n",
  211. pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
  212. JSVAL_TO_OBJECT(v),
  213. JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
  214. JSVAL_TO_OBJECT(v))),
  215. kshape);
  216. #endif
  217. SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
  218. SCOPE_SET_BRANDED(scope);
  219. kshape = scope->shape;
  220. }
  221. vword = JSVAL_OBJECT_TO_PCVAL(v);
  222. break;
  223. }
  224. }
  225. }
  226. /* If getting a value via a stub getter, we can cache the slot. */
  227. if (!(cs->format & JOF_SET) &&
  228. SPROP_HAS_STUB_GETTER(sprop) &&
  229. SPROP_HAS_VALID_SLOT(sprop, scope)) {
  230. /* Great, let's cache sprop's slot and use it on cache hit. */
  231. vword = SLOT_TO_PCVAL(sprop->slot);
  232. } else {
  233. /* Best we can do is to cache sprop (still a nice speedup). */
  234. vword = SPROP_TO_PCVAL(sprop);
  235. }
  236. } while (0);
  237. /*
  238. * Our caller preserved the scope shape prior to the js_GetPropertyHelper
  239. * or similar call out of the interpreter. We want to cache under that
  240. * shape if op is overtly mutating, to bias for the case where the mutator
  241. * udpates shape predictably.
  242. *
  243. * Note that an apparently non-mutating op such as JSOP_NAME may still
  244. * mutate the base object via, e.g., lazy standard class initialization,
  245. * but that is a one-time event and we'll have to miss the old shape and
  246. * re-fill under the new one.
  247. */
  248. if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)
  249. kshape = scope->shape;
  250. khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
  251. if (obj == pobj) {
  252. JS_ASSERT(kshape != 0 || scope->shape != 0);
  253. JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
  254. JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
  255. } else {
  256. if (op == JSOP_LENGTH) {
  257. atom = cx->runtime->atomState.lengthAtom;
  258. } else {
  259. pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
  260. GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
  261. }
  262. JS_ASSERT_IF(scopeIndex == 0,
  263. protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);
  264. if (scopeIndex != 0 || protoIndex != 1) {
  265. khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
  266. PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
  267. cache->pcrecycles++);
  268. pc = (jsbytecode *) atom;
  269. kshape = (jsuword) obj;
  270. }
  271. }
  272. entry = &cache->table[khash];
  273. PCMETER(if (entry != *entryp) cache->modfills++);
  274. PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);
  275. entry->kpc = pc;
  276. entry->kshape = kshape;
  277. entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);
  278. entry->vword = vword;
  279. *entryp = entry;
  280. cache->empty = JS_FALSE;
  281. PCMETER(cache->fills++);
  282. }
  283. JSAtom *
  284. js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
  285. JSObject **objp, JSObject **pobjp,
  286. JSPropCacheEntry **entryp)
  287. {
  288. JSOp op;
  289. const JSCodeSpec *cs;
  290. ptrdiff_t pcoff;
  291. JSAtom *atom;
  292. JSObject *obj, *pobj, *tmp;
  293. JSPropCacheEntry *entry;
  294. uint32 vcap;
  295. JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
  296. < cx->fp->script->length);
  297. op = (JSOp) *pc;
  298. cs = &js_CodeSpec[op];
  299. if (op == JSOP_LENGTH) {
  300. atom = cx->runtime->atomState.lengthAtom;
  301. } else {
  302. pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
  303. GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
  304. }
  305. obj = *objp;
  306. JS_ASSERT(OBJ_IS_NATIVE(obj));
  307. entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];
  308. *entryp = entry;
  309. vcap = entry->vcap;
  310. if (entry->kpc != (jsbytecode *) atom) {
  311. PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
  312. #ifdef DEBUG_notme
  313. entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
  314. fprintf(stderr,
  315. "id miss for %s from %s:%u"
  316. " (pc %u, kpc %u, kshape %u, shape %u)\n",
  317. js_AtomToPrintableString(cx, atom),
  318. cx->fp->script->filename,
  319. js_PCToLineNumber(cx, cx->fp->script, pc),
  320. pc - cx->fp->script->code,
  321. entry->kpc - cx->fp->script->code,
  322. entry->kshape,
  323. OBJ_SHAPE(obj));
  324. js_Disassemble1(cx, cx->fp->script, pc,
  325. PTRDIFF(pc, cx->fp->script->code, jsbytecode),
  326. JS_FALSE, stderr);
  327. #endif
  328. return atom;
  329. }
  330. if (entry->kshape != (jsuword) obj) {
  331. PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
  332. return atom;
  333. }
  334. pobj = obj;
  335. JS_LOCK_OBJ(cx, pobj);
  336. if (JOF_MODE(cs->format) == JOF_NAME) {
  337. while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
  338. tmp = LOCKED_OBJ_GET_PARENT(pobj);
  339. if (!tmp || !OBJ_IS_NATIVE(tmp))
  340. break;
  341. JS_UNLOCK_OBJ(cx, pobj);
  342. pobj = tmp;
  343. JS_LOCK_OBJ(cx, pobj);
  344. vcap -= PCVCAP_PROTOSIZE;
  345. }
  346. *objp = pobj;
  347. }
  348. while (vcap & PCVCAP_PROTOMASK) {
  349. tmp = LOCKED_OBJ_GET_PROTO(pobj);
  350. if (!tmp || !OBJ_IS_NATIVE(tmp))
  351. break;
  352. JS_UNLOCK_OBJ(cx, pobj);
  353. pobj = tmp;
  354. JS_LOCK_OBJ(cx, pobj);
  355. --vcap;
  356. }
  357. if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {
  358. #ifdef DEBUG
  359. jsid id = ATOM_TO_JSID(atom);
  360. CHECK_FOR_STRING_INDEX(id);
  361. JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));
  362. JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);
  363. #endif
  364. *pobjp = pobj;
  365. return NULL;
  366. }
  367. PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
  368. JS_UNLOCK_OBJ(cx, pobj);
  369. return atom;
  370. }
  371. #ifdef DEBUG
  372. #define ASSERT_CACHE_IS_EMPTY(cache) \
  373. JS_BEGIN_MACRO \
  374. JSPropertyCache *cache_ = (cache); \
  375. uintN i_; \
  376. JS_ASSERT(cache_->empty); \
  377. for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
  378. JS_ASSERT(!cache_->table[i_].kpc); \
  379. JS_ASSERT(!cache_->table[i_].kshape); \
  380. JS_ASSERT(!cache_->table[i_].vcap); \
  381. JS_ASSERT(!cache_->table[i_].vword); \
  382. } \
  383. JS_END_MACRO
  384. #else
  385. #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
  386. #endif
  387. JS_STATIC_ASSERT(PCVAL_NULL == 0);
  388. void
  389. js_FlushPropertyCache(JSContext *cx)
  390. {
  391. JSPropertyCache *cache;
  392. cache = &JS_PROPERTY_CACHE(cx);
  393. if (cache->empty) {
  394. ASSERT_CACHE_IS_EMPTY(cache);
  395. return;
  396. }
  397. memset(cache->table, 0, sizeof cache->table);
  398. cache->empty = JS_TRUE;
  399. #ifdef JS_PROPERTY_CACHE_METERING
  400. { static FILE *fp;
  401. if (!fp)
  402. fp = fopen("/tmp/propcache.stats", "w");
  403. if (fp) {
  404. fputs("Property cache stats for ", fp);
  405. #ifdef JS_THREADSAFE
  406. fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
  407. #endif
  408. fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
  409. # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
  410. P(fills);
  411. P(nofills);
  412. P(rofills);
  413. P(disfills);
  414. P(oddfills);
  415. P(modfills);
  416. P(brandfills);
  417. P(noprotos);
  418. P(longchains);
  419. P(recycles);
  420. P(pcrecycles);
  421. P(tests);
  422. P(pchits);
  423. P(protopchits);
  424. P(initests);
  425. P(inipchits);
  426. P(inipcmisses);
  427. P(settests);
  428. P(addpchits);
  429. P(setpchits);
  430. P(setpcmisses);
  431. P(slotchanges);
  432. P(setmisses);
  433. P(idmisses);
  434. P(komisses);
  435. P(vcmisses);
  436. P(misses);
  437. P(flushes);
  438. P(pcpurges);
  439. # undef P
  440. fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
  441. (100. * cache->pchits) / cache->tests,
  442. (100. * cache->protopchits) / cache->tests,
  443. (100. * (cache->addpchits + cache->setpchits))
  444. / cache->settests,
  445. (100. * cache->inipchits) / cache->initests,
  446. (100. * (cache->tests - cache->misses)) / cache->tests);
  447. fflush(fp);
  448. }
  449. }
  450. #endif
  451. PCMETER(cache->flushes++);
  452. }
  453. void
  454. js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)
  455. {
  456. JSPropertyCache *cache;
  457. JSPropCacheEntry *entry;
  458. cache = &JS_PROPERTY_CACHE(cx);
  459. for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
  460. entry++) {
  461. if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
  462. entry->kpc = NULL;
  463. entry->kshape = 0;
  464. #ifdef DEBUG
  465. entry->vcap = entry->vword = 0;
  466. #endif
  467. }
  468. }
  469. }
  470. void
  471. js_DisablePropertyCache(JSContext *cx)
  472. {
  473. JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
  474. ++JS_PROPERTY_CACHE(cx).disabled;
  475. }
  476. void
  477. js_EnablePropertyCache(JSContext *cx)
  478. {
  479. --JS_PROPERTY_CACHE(cx).disabled;
  480. JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
  481. }
  482. /*
  483. * Check if the current arena has enough space to fit nslots after sp and, if
  484. * so, reserve the necessary space.
  485. */
  486. static JSBool
  487. AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
  488. {
  489. uintN surplus;
  490. jsval *sp2;
  491. JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
  492. JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
  493. surplus = (jsval *) cx->stackPool.current->avail - sp;
  494. if (nslots <= surplus)
  495. return JS_TRUE;
  496. /*
  497. * No room before current->avail, check if the arena has enough space to
  498. * fit the missing slots before the limit.
  499. */
  500. if (nslots > (size_t) ((jsval *) cx->stackPool.current->limit - sp))
  501. return JS_FALSE;
  502. JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
  503. (nslots - surplus) * sizeof(jsval));
  504. JS_ASSERT(sp2 == sp + surplus);
  505. return JS_TRUE;
  506. }
  507. JS_STATIC_INTERPRET jsval *
  508. js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
  509. {
  510. jsval *sp;
  511. if (!cx->stackPool.first.next) {
  512. int64 *timestamp;
  513. JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
  514. &cx->stackPool, sizeof *timestamp);
  515. if (!timestamp) {
  516. js_ReportOutOfScriptQuota(cx);
  517. return NULL;
  518. }
  519. *timestamp = JS_Now();
  520. }
  521. if (markp)
  522. *markp = JS_ARENA_MARK(&cx->stackPool);
  523. JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
  524. if (!sp)
  525. js_ReportOutOfScriptQuota(cx);
  526. return sp;
  527. }
  528. JS_STATIC_INTERPRET void
  529. js_FreeRawStack(JSContext *cx, void *mark)
  530. {
  531. JS_ARENA_RELEASE(&cx->stackPool, mark);
  532. }
  533. JS_FRIEND_API(jsval *)
  534. js_AllocStack(JSContext *cx, uintN nslots, void **markp)
  535. {
  536. jsval *sp;
  537. JSArena *a;
  538. JSStackHeader *sh;
  539. /* Callers don't check for zero nslots: we do to avoid empty segments. */
  540. if (nslots == 0) {
  541. *markp = NULL;
  542. return (jsval *) JS_ARENA_MARK(&cx->stackPool);
  543. }
  544. /* Allocate 2 extra slots for the stack segment header we'll likely need. */
  545. sp = js_AllocRawStack(cx, 2 + nslots, markp);
  546. if (!sp)
  547. return NULL;
  548. /* Try to avoid another header if we can piggyback on the last segment. */
  549. a = cx->stackPool.current;
  550. sh = cx->stackHeaders;
  551. if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
  552. /* Extend the last stack segment, give back the 2 header slots. */
  553. sh->nslots += nslots;
  554. a->avail -= 2 * sizeof(jsval);
  555. } else {
  556. /*
  557. * Need a new stack segment, so allocate and push a stack segment
  558. * header from the 2 extra slots.
  559. */
  560. sh = (JSStackHeader *)sp;
  561. sh->nslots = nslots;
  562. sh->down = cx->stackHeaders;
  563. cx->stackHeaders = sh;
  564. sp += 2;
  565. }
  566. /*
  567. * Store JSVAL_NULL using memset, to let compilers optimize as they see
  568. * fit, in case a caller allocates and pushes GC-things one by one, which
  569. * could nest a last-ditch GC that will scan this segment.
  570. */
  571. memset(sp, 0, nslots * sizeof(jsval));
  572. return sp;
  573. }
  574. JS_FRIEND_API(void)
  575. js_FreeStack(JSContext *cx, void *mark)
  576. {
  577. JSStackHeader *sh;
  578. jsuword slotdiff;
  579. /* Check for zero nslots allocation special case. */
  580. if (!mark)
  581. return;
  582. /* We can assert because js_FreeStack always balances js_AllocStack. */
  583. sh = cx->stackHeaders;
  584. JS_ASSERT(sh);
  585. /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
  586. slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
  587. if (slotdiff < (jsuword)sh->nslots)
  588. sh->nslots = slotdiff;
  589. else
  590. cx->stackHeaders = sh->down;
  591. /* Release the stackPool space allocated since mark was set. */
  592. JS_ARENA_RELEASE(&cx->stackPool, mark);
  593. }
  594. JSObject *
  595. js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
  596. {
  597. JSObject *obj, *cursor, *clonedChild, *parent;
  598. JSTempValueRooter tvr;
  599. obj = fp->blockChain;
  600. if (!obj) {
  601. /*
  602. * Don't force a call object for a lightweight function call, but do
  603. * insist that there is a call object for a heavyweight function call.
  604. */
  605. JS_ASSERT(!fp->fun ||
  606. !(fp->fun->flags & JSFUN_HEAVYWEIGHT) ||
  607. fp->callobj);
  608. JS_ASSERT(fp->scopeChain);
  609. return fp->scopeChain;
  610. }
  611. /*
  612. * We have one or more lexical scopes to reflect into fp->scopeChain, so
  613. * make sure there's a call object at the current head of the scope chain,
  614. * if this frame is a call frame.
  615. */
  616. if (fp->fun && !fp->callobj) {
  617. JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
  618. OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
  619. if (!js_GetCallObject(cx, fp, fp->scopeChain))
  620. return NULL;
  621. }
  622. /*
  623. * Clone the block chain. To avoid recursive cloning we set the parent of
  624. * the cloned child after we clone the parent. In the following loop when
  625. * clonedChild is null it indicates the first iteration when no special GC
  626. * rooting is necessary. On the second and the following iterations we
  627. * have to protect cloned so far chain against the GC during cloning of
  628. * the cursor object.
  629. */
  630. cursor = obj;
  631. clonedChild = NULL;
  632. for (;;) {
  633. parent = OBJ_GET_PARENT(cx, cursor);
  634. /*
  635. * We pass fp->scopeChain and not null even if we override the parent
  636. * slot later as null triggers useless calculations of slot's value in
  637. * js_NewObject that js_CloneBlockObject calls.
  638. */
  639. cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);
  640. if (!cursor) {
  641. if (clonedChild)
  642. JS_POP_TEMP_ROOT(cx, &tvr);
  643. return NULL;
  644. }
  645. if (!clonedChild) {
  646. /*
  647. * The first iteration. Check if other follow and root obj if so
  648. * to protect the whole cloned chain against GC.
  649. */
  650. obj = cursor;
  651. if (!parent)
  652. break;
  653. JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
  654. } else {
  655. /*
  656. * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to
  657. * other threads.
  658. */
  659. STOBJ_SET_PARENT(clonedChild, cursor);
  660. if (!parent) {
  661. JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));
  662. JS_POP_TEMP_ROOT(cx, &tvr);
  663. break;
  664. }
  665. }
  666. clonedChild = cursor;
  667. cursor = parent;
  668. }
  669. fp->flags |= JSFRAME_POP_BLOCKS;
  670. fp->scopeChain = obj;
  671. fp->blockChain = NULL;
  672. return obj;
  673. }
  674. JSBool
  675. js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
  676. {
  677. jsval v;
  678. JSObject *obj;
  679. v = vp[1];
  680. if (JSVAL_IS_OBJECT(v)) {
  681. obj = JS_THIS_OBJECT(cx, vp);
  682. if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
  683. return JS_FALSE;
  684. v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  685. }
  686. *thisvp = v;
  687. return JS_TRUE;
  688. }
  689. /*
  690. * ECMA requires "the global object", but in embeddings such as the browser,
  691. * which have multiple top-level objects (windows, frames, etc. in the DOM),
  692. * we prefer fun's parent. An example that causes this code to run:
  693. *
  694. * // in window w1
  695. * function f() { return this }
  696. * function g() { return f }
  697. *
  698. * // in window w2
  699. * var h = w1.g()
  700. * alert(h() == w1)
  701. *
  702. * The alert should display "true".
  703. */
  704. JS_STATIC_INTERPRET JSObject *
  705. js_ComputeGlobalThis(JSContext *cx, JSBool lazy, jsval *argv)
  706. {
  707. JSObject *thisp;
  708. if (JSVAL_IS_PRIMITIVE(argv[-2]) ||
  709. !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) {
  710. thisp = cx->globalObject;
  711. } else {
  712. JSStackFrame *fp;
  713. jsid id;
  714. jsval v;
  715. uintN attrs;
  716. JSBool ok;
  717. JSObject *parent;
  718. /*
  719. * Walk up the parent chain, first checking that the running script
  720. * has access to the callee's parent object. Note that if lazy, the
  721. * running script whose principals we want to check is the script
  722. * associated with fp->down, not with fp.
  723. *
  724. * FIXME: 417851 -- this access check should not be required, as it
  725. * imposes a performance penalty on all js_ComputeGlobalThis calls,
  726. * and it represents a maintenance hazard.
  727. */
  728. fp = cx->fp; /* quell GCC overwarning */
  729. if (lazy) {
  730. JS_ASSERT(fp->argv == argv);
  731. fp->dormantNext = cx->dormantFrameChain;
  732. cx->dormantFrameChain = fp;
  733. cx->fp = fp->down;
  734. fp->down = NULL;
  735. }
  736. thisp = JSVAL_TO_OBJECT(argv[-2]);
  737. id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
  738. ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);
  739. if (lazy) {
  740. cx->dormantFrameChain = fp->dormantNext;
  741. fp->dormantNext = NULL;
  742. fp->down = cx->fp;
  743. cx->fp = fp;
  744. }
  745. if (!ok)
  746. return NULL;
  747. thisp = JSVAL_IS_VOID(v)
  748. ? OBJ_GET_PARENT(cx, thisp)
  749. : JSVAL_TO_OBJECT(v);
  750. while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
  751. thisp = parent;
  752. }
  753. OBJ_TO_OUTER_OBJECT(cx, thisp);
  754. if (!thisp)
  755. return NULL;
  756. argv[-1] = OBJECT_TO_JSVAL(thisp);
  757. return thisp;
  758. }
  759. static JSObject *
  760. ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
  761. {
  762. JSObject *thisp;
  763. JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
  764. if (!JSVAL_IS_OBJECT(argv[-1])) {
  765. if (!js_PrimitiveToObject(cx, &argv[-1]))
  766. return NULL;
  767. thisp = JSVAL_TO_OBJECT(argv[-1]);
  768. } else {
  769. thisp = JSVAL_TO_OBJECT(argv[-1]);
  770. if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)
  771. return js_ComputeGlobalThis(cx, lazy, argv);
  772. if (thisp->map->ops->thisObject) {
  773. /* Some objects (e.g., With) delegate 'this' to another object. */
  774. thisp = thisp->map->ops->thisObject(cx, thisp);
  775. if (!thisp)
  776. return NULL;
  777. }
  778. OBJ_TO_OUTER_OBJECT(cx, thisp);
  779. if (!thisp)
  780. return NULL;
  781. argv[-1] = OBJECT_TO_JSVAL(thisp);
  782. }
  783. return thisp;
  784. }
  785. JSObject *
  786. js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
  787. {
  788. if (JSVAL_IS_NULL(argv[-1]))
  789. return js_ComputeGlobalThis(cx, lazy, argv);
  790. return ComputeThis(cx, lazy, argv);
  791. }
  792. #if JS_HAS_NO_SUCH_METHOD
  793. #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE
  794. #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1)
  795. JSClass js_NoSuchMethodClass = {
  796. "NoSuchMethod",
  797. JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
  798. JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
  799. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  800. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  801. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
  802. };
  803. JS_BEGIN_EXTERN_C
  804. JSObject*
  805. js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
  806. JS_END_EXTERN_C
  807. JSObject*
  808. js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
  809. {
  810. JSObject *proto;
  811. proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
  812. NULL, NULL, NULL);
  813. if (!proto)
  814. return NULL;
  815. OBJ_CLEAR_PROTO(cx, proto);
  816. return proto;
  817. }
  818. /*
  819. * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
  820. * the base object, we search for the __noSuchMethod__ method in the base.
  821. * If it exists, we store the method and the property's id into an object of
  822. * NoSuchMethod class and store this object into the callee's stack slot.
  823. * Later, js_Invoke will recognise such an object and transfer control to
  824. * NoSuchMethod that invokes the method like:
  825. *
  826. * this.__noSuchMethod__(id, args)
  827. *
  828. * where id is the name of the method that this invocation attempted to
  829. * call by name, and args is an Array containing this invocation's actual
  830. * parameters.
  831. */
  832. JS_STATIC_INTERPRET JSBool
  833. js_OnUnknownMethod(JSContext *cx, jsval *vp)
  834. {
  835. JSObject *obj;
  836. jsid id;
  837. JSTempValueRooter tvr;
  838. JSBool ok;
  839. JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
  840. obj = JSVAL_TO_OBJECT(vp[1]);
  841. JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
  842. MUST_FLOW_THROUGH("out");
  843. id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
  844. #if JS_HAS_XML_SUPPORT
  845. if (OBJECT_IS_XML(cx, obj)) {
  846. JSXMLObjectOps *ops;
  847. ops = (JSXMLObjectOps *) obj->map->ops;
  848. obj = ops->getMethod(cx, obj, id, &tvr.u.value);
  849. if (!obj) {
  850. ok = JS_FALSE;
  851. goto out;
  852. }
  853. vp[1] = OBJECT_TO_JSVAL(obj);
  854. } else
  855. #endif
  856. {
  857. ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
  858. if (!ok)
  859. goto out;
  860. }
  861. if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
  862. vp[0] = tvr.u.value;
  863. } else {
  864. #if JS_HAS_XML_SUPPORT
  865. /* Extract the function name from function::name qname. */
  866. if (!JSVAL_IS_PRIMITIVE(vp[0])) {
  867. obj = JSVAL_TO_OBJECT(vp[0]);
  868. ok = js_IsFunctionQName(cx, obj, &id);
  869. if (!ok)
  870. goto out;
  871. if (id != 0)
  872. vp[0] = ID_TO_VALUE(id);
  873. }
  874. #endif
  875. obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
  876. if (!obj) {
  877. ok = JS_FALSE;
  878. goto out;
  879. }
  880. obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
  881. obj->fslots[JSSLOT_SAVED_ID] = vp[0];
  882. vp[0] = OBJECT_TO_JSVAL(obj);
  883. }
  884. ok = JS_TRUE;
  885. out:
  886. JS_POP_TEMP_ROOT(cx, &tvr);
  887. return ok;
  888. }
  889. static JSBool
  890. NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
  891. {
  892. jsval *invokevp;
  893. void *mark;
  894. JSBool ok;
  895. JSObject *obj, *argsobj;
  896. invokevp = js_AllocStack(cx, 2 + 2, &mark);
  897. if (!invokevp)
  898. return JS_FALSE;
  899. JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[0]));
  900. JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
  901. obj = JSVAL_TO_OBJECT(vp[0]);
  902. JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NoSuchMethodClass);
  903. invokevp[0] = obj->fslots[JSSLOT_FOUND_FUNCTION];
  904. invokevp[1] = vp[1];
  905. invokevp[2] = obj->fslots[JSSLOT_SAVED_ID];
  906. argsobj = js_NewArrayObject(cx, argc, vp + 2);
  907. if (!argsobj) {
  908. ok = JS_FALSE;
  909. } else {
  910. invokevp[3] = OBJECT_TO_JSVAL(argsobj);
  911. ok = (flags & JSINVOKE_CONSTRUCT)
  912. ? js_InvokeConstructor(cx, 2, JS_TRUE, invokevp)
  913. : js_Invoke(cx, 2, invokevp, flags);
  914. vp[0] = invokevp[0];
  915. }
  916. js_FreeStack(cx, mark);
  917. return ok;
  918. }
  919. #endif /* JS_HAS_NO_SUCH_METHOD */
  920. /*
  921. * We check if the function accepts a primitive value as |this|. For that we
  922. * use a table that maps value's tag into the corresponding function flag.
  923. */
  924. JS_STATIC_ASSERT(JSVAL_INT == 1);
  925. JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
  926. JS_STATIC_ASSERT(JSVAL_STRING == 4);
  927. JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);
  928. const uint16 js_PrimitiveTestFlags[] = {
  929. JSFUN_THISP_NUMBER, /* INT */
  930. JSFUN_THISP_NUMBER, /* DOUBLE */
  931. JSFUN_THISP_NUMBER, /* INT */
  932. JSFUN_THISP_STRING, /* STRING */
  933. JSFUN_THISP_NUMBER, /* INT */
  934. JSFUN_THISP_BOOLEAN, /* BOOLEAN */
  935. JSFUN_THISP_NUMBER /* INT */
  936. };
  937. /*
  938. * Find a function reference and its 'this' object implicit first parameter
  939. * under argc arguments on cx's stack, and call the function. Push missing
  940. * required arguments, allocate declared local variables, and pop everything
  941. * when done. Then push the return value.
  942. */
  943. JS_FRIEND_API(JSBool)
  944. js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
  945. {
  946. void *mark;
  947. JSStackFrame frame;
  948. jsval *sp, *argv, *newvp;
  949. jsval v;
  950. JSObject *funobj, *parent;
  951. JSBool ok;
  952. JSClass *clasp;
  953. JSObjectOps *ops;
  954. JSNative native;
  955. JSFunction *fun;
  956. JSScript *script;
  957. uintN nslots, i;
  958. uint32 rootedArgsFlag;
  959. JSInterpreterHook hook;
  960. void *hookData;
  961. /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
  962. JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
  963. JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
  964. /*
  965. * Mark the top of stack and load frequently-used registers. After this
  966. * point the control should flow through label out2: to return.
  967. */
  968. mark = JS_ARENA_MARK(&cx->stackPool);
  969. v = *vp;
  970. if (JSVAL_IS_PRIMITIVE(v))
  971. goto bad;
  972. funobj = JSVAL_TO_OBJECT(v);
  973. parent = OBJ_GET_PARENT(cx, funobj);
  974. clasp = OBJ_GET_CLASS(cx, funobj);
  975. if (clasp != &js_FunctionClass) {
  976. #if JS_HAS_NO_SUCH_METHOD
  977. if (clasp == &js_NoSuchMethodClass) {
  978. ok = NoSuchMethod(cx, argc, vp, flags);
  979. goto out2;
  980. }
  981. #endif
  982. /* Function is inlined, all other classes use object ops. */
  983. ops = funobj->map->ops;
  984. /*
  985. * XXX this makes no sense -- why convert to function if clasp->call?
  986. * XXX better to call that hook without converting
  987. * XXX the only thing that needs fixing is liveconnect
  988. *
  989. * Try converting to function, for closure and API compatibility.
  990. * We attempt the conversion under all circumstances for 1.2, but
  991. * only if there is a call op defined otherwise.
  992. */
  993. if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
  994. ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
  995. if (!ok)
  996. goto out2;
  997. if (VALUE_IS_FUNCTION(cx, v)) {
  998. /* Make vp refer to funobj to keep it available as argv[-2]. */
  999. *vp = v;
  1000. funobj = JSVAL_TO_OBJECT(v);
  1001. parent = OBJ_GET_PARENT(cx, funobj);
  1002. goto have_fun;
  1003. }
  1004. }
  1005. fun = NULL;
  1006. script = NULL;
  1007. nslots = 0;
  1008. /* Try a call or construct native object op. */
  1009. if (flags & JSINVOKE_CONSTRUCT) {
  1010. if (!JSVAL_IS_OBJECT(vp[1])) {
  1011. ok = js_PrimitiveToObject(cx, &vp[1]);
  1012. if (!ok)
  1013. goto out2;
  1014. }
  1015. native = ops->construct;
  1016. } else {
  1017. native = ops->call;
  1018. }
  1019. if (!native)
  1020. goto bad;
  1021. } else {
  1022. have_fun:
  1023. /* Get private data and set derived locals from it. */
  1024. fun = GET_FUNCTION_PRIVATE(cx, funobj);
  1025. nslots = FUN_MINARGS(fun);
  1026. nslots = (nslots > argc) ? nslots - argc : 0;
  1027. if (FUN_INTERPRETED(fun)) {
  1028. native = NULL;
  1029. script = fun->u.i.script;
  1030. } else {
  1031. native = fun->u.n.native;
  1032. script = NULL;
  1033. nslots += fun->u.n.extra;
  1034. }
  1035. if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
  1036. /* Handle bound method special case. */
  1037. vp[1] = OBJECT_TO_JSVAL(parent);
  1038. } else if (!JSVAL_IS_OBJECT(vp[1])) {
  1039. JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
  1040. if (PRIMITIVE_THIS_TEST(fun, vp[1]))
  1041. goto start_call;
  1042. }
  1043. }
  1044. if (flags & JSINVOKE_CONSTRUCT) {
  1045. JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
  1046. } else {
  1047. /*
  1048. * We must call js_ComputeThis in case we are not called from the
  1049. * interpreter, where a prior bytecode has computed an appropriate
  1050. * |this| already.
  1051. *
  1052. * But we need to compute |this| eagerly only for so-called "slow"
  1053. * (i.e., not fast) native functions. Fast natives must use either
  1054. * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
  1055. * the appropriate this-computing bytecode, e.g., JSOP_THIS.
  1056. */
  1057. if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
  1058. if (!js_ComputeThis(cx, JS_FALSE, vp + 2)) {
  1059. ok = JS_FALSE;
  1060. goto out2;
  1061. }
  1062. flags |= JSFRAME_COMPUTED_THIS;
  1063. }
  1064. }
  1065. start_call:
  1066. if (native && fun && (fun->flags & JSFUN_FAST_NATIVE)) {
  1067. #ifdef DEBUG_NOT_THROWING
  1068. JSBool alreadyThrowing = cx->throwing;
  1069. #endif
  1070. JS_ASSERT(nslots == 0);
  1071. #if JS_HAS_LVALUE_RETURN
  1072. /* Set by JS_SetCallReturnValue2, used to return reference types. */
  1073. cx->rval2set = JS_FALSE;
  1074. #endif
  1075. ok = ((JSFastNative) native)(cx, argc, vp);
  1076. JS_RUNTIME_METER(cx->runtime, nativeCalls);
  1077. #ifdef DEBUG_NOT_THROWING
  1078. if (ok && !alreadyThrowing)
  1079. ASSERT_NOT_THROWING(cx);
  1080. #endif
  1081. goto out2;
  1082. }
  1083. argv = vp + 2;
  1084. sp = argv + argc;
  1085. rootedArgsFlag = JSFRAME_ROOTED_ARGV;
  1086. if (nslots != 0) {
  1087. /*
  1088. * The extra slots required by the function continue with argument
  1089. * slots. Thus, when the last stack pool arena does not have room to
  1090. * fit nslots right after sp and AllocateAfterSP fails, we have to copy
  1091. * [vp..vp+2+argc) slots and clear rootedArgsFlag to root the copy.
  1092. */
  1093. if (!AllocateAfterSP(cx, sp, nslots)) {
  1094. rootedArgsFlag = 0;
  1095. newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
  1096. if (!newvp) {
  1097. ok = JS_FALSE;
  1098. goto out2;
  1099. }
  1100. memcpy(newvp, vp, (2 + argc) * sizeof(jsval));
  1101. argv = newvp + 2;
  1102. sp = argv + argc;
  1103. }
  1104. /* Push void to initialize missing args. */
  1105. i = nslots;
  1106. do {
  1107. *sp++ = JSVAL_VOID;
  1108. } while (--i != 0);
  1109. }
  1110. /* Allocate space for local variables and stack of interpreted function. */
  1111. if (script && script->nslots != 0) {
  1112. if (!AllocateAfterSP(cx, sp, script->nslots)) {
  1113. /* NB: Discontinuity between argv and slots, stack slots. */
  1114. sp = js_AllocRawStack(cx, script->nslots, NULL);
  1115. if (!sp) {
  1116. ok = JS_FALSE;
  1117. goto out2;
  1118. }
  1119. }
  1120. /* Push void to initialize local variables. */
  1121. for (jsval *end = sp + fun->u.i.nvars; sp != end; ++sp)
  1122. *sp = JSVAL_VOID;
  1123. }
  1124. /*
  1125. * Initialize the frame.
  1126. *
  1127. * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
  1128. * can be a primitive value here for those native functions specified with
  1129. * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
  1130. */
  1131. frame.thisp = (JSObject *)vp[1];
  1132. frame.varobj = NULL;
  1133. frame.callobj = frame.argsobj = NULL;
  1134. frame.script = script;
  1135. frame.callee = funobj;
  1136. frame.fun = fun;
  1137. frame.argc = argc;
  1138. frame.argv = argv;
  1139. /* Default return value for a constructor is the new object. */
  1140. frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
  1141. frame.down = cx->fp;
  1142. frame.annotation = NULL;
  1143. frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
  1144. frame.regs = NULL;
  1145. frame.imacpc = NULL;
  1146. frame.slots = NULL;
  1147. frame.sharpDepth = 0;
  1148. frame.sharpArray = NULL;
  1149. frame.flags = flags | rootedArgsFlag;
  1150. frame.dormantNext = NULL;
  1151. frame.xmlNamespace = NULL;
  1152. frame.blockChain = NULL;
  1153. MUST_FLOW_THROUGH("out");
  1154. cx->fp = &frame;
  1155. /* Init these now in case we goto out before first hook call. */
  1156. hook = cx->debugHooks->callHook;
  1157. hookData = NULL;
  1158. /* call the hook if present */
  1159. if (hook && (native || script))
  1160. hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
  1161. /* Call the function, either a native method or an interpreted script. */
  1162. if (native) {
  1163. #ifdef DEBUG_NOT_THROWING
  1164. JSBool alreadyThrowing = cx->throwing;
  1165. #endif
  1166. #if JS_HAS_LVALUE_RETURN
  1167. /* Set by JS_SetCallReturnValue2, used to return reference types. */
  1168. cx->rval2set = JS_FALSE;
  1169. #endif
  1170. /* If native, use caller varobj and scopeChain for eval. */
  1171. JS_ASSERT(!frame.varobj);
  1172. JS_ASSERT(!frame.scopeChain);
  1173. if (frame.down) {
  1174. frame.varobj = frame.down->varobj;
  1175. frame.scopeChain = frame.down->scopeChain;
  1176. }
  1177. /* But ensure that we have a scope chain. */
  1178. if (!frame.scopeChain)
  1179. frame.scopeChain = parent;
  1180. frame.displaySave = NULL;
  1181. ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
  1182. JS_RUNTIME_METER(cx->runtime, nativeCalls);
  1183. #ifdef DEBUG_NOT_THROWING
  1184. if (ok && !alreadyThrowing)
  1185. ASSERT_NOT_THROWING(cx);
  1186. #endif
  1187. } else if (script) {
  1188. /* Use parent scope so js_GetCallObject can find the right "Call". */
  1189. frame.scopeChain = parent;
  1190. if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
  1191. /* Scope with a call object parented by the callee's parent. */
  1192. if (!js_GetCallObject(cx, &frame, parent)) {
  1193. ok = JS_FALSE;
  1194. goto out;
  1195. }
  1196. }
  1197. frame.slots = sp - fun->u.i.nvars;
  1198. ok = js_Interpret(cx);
  1199. } else {
  1200. /* fun might be onerror trying to report a syntax error in itself. */
  1201. frame.scopeChain = NULL;
  1202. frame.displaySave = NULL;
  1203. ok = JS_TRUE;
  1204. }
  1205. out:
  1206. if (hookData) {
  1207. hook = cx->debugHooks->callHook;
  1208. if (hook)
  1209. hook(cx, &frame, JS_FALSE, &ok, hookData);
  1210. }
  1211. /* If frame has a call object, sync values and clear back-pointer. */
  1212. if (frame.callobj)
  1213. ok &= js_PutCallObject(cx, &frame);
  1214. /* If frame has an arguments object, sync values and clear back-pointer. */
  1215. if (frame.argsobj)
  1216. ok &= js_PutArgsObject(cx, &frame);
  1217. *vp = frame.rval;
  1218. /* Restore cx->fp now that we're done releasing frame objects. */
  1219. cx->fp = frame.down;
  1220. out2:
  1221. /* Pop everything we may have allocated off the stack. */
  1222. JS_ARENA_RELEASE(&cx->stackPool, mark);
  1223. if (!ok)
  1224. *vp = JSVAL_NULL;
  1225. return ok;
  1226. bad:
  1227. js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
  1228. ok = JS_FALSE;
  1229. goto out2;
  1230. }
  1231. JSBool
  1232. js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
  1233. uintN argc, jsval *argv, jsval *rval)
  1234. {
  1235. jsval *invokevp;
  1236. void *mark;
  1237. JSBool ok;
  1238. invokevp = js_AllocStack(cx, 2 + argc, &mark);
  1239. if (!invokevp)
  1240. return JS_FALSE;
  1241. invokevp[0] = fval;
  1242. invokevp[1] = OBJECT_TO_JSVAL(obj);
  1243. memcpy(invokevp + 2, argv, argc * sizeof *argv);
  1244. ok = js_Invoke(cx, argc, invokevp, flags);
  1245. if (ok) {
  1246. /*
  1247. * Store *rval in the a scoped local root if a scope is open, else in
  1248. * the lastInternalResult pigeon-hole GC root, solely so users of
  1249. * js_InternalInvoke and its direct and indirect (js_ValueToString for
  1250. * example) callers do not need to manage roots for local, temporary
  1251. * references to such results.
  1252. */
  1253. *rval = *invokevp;
  1254. if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
  1255. if (cx->localRootStack) {
  1256. if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
  1257. ok = JS_FALSE;
  1258. } else {
  1259. cx->weakRoots.lastInternalResult = *rval;
  1260. }
  1261. }
  1262. }
  1263. js_FreeStack(cx, mark);
  1264. return ok;
  1265. }
  1266. JSBool
  1267. js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
  1268. JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
  1269. {
  1270. JSSecurityCallbacks *callbacks;
  1271. /*
  1272. * js_InternalInvoke could result in another try to get or set the same id
  1273. * again, see bug 355497.
  1274. */
  1275. JS_CHECK_RECURSION(cx, return JS_FALSE);
  1276. /*
  1277. * Check general (not object-ops/class-specific) access from the running
  1278. * script to obj.id only if id has a scripted getter or setter that we're
  1279. * about to invoke. If we don't check this case, nothing else will -- no
  1280. * other native code has the chance to check.
  1281. *
  1282. * Contrast this non-native (scripted) case with native getter and setter
  1283. * accesses, where the native itself must do an access check, if security
  1284. * policies requires it. We make a checkAccess or checkObjectAccess call
  1285. * back to the embedding program only in those cases where we're not going
  1286. * to call an embedding-defined native function, getter, setter, or class
  1287. * hook anyway. Where we do call such a native, there's no need for the
  1288. * engine to impose a separate access check callback on all embeddings --
  1289. * many embeddings have no security policy at all.
  1290. */
  1291. JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
  1292. callbacks = JS_GetSecurityCallbacks(cx);
  1293. if (callbacks &&
  1294. callbacks->checkObjectAccess &&
  1295. VALUE_IS_FUNCTION(cx, fval) &&
  1296. FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) &&
  1297. !callbacks->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) {
  1298. return JS_FALSE;
  1299. }
  1300. return js_InternalCall(cx, obj, fval, argc, argv, rval);
  1301. }
  1302. JSBool
  1303. js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
  1304. JSStackFrame *down, uintN flags, jsval *result)
  1305. {
  1306. JSInterpreterHook hook;
  1307. void *hookData, *mark;
  1308. JSStackFrame *oldfp, frame;
  1309. JSObject *obj, *tmp;
  1310. JSBool ok;
  1311. #ifdef INCLUDE_MOZILLA_DTRACE
  1312. if (JAVASCRIPT_EXECUTE_START_ENABLED())
  1313. jsdtrace_execute_start(script);
  1314. #endif
  1315. hook = cx->debugHooks->executeHook;
  1316. hookData = mark = NULL;
  1317. oldfp = cx->fp;
  1318. frame.script = script;
  1319. if (down) {
  1320. /* Propagate arg state for eval and the debugger API. */
  1321. frame.callobj = down->callobj;
  1322. frame.argsobj = down->argsobj;
  1323. frame.varobj = down->varobj;
  1324. frame.callee = down->callee;
  1325. frame.fun = down->fun;
  1326. frame.thisp = down->thisp;
  1327. if (down->flags & JSFRAME_COMPUTED_THIS)
  1328. flags |= JSFRAME_COMPUTED_THIS;
  1329. frame.argc = down->argc;
  1330. frame.argv = down->argv;
  1331. frame.annotation = down->annotation;
  1332. frame.sharpArray = down->sharpArray;
  1333. JS_ASSERT(script->nfixed == 0);
  1334. } else {
  1335. frame.callobj = frame.argsobj = NULL;
  1336. obj = chain;
  1337. if (cx->options & JSOPTION_VAROBJFIX) {
  1338. while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
  1339. obj = tmp;
  1340. }
  1341. frame.varobj = obj;
  1342. frame.callee = NULL;
  1343. frame.fun = NULL;
  1344. frame.thisp = chain;
  1345. frame.argc = 0;
  1346. frame.argv = NULL;
  1347. frame.annotation = NULL;
  1348. frame.sharpArray = NULL;
  1349. }
  1350. frame.imacpc = NULL;
  1351. if (script->nslots != 0) {
  1352. frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
  1353. if (!frame.slots) {
  1354. ok = JS_FALSE;
  1355. goto out;
  1356. }
  1357. memset(frame.slots, 0, script->nfixed * sizeof(jsval));
  1358. } else {
  1359. frame.slots = NULL;
  1360. }
  1361. frame.rval = JSVAL_VOID;
  1362. frame.down = down;
  1363. frame.scopeChain = chain;
  1364. frame.regs = NULL;
  1365. frame.sharpDepth = 0;
  1366. frame.flags = flags;
  1367. frame.dormantNext = NULL;
  1368. frame.xmlNamespace = NULL;
  1369. frame.blockChain = NULL;
  1370. /*
  1371. * Here we wrap the call to js_Interpret with code to (conditionally)
  1372. * save and restore the old stack frame chain into a chain of 'dormant'
  1373. * frame chains. Since we are replacing cx->fp, we were running into
  1374. * the problem that if GC was called under this frame, some of the GC
  1375. * things associated with the old frame chain (available here only in
  1376. * the C variable 'oldfp') were not rooted and were being collected.
  1377. *
  1378. * So, now we preserve the links to these 'dormant' frame chains in cx
  1379. * before calling js_Interpret and cleanup afterwards. The GC walks
  1380. * these dormant chains and marks objects in the same way that it marks
  1381. * objects in the primary cx->fp chain.
  1382. */
  1383. if (oldfp && oldfp != down) {
  1384. JS_ASSERT(!oldfp->dormantNext);
  1385. oldfp->dormantNext = cx->dormantFrameChain;
  1386. cx->dormantFrameChain = oldfp;
  1387. }
  1388. cx->fp = &frame;
  1389. if (!down) {
  1390. OBJ_TO_OUTER_OBJECT(cx, frame.thisp);
  1391. if (!frame.thisp) {
  1392. ok = JS_FALSE;
  1393. goto out2;
  1394. }
  1395. frame.flags |= JSFRAME_COMPUTED_THIS;
  1396. }
  1397. if (hook) {
  1398. hookData = hook(cx, &frame, JS_TRUE, 0,
  1399. cx->debugHooks->executeHookData);
  1400. }
  1401. ok = js_Interpret(cx);
  1402. if (result)
  1403. *result = frame.rval;
  1404. if (hookData) {
  1405. hook = cx->debugHooks->executeHook;
  1406. if (hook)
  1407. hook(cx, &frame, JS_FALSE, &ok, hookData);
  1408. }
  1409. out2:
  1410. if (mark)
  1411. js_FreeRawStack(cx, mark);
  1412. cx->fp = oldfp;
  1413. if (oldfp && oldfp != down) {
  1414. JS_ASSERT(cx->dormantFrameChain == oldfp);
  1415. cx->dormantFrameChain = oldfp->dormantNext;
  1416. oldfp->dormantNext = NULL;
  1417. }
  1418. out:
  1419. #ifdef INCLUDE_MOZILLA_DTRACE
  1420. if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
  1421. jsdtrace_execute_done(script);
  1422. #endif
  1423. return ok;
  1424. }
  1425. JSBo

Large files files are truncated, but you can click here to view the full file