PageRenderTime 61ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2127 lines | 1648 code | 232 blank | 247 comment | 338 complexity | 9c5a81c78934020d816cc510d7eacc68 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. * JS object implementation.
  42. */
  43. #include "jsstddef.h"
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include "jstypes.h"
  47. #include "jsarena.h" /* Added by JSIFY */
  48. #include "jsbit.h"
  49. #include "jsutil.h" /* Added by JSIFY */
  50. #include "jshash.h" /* Added by JSIFY */
  51. #include "jsdhash.h"
  52. #include "jsprf.h"
  53. #include "jsapi.h"
  54. #include "jsarray.h"
  55. #include "jsatom.h"
  56. #include "jsbool.h"
  57. #include "jsbuiltins.h"
  58. #include "jscntxt.h"
  59. #include "jsversion.h"
  60. #include "jsemit.h"
  61. #include "jsfun.h"
  62. #include "jsgc.h"
  63. #include "jsinterp.h"
  64. #include "jslock.h"
  65. #include "jsnum.h"
  66. #include "jsobj.h"
  67. #include "jsopcode.h"
  68. #include "jsparse.h"
  69. #include "jsscope.h"
  70. #include "jsscript.h"
  71. #include "jsstr.h"
  72. #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
  73. #include "jsstaticcheck.h"
  74. #if JS_HAS_GENERATORS
  75. #include "jsiter.h"
  76. #endif
  77. #if JS_HAS_XML_SUPPORT
  78. #include "jsxml.h"
  79. #endif
  80. #if JS_HAS_XDR
  81. #include "jsxdrapi.h"
  82. #endif
  83. #ifdef INCLUDE_MOZILLA_DTRACE
  84. #include "jsdtracef.h"
  85. #endif
  86. #include "jsautooplen.h"
  87. #ifdef JS_THREADSAFE
  88. #define NATIVE_DROP_PROPERTY js_DropProperty
  89. extern void
  90. js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
  91. #else
  92. #define NATIVE_DROP_PROPERTY NULL
  93. #endif
  94. JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
  95. js_NewObjectMap, js_DestroyObjectMap,
  96. js_LookupProperty, js_DefineProperty,
  97. js_GetProperty, js_SetProperty,
  98. js_GetAttributes, js_SetAttributes,
  99. js_DeleteProperty, js_DefaultValue,
  100. js_Enumerate, js_CheckAccess,
  101. NULL, NATIVE_DROP_PROPERTY,
  102. js_Call, js_Construct,
  103. NULL, js_HasInstance,
  104. js_SetProtoOrParent, js_SetProtoOrParent,
  105. js_TraceObject, js_Clear,
  106. js_GetRequiredSlot, js_SetRequiredSlot
  107. };
  108. JSClass js_ObjectClass = {
  109. js_Object_str,
  110. JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
  111. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  112. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  113. JSCLASS_NO_OPTIONAL_MEMBERS
  114. };
  115. #if JS_HAS_OBJ_PROTO_PROP
  116. static JSBool
  117. obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  118. static JSBool
  119. obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  120. static JSBool
  121. obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
  122. static JSPropertySpec object_props[] = {
  123. /* These two must come first; see object_props[slot].name usage below. */
  124. {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
  125. obj_getSlot, obj_setSlot},
  126. {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
  127. obj_getSlot, obj_setSlot},
  128. {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
  129. obj_getCount, NULL},
  130. {0,0,0,0,0}
  131. };
  132. /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
  133. #define JSSLOT_COUNT 2
  134. static JSBool
  135. ReportStrictSlot(JSContext *cx, uint32 slot)
  136. {
  137. if (slot == JSSLOT_PROTO)
  138. return JS_TRUE;
  139. return JS_ReportErrorFlagsAndNumber(cx,
  140. JSREPORT_WARNING | JSREPORT_STRICT,
  141. js_GetErrorMessage, NULL,
  142. JSMSG_DEPRECATED_USAGE,
  143. object_props[slot].name);
  144. }
  145. static JSBool
  146. obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  147. {
  148. uint32 slot;
  149. jsid propid;
  150. JSAccessMode mode;
  151. uintN attrs;
  152. JSObject *pobj;
  153. JSClass *clasp;
  154. JSExtendedClass *xclasp;
  155. slot = (uint32) JSVAL_TO_INT(id);
  156. if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
  157. propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
  158. mode = JSACC_PROTO;
  159. } else {
  160. propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
  161. mode = JSACC_PARENT;
  162. }
  163. /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
  164. if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
  165. return JS_FALSE;
  166. pobj = JSVAL_TO_OBJECT(*vp);
  167. if (pobj) {
  168. clasp = OBJ_GET_CLASS(cx, pobj);
  169. if (clasp == &js_CallClass || clasp == &js_BlockClass) {
  170. /* Censor activations and lexical scopes per ECMA-262. */
  171. *vp = JSVAL_NULL;
  172. } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
  173. xclasp = (JSExtendedClass *) clasp;
  174. if (xclasp->outerObject) {
  175. pobj = xclasp->outerObject(cx, pobj);
  176. if (!pobj)
  177. return JS_FALSE;
  178. *vp = OBJECT_TO_JSVAL(pobj);
  179. }
  180. }
  181. }
  182. return JS_TRUE;
  183. }
  184. static JSBool
  185. obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  186. {
  187. JSObject *pobj;
  188. uint32 slot;
  189. jsid propid;
  190. uintN attrs;
  191. if (!JSVAL_IS_OBJECT(*vp))
  192. return JS_TRUE;
  193. pobj = JSVAL_TO_OBJECT(*vp);
  194. if (pobj) {
  195. /*
  196. * Innerize pobj here to avoid sticking unwanted properties on the
  197. * outer object. This ensures that any with statements only grant
  198. * access to the inner object.
  199. */
  200. OBJ_TO_INNER_OBJECT(cx, pobj);
  201. if (!pobj)
  202. return JS_FALSE;
  203. }
  204. slot = (uint32) JSVAL_TO_INT(id);
  205. if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
  206. return JS_FALSE;
  207. /* __parent__ is readonly and permanent, only __proto__ may be set. */
  208. propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
  209. if (!OBJ_CHECK_ACCESS(cx, obj, propid,
  210. (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp,
  211. &attrs)) {
  212. return JS_FALSE;
  213. }
  214. return js_SetProtoOrParent(cx, obj, slot, pobj);
  215. }
  216. static JSBool
  217. obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  218. {
  219. jsval iter_state;
  220. jsid num_properties;
  221. JSBool ok;
  222. if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
  223. return JS_FALSE;
  224. /* Get the number of properties to enumerate. */
  225. iter_state = JSVAL_NULL;
  226. ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
  227. if (!ok)
  228. goto out;
  229. if (!JSVAL_IS_INT(num_properties)) {
  230. JS_ASSERT(0);
  231. *vp = JSVAL_ZERO;
  232. goto out;
  233. }
  234. *vp = num_properties;
  235. out:
  236. if (iter_state != JSVAL_NULL)
  237. ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
  238. return ok;
  239. }
  240. #else /* !JS_HAS_OBJ_PROTO_PROP */
  241. #define object_props NULL
  242. #endif /* !JS_HAS_OBJ_PROTO_PROP */
  243. JSBool
  244. js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
  245. {
  246. JSSetSlotRequest ssr;
  247. JSRuntime *rt;
  248. /* Optimize the null case to avoid the unnecessary overhead of js_GC. */
  249. if (!pobj) {
  250. JS_LOCK_OBJ(cx, obj);
  251. if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) {
  252. JS_UNLOCK_OBJ(cx, obj);
  253. return JS_FALSE;
  254. }
  255. LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL);
  256. JS_UNLOCK_OBJ(cx, obj);
  257. return JS_TRUE;
  258. }
  259. ssr.obj = obj;
  260. ssr.pobj = pobj;
  261. ssr.slot = (uint16) slot;
  262. ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR;
  263. rt = cx->runtime;
  264. JS_LOCK_GC(rt);
  265. ssr.next = rt->setSlotRequests;
  266. rt->setSlotRequests = &ssr;
  267. for (;;) {
  268. js_GC(cx, GC_SET_SLOT_REQUEST);
  269. JS_UNLOCK_GC(rt);
  270. if (!rt->setSlotRequests)
  271. break;
  272. JS_LOCK_GC(rt);
  273. }
  274. if (ssr.errnum != JSMSG_NOT_AN_ERROR) {
  275. if (ssr.errnum == JSMSG_OUT_OF_MEMORY) {
  276. JS_ReportOutOfMemory(cx);
  277. } else {
  278. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum,
  279. #if JS_HAS_OBJ_PROTO_PROP
  280. object_props[slot].name
  281. #else
  282. (slot == JSSLOT_PROTO) ? js_proto_str
  283. : js_parent_str
  284. #endif
  285. );
  286. }
  287. return JS_FALSE;
  288. }
  289. // Maintain the "any Array prototype has indexed properties hazard" flag.
  290. if (slot == JSSLOT_PROTO &&
  291. OBJ_IS_ARRAY(cx, pobj) &&
  292. pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) {
  293. rt->anyArrayProtoHasElement = JS_TRUE;
  294. }
  295. return JS_TRUE;
  296. }
  297. static JSHashNumber
  298. js_hash_object(const void *key)
  299. {
  300. return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
  301. }
  302. static JSHashEntry *
  303. MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
  304. {
  305. JSSharpObjectMap *map;
  306. JSHashTable *table;
  307. JSHashNumber hash;
  308. JSHashEntry **hep, *he;
  309. jsatomid sharpid;
  310. JSIdArray *ida;
  311. JSBool ok;
  312. jsint i, length;
  313. jsid id;
  314. #if JS_HAS_GETTER_SETTER
  315. JSObject *obj2;
  316. JSProperty *prop;
  317. uintN attrs;
  318. #endif
  319. jsval val;
  320. JS_CHECK_RECURSION(cx, return NULL);
  321. map = &cx->sharpObjectMap;
  322. table = map->table;
  323. hash = js_hash_object(obj);
  324. hep = JS_HashTableRawLookup(table, hash, obj);
  325. he = *hep;
  326. if (!he) {
  327. sharpid = 0;
  328. he = JS_HashTableRawAdd(table, hep, hash, obj,
  329. JS_UINT32_TO_PTR(sharpid));
  330. if (!he) {
  331. JS_ReportOutOfMemory(cx);
  332. return NULL;
  333. }
  334. /*
  335. * Increment map->depth to protect js_EnterSharpObject from reentering
  336. * itself badly. Without this fix, if we reenter the basis case where
  337. * map->depth == 0, when unwinding the inner call we will destroy the
  338. * newly-created hash table and crash.
  339. */
  340. ++map->depth;
  341. ida = JS_Enumerate(cx, obj);
  342. --map->depth;
  343. if (!ida)
  344. return NULL;
  345. ok = JS_TRUE;
  346. for (i = 0, length = ida->length; i < length; i++) {
  347. id = ida->vector[i];
  348. #if JS_HAS_GETTER_SETTER
  349. ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
  350. if (!ok)
  351. break;
  352. if (!prop)
  353. continue;
  354. ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
  355. if (ok) {
  356. if (OBJ_IS_NATIVE(obj2) &&
  357. (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
  358. val = JSVAL_NULL;
  359. if (attrs & JSPROP_GETTER)
  360. val = (jsval) ((JSScopeProperty*)prop)->getter;
  361. if (attrs & JSPROP_SETTER) {
  362. if (val != JSVAL_NULL) {
  363. /* Mark the getter, then set val to setter. */
  364. ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
  365. NULL)
  366. != NULL);
  367. }
  368. val = (jsval) ((JSScopeProperty*)prop)->setter;
  369. }
  370. } else {
  371. ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
  372. }
  373. }
  374. OBJ_DROP_PROPERTY(cx, obj2, prop);
  375. #else
  376. ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
  377. #endif
  378. if (!ok)
  379. break;
  380. if (!JSVAL_IS_PRIMITIVE(val) &&
  381. !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
  382. ok = JS_FALSE;
  383. break;
  384. }
  385. }
  386. if (!ok || !idap)
  387. JS_DestroyIdArray(cx, ida);
  388. if (!ok)
  389. return NULL;
  390. } else {
  391. sharpid = JS_PTR_TO_UINT32(he->value);
  392. if (sharpid == 0) {
  393. sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
  394. he->value = JS_UINT32_TO_PTR(sharpid);
  395. }
  396. ida = NULL;
  397. }
  398. if (idap)
  399. *idap = ida;
  400. return he;
  401. }
  402. JSHashEntry *
  403. js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
  404. jschar **sp)
  405. {
  406. JSSharpObjectMap *map;
  407. JSHashTable *table;
  408. JSIdArray *ida;
  409. JSHashNumber hash;
  410. JSHashEntry *he, **hep;
  411. jsatomid sharpid;
  412. char buf[20];
  413. size_t len;
  414. if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
  415. return NULL;
  416. /* Set to null in case we return an early error. */
  417. *sp = NULL;
  418. map = &cx->sharpObjectMap;
  419. table = map->table;
  420. if (!table) {
  421. table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
  422. JS_CompareValues, NULL, NULL);
  423. if (!table) {
  424. JS_ReportOutOfMemory(cx);
  425. return NULL;
  426. }
  427. map->table = table;
  428. JS_KEEP_ATOMS(cx->runtime);
  429. }
  430. /* From this point the control must flow either through out: or bad:. */
  431. ida = NULL;
  432. if (map->depth == 0) {
  433. he = MarkSharpObjects(cx, obj, &ida);
  434. if (!he)
  435. goto bad;
  436. JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
  437. if (!idap) {
  438. JS_DestroyIdArray(cx, ida);
  439. ida = NULL;
  440. }
  441. } else {
  442. hash = js_hash_object(obj);
  443. hep = JS_HashTableRawLookup(table, hash, obj);
  444. he = *hep;
  445. /*
  446. * It's possible that the value of a property has changed from the
  447. * first time the object's properties are traversed (when the property
  448. * ids are entered into the hash table) to the second (when they are
  449. * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
  450. * idempotent.
  451. */
  452. if (!he) {
  453. he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
  454. if (!he) {
  455. JS_ReportOutOfMemory(cx);
  456. goto bad;
  457. }
  458. sharpid = 0;
  459. goto out;
  460. }
  461. }
  462. sharpid = JS_PTR_TO_UINT32(he->value);
  463. if (sharpid != 0) {
  464. len = JS_snprintf(buf, sizeof buf, "#%u%c",
  465. sharpid >> SHARP_ID_SHIFT,
  466. (sharpid & SHARP_BIT) ? '#' : '=');
  467. *sp = js_InflateString(cx, buf, &len);
  468. if (!*sp) {
  469. if (ida)
  470. JS_DestroyIdArray(cx, ida);
  471. goto bad;
  472. }
  473. }
  474. out:
  475. JS_ASSERT(he);
  476. if ((sharpid & SHARP_BIT) == 0) {
  477. if (idap && !ida) {
  478. ida = JS_Enumerate(cx, obj);
  479. if (!ida) {
  480. if (*sp) {
  481. JS_free(cx, *sp);
  482. *sp = NULL;
  483. }
  484. goto bad;
  485. }
  486. }
  487. map->depth++;
  488. }
  489. if (idap)
  490. *idap = ida;
  491. return he;
  492. bad:
  493. /* Clean up the sharpObjectMap table on outermost error. */
  494. if (map->depth == 0) {
  495. JS_UNKEEP_ATOMS(cx->runtime);
  496. map->sharpgen = 0;
  497. JS_HashTableDestroy(map->table);
  498. map->table = NULL;
  499. }
  500. return NULL;
  501. }
  502. void
  503. js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
  504. {
  505. JSSharpObjectMap *map;
  506. JSIdArray *ida;
  507. map = &cx->sharpObjectMap;
  508. JS_ASSERT(map->depth > 0);
  509. if (--map->depth == 0) {
  510. JS_UNKEEP_ATOMS(cx->runtime);
  511. map->sharpgen = 0;
  512. JS_HashTableDestroy(map->table);
  513. map->table = NULL;
  514. }
  515. if (idap) {
  516. ida = *idap;
  517. if (ida) {
  518. JS_DestroyIdArray(cx, ida);
  519. *idap = NULL;
  520. }
  521. }
  522. }
  523. static intN
  524. gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
  525. {
  526. JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
  527. "sharp table entry");
  528. return JS_DHASH_NEXT;
  529. }
  530. void
  531. js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
  532. {
  533. JS_ASSERT(map->depth > 0);
  534. JS_ASSERT(map->table);
  535. /*
  536. * During recursive calls to MarkSharpObjects a non-native object or
  537. * object with a custom getProperty method can potentially return an
  538. * unrooted value or even cut from the object graph an argument of one of
  539. * MarkSharpObjects recursive invocations. So we must protect map->table
  540. * entries against GC.
  541. *
  542. * We can not simply use JSTempValueRooter to mark the obj argument of
  543. * MarkSharpObjects during recursion as we have to protect *all* entries
  544. * in JSSharpObjectMap including those that contains otherwise unreachable
  545. * objects just allocated through custom getProperty. Otherwise newer
  546. * allocations can re-use the address of an object stored in the hashtable
  547. * confusing js_EnterSharpObject. So to address the problem we simply
  548. * mark all objects from map->table.
  549. *
  550. * An alternative "proper" solution is to use JSTempValueRooter in
  551. * MarkSharpObjects with code to remove during finalization entries
  552. * with otherwise unreachable objects. But this is way too complex
  553. * to justify spending efforts.
  554. */
  555. JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
  556. }
  557. #if JS_HAS_TOSOURCE
  558. static JSBool
  559. obj_toSource(JSContext *cx, uintN argc, jsval *vp)
  560. {
  561. JSBool ok, outermost;
  562. JSObject *obj;
  563. JSHashEntry *he;
  564. JSIdArray *ida;
  565. jschar *chars, *ochars, *vsharp;
  566. const jschar *idstrchars, *vchars;
  567. size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
  568. const char *comma;
  569. jsint i, j, length, valcnt;
  570. jsid id;
  571. #if JS_HAS_GETTER_SETTER
  572. JSObject *obj2;
  573. JSProperty *prop;
  574. uintN attrs;
  575. #endif
  576. jsval *val;
  577. jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
  578. JSTempValueRooter tvr;
  579. JSString *gsopold[2];
  580. JSString *gsop[2];
  581. JSString *idstr, *valstr, *str;
  582. JS_CHECK_RECURSION(cx, return JS_FALSE);
  583. MUST_FLOW_THROUGH("out");
  584. JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
  585. /* If outermost, we need parentheses to be an expression, not a block. */
  586. outermost = (cx->sharpObjectMap.depth == 0);
  587. obj = JS_THIS_OBJECT(cx, vp);
  588. if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
  589. ok = JS_FALSE;
  590. goto out;
  591. }
  592. if (IS_SHARP(he)) {
  593. /*
  594. * We didn't enter -- obj is already "sharp", meaning we've visited it
  595. * already in our depth first search, and therefore chars contains a
  596. * string of the form "#n#".
  597. */
  598. JS_ASSERT(!ida);
  599. #if JS_HAS_SHARP_VARS
  600. nchars = js_strlen(chars);
  601. #else
  602. chars[0] = '{';
  603. chars[1] = '}';
  604. chars[2] = 0;
  605. nchars = 2;
  606. #endif
  607. goto make_string;
  608. }
  609. JS_ASSERT(ida);
  610. ok = JS_TRUE;
  611. if (!chars) {
  612. /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
  613. chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
  614. nchars = 0;
  615. if (!chars)
  616. goto error;
  617. if (outermost)
  618. chars[nchars++] = '(';
  619. } else {
  620. /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
  621. MAKE_SHARP(he);
  622. nchars = js_strlen(chars);
  623. chars = (jschar *)
  624. realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
  625. if (!chars) {
  626. free(ochars);
  627. goto error;
  628. }
  629. if (outermost) {
  630. /*
  631. * No need for parentheses around the whole shebang, because #n=
  632. * unambiguously begins an object initializer, and never a block
  633. * statement.
  634. */
  635. outermost = JS_FALSE;
  636. }
  637. }
  638. chars[nchars++] = '{';
  639. comma = NULL;
  640. /*
  641. * We have four local roots for cooked and raw value GC safety. Hoist the
  642. * "localroot + 2" out of the loop using the val local, which refers to
  643. * the raw (unconverted, "uncooked") values.
  644. */
  645. val = localroot + 2;
  646. for (i = 0, length = ida->length; i < length; i++) {
  647. JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
  648. /* Get strings for id and value and GC-root them via vp. */
  649. id = ida->vector[i];
  650. #if JS_HAS_GETTER_SETTER
  651. ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
  652. if (!ok)
  653. goto error;
  654. #endif
  655. /*
  656. * Convert id to a jsval and then to a string. Decide early whether we
  657. * prefer get/set or old getter/setter syntax.
  658. */
  659. idstr = js_ValueToString(cx, ID_TO_VALUE(id));
  660. if (!idstr) {
  661. ok = JS_FALSE;
  662. OBJ_DROP_PROPERTY(cx, obj2, prop);
  663. goto error;
  664. }
  665. *vp = STRING_TO_JSVAL(idstr); /* local root */
  666. idIsLexicalIdentifier = js_IsIdentifier(idstr);
  667. needOldStyleGetterSetter =
  668. !idIsLexicalIdentifier ||
  669. js_CheckKeyword(JSSTRING_CHARS(idstr),
  670. JSSTRING_LENGTH(idstr)) != TOK_EOF;
  671. #if JS_HAS_GETTER_SETTER
  672. valcnt = 0;
  673. if (prop) {
  674. ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
  675. if (!ok) {
  676. OBJ_DROP_PROPERTY(cx, obj2, prop);
  677. goto error;
  678. }
  679. if (OBJ_IS_NATIVE(obj2) &&
  680. (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
  681. if (attrs & JSPROP_GETTER) {
  682. val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
  683. gsopold[valcnt] =
  684. ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
  685. gsop[valcnt] =
  686. ATOM_TO_STRING(cx->runtime->atomState.getAtom);
  687. valcnt++;
  688. }
  689. if (attrs & JSPROP_SETTER) {
  690. val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
  691. gsopold[valcnt] =
  692. ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
  693. gsop[valcnt] =
  694. ATOM_TO_STRING(cx->runtime->atomState.setAtom);
  695. valcnt++;
  696. }
  697. } else {
  698. valcnt = 1;
  699. gsop[0] = NULL;
  700. gsopold[0] = NULL;
  701. ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
  702. }
  703. OBJ_DROP_PROPERTY(cx, obj2, prop);
  704. }
  705. #else /* !JS_HAS_GETTER_SETTER */
  706. /*
  707. * We simplify the source code at the price of minor dead code bloat in
  708. * the ECMA version (for testing only, see jsversion.h). The null
  709. * default values in gsop[j] suffice to disable non-ECMA getter and
  710. * setter code.
  711. */
  712. valcnt = 1;
  713. gsop[0] = NULL;
  714. gsopold[0] = NULL;
  715. ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
  716. #endif /* !JS_HAS_GETTER_SETTER */
  717. if (!ok)
  718. goto error;
  719. /*
  720. * If id is a string that's not an identifier, then it needs to be
  721. * quoted. Also, negative integer ids must be quoted.
  722. */
  723. if (JSID_IS_ATOM(id)
  724. ? !idIsLexicalIdentifier
  725. : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
  726. idstr = js_QuoteString(cx, idstr, (jschar)'\'');
  727. if (!idstr) {
  728. ok = JS_FALSE;
  729. goto error;
  730. }
  731. *vp = STRING_TO_JSVAL(idstr); /* local root */
  732. }
  733. JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
  734. for (j = 0; j < valcnt; j++) {
  735. /* Convert val[j] to its canonical source form. */
  736. valstr = js_ValueToSource(cx, val[j]);
  737. if (!valstr) {
  738. ok = JS_FALSE;
  739. goto error;
  740. }
  741. localroot[j] = STRING_TO_JSVAL(valstr); /* local root */
  742. JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
  743. if (vchars[0] == '#')
  744. needOldStyleGetterSetter = JS_TRUE;
  745. if (needOldStyleGetterSetter)
  746. gsop[j] = gsopold[j];
  747. /* If val[j] is a non-sharp object, consider sharpening it. */
  748. vsharp = NULL;
  749. vsharplength = 0;
  750. #if JS_HAS_SHARP_VARS
  751. if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
  752. he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
  753. &vsharp);
  754. if (!he) {
  755. ok = JS_FALSE;
  756. goto error;
  757. }
  758. if (IS_SHARP(he)) {
  759. vchars = vsharp;
  760. vlength = js_strlen(vchars);
  761. needOldStyleGetterSetter = JS_TRUE;
  762. gsop[j] = gsopold[j];
  763. } else {
  764. if (vsharp) {
  765. vsharplength = js_strlen(vsharp);
  766. MAKE_SHARP(he);
  767. needOldStyleGetterSetter = JS_TRUE;
  768. gsop[j] = gsopold[j];
  769. }
  770. js_LeaveSharpObject(cx, NULL);
  771. }
  772. }
  773. #endif
  774. #ifndef OLD_GETTER_SETTER
  775. /*
  776. * Remove '(function ' from the beginning of valstr and ')' from the
  777. * end so that we can put "get" in front of the function definition.
  778. */
  779. if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
  780. !needOldStyleGetterSetter) {
  781. JSFunction *fun = JS_ValueToFunction(cx, val[j]);
  782. const jschar *start = vchars;
  783. const jschar *end = vchars + vlength;
  784. uint8 parenChomp = 0;
  785. if (vchars[0] == '(') {
  786. vchars++;
  787. parenChomp = 1;
  788. }
  789. /*
  790. * Try to jump "getter" or "setter" keywords, if we suspect
  791. * they might appear here. This code can be confused by people
  792. * defining Function.prototype.toString, so let's be cautious.
  793. */
  794. if (JSFUN_GETTER_TEST(fun->flags) ||
  795. JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
  796. const jschar *tmp = js_strchr_limit(vchars, ' ', end);
  797. if (tmp)
  798. vchars = tmp + 1;
  799. }
  800. /* Try to jump "function" keyword. */
  801. if (vchars)
  802. vchars = js_strchr_limit(vchars, ' ', end);
  803. if (vchars) {
  804. if (*vchars == ' ')
  805. vchars++;
  806. vlength = end - vchars - parenChomp;
  807. } else {
  808. gsop[j] = NULL;
  809. vchars = start;
  810. }
  811. }
  812. #else
  813. needOldStyleGetterSetter = JS_TRUE;
  814. gsop[j] = gsopold[j];
  815. #endif
  816. #define SAFE_ADD(n) \
  817. JS_BEGIN_MACRO \
  818. size_t n_ = (n); \
  819. curlen += n_; \
  820. if (curlen < n_) \
  821. goto overflow; \
  822. JS_END_MACRO
  823. curlen = nchars;
  824. if (comma)
  825. SAFE_ADD(2);
  826. SAFE_ADD(idstrlength + 1);
  827. if (gsop[j])
  828. SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
  829. SAFE_ADD(vsharplength);
  830. SAFE_ADD(vlength);
  831. /* Account for the trailing null. */
  832. SAFE_ADD((outermost ? 2 : 1) + 1);
  833. #undef SAFE_ADD
  834. if (curlen > (size_t)-1 / sizeof(jschar))
  835. goto overflow;
  836. /* Allocate 1 + 1 at end for closing brace and terminating 0. */
  837. chars = (jschar *)
  838. realloc((ochars = chars), curlen * sizeof(jschar));
  839. if (!chars) {
  840. /* Save code space on error: let JS_free ignore null vsharp. */
  841. JS_free(cx, vsharp);
  842. free(ochars);
  843. goto error;
  844. }
  845. if (comma) {
  846. chars[nchars++] = comma[0];
  847. chars[nchars++] = comma[1];
  848. }
  849. comma = ", ";
  850. if (needOldStyleGetterSetter) {
  851. js_strncpy(&chars[nchars], idstrchars, idstrlength);
  852. nchars += idstrlength;
  853. if (gsop[j]) {
  854. chars[nchars++] = ' ';
  855. gsoplength = JSSTRING_LENGTH(gsop[j]);
  856. js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
  857. gsoplength);
  858. nchars += gsoplength;
  859. }
  860. chars[nchars++] = ':';
  861. } else { /* New style "decompilation" */
  862. if (gsop[j]) {
  863. gsoplength = JSSTRING_LENGTH(gsop[j]);
  864. js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
  865. gsoplength);
  866. nchars += gsoplength;
  867. chars[nchars++] = ' ';
  868. }
  869. js_strncpy(&chars[nchars], idstrchars, idstrlength);
  870. nchars += idstrlength;
  871. /* Extraneous space after id here will be extracted later */
  872. chars[nchars++] = gsop[j] ? ' ' : ':';
  873. }
  874. if (vsharplength) {
  875. js_strncpy(&chars[nchars], vsharp, vsharplength);
  876. nchars += vsharplength;
  877. }
  878. js_strncpy(&chars[nchars], vchars, vlength);
  879. nchars += vlength;
  880. if (vsharp)
  881. JS_free(cx, vsharp);
  882. }
  883. }
  884. chars[nchars++] = '}';
  885. if (outermost)
  886. chars[nchars++] = ')';
  887. chars[nchars] = 0;
  888. error:
  889. js_LeaveSharpObject(cx, &ida);
  890. if (!ok) {
  891. if (chars)
  892. free(chars);
  893. goto out;
  894. }
  895. if (!chars) {
  896. JS_ReportOutOfMemory(cx);
  897. ok = JS_FALSE;
  898. goto out;
  899. }
  900. make_string:
  901. str = js_NewString(cx, chars, nchars);
  902. if (!str) {
  903. free(chars);
  904. ok = JS_FALSE;
  905. goto out;
  906. }
  907. *vp = STRING_TO_JSVAL(str);
  908. ok = JS_TRUE;
  909. out:
  910. JS_POP_TEMP_ROOT(cx, &tvr);
  911. return ok;
  912. overflow:
  913. JS_free(cx, vsharp);
  914. free(chars);
  915. chars = NULL;
  916. goto error;
  917. }
  918. #endif /* JS_HAS_TOSOURCE */
  919. static JSBool
  920. obj_toString(JSContext *cx, uintN argc, jsval *vp)
  921. {
  922. JSObject *obj;
  923. jschar *chars;
  924. size_t nchars;
  925. const char *clazz, *prefix;
  926. JSString *str;
  927. obj = JS_THIS_OBJECT(cx, vp);
  928. if (!obj)
  929. return JS_FALSE;
  930. obj = js_GetWrappedObject(cx, obj);
  931. clazz = OBJ_GET_CLASS(cx, obj)->name;
  932. nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
  933. chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
  934. if (!chars)
  935. return JS_FALSE;
  936. prefix = "[object ";
  937. nchars = 0;
  938. while ((chars[nchars] = (jschar)*prefix) != 0)
  939. nchars++, prefix++;
  940. while ((chars[nchars] = (jschar)*clazz) != 0)
  941. nchars++, clazz++;
  942. chars[nchars++] = ']';
  943. chars[nchars] = 0;
  944. str = js_NewString(cx, chars, nchars);
  945. if (!str) {
  946. JS_free(cx, chars);
  947. return JS_FALSE;
  948. }
  949. *vp = STRING_TO_JSVAL(str);
  950. return JS_TRUE;
  951. }
  952. static JSBool
  953. obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
  954. {
  955. jsval thisv;
  956. JSString *str;
  957. thisv = JS_THIS(cx, vp);
  958. if (JSVAL_IS_NULL(thisv))
  959. return JS_FALSE;
  960. str = js_ValueToString(cx, thisv);
  961. if (!str)
  962. return JS_FALSE;
  963. *vp = STRING_TO_JSVAL(str);
  964. return JS_TRUE;
  965. }
  966. static JSBool
  967. obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
  968. {
  969. *vp = JS_THIS(cx, vp);
  970. return !JSVAL_IS_NULL(*vp);
  971. }
  972. /*
  973. * Check whether principals subsumes scopeobj's principals, and return true
  974. * if so (or if scopeobj has no principals, for backward compatibility with
  975. * the JS API, which does not require principals), and false otherwise.
  976. */
  977. JSBool
  978. js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
  979. JSPrincipals *principals, JSAtom *caller)
  980. {
  981. JSSecurityCallbacks *callbacks;
  982. JSPrincipals *scopePrincipals;
  983. const char *callerstr;
  984. callbacks = JS_GetSecurityCallbacks(cx);
  985. if (callbacks && callbacks->findObjectPrincipals) {
  986. scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
  987. if (!principals || !scopePrincipals ||
  988. !principals->subsume(principals, scopePrincipals)) {
  989. callerstr = js_AtomToPrintableString(cx, caller);
  990. if (!callerstr)
  991. return JS_FALSE;
  992. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  993. JSMSG_BAD_INDIRECT_CALL, callerstr);
  994. return JS_FALSE;
  995. }
  996. }
  997. return JS_TRUE;
  998. }
  999. JSObject *
  1000. js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
  1001. {
  1002. JSClass *clasp;
  1003. JSExtendedClass *xclasp;
  1004. JSObject *inner;
  1005. if (!scopeobj)
  1006. goto bad;
  1007. OBJ_TO_INNER_OBJECT(cx, scopeobj);
  1008. if (!scopeobj)
  1009. return NULL;
  1010. inner = scopeobj;
  1011. /* XXX This is an awful gross hack. */
  1012. while (scopeobj) {
  1013. clasp = OBJ_GET_CLASS(cx, scopeobj);
  1014. if (clasp->flags & JSCLASS_IS_EXTENDED) {
  1015. xclasp = (JSExtendedClass*)clasp;
  1016. if (xclasp->innerObject &&
  1017. xclasp->innerObject(cx, scopeobj) != scopeobj) {
  1018. goto bad;
  1019. }
  1020. }
  1021. scopeobj = OBJ_GET_PARENT(cx, scopeobj);
  1022. }
  1023. return inner;
  1024. bad:
  1025. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  1026. JSMSG_BAD_INDIRECT_CALL, caller);
  1027. return NULL;
  1028. }
  1029. const char *
  1030. js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
  1031. JSPrincipals *principals, uintN *linenop)
  1032. {
  1033. uint32 flags;
  1034. #ifdef DEBUG
  1035. JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
  1036. #endif
  1037. JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
  1038. flags = JS_GetScriptFilenameFlags(caller->script);
  1039. if ((flags & JSFILENAME_PROTECTED) &&
  1040. principals &&
  1041. strcmp(principals->codebase, "[System Principal]")) {
  1042. *linenop = 0;
  1043. return principals->codebase;
  1044. }
  1045. if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
  1046. JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
  1047. *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
  1048. } else {
  1049. *linenop = js_FramePCToLineNumber(cx, caller);
  1050. }
  1051. return caller->script->filename;
  1052. }
  1053. static JSBool
  1054. obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1055. {
  1056. JSStackFrame *fp, *caller;
  1057. JSBool indirectCall;
  1058. JSObject *scopeobj;
  1059. JSString *str;
  1060. const char *file;
  1061. uintN line;
  1062. JSPrincipals *principals;
  1063. uint32 tcflags;
  1064. JSScript *script;
  1065. JSBool ok;
  1066. #if JS_HAS_EVAL_THIS_SCOPE
  1067. JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
  1068. JSObject *setCallerScopeChain = NULL;
  1069. JSBool setCallerVarObj = JS_FALSE;
  1070. #endif
  1071. fp = cx->fp;
  1072. caller = JS_GetScriptedCaller(cx, fp);
  1073. indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
  1074. /*
  1075. * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
  1076. * calls that attempt to use a non-global object as the "with" object in
  1077. * the former indirect case.
  1078. */
  1079. scopeobj = OBJ_GET_PARENT(cx, obj);
  1080. if (scopeobj) {
  1081. scopeobj = js_GetWrappedObject(cx, obj);
  1082. scopeobj = OBJ_GET_PARENT(cx, scopeobj);
  1083. }
  1084. if (indirectCall || scopeobj) {
  1085. uintN flags = scopeobj
  1086. ? JSREPORT_ERROR
  1087. : JSREPORT_STRICT | JSREPORT_WARNING;
  1088. if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
  1089. JSMSG_BAD_INDIRECT_CALL,
  1090. js_eval_str)) {
  1091. return JS_FALSE;
  1092. }
  1093. }
  1094. if (!JSVAL_IS_STRING(argv[0])) {
  1095. *rval = argv[0];
  1096. return JS_TRUE;
  1097. }
  1098. /*
  1099. * If the caller is a lightweight function and doesn't have a variables
  1100. * object, then we need to provide one for the compiler to stick any
  1101. * declared (var) variables into.
  1102. */
  1103. if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
  1104. return JS_FALSE;
  1105. /* eval no longer takes an optional trailing argument. */
  1106. if (argc >= 2 &&
  1107. !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT,
  1108. js_GetErrorMessage, NULL,
  1109. JSMSG_EVAL_ARITY)) {
  1110. return JS_FALSE;
  1111. }
  1112. /* From here on, control must exit through label out with ok set. */
  1113. MUST_FLOW_THROUGH("out");
  1114. if (!scopeobj) {
  1115. #if JS_HAS_EVAL_THIS_SCOPE
  1116. /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
  1117. if (indirectCall) {
  1118. callerScopeChain = js_GetScopeChain(cx, caller);
  1119. if (!callerScopeChain) {
  1120. ok = JS_FALSE;
  1121. goto out;
  1122. }
  1123. OBJ_TO_INNER_OBJECT(cx, obj);
  1124. if (!obj) {
  1125. ok = JS_FALSE;
  1126. goto out;
  1127. }
  1128. if (obj != callerScopeChain) {
  1129. ok = js_CheckPrincipalsAccess(cx, obj,
  1130. caller->script->principals,
  1131. cx->runtime->atomState.evalAtom);
  1132. if (!ok)
  1133. goto out;
  1134. scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
  1135. if (!scopeobj) {
  1136. ok = JS_FALSE;
  1137. goto out;
  1138. }
  1139. /* Set fp->scopeChain too, for the compiler. */
  1140. caller->scopeChain = fp->scopeChain = scopeobj;
  1141. /* Remember scopeobj so we can null its private when done. */
  1142. setCallerScopeChain = scopeobj;
  1143. }
  1144. callerVarObj = caller->varobj;
  1145. if (obj != callerVarObj) {
  1146. /* Set fp->varobj too, for the compiler. */
  1147. caller->varobj = fp->varobj = obj;
  1148. setCallerVarObj = JS_TRUE;
  1149. }
  1150. }
  1151. #endif
  1152. /*
  1153. * Compile using caller's current scope object.
  1154. *
  1155. * NB: This means that native callers (who reach this point through
  1156. * the C API) must use the two parameter form.
  1157. */
  1158. if (caller) {
  1159. scopeobj = js_GetScopeChain(cx, caller);
  1160. if (!scopeobj) {
  1161. ok = JS_FALSE;
  1162. goto out;
  1163. }
  1164. }
  1165. }
  1166. /* Ensure we compile this eval with the right object in the scope chain. */
  1167. scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
  1168. if (!scopeobj) {
  1169. ok = JS_FALSE;
  1170. goto out;
  1171. }
  1172. str = JSVAL_TO_STRING(argv[0]);
  1173. if (caller) {
  1174. principals = JS_EvalFramePrincipals(cx, fp, caller);
  1175. file = js_ComputeFilename(cx, caller, principals, &line);
  1176. } else {
  1177. file = NULL;
  1178. line = 0;
  1179. principals = NULL;
  1180. }
  1181. tcflags = TCF_COMPILE_N_GO;
  1182. if (caller)
  1183. tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
  1184. script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
  1185. JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
  1186. NULL, file, line);
  1187. if (!script) {
  1188. ok = JS_FALSE;
  1189. goto out;
  1190. }
  1191. if (argc < 2) {
  1192. /* Execute using caller's new scope object (might be a Call object). */
  1193. if (caller)
  1194. scopeobj = caller->scopeChain;
  1195. }
  1196. /*
  1197. * Belt-and-braces: check that the lesser of eval's principals and the
  1198. * caller's principals has access to scopeobj.
  1199. */
  1200. ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
  1201. cx->runtime->atomState.evalAtom);
  1202. if (ok)
  1203. ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
  1204. script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
  1205. JS_SCRIPTS_TO_GC(cx) = script;
  1206. #ifdef CHECK_SCRIPT_OWNER
  1207. script->owner = NULL;
  1208. #endif
  1209. out:
  1210. #if JS_HAS_EVAL_THIS_SCOPE
  1211. /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
  1212. if (setCallerScopeChain) {
  1213. caller->scopeChain = callerScopeChain;
  1214. JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
  1215. JS_SetPrivate(cx, setCallerScopeChain, NULL);
  1216. }
  1217. if (setCallerVarObj)
  1218. caller->varobj = callerVarObj;
  1219. #endif
  1220. return ok;
  1221. }
  1222. #if JS_HAS_OBJ_WATCHPOINT
  1223. static JSBool
  1224. obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
  1225. void *closure)
  1226. {
  1227. JSObject *callable;
  1228. JSSecurityCallbacks *callbacks;
  1229. JSStackFrame *caller;
  1230. JSPrincipals *subject, *watcher;
  1231. JSResolvingKey key;
  1232. JSResolvingEntry *entry;
  1233. uint32 generation;
  1234. jsval argv[3];
  1235. JSBool ok;
  1236. callable = (JSObject *) closure;
  1237. callbacks = JS_GetSecurityCallbacks(cx);
  1238. if (callbacks && callbacks->findObjectPrincipals) {
  1239. /* Skip over any obj_watch_* frames between us and the real subject. */
  1240. caller = JS_GetScriptedCaller(cx, cx->fp);
  1241. if (caller) {
  1242. /*
  1243. * Only call the watch handler if the watcher is allowed to watch
  1244. * the currently executing script.
  1245. */
  1246. watcher = callbacks->findObjectPrincipals(cx, callable);
  1247. subject = JS_StackFramePrincipals(cx, caller);
  1248. if (watcher && subject && !watcher->subsume(watcher, subject)) {
  1249. /* Silently don't call the watch handler. */
  1250. return JS_TRUE;
  1251. }
  1252. }
  1253. }
  1254. /* Avoid recursion on (obj, id) already being watched on cx. */
  1255. key.obj = obj;
  1256. key.id = id;
  1257. if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
  1258. return JS_FALSE;
  1259. if (!entry)
  1260. return JS_TRUE;
  1261. generation = cx->resolvingTable->generation;
  1262. argv[0] = id;
  1263. argv[1] = old;
  1264. argv[2] = *nvp;
  1265. ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
  1266. js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
  1267. return ok;
  1268. }
  1269. static JSBool
  1270. obj_watch(JSContext *cx, uintN argc, jsval *vp)
  1271. {
  1272. JSObject *callable;
  1273. jsval userid, value;
  1274. jsid propid;
  1275. JSObject *obj;
  1276. uintN attrs;
  1277. if (argc <= 1) {
  1278. js_ReportMissingArg(cx, vp, 1);
  1279. return JS_FALSE;
  1280. }
  1281. callable = js_ValueToCallableObject(cx, &vp[3], 0);
  1282. if (!callable)
  1283. return JS_FALSE;
  1284. /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
  1285. userid = vp[2];
  1286. if (!JS_ValueToId(cx, userid, &propid))
  1287. return JS_FALSE;
  1288. obj = JS_THIS_OBJECT(cx, vp);
  1289. if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
  1290. return JS_FALSE;
  1291. if (attrs & JSPROP_READONLY)
  1292. return JS_TRUE;
  1293. *vp = JSVAL_VOID;
  1294. if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
  1295. return JS_FALSE;
  1296. return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
  1297. }
  1298. static JSBool
  1299. obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
  1300. {
  1301. JSObject *obj;
  1302. obj = JS_THIS_OBJECT(cx, vp);
  1303. if (!obj)
  1304. return JS_FALSE;
  1305. *vp = JSVAL_VOID;
  1306. return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
  1307. NULL, NULL);
  1308. }
  1309. #endif /* JS_HAS_OBJ_WATCHPOINT */
  1310. /*
  1311. * Prototype and property query methods, to complement the 'in' and
  1312. * 'instanceof' operators.
  1313. */
  1314. /* Proposed ECMA 15.2.4.5. */
  1315. static JSBool
  1316. obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
  1317. {
  1318. JSObject *obj;
  1319. obj = JS_THIS_OBJECT(cx, vp);
  1320. return obj &&
  1321. js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
  1322. }
  1323. JSBool
  1324. js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
  1325. jsval *vp)
  1326. {
  1327. jsid id;
  1328. JSObject *obj;
  1329. if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
  1330. return JS_FALSE;
  1331. obj = JS_THIS_OBJECT(cx, vp);
  1332. return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
  1333. }
  1334. JSBool
  1335. js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
  1336. jsval *vp)
  1337. {
  1338. JSObject *obj2;
  1339. JSProperty *prop;
  1340. JSScopeProperty *sprop;
  1341. if (!lookup(cx, obj, id, &obj2, &prop))
  1342. return JS_FALSE;
  1343. if (!prop) {
  1344. *vp = JSVAL_FALSE;
  1345. } else if (obj2 == obj) {
  1346. *vp = JSVAL_TRUE;
  1347. } else {
  1348. JSClass *clasp;
  1349. JSExtendedClass *xclasp;
  1350. JSObject *outer;
  1351. clasp = OBJ_GET_CLASS(cx, obj2);
  1352. if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
  1353. !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
  1354. outer = NULL;
  1355. } else {
  1356. outer = xclasp->outerObject(cx, obj2);
  1357. if (!outer)
  1358. return JS_FALSE;
  1359. }
  1360. if (outer == obj) {
  1361. *vp = JSVAL_TRUE;
  1362. } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
  1363. /*
  1364. * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
  1365. * delegated property makes that property appear to be direct in
  1366. * all delegating instances of the same native class. This hack
  1367. * avoids bloating every function instance with its own 'length'
  1368. * (AKA 'arity') property. But it must not extend across class
  1369. * boundaries, to avoid making hasOwnProperty lie (bug 320854).
  1370. *
  1371. * It's not really a hack, of course: a permanent property can't
  1372. * be deleted, and JSPROP_SHARED means "don't allocate a slot in
  1373. * any instance, prototype or delegating". Without a slot, and
  1374. * without the ability to remove and recreate (with differences)
  1375. * the property, there is no way to tell whether it is directly
  1376. * owned, or indirectly delegated.
  1377. */
  1378. sprop = (JSScopeProperty *)prop;
  1379. *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
  1380. } else {
  1381. *vp = JSVAL_FALSE;
  1382. }
  1383. }
  1384. if (prop)
  1385. OBJ_DROP_PROPERTY(cx, obj2, prop);
  1386. return JS_TRUE;
  1387. }
  1388. #ifdef JS_TRACER
  1389. static int32 FASTCALL
  1390. Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
  1391. {
  1392. jsid id;
  1393. jsval v;
  1394. if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id))
  1395. return JSVAL_TO_BOOLEAN(JSVAL_VOID);
  1396. if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v))
  1397. return JSVAL_TO_BOOLEAN(JSVAL_VOID);
  1398. JS_ASSERT(JSVAL_IS_BOOLEAN(v));
  1399. return JSVAL_TO_BOOLEAN(v);
  1400. }
  1401. #endif
  1402. /* Proposed ECMA 15.2.4.6. */
  1403. static JSBool
  1404. obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
  1405. {
  1406. JSBool b;
  1407. if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
  1408. argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
  1409. return JS_FALSE;
  1410. }
  1411. *vp = BOOLEAN_TO_JSVAL(b);
  1412. return JS_TRUE;
  1413. }
  1414. /* Proposed ECMA 15.2.4.7. */
  1415. static JSBool
  1416. obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
  1417. {
  1418. jsid id;
  1419. JSObject *obj;
  1420. if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
  1421. return JS_FALSE;
  1422. obj = JS_THIS_OBJECT(cx, vp);
  1423. return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
  1424. }
  1425. #ifdef JS_TRACER
  1426. static int32 FASTCALL
  1427. Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
  1428. {
  1429. jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
  1430. jsval v;
  1431. if (!js_PropertyIsEnumerable(cx, obj, id, &v))
  1432. return JSVAL_TO_BOOLEAN(JSVAL_VOID);
  1433. JS_ASSERT(JSVAL_IS_BOOLEAN(v));
  1434. return JSVAL_TO_BOOLEAN(v);
  1435. }
  1436. #endif
  1437. JSBool
  1438. js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
  1439. {
  1440. JSObject *pobj;
  1441. uintN attrs;
  1442. JSProperty *prop;
  1443. JSBool ok;
  1444. if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
  1445. return JS_FALSE;
  1446. if (!prop) {
  1447. *vp = JSVAL_FALSE;
  1448. return JS_TRUE;
  1449. }
  1450. /*
  1451. * XXX ECMA spec error compatible: return false unless hasOwnProperty.
  1452. * The ECMA spec really should be fixed so propertyIsEnumerable and the
  1453. * for..in loop agree on whether prototype properties are enumerable,
  1454. * obviously by fixing this method (not by breaking the for..in loop!).
  1455. *
  1456. * We check here for shared permanent prototype properties, which should
  1457. * be treated as if they are local to obj. They are an implementation
  1458. * technique used to satisfy ECMA requirements; users should not be able
  1459. * to distinguish a shared permanent proto-property from a local one.
  1460. */
  1461. if (pobj != obj &&
  1462. !(OBJ_IS_NATIVE(pobj) &&
  1463. SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
  1464. OBJ_DROP_PROPERTY(cx, pobj, prop);
  1465. *vp = JSVAL_FALSE;
  1466. return JS_TRUE;
  1467. }
  1468. ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs);
  1469. OBJ_DROP_PROPERTY(cx, pobj, prop);
  1470. if (ok)
  1471. *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
  1472. return ok;
  1473. }
  1474. #if JS_HAS_GETTER_SETTER
  1475. static JSBool
  1476. obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
  1477. {
  1478. jsval fval, junk;
  1479. jsid id;
  1480. JSObject *obj;
  1481. uintN attrs;
  1482. if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
  1483. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  1484. JSMSG_BAD_GETTER_OR_SETTER,
  1485. js_getter_str);
  1486. return JS_FALSE;
  1487. }
  1488. fval = vp[3];
  1489. if (!JS_ValueToId(cx, vp[2], &id))
  1490. return JS_FALSE;
  1491. obj = JS_THIS_OBJECT(cx, vp);
  1492. if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
  1493. return JS_FALSE;
  1494. /*
  1495. * Getters and setters are just like watchpoints from an access
  1496. * control point of view.
  1497. */
  1498. if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
  1499. return JS_FALSE;
  1500. *vp = JSVAL_VOID;
  1501. return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
  1502. (JSPropertyOp) JSVAL_TO_OBJECT(fval),
  1503. JS_PropertyStub,
  1504. JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
  1505. NULL);
  1506. }
  1507. static JSBool
  1508. obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
  1509. {
  1510. jsval fval, junk;
  1511. jsid id;
  1512. JSObject *obj;
  1513. uintN attrs;
  1514. if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
  1515. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  1516. JSMSG_BAD_GETTER_OR_SETTER,
  1517. js_setter_str);
  1518. return JS_FALSE;
  1519. }
  1520. fval = vp[3];
  1521. if (!JS_ValueToId(cx, vp[2], &id))
  1522. return JS_FALSE;
  1523. obj = JS_THIS_OBJECT(cx, vp);
  1524. if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
  1525. return JS_FALSE;
  1526. /*
  1527. * Getters and setters are just like watchpoints from an access
  1528. * control point of view.
  1529. */
  1530. if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
  1531. return JS_FALSE;
  1532. *vp = JSVAL_VOID;
  1533. return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
  1534. JS_PropertyStub,
  1535. (JSPropertyOp) JSVAL_TO_OBJECT(fval),
  1536. JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
  1537. NULL);
  1538. }
  1539. static JSBool
  1540. obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
  1541. {
  1542. jsid id;
  1543. JSObject *obj, *pobj;
  1544. JSProperty *prop;
  1545. JSScopeProperty *sprop;
  1546. if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
  1547. return JS_FALSE;
  1548. obj = JS_THIS_OBJECT(cx, vp);
  1549. if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
  1550. return JS_FALSE;
  1551. *vp = JSVAL_VOID;
  1552. if (prop) {
  1553. if (OBJ_IS_NATIVE(pobj)) {
  1554. sprop = (JSScopeProperty *) prop;
  1555. if (sprop->attrs & JSPROP_GETTER)
  1556. *vp = OBJECT_TO_JSVAL(sprop->getter);
  1557. }
  1558. OBJ_DROP_PROPERTY(cx, pobj, prop);
  1559. }
  1560. return JS_TRUE;
  1561. }
  1562. static JSBool
  1563. obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
  1564. {
  1565. jsid id;
  1566. JSObject *obj, *pobj;
  1567. JSProperty *prop;
  1568. JSScopeProperty *sprop;
  1569. if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
  1570. return JS_FALSE;
  1571. obj = JS_THIS_OBJECT(cx, vp);
  1572. if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
  1573. return JS_FALSE;
  1574. *vp = JSVAL_VOID;
  1575. if (prop) {
  1576. if (OBJ_IS_NATIVE(pobj)) {
  1577. sprop = (JSScopeProperty *) prop;
  1578. if (sprop->attrs & JSPROP_SETTER)
  1579. *vp = OBJECT_TO_JSVAL(sprop->setter);
  1580. }
  1581. OBJ_DROP_PROPERTY(cx, pobj, prop);
  1582. }
  1583. return JS_TRUE;
  1584. }
  1585. #endif /* JS_HAS_GETTER_SETTER */
  1586. JSBool
  1587. obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
  1588. {
  1589. JSObject *obj;
  1590. uintN attrs;
  1591. if (argc == 0) {
  1592. js_ReportMissingArg(cx, vp, 0);
  1593. return JS_FALSE;
  1594. }
  1595. obj = js_ValueToNonNullObject(cx, vp[2]);
  1596. if (!obj)
  1597. return JS_FALSE;
  1598. vp[2] = OBJECT_TO_JSVAL(obj);
  1599. return OBJ_CHECK_ACCESS(cx, obj,
  1600. ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
  1601. JSACC_PROTO, vp, &attrs);
  1602. }
  1603. #if JS_HAS_OBJ_WATCHPOINT
  1604. const char js_watch_str[] = "watch";
  1605. const char js_unwatch_str[] = "unwatch";
  1606. #endif
  1607. const char js_hasOwnProperty_str[] = "hasOwnProperty";
  1608. const char js_isPrototypeOf_str[] = "isPrototypeOf";
  1609. const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
  1610. #if JS_HAS_GETTER_SETTER
  1611. const char js_defineGetter_str[] = "__defineGetter__";
  1612. const char js_defineSetter_str[] = "__defineSetter__";
  1613. const char js_lookupGetter_str[] = "__lookupGetter__";
  1614. const char js_lookupSetter_str[] = "__lookupSetter__";
  1615. #endif
  1616. JS_DEFINE_TRCINFO_1(obj_hasOwnProperty,
  1617. (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0)))
  1618. JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable,
  1619. (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0)))
  1620. static JSFunctionSpec object_methods[] = {
  1621. #if JS_HAS_TOSOURCE
  1622. JS_FN(js_toSource_str, obj_toSource, 0,0),
  1623. #endif
  1624. JS_FN(js_toString_str, obj_toString, 0,0),
  1625. JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
  1626. JS_FN(js_valueOf_str, obj_valueOf, 0,0),
  1627. #if JS_HAS_OBJ_WATCHPOINT
  1628. JS_FN(js_watch_str, obj_watch, 2,0),
  1629. JS_FN(js_unwatch_str, obj_unwatch, 1,0),
  1630. #endif
  1631. JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,
  1632. obj_hasOwnProperty_trcinfo),
  1633. JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
  1634. JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,
  1635. obj_propertyIsEnumerable_trcinfo),
  1636. #if JS_HAS_GETTER_SETTER
  1637. JS_FN(js_defineGetter_str, obj_defineGetter, 2,0),
  1638. JS_FN(js_defineSetter_str, obj_defineSetter, 2,0),
  1639. JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
  1640. JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
  1641. #endif
  1642. JS_FS_END
  1643. };
  1644. static JSFunctionSpec object_static_methods[] = {
  1645. JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
  1646. JS_FS_END
  1647. };
  1648. JSBool
  1649. js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1650. {
  1651. if (argc == 0) {
  1652. /* Trigger logic below to construct a blank object. */
  1653. obj = NULL;
  1654. } else {
  1655. /* If argv[0] is null or undefined, obj comes back null. */
  1656. if (!js_ValueToObject(cx, argv[0], &obj))
  1657. return JS_FALSE;
  1658. }
  1659. if (!obj) {
  1660. JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
  1661. if (cx->fp->flags & JSFRAME_CONSTRUCTING)
  1662. return JS_TRUE;
  1663. obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
  1664. if (!obj)
  1665. return JS_FALSE;
  1666. }
  1667. *rval = OBJECT_TO_JSVAL(obj);
  1668. return JS_TRUE;
  1669. }
  1670. /*
  1671. * ObjectOps and Class for with-statement stack objects.
  1672. */
  1673. static JSBool
  1674. with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
  1675. JSProperty **propp)
  1676. {
  1677. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1678. if (!proto)
  1679. return js_LookupProperty(cx, obj, id, objp, propp);
  1680. return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
  1681. }
  1682. static JSBool
  1683. with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
  1684. {
  1685. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1686. if (!proto)
  1687. return js_GetProperty(cx, obj, id, vp);
  1688. return OBJ_GET_PROPERTY(cx, proto, id, vp);
  1689. }
  1690. static JSBool
  1691. with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
  1692. {
  1693. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1694. if (!proto)
  1695. return js_SetProperty(cx, obj, id, vp);
  1696. return OBJ_SET_PROPERTY(cx, proto, id, vp);
  1697. }
  1698. static JSBool
  1699. with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
  1700. uintN *attrsp)
  1701. {
  1702. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1703. if (!proto)
  1704. return js_GetAttributes(cx, obj, id, prop, attrsp);
  1705. return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
  1706. }
  1707. static JSBool
  1708. with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
  1709. uintN *attrsp)
  1710. {
  1711. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1712. if (!proto)
  1713. return js_SetAttributes(cx, obj, id, prop, attrsp);
  1714. return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
  1715. }
  1716. static JSBool
  1717. with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
  1718. {
  1719. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1720. if (!proto)
  1721. return js_DeleteProperty(cx, obj, id, rval);
  1722. return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
  1723. }
  1724. static JSBool
  1725. with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
  1726. {
  1727. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1728. if (!proto)
  1729. return js_DefaultValue(cx, obj, hint, vp);
  1730. return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
  1731. }
  1732. static JSBool
  1733. with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
  1734. jsval *statep, jsid *idp)
  1735. {
  1736. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1737. if (!proto)
  1738. return js_Enumerate(cx, obj, enum_op, statep, idp);
  1739. return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
  1740. }
  1741. static JSBool
  1742. with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
  1743. jsval *vp, uintN *attrsp)
  1744. {
  1745. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1746. if (!proto)
  1747. return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
  1748. return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
  1749. }
  1750. static JSObject *
  1751. with_ThisObject(JSContext *cx, JSObject *obj)
  1752. {
  1753. JSObject *proto = OBJ_GET_PROTO(cx, obj);
  1754. if (!proto)
  1755. return obj;
  1756. return OBJ_THIS_OBJECT(cx, proto);
  1757. }
  1758. JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
  1759. js_NewObjectMap, js_DestroyObjectMap,
  1760. with_LookupProperty, js_DefineProperty,
  1761. with_GetProperty, with_SetProperty,
  1762. with_GetAttributes, with_SetAttributes,
  1763. with_DeleteProperty, with_DefaultValue,
  1764. with_Enumerate, with_CheckAccess,
  1765. with_ThisObject, NATIVE_DROP_PROPERTY,
  1766. NULL, NULL,
  1767. NULL, NULL,
  1768. js_SetProtoOrParent, js_SetProtoOrParent,
  1769. js_TraceObject, js_Clear,
  1770. NULL, NULL
  1771. };
  1772. static JSObjectOps *
  1773. with_getObjectOps(JSContext *cx, JSClass *clasp)
  1774. {
  1775. return &js_WithObjectOps;
  1776. }
  1777. JSClass js_WithClass = {
  1778. "With",
  1779. JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
  1780. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  1781. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  1782. with_getObjectOps,
  1783. 0,0,0,0,0,0,0
  1784. };
  1785. JSObject *
  1786. js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
  1787. {
  1788. JSObject *obj;
  1789. obj = js_NewObject(cx, &js_WithClass, proto, parent, 0);
  1790. if (!obj)
  1791. return NULL;
  1792. STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
  1793. OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
  1794. return obj;
  1795. }
  1796. JSObject *
  1797. js_NewBlockObject(JSContext *cx)
  1798. {
  1799. JSObject *obj;
  1800. JSBool ok;
  1801. /*
  1802. * Null obj's proto slot so that Object.prototype.* does not pollute block
  1803. * scopes. Make sure obj has its own scope too, since clearing proto does
  1804. * not affect OBJ_SCOPE(obj).
  1805. */
  1806. obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0);
  1807. if (!obj)
  1808. return NULL;
  1809. JS_LOCK_OBJ(cx, obj);
  1810. ok = js_GetMutableScope(cx, obj) != NULL;
  1811. JS_UNLOCK_OBJ(cx, obj);
  1812. if (!ok)
  1813. return NULL;
  1814. OBJ_CLEAR_PROTO(cx, obj);
  1815. return obj;
  1816. }
  1817. JSObject *
  1818. js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
  1819. JSStackFrame *fp)
  1820. {
  1821. JSObject *clone;
  1822. JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass);
  1823. JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
  1824. clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0);
  1825. if (!clone)
  1826. return NULL;
  1827. STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp));
  1828. STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH,
  1829. OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH));
  1830. JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
  1831. return clone;
  1832. }
  1833. JSBool
  1834. js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
  1835. {
  1836. JSStackFrame *fp;
  1837. JSObject *obj;
  1838. uintN depth, count;
  1839. /* Blocks have one fixed slot available for the first local.*/
  1840. JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
  1841. fp = cx->fp;
  1842. obj = fp->scopeChain;
  1843. JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
  1844. JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp);
  1845. JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
  1846. /*
  1847. * Block objects should never be exposed to scripts. Thus the clone should
  1848. * not own the property map and rather always share it with the prototype
  1849. * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after
  1850. * we copy the stack slots into reserved slots.
  1851. */
  1852. JS_ASSERT(OBJ_SCOPE(obj)->object != obj);
  1853. /* Block objects should not have reserved slots before they are put. */
  1854. JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS);
  1855. /* The block and its locals must be on the current stack for GC safety. */
  1856. depth = OBJ_BLOCK_DEPTH(cx, obj);
  1857. count = OBJ_BLOCK_COUNT(cx, obj);
  1858. JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp)));
  1859. JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth));
  1860. /* See comments in CheckDestructuring from jsparse.c. */
  1861. JS_ASSERT(count >= 1);
  1862. depth += fp->script->nfixed;
  1863. obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth];
  1864. if (normalUnwind && count > 1) {
  1865. --count;
  1866. JS_LOCK_OBJ(cx, obj);
  1867. if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE))
  1868. normalUnwind = JS_FALSE;
  1869. else
  1870. memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
  1871. JS_UNLOCK_OBJ(cx, obj);
  1872. }
  1873. /* We must clear the private slot even with errors. */
  1874. JS_SetPrivate(cx, obj, NULL);
  1875. fp->scopeChain = OBJ_GET_PARENT(cx, obj);
  1876. return normalUnwind;
  1877. }
  1878. static JSBool
  1879. block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1880. {
  1881. uintN index;
  1882. JSStackFrame *fp;
  1883. JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
  1884. JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
  1885. if (!JSVAL_IS_INT(id))
  1886. return JS_TRUE;
  1887. index = (uint16) JSVAL_TO_INT(id);
  1888. fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
  1889. if (fp) {
  1890. index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
  1891. JS_ASSERT(index < fp->script->nslots);
  1892. *vp = fp->slots[index];
  1893. return JS_TRUE;
  1894. }
  1895. /* Reserve