PageRenderTime 1589ms CodeModel.GetById 14ms RepoModel.GetById 26ms app.codeStats 1ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1050 lines | 758 code | 131 blank | 161 comment | 169 complexity | 643f56208cf9fcbfd4c9348ef4603d00 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=78:
  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 iterators.
  42. */
  43. #include "jsstddef.h"
  44. #include <string.h> /* for memcpy */
  45. #include "jstypes.h"
  46. #include "jsutil.h"
  47. #include "jsarena.h"
  48. #include "jsapi.h"
  49. #include "jsarray.h"
  50. #include "jsatom.h"
  51. #include "jsbool.h"
  52. #include "jscntxt.h"
  53. #include "jsversion.h"
  54. #include "jsexn.h"
  55. #include "jsfun.h"
  56. #include "jsgc.h"
  57. #include "jsinterp.h"
  58. #include "jsiter.h"
  59. #include "jslock.h"
  60. #include "jsnum.h"
  61. #include "jsobj.h"
  62. #include "jsopcode.h"
  63. #include "jsscan.h"
  64. #include "jsscope.h"
  65. #include "jsscript.h"
  66. #if JS_HAS_XML_SUPPORT
  67. #include "jsxml.h"
  68. #endif
  69. #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
  70. #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
  71. #endif
  72. #if JS_HAS_GENERATORS
  73. static JSBool
  74. CloseGenerator(JSContext *cx, JSObject *genobj);
  75. #endif
  76. /*
  77. * Shared code to close iterator's state either through an explicit call or
  78. * when GC detects that the iterator is no longer reachable.
  79. */
  80. void
  81. js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
  82. {
  83. jsval state;
  84. JSObject *iterable;
  85. JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
  86. /* Avoid double work if js_CloseNativeIterator was called on obj. */
  87. state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
  88. if (JSVAL_IS_NULL(state))
  89. return;
  90. /* Protect against failure to fully initialize obj. */
  91. iterable = STOBJ_GET_PARENT(iterobj);
  92. if (iterable) {
  93. #if JS_HAS_XML_SUPPORT
  94. uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
  95. if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) {
  96. ((JSXMLObjectOps *) iterable->map->ops)->
  97. enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state,
  98. NULL, NULL);
  99. } else
  100. #endif
  101. OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
  102. }
  103. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
  104. }
  105. JSClass js_IteratorClass = {
  106. "Iterator",
  107. JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
  108. JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
  109. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  110. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  111. JSCLASS_NO_OPTIONAL_MEMBERS
  112. };
  113. static JSBool
  114. InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags)
  115. {
  116. jsval state;
  117. JSBool ok;
  118. JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
  119. /* Initialize iterobj in case of enumerate hook failure. */
  120. STOBJ_SET_PARENT(iterobj, obj);
  121. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
  122. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_FLAGS, INT_TO_JSVAL(flags));
  123. if (!js_RegisterCloseableIterator(cx, iterobj))
  124. return JS_FALSE;
  125. if (!obj)
  126. return JS_TRUE;
  127. ok =
  128. #if JS_HAS_XML_SUPPORT
  129. ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj))
  130. ? ((JSXMLObjectOps *) obj->map->ops)->
  131. enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
  132. :
  133. #endif
  134. OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL);
  135. if (!ok)
  136. return JS_FALSE;
  137. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
  138. if (flags & JSITER_ENUMERATE) {
  139. /*
  140. * The enumerating iterator needs the original object to suppress
  141. * enumeration of deleted or shadowed prototype properties. Since the
  142. * enumerator never escapes to scripts, we use the prototype slot to
  143. * store the original object.
  144. */
  145. JS_ASSERT(obj != iterobj);
  146. STOBJ_SET_PROTO(iterobj, obj);
  147. }
  148. return JS_TRUE;
  149. }
  150. static JSBool
  151. Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval)
  152. {
  153. JSBool keyonly;
  154. uintN flags;
  155. JSObject *obj;
  156. keyonly = js_ValueToBoolean(argv[1]);
  157. flags = keyonly ? 0 : JSITER_FOREACH;
  158. if (cx->fp->flags & JSFRAME_CONSTRUCTING) {
  159. /* XXX work around old valueOf call hidden beneath js_ValueToObject */
  160. if (!JSVAL_IS_PRIMITIVE(argv[0])) {
  161. obj = JSVAL_TO_OBJECT(argv[0]);
  162. } else {
  163. obj = js_ValueToNonNullObject(cx, argv[0]);
  164. if (!obj)
  165. return JS_FALSE;
  166. argv[0] = OBJECT_TO_JSVAL(obj);
  167. }
  168. return InitNativeIterator(cx, iterobj, obj, flags);
  169. }
  170. *rval = argv[0];
  171. return js_ValueToIterator(cx, flags, rval);
  172. }
  173. static JSBool
  174. NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval)
  175. {
  176. jsval vec[2];
  177. JSTempValueRooter tvr;
  178. JSObject *aobj;
  179. vec[0] = ID_TO_VALUE(key);
  180. vec[1] = val;
  181. JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr);
  182. aobj = js_NewArrayObject(cx, 2, vec);
  183. *rval = OBJECT_TO_JSVAL(aobj);
  184. JS_POP_TEMP_ROOT(cx, &tvr);
  185. return aobj != NULL;
  186. }
  187. static JSBool
  188. IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval)
  189. {
  190. JSObject *iterable;
  191. jsval state;
  192. uintN flags;
  193. JSBool foreach, ok;
  194. jsid id;
  195. JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass);
  196. iterable = OBJ_GET_PARENT(cx, obj);
  197. JS_ASSERT(iterable);
  198. state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE);
  199. if (JSVAL_IS_NULL(state))
  200. goto stop;
  201. flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS));
  202. JS_ASSERT(!(flags & JSITER_ENUMERATE));
  203. foreach = (flags & JSITER_FOREACH) != 0;
  204. ok =
  205. #if JS_HAS_XML_SUPPORT
  206. (foreach && OBJECT_IS_XML(cx, iterable))
  207. ? ((JSXMLObjectOps *) iterable->map->ops)->
  208. enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state,
  209. &id, rval)
  210. :
  211. #endif
  212. OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id);
  213. if (!ok)
  214. return JS_FALSE;
  215. STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state);
  216. if (JSVAL_IS_NULL(state))
  217. goto stop;
  218. if (foreach) {
  219. #if JS_HAS_XML_SUPPORT
  220. if (!OBJECT_IS_XML(cx, iterable) &&
  221. !OBJ_GET_PROPERTY(cx, iterable, id, rval)) {
  222. return JS_FALSE;
  223. }
  224. #endif
  225. if (!NewKeyValuePair(cx, id, *rval, rval))
  226. return JS_FALSE;
  227. } else {
  228. *rval = ID_TO_VALUE(id);
  229. }
  230. return JS_TRUE;
  231. stop:
  232. JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL);
  233. *rval = JSVAL_HOLE;
  234. return JS_TRUE;
  235. }
  236. JSBool
  237. js_ThrowStopIteration(JSContext *cx)
  238. {
  239. jsval v;
  240. JS_ASSERT(!JS_IsExceptionPending(cx));
  241. if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v))
  242. JS_SetPendingException(cx, v);
  243. return JS_FALSE;
  244. }
  245. static JSBool
  246. iterator_next(JSContext *cx, uintN argc, jsval *vp)
  247. {
  248. JSObject *obj;
  249. obj = JS_THIS_OBJECT(cx, vp);
  250. if (!JS_InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
  251. return JS_FALSE;
  252. if (!IteratorNextImpl(cx, obj, vp))
  253. return JS_FALSE;
  254. if (*vp == JSVAL_HOLE) {
  255. *vp = JSVAL_NULL;
  256. js_ThrowStopIteration(cx);
  257. return JS_FALSE;
  258. }
  259. return JS_TRUE;
  260. }
  261. static JSBool
  262. iterator_self(JSContext *cx, uintN argc, jsval *vp)
  263. {
  264. *vp = JS_THIS(cx, vp);
  265. return !JSVAL_IS_NULL(*vp);
  266. }
  267. #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
  268. static JSFunctionSpec iterator_methods[] = {
  269. JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
  270. JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
  271. JS_FS_END
  272. };
  273. uintN
  274. js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
  275. {
  276. if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass)
  277. return 0;
  278. return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
  279. }
  280. /*
  281. * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
  282. * Otherwise construct the default iterator.
  283. */
  284. JS_FRIEND_API(JSBool)
  285. js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
  286. {
  287. JSObject *obj;
  288. JSTempValueRooter tvr;
  289. JSAtom *atom;
  290. JSClass *clasp;
  291. JSExtendedClass *xclasp;
  292. JSBool ok;
  293. JSObject *iterobj;
  294. jsval arg;
  295. JS_ASSERT(!(flags & ~(JSITER_ENUMERATE |
  296. JSITER_FOREACH |
  297. JSITER_KEYVALUE)));
  298. /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
  299. JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH));
  300. /* XXX work around old valueOf call hidden beneath js_ValueToObject */
  301. if (!JSVAL_IS_PRIMITIVE(*vp)) {
  302. obj = JSVAL_TO_OBJECT(*vp);
  303. } else {
  304. /*
  305. * Enumerating over null and undefined gives an empty enumerator.
  306. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
  307. * the first production in 12.6.4 and step 4 of the second production,
  308. * but it's "web JS" compatible.
  309. */
  310. if ((flags & JSITER_ENUMERATE)) {
  311. if (!js_ValueToObject(cx, *vp, &obj))
  312. return JS_FALSE;
  313. if (!obj)
  314. goto default_iter;
  315. } else {
  316. obj = js_ValueToNonNullObject(cx, *vp);
  317. if (!obj)
  318. return JS_FALSE;
  319. }
  320. }
  321. JS_ASSERT(obj);
  322. JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
  323. clasp = OBJ_GET_CLASS(cx, obj);
  324. if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
  325. (xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
  326. iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
  327. if (!iterobj)
  328. goto bad;
  329. *vp = OBJECT_TO_JSVAL(iterobj);
  330. } else {
  331. atom = cx->runtime->atomState.iteratorAtom;
  332. #if JS_HAS_XML_SUPPORT
  333. if (OBJECT_IS_XML(cx, obj)) {
  334. if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp))
  335. goto bad;
  336. } else
  337. #endif
  338. {
  339. if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp))
  340. goto bad;
  341. }
  342. if (JSVAL_IS_VOID(*vp)) {
  343. default_iter:
  344. /*
  345. * Fail over to the default enumerating native iterator.
  346. *
  347. * Create iterobj with a NULL parent to ensure that we use the
  348. * correct scope chain to lookup the iterator's constructor. Since
  349. * we use the parent slot to keep track of the iterable, we must
  350. * fix it up after.
  351. */
  352. iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0);
  353. if (!iterobj)
  354. goto bad;
  355. /* Store in *vp to protect it from GC (callers must root vp). */
  356. *vp = OBJECT_TO_JSVAL(iterobj);
  357. if (!InitNativeIterator(cx, iterobj, obj, flags))
  358. goto bad;
  359. } else {
  360. arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
  361. if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg,
  362. vp)) {
  363. goto bad;
  364. }
  365. if (JSVAL_IS_PRIMITIVE(*vp)) {
  366. const char *printable = js_AtomToPrintableString(cx, atom);
  367. if (printable) {
  368. js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN,
  369. JSDVG_SEARCH_STACK, *vp, NULL,
  370. printable);
  371. }
  372. goto bad;
  373. }
  374. }
  375. }
  376. ok = JS_TRUE;
  377. out:
  378. if (obj)
  379. JS_POP_TEMP_ROOT(cx, &tvr);
  380. return ok;
  381. bad:
  382. ok = JS_FALSE;
  383. goto out;
  384. }
  385. JS_FRIEND_API(JSBool) JS_FASTCALL
  386. js_CloseIterator(JSContext *cx, jsval v)
  387. {
  388. JSObject *obj;
  389. JSClass *clasp;
  390. JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
  391. obj = JSVAL_TO_OBJECT(v);
  392. clasp = OBJ_GET_CLASS(cx, obj);
  393. if (clasp == &js_IteratorClass) {
  394. js_CloseNativeIterator(cx, obj);
  395. }
  396. #if JS_HAS_GENERATORS
  397. else if (clasp == &js_GeneratorClass) {
  398. if (!CloseGenerator(cx, obj))
  399. return JS_FALSE;
  400. }
  401. #endif
  402. return JS_TRUE;
  403. }
  404. static JSBool
  405. CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval)
  406. {
  407. JSObject *obj, *origobj;
  408. jsval state;
  409. JSBool foreach;
  410. jsid id;
  411. JSObject *obj2;
  412. JSBool cond;
  413. JSClass *clasp;
  414. JSExtendedClass *xclasp;
  415. JSProperty *prop;
  416. JSString *str;
  417. JS_ASSERT(flags & JSITER_ENUMERATE);
  418. JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
  419. obj = STOBJ_GET_PARENT(iterobj);
  420. origobj = STOBJ_GET_PROTO(iterobj);
  421. state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
  422. if (JSVAL_IS_NULL(state))
  423. goto stop;
  424. foreach = (flags & JSITER_FOREACH) != 0;
  425. #if JS_HAS_XML_SUPPORT
  426. /*
  427. * Treat an XML object specially only when it starts the prototype chain.
  428. * Otherwise we need to do the usual deleted and shadowed property checks.
  429. */
  430. if (obj == origobj && OBJECT_IS_XML(cx, obj)) {
  431. if (foreach) {
  432. JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops;
  433. if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state,
  434. &id, rval)) {
  435. return JS_FALSE;
  436. }
  437. } else {
  438. if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
  439. return JS_FALSE;
  440. }
  441. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
  442. if (JSVAL_IS_NULL(state))
  443. goto stop;
  444. } else
  445. #endif
  446. {
  447. restart:
  448. if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
  449. return JS_FALSE;
  450. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
  451. if (JSVAL_IS_NULL(state)) {
  452. #if JS_HAS_XML_SUPPORT
  453. if (OBJECT_IS_XML(cx, obj)) {
  454. /*
  455. * We just finished enumerating an XML obj that is present on
  456. * the prototype chain of a non-XML origobj. Stop further
  457. * prototype chain searches because XML objects don't
  458. * enumerate prototypes.
  459. */
  460. JS_ASSERT(origobj != obj);
  461. JS_ASSERT(!OBJECT_IS_XML(cx, origobj));
  462. } else
  463. #endif
  464. {
  465. obj = OBJ_GET_PROTO(cx, obj);
  466. if (obj) {
  467. STOBJ_SET_PARENT(iterobj, obj);
  468. if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL))
  469. return JS_FALSE;
  470. STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
  471. if (!JSVAL_IS_NULL(state))
  472. goto restart;
  473. }
  474. }
  475. goto stop;
  476. }
  477. /* Skip properties not in obj when looking from origobj. */
  478. if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop))
  479. return JS_FALSE;
  480. if (!prop)
  481. goto restart;
  482. OBJ_DROP_PROPERTY(cx, obj2, prop);
  483. /*
  484. * If the id was found in a prototype object or an unrelated object
  485. * (specifically, not in an inner object for obj), skip it. This step
  486. * means that all OBJ_LOOKUP_PROPERTY implementations must return an
  487. * object further along on the prototype chain, or else possibly an
  488. * object returned by the JSExtendedClass.outerObject optional hook.
  489. */
  490. if (obj != obj2) {
  491. cond = JS_FALSE;
  492. clasp = OBJ_GET_CLASS(cx, obj2);
  493. if (clasp->flags & JSCLASS_IS_EXTENDED) {
  494. xclasp = (JSExtendedClass *) clasp;
  495. cond = xclasp->outerObject &&
  496. xclasp->outerObject(cx, obj2) == obj;
  497. }
  498. if (!cond)
  499. goto restart;
  500. }
  501. if (foreach) {
  502. /* Get property querying the original object. */
  503. if (!OBJ_GET_PROPERTY(cx, origobj, id, rval))
  504. return JS_FALSE;
  505. }
  506. }
  507. if (foreach) {
  508. if (flags & JSITER_KEYVALUE) {
  509. if (!NewKeyValuePair(cx, id, *rval, rval))
  510. return JS_FALSE;
  511. }
  512. } else {
  513. /* Make rval a string for uniformity and compatibility. */
  514. str = js_ValueToString(cx, ID_TO_VALUE(id));
  515. if (!str)
  516. return JS_FALSE;
  517. *rval = STRING_TO_JSVAL(str);
  518. }
  519. return JS_TRUE;
  520. stop:
  521. JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL);
  522. *rval = JSVAL_HOLE;
  523. return JS_TRUE;
  524. }
  525. JS_FRIEND_API(JSBool)
  526. js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
  527. {
  528. uintN flags;
  529. /* Fast path for native iterators */
  530. if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) {
  531. flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
  532. if (flags & JSITER_ENUMERATE)
  533. return CallEnumeratorNext(cx, iterobj, flags, rval);
  534. /*
  535. * Call next directly as all the methods of the native iterator are
  536. * read-only and permanent.
  537. */
  538. if (!IteratorNextImpl(cx, iterobj, rval))
  539. return JS_FALSE;
  540. } else {
  541. jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
  542. if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval))
  543. return JS_FALSE;
  544. if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) {
  545. /* Check for StopIteration. */
  546. if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
  547. return JS_FALSE;
  548. /* Inline JS_ClearPendingException(cx). */
  549. cx->throwing = JS_FALSE;
  550. cx->exception = JSVAL_VOID;
  551. *rval = JSVAL_HOLE;
  552. return JS_TRUE;
  553. }
  554. }
  555. return JS_TRUE;
  556. }
  557. static JSBool
  558. stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
  559. {
  560. *bp = js_ValueIsStopIteration(v);
  561. return JS_TRUE;
  562. }
  563. JSClass js_StopIterationClass = {
  564. js_StopIteration_str,
  565. JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
  566. JS_PropertyStub, JS_PropertyStub,
  567. JS_PropertyStub, JS_PropertyStub,
  568. JS_EnumerateStub, JS_ResolveStub,
  569. JS_ConvertStub, JS_FinalizeStub,
  570. NULL, NULL,
  571. NULL, NULL,
  572. NULL, stopiter_hasInstance,
  573. NULL, NULL
  574. };
  575. #if JS_HAS_GENERATORS
  576. static void
  577. generator_finalize(JSContext *cx, JSObject *obj)
  578. {
  579. JSGenerator *gen;
  580. gen = (JSGenerator *) JS_GetPrivate(cx, obj);
  581. if (gen) {
  582. /*
  583. * gen can be open on shutdown when close hooks are ignored or when
  584. * the embedding cancels scheduled close hooks.
  585. */
  586. JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED ||
  587. gen->state == JSGEN_OPEN);
  588. JS_free(cx, gen);
  589. }
  590. }
  591. static void
  592. generator_trace(JSTracer *trc, JSObject *obj)
  593. {
  594. JSGenerator *gen;
  595. gen = (JSGenerator *) JS_GetPrivate(trc->context, obj);
  596. if (!gen)
  597. return;
  598. /*
  599. * js_TraceStackFrame does not recursively trace the down-linked frame
  600. * chain, so we insist that gen->frame has no parent to trace when the
  601. * generator is not running.
  602. */
  603. JS_ASSERT_IF(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING,
  604. !gen->frame.down);
  605. /*
  606. * FIXME be 390950. Generator's frame is a part of the JS stack when the
  607. * generator is running or closing. Thus tracing the frame in this case
  608. * here duplicates the work done in js_TraceContext.
  609. */
  610. js_TraceStackFrame(trc, &gen->frame);
  611. }
  612. JSClass js_GeneratorClass = {
  613. js_Generator_str,
  614. JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
  615. JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),
  616. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  617. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize,
  618. NULL, NULL, NULL, NULL,
  619. NULL, NULL, JS_CLASS_TRACE(generator_trace), NULL
  620. };
  621. /*
  622. * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
  623. * to the frame by which the generator function was activated. Create a new
  624. * JSGenerator object, which contains its own JSStackFrame that we populate
  625. * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
  626. * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  627. * if they are non-null.
  628. */
  629. JSObject *
  630. js_NewGenerator(JSContext *cx, JSStackFrame *fp)
  631. {
  632. JSObject *obj;
  633. uintN argc, nargs, nslots;
  634. JSGenerator *gen;
  635. jsval *slots;
  636. /* After the following return, failing control flow must goto bad. */
  637. obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL, 0);
  638. if (!obj)
  639. return NULL;
  640. /* Load and compute stack slot counts. */
  641. argc = fp->argc;
  642. nargs = JS_MAX(argc, fp->fun->nargs);
  643. nslots = 2 + nargs + fp->script->nslots;
  644. /* Allocate obj's private data struct. */
  645. gen = (JSGenerator *)
  646. JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
  647. if (!gen)
  648. goto bad;
  649. gen->obj = obj;
  650. /* Steal away objects reflecting fp and point them at gen->frame. */
  651. gen->frame.callobj = fp->callobj;
  652. if (fp->callobj) {
  653. JS_SetPrivate(cx, fp->callobj, &gen->frame);
  654. fp->callobj = NULL;
  655. }
  656. gen->frame.argsobj = fp->argsobj;
  657. if (fp->argsobj) {
  658. JS_SetPrivate(cx, fp->argsobj, &gen->frame);
  659. fp->argsobj = NULL;
  660. }
  661. /* These two references can be shared with fp until it goes away. */
  662. gen->frame.varobj = fp->varobj;
  663. gen->frame.thisp = fp->thisp;
  664. /* Copy call-invariant script and function references. */
  665. gen->frame.script = fp->script;
  666. gen->frame.callee = fp->callee;
  667. gen->frame.fun = fp->fun;
  668. /* Use slots to carve space out of gen->slots. */
  669. slots = gen->slots;
  670. gen->arena.next = NULL;
  671. gen->arena.base = (jsuword) slots;
  672. gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots);
  673. /* Copy rval, argv and vars. */
  674. gen->frame.rval = fp->rval;
  675. memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval));
  676. gen->frame.argc = nargs;
  677. gen->frame.argv = slots + 2;
  678. slots += 2 + nargs;
  679. memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval));
  680. /* Initialize or copy virtual machine state. */
  681. gen->frame.down = NULL;
  682. gen->frame.annotation = NULL;
  683. gen->frame.scopeChain = fp->scopeChain;
  684. gen->frame.imacpc = NULL;
  685. gen->frame.slots = slots;
  686. JS_ASSERT(StackBase(fp) == fp->regs->sp);
  687. gen->savedRegs.sp = slots + fp->script->nfixed;
  688. gen->savedRegs.pc = fp->regs->pc;
  689. gen->frame.regs = &gen->savedRegs;
  690. /* Copy remaining state (XXX sharp* and xml* should be local vars). */
  691. gen->frame.sharpDepth = 0;
  692. gen->frame.sharpArray = NULL;
  693. gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
  694. gen->frame.dormantNext = NULL;
  695. gen->frame.xmlNamespace = NULL;
  696. gen->frame.blockChain = NULL;
  697. /* Note that gen is newborn. */
  698. gen->state = JSGEN_NEWBORN;
  699. if (!JS_SetPrivate(cx, obj, gen)) {
  700. JS_free(cx, gen);
  701. goto bad;
  702. }
  703. return obj;
  704. bad:
  705. cx->weakRoots.newborn[GCX_OBJECT] = NULL;
  706. return NULL;
  707. }
  708. typedef enum JSGeneratorOp {
  709. JSGENOP_NEXT,
  710. JSGENOP_SEND,
  711. JSGENOP_THROW,
  712. JSGENOP_CLOSE
  713. } JSGeneratorOp;
  714. /*
  715. * Start newborn or restart yielding generator and perform the requested
  716. * operation inside its frame.
  717. */
  718. static JSBool
  719. SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
  720. JSGenerator *gen, jsval arg)
  721. {
  722. JSStackFrame *fp;
  723. JSArena *arena;
  724. JSBool ok;
  725. if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
  726. js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
  727. JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj),
  728. JS_GetFunctionId(gen->frame.fun));
  729. return JS_FALSE;
  730. }
  731. JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
  732. switch (op) {
  733. case JSGENOP_NEXT:
  734. case JSGENOP_SEND:
  735. if (gen->state == JSGEN_OPEN) {
  736. /*
  737. * Store the argument to send as the result of the yield
  738. * expression.
  739. */
  740. gen->savedRegs.sp[-1] = arg;
  741. }
  742. gen->state = JSGEN_RUNNING;
  743. break;
  744. case JSGENOP_THROW:
  745. JS_SetPendingException(cx, arg);
  746. gen->state = JSGEN_RUNNING;
  747. break;
  748. default:
  749. JS_ASSERT(op == JSGENOP_CLOSE);
  750. JS_SetPendingException(cx, JSVAL_ARETURN);
  751. gen->state = JSGEN_CLOSING;
  752. break;
  753. }
  754. /* Extend the current stack pool with gen->arena. */
  755. arena = cx->stackPool.current;
  756. JS_ASSERT(!arena->next);
  757. JS_ASSERT(!gen->arena.next);
  758. JS_ASSERT(cx->stackPool.current != &gen->arena);
  759. cx->stackPool.current = arena->next = &gen->arena;
  760. /* Push gen->frame around the interpreter activation. */
  761. fp = cx->fp;
  762. cx->fp = &gen->frame;
  763. gen->frame.down = fp;
  764. ok = js_Interpret(cx);
  765. cx->fp = fp;
  766. gen->frame.down = NULL;
  767. /* Retract the stack pool and sanitize gen->arena. */
  768. JS_ASSERT(!gen->arena.next);
  769. JS_ASSERT(arena->next == &gen->arena);
  770. JS_ASSERT(cx->stackPool.current == &gen->arena);
  771. cx->stackPool.current = arena;
  772. arena->next = NULL;
  773. if (gen->frame.flags & JSFRAME_YIELDING) {
  774. /* Yield cannot fail, throw or be called on closing. */
  775. JS_ASSERT(ok);
  776. JS_ASSERT(!cx->throwing);
  777. JS_ASSERT(gen->state == JSGEN_RUNNING);
  778. JS_ASSERT(op != JSGENOP_CLOSE);
  779. gen->frame.flags &= ~JSFRAME_YIELDING;
  780. gen->state = JSGEN_OPEN;
  781. return JS_TRUE;
  782. }
  783. gen->frame.rval = JSVAL_VOID;
  784. gen->state = JSGEN_CLOSED;
  785. if (ok) {
  786. /* Returned, explicitly or by falling off the end. */
  787. if (op == JSGENOP_CLOSE)
  788. return JS_TRUE;
  789. return js_ThrowStopIteration(cx);
  790. }
  791. /*
  792. * An error, silent termination by operation callback or an exception.
  793. * Propagate the condition to the caller.
  794. */
  795. return JS_FALSE;
  796. }
  797. static JSBool
  798. CloseGenerator(JSContext *cx, JSObject *obj)
  799. {
  800. JSGenerator *gen;
  801. JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
  802. gen = (JSGenerator *) JS_GetPrivate(cx, obj);
  803. if (!gen) {
  804. /* Generator prototype object. */
  805. return JS_TRUE;
  806. }
  807. if (gen->state == JSGEN_CLOSED)
  808. return JS_TRUE;
  809. return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID);
  810. }
  811. /*
  812. * Common subroutine of generator_(next|send|throw|close) methods.
  813. */
  814. static JSBool
  815. generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc)
  816. {
  817. JSObject *obj;
  818. JSGenerator *gen;
  819. jsval arg;
  820. obj = JS_THIS_OBJECT(cx, vp);
  821. if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
  822. return JS_FALSE;
  823. gen = (JSGenerator *) JS_GetPrivate(cx, obj);
  824. if (gen == NULL) {
  825. /* This happens when obj is the generator prototype. See bug 352885. */
  826. goto closed_generator;
  827. }
  828. if (gen->state == JSGEN_NEWBORN) {
  829. switch (op) {
  830. case JSGENOP_NEXT:
  831. case JSGENOP_THROW:
  832. break;
  833. case JSGENOP_SEND:
  834. if (argc >= 1 && !JSVAL_IS_VOID(vp[2])) {
  835. js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
  836. JSDVG_SEARCH_STACK, vp[2], NULL);
  837. return JS_FALSE;
  838. }
  839. break;
  840. default:
  841. JS_ASSERT(op == JSGENOP_CLOSE);
  842. gen->state = JSGEN_CLOSED;
  843. return JS_TRUE;
  844. }
  845. } else if (gen->state == JSGEN_CLOSED) {
  846. closed_generator:
  847. switch (op) {
  848. case JSGENOP_NEXT:
  849. case JSGENOP_SEND:
  850. return js_ThrowStopIteration(cx);
  851. case JSGENOP_THROW:
  852. JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID);
  853. return JS_FALSE;
  854. default:
  855. JS_ASSERT(op == JSGENOP_CLOSE);
  856. return JS_TRUE;
  857. }
  858. }
  859. arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0)
  860. ? vp[2]
  861. : JSVAL_VOID;
  862. if (!SendToGenerator(cx, op, obj, gen, arg))
  863. return JS_FALSE;
  864. *vp = gen->frame.rval;
  865. return JS_TRUE;
  866. }
  867. static JSBool
  868. generator_send(JSContext *cx, uintN argc, jsval *vp)
  869. {
  870. return generator_op(cx, JSGENOP_SEND, vp, argc);
  871. }
  872. static JSBool
  873. generator_next(JSContext *cx, uintN argc, jsval *vp)
  874. {
  875. return generator_op(cx, JSGENOP_NEXT, vp, argc);
  876. }
  877. static JSBool
  878. generator_throw(JSContext *cx, uintN argc, jsval *vp)
  879. {
  880. return generator_op(cx, JSGENOP_THROW, vp, argc);
  881. }
  882. static JSBool
  883. generator_close(JSContext *cx, uintN argc, jsval *vp)
  884. {
  885. return generator_op(cx, JSGENOP_CLOSE, vp, argc);
  886. }
  887. static JSFunctionSpec generator_methods[] = {
  888. JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
  889. JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
  890. JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
  891. JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
  892. JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
  893. JS_FS_END
  894. };
  895. #endif /* JS_HAS_GENERATORS */
  896. JSObject *
  897. js_InitIteratorClasses(JSContext *cx, JSObject *obj)
  898. {
  899. JSObject *proto, *stop;
  900. /* Idempotency required: we initialize several things, possibly lazily. */
  901. if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
  902. return NULL;
  903. if (stop)
  904. return stop;
  905. proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
  906. NULL, iterator_methods, NULL, NULL);
  907. if (!proto)
  908. return NULL;
  909. STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL);
  910. #if JS_HAS_GENERATORS
  911. /* Initialize the generator internals if configured. */
  912. if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
  913. NULL, generator_methods, NULL, NULL)) {
  914. return NULL;
  915. }
  916. #endif
  917. return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
  918. NULL, NULL, NULL, NULL);
  919. }