PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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. JSBool
  1426. js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
  1427. JSObject **objp, JSProperty **propp)
  1428. {
  1429. JSObject *obj2;
  1430. JSProperty *prop;
  1431. uintN oldAttrs, report;
  1432. JSBool isFunction;
  1433. jsval value;
  1434. const char *type, *name;
  1435. if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
  1436. return JS_FALSE;
  1437. if (propp) {
  1438. *objp = obj2;
  1439. *propp = prop;
  1440. }
  1441. if (!prop)
  1442. return JS_TRUE;
  1443. /*
  1444. * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.
  1445. * An assertion at label bad: will insist that it is null.
  1446. */
  1447. if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
  1448. OBJ_DROP_PROPERTY(cx, obj2, prop);
  1449. #ifdef DEBUG
  1450. prop = NULL;
  1451. #endif
  1452. goto bad;
  1453. }
  1454. /*
  1455. * From here, return true, or else goto bad on failure to null out params.
  1456. * If our caller doesn't want prop, drop it (we don't need it any longer).
  1457. */
  1458. if (!propp) {
  1459. OBJ_DROP_PROPERTY(cx, obj2, prop);
  1460. prop = NULL;
  1461. }
  1462. if (attrs == JSPROP_INITIALIZER) {
  1463. /* Allow the new object to override properties. */
  1464. if (obj2 != obj)
  1465. return JS_TRUE;
  1466. report = JSREPORT_WARNING | JSREPORT_STRICT;
  1467. } else {
  1468. /* We allow redeclaring some non-readonly properties. */
  1469. if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
  1470. /*
  1471. * Allow redeclaration of variables and functions, but insist that
  1472. * the new value is not a getter if the old value was, ditto for
  1473. * setters -- unless prop is impermanent (in which case anyone
  1474. * could delete it and redefine it, willy-nilly).
  1475. */
  1476. if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
  1477. return JS_TRUE;
  1478. if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
  1479. return JS_TRUE;
  1480. if (!(oldAttrs & JSPROP_PERMANENT))
  1481. return JS_TRUE;
  1482. }
  1483. report = JSREPORT_ERROR;
  1484. isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
  1485. if (!isFunction) {
  1486. if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
  1487. goto bad;
  1488. isFunction = VALUE_IS_FUNCTION(cx, value);
  1489. }
  1490. }
  1491. type = (attrs == JSPROP_INITIALIZER)
  1492. ? "property"
  1493. : (oldAttrs & attrs & JSPROP_GETTER)
  1494. ? js_getter_str
  1495. : (oldAttrs & attrs & JSPROP_SETTER)
  1496. ? js_setter_str
  1497. : (oldAttrs & JSPROP_READONLY)
  1498. ? js_const_str
  1499. : isFunction
  1500. ? js_function_str
  1501. : js_var_str;
  1502. name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
  1503. if (!name)
  1504. goto bad;
  1505. return JS_ReportErrorFlagsAndNumber(cx, report,
  1506. js_GetErrorMessage, NULL,
  1507. JSMSG_REDECLARED_VAR,
  1508. type, name);
  1509. bad:
  1510. if (propp) {
  1511. *objp = NULL;
  1512. *propp = NULL;
  1513. }
  1514. JS_ASSERT(!prop);
  1515. return JS_FALSE;
  1516. }
  1517. JSBool
  1518. js_StrictlyEqual(JSContext *cx, jsval lval, jsval rval)
  1519. {
  1520. jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
  1521. jsdouble ld, rd;
  1522. if (ltag == rtag) {
  1523. if (ltag == JSVAL_STRING) {
  1524. JSString *lstr = JSVAL_TO_STRING(lval),
  1525. *rstr = JSVAL_TO_STRING(rval);
  1526. return js_EqualStrings(lstr, rstr);
  1527. }
  1528. if (ltag == JSVAL_DOUBLE) {
  1529. ld = *JSVAL_TO_DOUBLE(lval);
  1530. rd = *JSVAL_TO_DOUBLE(rval);
  1531. return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
  1532. }
  1533. if (ltag == JSVAL_OBJECT &&
  1534. lval != rval &&
  1535. !JSVAL_IS_NULL(lval) &&
  1536. !JSVAL_IS_NULL(rval)) {
  1537. JSObject *lobj, *robj;
  1538. lobj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(lval));
  1539. robj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(rval));
  1540. lval = OBJECT_TO_JSVAL(lobj);
  1541. rval = OBJECT_TO_JSVAL(robj);
  1542. }
  1543. return lval == rval;
  1544. }
  1545. if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
  1546. ld = *JSVAL_TO_DOUBLE(lval);
  1547. rd = JSVAL_TO_INT(rval);
  1548. return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
  1549. }
  1550. if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
  1551. ld = JSVAL_TO_INT(lval);
  1552. rd = *JSVAL_TO_DOUBLE(rval);
  1553. return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
  1554. }
  1555. return lval == rval;
  1556. }
  1557. JSBool
  1558. js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
  1559. {
  1560. JSFunction *fun, *fun2;
  1561. JSObject *obj, *obj2, *proto, *parent;
  1562. jsval lval, rval;
  1563. JSClass *clasp;
  1564. fun = NULL;
  1565. obj2 = NULL;
  1566. lval = *vp;
  1567. if (!JSVAL_IS_OBJECT(lval) ||
  1568. (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
  1569. /* XXX clean up to avoid special cases above ObjectOps layer */
  1570. OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
  1571. !obj2->map->ops->construct)
  1572. {
  1573. fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
  1574. if (!fun)
  1575. return JS_FALSE;
  1576. }
  1577. clasp = &js_ObjectClass;
  1578. if (!obj2) {
  1579. proto = parent = NULL;
  1580. fun = NULL;
  1581. } else {
  1582. /*
  1583. * Get the constructor prototype object for this function.
  1584. * Use the nominal 'this' parameter slot, vp[1], as a local
  1585. * root to protect this prototype, in case it has no other
  1586. * strong refs.
  1587. */
  1588. if (!OBJ_GET_PROPERTY(cx, obj2,
  1589. ATOM_TO_JSID(cx->runtime->atomState
  1590. .classPrototypeAtom),
  1591. &vp[1])) {
  1592. return JS_FALSE;
  1593. }
  1594. rval = vp[1];
  1595. proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
  1596. parent = OBJ_GET_PARENT(cx, obj2);
  1597. if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
  1598. fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
  1599. if (!FUN_INTERPRETED(fun2) &&
  1600. !(fun2->flags & JSFUN_TRACEABLE) &&
  1601. fun2->u.n.u.clasp) {
  1602. clasp = fun2->u.n.u.clasp;
  1603. }
  1604. }
  1605. }
  1606. obj = js_NewObject(cx, clasp, proto, parent, 0);
  1607. if (!obj)
  1608. return JS_FALSE;
  1609. /* Now we have an object with a constructor method; call it. */
  1610. vp[1] = OBJECT_TO_JSVAL(obj);
  1611. if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {
  1612. cx->weakRoots.newborn[GCX_OBJECT] = NULL;
  1613. return JS_FALSE;
  1614. }
  1615. /* Check the return value and if it's primitive, force it to be obj. */
  1616. rval = *vp;
  1617. if (clampReturn && JSVAL_IS_PRIMITIVE(rval)) {
  1618. if (!fun) {
  1619. /* native [[Construct]] returning primitive is error */
  1620. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  1621. JSMSG_BAD_NEW_RESULT,
  1622. js_ValueToPrintableString(cx, rval));
  1623. return JS_FALSE;
  1624. }
  1625. *vp = OBJECT_TO_JSVAL(obj);
  1626. }
  1627. JS_RUNTIME_METER(cx->runtime, constructs);
  1628. return JS_TRUE;
  1629. }
  1630. JSBool
  1631. js_InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
  1632. {
  1633. JS_ASSERT(!JSVAL_IS_INT(idval));
  1634. #if JS_HAS_XML_SUPPORT
  1635. if (!JSVAL_IS_PRIMITIVE(idval)) {
  1636. if (OBJECT_IS_XML(cx, obj)) {
  1637. *idp = OBJECT_JSVAL_TO_JSID(idval);
  1638. return JS_TRUE;
  1639. }
  1640. if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
  1641. return JS_FALSE;
  1642. if (*idp != 0)
  1643. return JS_TRUE;
  1644. }
  1645. #endif
  1646. return js_ValueToStringId(cx, idval, idp);
  1647. }
  1648. /*
  1649. * Enter the new with scope using an object at sp[-1] and associate the depth
  1650. * of the with block with sp + stackIndex.
  1651. */
  1652. JS_STATIC_INTERPRET JSBool
  1653. js_EnterWith(JSContext *cx, jsint stackIndex)
  1654. {
  1655. JSStackFrame *fp;
  1656. jsval *sp;
  1657. JSObject *obj, *parent, *withobj;
  1658. fp = cx->fp;
  1659. sp = fp->regs->sp;
  1660. JS_ASSERT(stackIndex < 0);
  1661. JS_ASSERT(StackBase(fp) <= sp + stackIndex);
  1662. if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
  1663. obj = JSVAL_TO_OBJECT(sp[-1]);
  1664. } else {
  1665. obj = js_ValueToNonNullObject(cx, sp[-1]);
  1666. if (!obj)
  1667. return JS_FALSE;
  1668. sp[-1] = OBJECT_TO_JSVAL(obj);
  1669. }
  1670. parent = js_GetScopeChain(cx, fp);
  1671. if (!parent)
  1672. return JS_FALSE;
  1673. OBJ_TO_INNER_OBJECT(cx, obj);
  1674. if (!obj)
  1675. return JS_FALSE;
  1676. withobj = js_NewWithObject(cx, obj, parent,
  1677. sp + stackIndex - StackBase(fp));
  1678. if (!withobj)
  1679. return JS_FALSE;
  1680. fp->scopeChain = withobj;
  1681. js_DisablePropertyCache(cx);
  1682. return JS_TRUE;
  1683. }
  1684. JS_STATIC_INTERPRET void
  1685. js_LeaveWith(JSContext *cx)
  1686. {
  1687. JSObject *withobj;
  1688. withobj = cx->fp->scopeChain;
  1689. JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
  1690. JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
  1691. JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
  1692. cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
  1693. JS_SetPrivate(cx, withobj, NULL);
  1694. js_EnablePropertyCache(cx);
  1695. }
  1696. JSClass *
  1697. js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
  1698. {
  1699. JSClass *clasp;
  1700. clasp = OBJ_GET_CLASS(cx, obj);
  1701. if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
  1702. OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
  1703. OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
  1704. return clasp;
  1705. }
  1706. return NULL;
  1707. }
  1708. JS_STATIC_INTERPRET jsint
  1709. js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
  1710. {
  1711. jsint n;
  1712. JSObject *obj;
  1713. JSClass *clasp;
  1714. n = 0;
  1715. for (obj = fp->scopeChain;
  1716. (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
  1717. obj = OBJ_GET_PARENT(cx, obj)) {
  1718. if (clasp == &js_WithClass)
  1719. ++n;
  1720. }
  1721. return n;
  1722. }
  1723. /*
  1724. * Unwind block and scope chains to match the given depth. The function sets
  1725. * fp->sp on return to stackDepth.
  1726. */
  1727. JSBool
  1728. js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
  1729. JSBool normalUnwind)
  1730. {
  1731. JSObject *obj;
  1732. JSClass *clasp;
  1733. JS_ASSERT(stackDepth >= 0);
  1734. JS_ASSERT(StackBase(fp) + stackDepth <= fp->regs->sp);
  1735. for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
  1736. JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
  1737. if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
  1738. break;
  1739. }
  1740. fp->blockChain = obj;
  1741. for (;;) {
  1742. obj = fp->scopeChain;
  1743. clasp = js_IsActiveWithOrBlock(cx, obj, stackDepth);
  1744. if (!clasp)
  1745. break;
  1746. if (clasp == &js_BlockClass) {
  1747. /* Don't fail until after we've updated all stacks. */
  1748. normalUnwind &= js_PutBlockObject(cx, normalUnwind);
  1749. } else {
  1750. js_LeaveWith(cx);
  1751. }
  1752. }
  1753. fp->regs->sp = StackBase(fp) + stackDepth;
  1754. return normalUnwind;
  1755. }
  1756. JS_STATIC_INTERPRET JSBool
  1757. js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
  1758. {
  1759. jsval v;
  1760. jsdouble d;
  1761. v = *vp;
  1762. if (JSVAL_IS_DOUBLE(v)) {
  1763. d = *JSVAL_TO_DOUBLE(v);
  1764. } else if (JSVAL_IS_INT(v)) {
  1765. d = JSVAL_TO_INT(v);
  1766. } else {
  1767. d = js_ValueToNumber(cx, vp);
  1768. if (JSVAL_IS_NULL(*vp))
  1769. return JS_FALSE;
  1770. JS_ASSERT(JSVAL_IS_NUMBER(*vp) || *vp == JSVAL_TRUE);
  1771. /* Store the result of v conversion back in vp for post increments. */
  1772. if ((cs->format & JOF_POST) &&
  1773. *vp == JSVAL_TRUE
  1774. && !js_NewNumberInRootedValue(cx, d, vp)) {
  1775. return JS_FALSE;
  1776. }
  1777. }
  1778. (cs->format & JOF_INC) ? d++ : d--;
  1779. if (!js_NewNumberInRootedValue(cx, d, vp2))
  1780. return JS_FALSE;
  1781. if (!(cs->format & JOF_POST))
  1782. *vp = *vp2;
  1783. return JS_TRUE;
  1784. }
  1785. #ifdef DEBUG
  1786. JS_STATIC_INTERPRET void
  1787. js_TraceOpcode(JSContext *cx, jsint len)
  1788. {
  1789. FILE *tracefp;
  1790. JSStackFrame *fp;
  1791. JSFrameRegs *regs;
  1792. JSOp prevop;
  1793. intN ndefs, n, nuses;
  1794. jsval *siter;
  1795. JSString *str;
  1796. JSOp op;
  1797. tracefp = (FILE *) cx->tracefp;
  1798. JS_ASSERT(tracefp);
  1799. fp = cx->fp;
  1800. regs = fp->regs;
  1801. if (len != 0) {
  1802. prevop = (JSOp) regs->pc[-len];
  1803. ndefs = js_CodeSpec[prevop].ndefs;
  1804. if (ndefs != 0) {
  1805. for (n = -ndefs; n < 0; n++) {
  1806. char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
  1807. NULL);
  1808. if (bytes) {
  1809. fprintf(tracefp, "%s %s",
  1810. (n == -ndefs) ? " output:" : ",",
  1811. bytes);
  1812. JS_free(cx, bytes);
  1813. }
  1814. }
  1815. fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
  1816. }
  1817. fprintf(tracefp, " stack: ");
  1818. for (siter = StackBase(fp); siter < regs->sp; siter++) {
  1819. str = js_ValueToString(cx, *siter);
  1820. if (!str)
  1821. fputs("<null>", tracefp);
  1822. else
  1823. js_FileEscapedString(tracefp, str, 0);
  1824. fputc(' ', tracefp);
  1825. }
  1826. fputc('\n', tracefp);
  1827. }
  1828. fprintf(tracefp, "%4u: ",
  1829. js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
  1830. js_Disassemble1(cx, fp->script, regs->pc,
  1831. PTRDIFF(regs->pc, fp->script->code, jsbytecode),
  1832. JS_FALSE, tracefp);
  1833. op = (JSOp) *regs->pc;
  1834. nuses = js_CodeSpec[op].nuses;
  1835. if (nuses != 0) {
  1836. for (n = -nuses; n < 0; n++) {
  1837. char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
  1838. NULL);
  1839. if (bytes) {
  1840. fprintf(tracefp, "%s %s",
  1841. (n == -nuses) ? " inputs:" : ",",
  1842. bytes);
  1843. JS_free(cx, bytes);
  1844. }
  1845. }
  1846. fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
  1847. }
  1848. }
  1849. #endif /* DEBUG */
  1850. #ifdef JS_OPMETER
  1851. # include <stdlib.h>
  1852. # define HIST_NSLOTS 8
  1853. /*
  1854. * The second dimension is hardcoded at 256 because we know that many bits fit
  1855. * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address
  1856. * any particular row.
  1857. */
  1858. static uint32 succeeds[JSOP_LIMIT][256];
  1859. static uint32 slot_ops[JSOP_LIMIT][HIST_NSLOTS];
  1860. JS_STATIC_INTERPRET void
  1861. js_MeterOpcodePair(JSOp op1, JSOp op2)
  1862. {
  1863. if (op1 != JSOP_STOP)
  1864. ++succeeds[op1][op2];
  1865. }
  1866. JS_STATIC_INTERPRET void
  1867. js_MeterSlotOpcode(JSOp op, uint32 slot)
  1868. {
  1869. if (slot < HIST_NSLOTS)
  1870. ++slot_ops[op][slot];
  1871. }
  1872. typedef struct Edge {
  1873. const char *from;
  1874. const char *to;
  1875. uint32 count;
  1876. } Edge;
  1877. static int
  1878. compare_edges(const void *a, const void *b)
  1879. {
  1880. const Edge *ea = (const Edge *) a;
  1881. const Edge *eb = (const Edge *) b;
  1882. return (int32)eb->count - (int32)ea->count;
  1883. }
  1884. void
  1885. js_DumpOpMeters()
  1886. {
  1887. const char *name, *from, *style;
  1888. FILE *fp;
  1889. uint32 total, count;
  1890. uint32 i, j, nedges;
  1891. Edge *graph;
  1892. name = getenv("JS_OPMETER_FILE");
  1893. if (!name)
  1894. name = "/tmp/ops.dot";
  1895. fp = fopen(name, "w");
  1896. if (!fp) {
  1897. perror(name);
  1898. return;
  1899. }
  1900. total = nedges = 0;
  1901. for (i = 0; i < JSOP_LIMIT; i++) {
  1902. for (j = 0; j < JSOP_LIMIT; j++) {
  1903. count = succeeds[i][j];
  1904. if (count != 0) {
  1905. total += count;
  1906. ++nedges;
  1907. }
  1908. }
  1909. }
  1910. # define SIGNIFICANT(count,total) (200. * (count) >= (total))
  1911. graph = (Edge *) calloc(nedges, sizeof graph[0]);
  1912. for (i = nedges = 0; i < JSOP_LIMIT; i++) {
  1913. from = js_CodeName[