Parent Directory
|
Revision Log
Use SpiderMonkey from Firefox 3.1b2.
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 | /* |
42 | * JS object implementation. |
43 | */ |
44 | #include "jsstddef.h" |
45 | #include <stdlib.h> |
46 | #include <string.h> |
47 | #include "jstypes.h" |
48 | #include "jsarena.h" /* Added by JSIFY */ |
49 | #include "jsbit.h" |
50 | #include "jsutil.h" /* Added by JSIFY */ |
51 | #include "jshash.h" /* Added by JSIFY */ |
52 | #include "jsdhash.h" |
53 | #include "jsprf.h" |
54 | #include "jsapi.h" |
55 | #include "jsarray.h" |
56 | #include "jsatom.h" |
57 | #include "jsbool.h" |
58 | #include "jsbuiltins.h" |
59 | #include "jscntxt.h" |
60 | #include "jsversion.h" |
61 | #include "jsemit.h" |
62 | #include "jsfun.h" |
63 | #include "jsgc.h" |
64 | #include "jsinterp.h" |
65 | #include "jslock.h" |
66 | #include "jsnum.h" |
67 | #include "jsobj.h" |
68 | #include "jsopcode.h" |
69 | #include "jsparse.h" |
70 | #include "jsscope.h" |
71 | #include "jsscript.h" |
72 | #include "jsstr.h" |
73 | #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ |
74 | #include "jsstaticcheck.h" |
75 | |
76 | #if JS_HAS_GENERATORS |
77 | #include "jsiter.h" |
78 | #endif |
79 | |
80 | #if JS_HAS_XML_SUPPORT |
81 | #include "jsxml.h" |
82 | #endif |
83 | |
84 | #if JS_HAS_XDR |
85 | #include "jsxdrapi.h" |
86 | #endif |
87 | |
88 | #ifdef INCLUDE_MOZILLA_DTRACE |
89 | #include "jsdtracef.h" |
90 | #endif |
91 | |
92 | #include "jsautooplen.h" |
93 | |
94 | #ifdef JS_THREADSAFE |
95 | #define NATIVE_DROP_PROPERTY js_DropProperty |
96 | |
97 | extern void |
98 | js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); |
99 | #else |
100 | #define NATIVE_DROP_PROPERTY NULL |
101 | #endif |
102 | |
103 | JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { |
104 | js_NewObjectMap, js_DestroyObjectMap, |
105 | js_LookupProperty, js_DefineProperty, |
106 | js_GetProperty, js_SetProperty, |
107 | js_GetAttributes, js_SetAttributes, |
108 | js_DeleteProperty, js_DefaultValue, |
109 | js_Enumerate, js_CheckAccess, |
110 | NULL, NATIVE_DROP_PROPERTY, |
111 | js_Call, js_Construct, |
112 | NULL, js_HasInstance, |
113 | js_SetProtoOrParent, js_SetProtoOrParent, |
114 | js_TraceObject, js_Clear, |
115 | js_GetRequiredSlot, js_SetRequiredSlot |
116 | }; |
117 | |
118 | JSClass js_ObjectClass = { |
119 | js_Object_str, |
120 | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), |
121 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
122 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
123 | JSCLASS_NO_OPTIONAL_MEMBERS |
124 | }; |
125 | |
126 | #if JS_HAS_OBJ_PROTO_PROP |
127 | |
128 | static JSBool |
129 | obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); |
130 | |
131 | static JSBool |
132 | obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); |
133 | |
134 | static JSBool |
135 | obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp); |
136 | |
137 | static JSPropertySpec object_props[] = { |
138 | /* These two must come first; see object_props[slot].name usage below. */ |
139 | {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, |
140 | obj_getSlot, obj_setSlot}, |
141 | {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, |
142 | obj_getSlot, obj_setSlot}, |
143 | {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, |
144 | obj_getCount, NULL}, |
145 | {0,0,0,0,0} |
146 | }; |
147 | |
148 | /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */ |
149 | #define JSSLOT_COUNT 2 |
150 | |
151 | static JSBool |
152 | ReportStrictSlot(JSContext *cx, uint32 slot) |
153 | { |
154 | if (slot == JSSLOT_PROTO) |
155 | return JS_TRUE; |
156 | return JS_ReportErrorFlagsAndNumber(cx, |
157 | JSREPORT_WARNING | JSREPORT_STRICT, |
158 | js_GetErrorMessage, NULL, |
159 | JSMSG_DEPRECATED_USAGE, |
160 | object_props[slot].name); |
161 | } |
162 | |
163 | static JSBool |
164 | obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
165 | { |
166 | uint32 slot; |
167 | jsid propid; |
168 | JSAccessMode mode; |
169 | uintN attrs; |
170 | JSObject *pobj; |
171 | JSClass *clasp; |
172 | JSExtendedClass *xclasp; |
173 | |
174 | slot = (uint32) JSVAL_TO_INT(id); |
175 | if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { |
176 | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); |
177 | mode = JSACC_PROTO; |
178 | } else { |
179 | propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); |
180 | mode = JSACC_PARENT; |
181 | } |
182 | |
183 | /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ |
184 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) |
185 | return JS_FALSE; |
186 | |
187 | pobj = JSVAL_TO_OBJECT(*vp); |
188 | if (pobj) { |
189 | clasp = OBJ_GET_CLASS(cx, pobj); |
190 | if (clasp == &js_CallClass || clasp == &js_BlockClass) { |
191 | /* Censor activations and lexical scopes per ECMA-262. */ |
192 | *vp = JSVAL_NULL; |
193 | } else if (clasp->flags & JSCLASS_IS_EXTENDED) { |
194 | xclasp = (JSExtendedClass *) clasp; |
195 | if (xclasp->outerObject) { |
196 | pobj = xclasp->outerObject(cx, pobj); |
197 | if (!pobj) |
198 | return JS_FALSE; |
199 | *vp = OBJECT_TO_JSVAL(pobj); |
200 | } |
201 | } |
202 | } |
203 | return JS_TRUE; |
204 | } |
205 | |
206 | static JSBool |
207 | obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
208 | { |
209 | JSObject *pobj; |
210 | uint32 slot; |
211 | jsid propid; |
212 | uintN attrs; |
213 | |
214 | if (!JSVAL_IS_OBJECT(*vp)) |
215 | return JS_TRUE; |
216 | pobj = JSVAL_TO_OBJECT(*vp); |
217 | |
218 | if (pobj) { |
219 | /* |
220 | * Innerize pobj here to avoid sticking unwanted properties on the |
221 | * outer object. This ensures that any with statements only grant |
222 | * access to the inner object. |
223 | */ |
224 | OBJ_TO_INNER_OBJECT(cx, pobj); |
225 | if (!pobj) |
226 | return JS_FALSE; |
227 | } |
228 | slot = (uint32) JSVAL_TO_INT(id); |
229 | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) |
230 | return JS_FALSE; |
231 | |
232 | /* __parent__ is readonly and permanent, only __proto__ may be set. */ |
233 | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); |
234 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, |
235 | (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, |
236 | &attrs)) { |
237 | return JS_FALSE; |
238 | } |
239 | |
240 | return js_SetProtoOrParent(cx, obj, slot, pobj); |
241 | } |
242 | |
243 | static JSBool |
244 | obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
245 | { |
246 | jsval iter_state; |
247 | jsid num_properties; |
248 | JSBool ok; |
249 | |
250 | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) |
251 | return JS_FALSE; |
252 | |
253 | /* Get the number of properties to enumerate. */ |
254 | iter_state = JSVAL_NULL; |
255 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); |
256 | if (!ok) |
257 | goto out; |
258 | |
259 | if (!JSVAL_IS_INT(num_properties)) { |
260 | JS_ASSERT(0); |
261 | *vp = JSVAL_ZERO; |
262 | goto out; |
263 | } |
264 | *vp = num_properties; |
265 | |
266 | out: |
267 | if (iter_state != JSVAL_NULL) |
268 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); |
269 | return ok; |
270 | } |
271 | |
272 | #else /* !JS_HAS_OBJ_PROTO_PROP */ |
273 | |
274 | #define object_props NULL |
275 | |
276 | #endif /* !JS_HAS_OBJ_PROTO_PROP */ |
277 | |
278 | JSBool |
279 | js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) |
280 | { |
281 | JSSetSlotRequest ssr; |
282 | JSRuntime *rt; |
283 | |
284 | /* Optimize the null case to avoid the unnecessary overhead of js_GC. */ |
285 | if (!pobj) { |
286 | JS_LOCK_OBJ(cx, obj); |
287 | if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) { |
288 | JS_UNLOCK_OBJ(cx, obj); |
289 | return JS_FALSE; |
290 | } |
291 | LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL); |
292 | JS_UNLOCK_OBJ(cx, obj); |
293 | return JS_TRUE; |
294 | } |
295 | |
296 | ssr.obj = obj; |
297 | ssr.pobj = pobj; |
298 | ssr.slot = (uint16) slot; |
299 | ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR; |
300 | |
301 | rt = cx->runtime; |
302 | JS_LOCK_GC(rt); |
303 | ssr.next = rt->setSlotRequests; |
304 | rt->setSlotRequests = &ssr; |
305 | for (;;) { |
306 | js_GC(cx, GC_SET_SLOT_REQUEST); |
307 | JS_UNLOCK_GC(rt); |
308 | if (!rt->setSlotRequests) |
309 | break; |
310 | JS_LOCK_GC(rt); |
311 | } |
312 | |
313 | if (ssr.errnum != JSMSG_NOT_AN_ERROR) { |
314 | if (ssr.errnum == JSMSG_OUT_OF_MEMORY) { |
315 | JS_ReportOutOfMemory(cx); |
316 | } else { |
317 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum, |
318 | #if JS_HAS_OBJ_PROTO_PROP |
319 | object_props[slot].name |
320 | #else |
321 | (slot == JSSLOT_PROTO) ? js_proto_str |
322 | : js_parent_str |
323 | #endif |
324 | ); |
325 | } |
326 | return JS_FALSE; |
327 | } |
328 | |
329 | // Maintain the "any Array prototype has indexed properties hazard" flag. |
330 | if (slot == JSSLOT_PROTO && |
331 | OBJ_IS_ARRAY(cx, pobj) && |
332 | pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) { |
333 | rt->anyArrayProtoHasElement = JS_TRUE; |
334 | } |
335 | return JS_TRUE; |
336 | } |
337 | |
338 | static JSHashNumber |
339 | js_hash_object(const void *key) |
340 | { |
341 | return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; |
342 | } |
343 | |
344 | static JSHashEntry * |
345 | MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) |
346 | { |
347 | JSSharpObjectMap *map; |
348 | JSHashTable *table; |
349 | JSHashNumber hash; |
350 | JSHashEntry **hep, *he; |
351 | jsatomid sharpid; |
352 | JSIdArray *ida; |
353 | JSBool ok; |
354 | jsint i, length; |
355 | jsid id; |
356 | #if JS_HAS_GETTER_SETTER |
357 | JSObject *obj2; |
358 | JSProperty *prop; |
359 | uintN attrs; |
360 | #endif |
361 | jsval val; |
362 | |
363 | JS_CHECK_RECURSION(cx, return NULL); |
364 | |
365 | map = &cx->sharpObjectMap; |
366 | table = map->table; |
367 | hash = js_hash_object(obj); |
368 | hep = JS_HashTableRawLookup(table, hash, obj); |
369 | he = *hep; |
370 | if (!he) { |
371 | sharpid = 0; |
372 | he = JS_HashTableRawAdd(table, hep, hash, obj, |
373 | JS_UINT32_TO_PTR(sharpid)); |
374 | if (!he) { |
375 | JS_ReportOutOfMemory(cx); |
376 | return NULL; |
377 | } |
378 | |
379 | /* |
380 | * Increment map->depth to protect js_EnterSharpObject from reentering |
381 | * itself badly. Without this fix, if we reenter the basis case where |
382 | * map->depth == 0, when unwinding the inner call we will destroy the |
383 | * newly-created hash table and crash. |
384 | */ |
385 | ++map->depth; |
386 | ida = JS_Enumerate(cx, obj); |
387 | --map->depth; |
388 | if (!ida) |
389 | return NULL; |
390 | |
391 | ok = JS_TRUE; |
392 | for (i = 0, length = ida->length; i < length; i++) { |
393 | id = ida->vector[i]; |
394 | #if JS_HAS_GETTER_SETTER |
395 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); |
396 | if (!ok) |
397 | break; |
398 | if (!prop) |
399 | continue; |
400 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); |
401 | if (ok) { |
402 | if (OBJ_IS_NATIVE(obj2) && |
403 | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
404 | val = JSVAL_NULL; |
405 | if (attrs & JSPROP_GETTER) |
406 | val = (jsval) ((JSScopeProperty*)prop)->getter; |
407 | if (attrs & JSPROP_SETTER) { |
408 | if (val != JSVAL_NULL) { |
409 | /* Mark the getter, then set val to setter. */ |
410 | ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), |
411 | NULL) |
412 | != NULL); |
413 | } |
414 | val = (jsval) ((JSScopeProperty*)prop)->setter; |
415 | } |
416 | } else { |
417 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); |
418 | } |
419 | } |
420 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
421 | #else |
422 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); |
423 | #endif |
424 | if (!ok) |
425 | break; |
426 | if (!JSVAL_IS_PRIMITIVE(val) && |
427 | !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { |
428 | ok = JS_FALSE; |
429 | break; |
430 | } |
431 | } |
432 | if (!ok || !idap) |
433 | JS_DestroyIdArray(cx, ida); |
434 | if (!ok) |
435 | return NULL; |
436 | } else { |
437 | sharpid = JS_PTR_TO_UINT32(he->value); |
438 | if (sharpid == 0) { |
439 | sharpid = ++map->sharpgen << SHARP_ID_SHIFT; |
440 | he->value = JS_UINT32_TO_PTR(sharpid); |
441 | } |
442 | ida = NULL; |
443 | } |
444 | if (idap) |
445 | *idap = ida; |
446 | return he; |
447 | } |
448 | |
449 | JSHashEntry * |
450 | js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, |
451 | jschar **sp) |
452 | { |
453 | JSSharpObjectMap *map; |
454 | JSHashTable *table; |
455 | JSIdArray *ida; |
456 | JSHashNumber hash; |
457 | JSHashEntry *he, **hep; |
458 | jsatomid sharpid; |
459 | char buf[20]; |
460 | size_t len; |
461 | |
462 | if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP)) |
463 | return NULL; |
464 | |
465 | /* Set to null in case we return an early error. */ |
466 | *sp = NULL; |
467 | map = &cx->sharpObjectMap; |
468 | table = map->table; |
469 | if (!table) { |
470 | table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, |
471 | JS_CompareValues, NULL, NULL); |
472 | if (!table) { |
473 | JS_ReportOutOfMemory(cx); |
474 | return NULL; |
475 | } |
476 | map->table = table; |
477 | JS_KEEP_ATOMS(cx->runtime); |
478 | } |
479 | |
480 | /* From this point the control must flow either through out: or bad:. */ |
481 | ida = NULL; |
482 | if (map->depth == 0) { |
483 | he = MarkSharpObjects(cx, obj, &ida); |
484 | if (!he) |
485 | goto bad; |
486 | JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); |
487 | if (!idap) { |
488 | JS_DestroyIdArray(cx, ida); |
489 | ida = NULL; |
490 | } |
491 | } else { |
492 | hash = js_hash_object(obj); |
493 | hep = JS_HashTableRawLookup(table, hash, obj); |
494 | he = *hep; |
495 | |
496 | /* |
497 | * It's possible that the value of a property has changed from the |
498 | * first time the object's properties are traversed (when the property |
499 | * ids are entered into the hash table) to the second (when they are |
500 | * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not |
501 | * idempotent. |
502 | */ |
503 | if (!he) { |
504 | he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); |
505 | if (!he) { |
506 | JS_ReportOutOfMemory(cx); |
507 | goto bad; |
508 | } |
509 | sharpid = 0; |
510 | goto out; |
511 | } |
512 | } |
513 | |
514 | sharpid = JS_PTR_TO_UINT32(he->value); |
515 | if (sharpid != 0) { |
516 | len = JS_snprintf(buf, sizeof buf, "#%u%c", |
517 | sharpid >> SHARP_ID_SHIFT, |
518 | (sharpid & SHARP_BIT) ? '#' : '='); |
519 | *sp = js_InflateString(cx, buf, &len); |
520 | if (!*sp) { |
521 | if (ida) |
522 | JS_DestroyIdArray(cx, ida); |
523 | goto bad; |
524 | } |
525 | } |
526 | |
527 | out: |
528 | JS_ASSERT(he); |
529 | if ((sharpid & SHARP_BIT) == 0) { |
530 | if (idap && !ida) { |
531 | ida = JS_Enumerate(cx, obj); |
532 | if (!ida) { |
533 | if (*sp) { |
534 | JS_free(cx, *sp); |
535 | *sp = NULL; |
536 | } |
537 | goto bad; |
538 | } |
539 | } |
540 | map->depth++; |
541 | } |
542 | |
543 | if (idap) |
544 | *idap = ida; |
545 | return he; |
546 | |
547 | bad: |
548 | /* Clean up the sharpObjectMap table on outermost error. */ |
549 | if (map->depth == 0) { |
550 | JS_UNKEEP_ATOMS(cx->runtime); |
551 | map->sharpgen = 0; |
552 | JS_HashTableDestroy(map->table); |
553 | map->table = NULL; |
554 | } |
555 | return NULL; |
556 | } |
557 | |
558 | void |
559 | js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) |
560 | { |
561 | JSSharpObjectMap *map; |
562 | JSIdArray *ida; |
563 | |
564 | map = &cx->sharpObjectMap; |
565 | JS_ASSERT(map->depth > 0); |
566 | if (--map->depth == 0) { |
567 | JS_UNKEEP_ATOMS(cx->runtime); |
568 | map->sharpgen = 0; |
569 | JS_HashTableDestroy(map->table); |
570 | map->table = NULL; |
571 | } |
572 | if (idap) { |
573 | ida = *idap; |
574 | if (ida) { |
575 | JS_DestroyIdArray(cx, ida); |
576 | *idap = NULL; |
577 | } |
578 | } |
579 | } |
580 | |
581 | static intN |
582 | gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) |
583 | { |
584 | JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key, |
585 | "sharp table entry"); |
586 | return JS_DHASH_NEXT; |
587 | } |
588 | |
589 | void |
590 | js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) |
591 | { |
592 | JS_ASSERT(map->depth > 0); |
593 | JS_ASSERT(map->table); |
594 | |
595 | /* |
596 | * During recursive calls to MarkSharpObjects a non-native object or |
597 | * object with a custom getProperty method can potentially return an |
598 | * unrooted value or even cut from the object graph an argument of one of |
599 | * MarkSharpObjects recursive invocations. So we must protect map->table |
600 | * entries against GC. |
601 | * |
602 | * We can not simply use JSTempValueRooter to mark the obj argument of |
603 | * MarkSharpObjects during recursion as we have to protect *all* entries |
604 | * in JSSharpObjectMap including those that contains otherwise unreachable |
605 | * objects just allocated through custom getProperty. Otherwise newer |
606 | * allocations can re-use the address of an object stored in the hashtable |
607 | * confusing js_EnterSharpObject. So to address the problem we simply |
608 | * mark all objects from map->table. |
609 | * |
610 | * An alternative "proper" solution is to use JSTempValueRooter in |
611 | * MarkSharpObjects with code to remove during finalization entries |
612 | * with otherwise unreachable objects. But this is way too complex |
613 | * to justify spending efforts. |
614 | */ |
615 | JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc); |
616 | } |
617 | |
618 | #if JS_HAS_TOSOURCE |
619 | static JSBool |
620 | obj_toSource(JSContext *cx, uintN argc, jsval *vp) |
621 | { |
622 | JSBool ok, outermost; |
623 | JSObject *obj; |
624 | JSHashEntry *he; |
625 | JSIdArray *ida; |
626 | jschar *chars, *ochars, *vsharp; |
627 | const jschar *idstrchars, *vchars; |
628 | size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; |
629 | const char *comma; |
630 | jsint i, j, length, valcnt; |
631 | jsid id; |
632 | #if JS_HAS_GETTER_SETTER |
633 | JSObject *obj2; |
634 | JSProperty *prop; |
635 | uintN attrs; |
636 | #endif |
637 | jsval *val; |
638 | jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; |
639 | JSTempValueRooter tvr; |
640 | JSString *gsopold[2]; |
641 | JSString *gsop[2]; |
642 | JSString *idstr, *valstr, *str; |
643 | |
644 | JS_CHECK_RECURSION(cx, return JS_FALSE); |
645 | |
646 | MUST_FLOW_THROUGH("out"); |
647 | JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr); |
648 | |
649 | /* If outermost, we need parentheses to be an expression, not a block. */ |
650 | outermost = (cx->sharpObjectMap.depth == 0); |
651 | obj = JS_THIS_OBJECT(cx, vp); |
652 | if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) { |
653 | ok = JS_FALSE; |
654 | goto out; |
655 | } |
656 | if (IS_SHARP(he)) { |
657 | /* |
658 | * We didn't enter -- obj is already "sharp", meaning we've visited it |
659 | * already in our depth first search, and therefore chars contains a |
660 | * string of the form "#n#". |
661 | */ |
662 | JS_ASSERT(!ida); |
663 | #if JS_HAS_SHARP_VARS |
664 | nchars = js_strlen(chars); |
665 | #else |
666 | chars[0] = '{'; |
667 | chars[1] = '}'; |
668 | chars[2] = 0; |
669 | nchars = 2; |
670 | #endif |
671 | goto make_string; |
672 | } |
673 | JS_ASSERT(ida); |
674 | ok = JS_TRUE; |
675 | |
676 | if (!chars) { |
677 | /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ |
678 | chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); |
679 | nchars = 0; |
680 | if (!chars) |
681 | goto error; |
682 | if (outermost) |
683 | chars[nchars++] = '('; |
684 | } else { |
685 | /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ |
686 | MAKE_SHARP(he); |
687 | nchars = js_strlen(chars); |
688 | chars = (jschar *) |
689 | realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); |
690 | if (!chars) { |
691 | free(ochars); |
692 | goto error; |
693 | } |
694 | if (outermost) { |
695 | /* |
696 | * No need for parentheses around the whole shebang, because #n= |
697 | * unambiguously begins an object initializer, and never a block |
698 | * statement. |
699 | */ |
700 | outermost = JS_FALSE; |
701 | } |
702 | } |
703 | |
704 | chars[nchars++] = '{'; |
705 | |
706 | comma = NULL; |
707 | |
708 | /* |
709 | * We have four local roots for cooked and raw value GC safety. Hoist the |
710 | * "localroot + 2" out of the loop using the val local, which refers to |
711 | * the raw (unconverted, "uncooked") values. |
712 | */ |
713 | val = localroot + 2; |
714 | |
715 | for (i = 0, length = ida->length; i < length; i++) { |
716 | JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; |
717 | |
718 | /* Get strings for id and value and GC-root them via vp. */ |
719 | id = ida->vector[i]; |
720 | |
721 | #if JS_HAS_GETTER_SETTER |
722 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); |
723 | if (!ok) |
724 | goto error; |
725 | #endif |
726 | |
727 | /* |
728 | * Convert id to a jsval and then to a string. Decide early whether we |
729 | * prefer get/set or old getter/setter syntax. |
730 | */ |
731 | idstr = js_ValueToString(cx, ID_TO_VALUE(id)); |
732 | if (!idstr) { |
733 | ok = JS_FALSE; |
734 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
735 | goto error; |
736 | } |
737 | *vp = STRING_TO_JSVAL(idstr); /* local root */ |
738 | idIsLexicalIdentifier = js_IsIdentifier(idstr); |
739 | needOldStyleGetterSetter = |
740 | !idIsLexicalIdentifier || |
741 | js_CheckKeyword(JSSTRING_CHARS(idstr), |
742 | JSSTRING_LENGTH(idstr)) != TOK_EOF; |
743 | |
744 | #if JS_HAS_GETTER_SETTER |
745 | |
746 | valcnt = 0; |
747 | if (prop) { |
748 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); |
749 | if (!ok) { |
750 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
751 | goto error; |
752 | } |
753 | if (OBJ_IS_NATIVE(obj2) && |
754 | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
755 | if (attrs & JSPROP_GETTER) { |
756 | val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; |
757 | gsopold[valcnt] = |
758 | ATOM_TO_STRING(cx->runtime->atomState.getterAtom); |
759 | gsop[valcnt] = |
760 | ATOM_TO_STRING(cx->runtime->atomState.getAtom); |
761 | |
762 | valcnt++; |
763 | } |
764 | if (attrs & JSPROP_SETTER) { |
765 | val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; |
766 | gsopold[valcnt] = |
767 | ATOM_TO_STRING(cx->runtime->atomState.setterAtom); |
768 | gsop[valcnt] = |
769 | ATOM_TO_STRING(cx->runtime->atomState.setAtom); |
770 | |
771 | valcnt++; |
772 | } |
773 | } else { |
774 | valcnt = 1; |
775 | gsop[0] = NULL; |
776 | gsopold[0] = NULL; |
777 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); |
778 | } |
779 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
780 | } |
781 | |
782 | #else /* !JS_HAS_GETTER_SETTER */ |
783 | |
784 | /* |
785 | * We simplify the source code at the price of minor dead code bloat in |
786 | * the ECMA version (for testing only, see jsversion.h). The null |
787 | * default values in gsop[j] suffice to disable non-ECMA getter and |
788 | * setter code. |
789 | */ |
790 | valcnt = 1; |
791 | gsop[0] = NULL; |
792 | gsopold[0] = NULL; |
793 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); |
794 | |
795 | #endif /* !JS_HAS_GETTER_SETTER */ |
796 | |
797 | if (!ok) |
798 | goto error; |
799 | |
800 | /* |
801 | * If id is a string that's not an identifier, then it needs to be |
802 | * quoted. Also, negative integer ids must be quoted. |
803 | */ |
804 | if (JSID_IS_ATOM(id) |
805 | ? !idIsLexicalIdentifier |
806 | : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { |
807 | idstr = js_QuoteString(cx, idstr, (jschar)'\''); |
808 | if (!idstr) { |
809 | ok = JS_FALSE; |
810 | goto error; |
811 | } |
812 | *vp = STRING_TO_JSVAL(idstr); /* local root */ |
813 | } |
814 | JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength); |
815 | |
816 | for (j = 0; j < valcnt; j++) { |
817 | /* Convert val[j] to its canonical source form. */ |
818 | valstr = js_ValueToSource(cx, val[j]); |
819 | if (!valstr) { |
820 | ok = JS_FALSE; |
821 | goto error; |
822 | } |
823 | localroot[j] = STRING_TO_JSVAL(valstr); /* local root */ |
824 | JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength); |
825 | |
826 | if (vchars[0] == '#') |
827 | needOldStyleGetterSetter = JS_TRUE; |
828 | |
829 | if (needOldStyleGetterSetter) |
830 | gsop[j] = gsopold[j]; |
831 | |
832 | /* If val[j] is a non-sharp object, consider sharpening it. */ |
833 | vsharp = NULL; |
834 | vsharplength = 0; |
835 | #if JS_HAS_SHARP_VARS |
836 | if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { |
837 | he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, |
838 | &vsharp); |
839 | if (!he) { |
840 | ok = JS_FALSE; |
841 | goto error; |
842 | } |
843 | if (IS_SHARP(he)) { |
844 | vchars = vsharp; |
845 | vlength = js_strlen(vchars); |
846 | needOldStyleGetterSetter = JS_TRUE; |
847 | gsop[j] = gsopold[j]; |
848 | } else { |
849 | if (vsharp) { |
850 | vsharplength = js_strlen(vsharp); |
851 | MAKE_SHARP(he); |
852 | needOldStyleGetterSetter = JS_TRUE; |
853 | gsop[j] = gsopold[j]; |
854 | } |
855 | js_LeaveSharpObject(cx, NULL); |
856 | } |
857 | } |
858 | #endif |
859 | |
860 | #ifndef OLD_GETTER_SETTER |
861 | /* |
862 | * Remove '(function ' from the beginning of valstr and ')' from the |
863 | * end so that we can put "get" in front of the function definition. |
864 | */ |
865 | if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && |
866 | !needOldStyleGetterSetter) { |
867 | JSFunction *fun = JS_ValueToFunction(cx, val[j]); |
868 | const jschar *start = vchars; |
869 | const jschar *end = vchars + vlength; |
870 | |
871 | uint8 parenChomp = 0; |
872 | if (vchars[0] == '(') { |
873 | vchars++; |
874 | parenChomp = 1; |
875 | } |
876 | |
877 | /* |
878 | * Try to jump "getter" or "setter" keywords, if we suspect |
879 | * they might appear here. This code can be confused by people |
880 | * defining Function.prototype.toString, so let's be cautious. |
881 | */ |
882 | if (JSFUN_GETTER_TEST(fun->flags) || |
883 | JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */ |
884 | const jschar *tmp = js_strchr_limit(vchars, ' ', end); |
885 | if (tmp) |
886 | vchars = tmp + 1; |
887 | } |
888 | |
889 | /* Try to jump "function" keyword. */ |
890 | if (vchars) |
891 | vchars = js_strchr_limit(vchars, ' ', end); |
892 | |
893 | if (vchars) { |
894 | if (*vchars == ' ') |
895 | vchars++; |
896 | vlength = end - vchars - parenChomp; |
897 | } else { |
898 | gsop[j] = NULL; |
899 | vchars = start; |
900 | } |
901 | } |
902 | #else |
903 | needOldStyleGetterSetter = JS_TRUE; |
904 | gsop[j] = gsopold[j]; |
905 | #endif |
906 | |
907 | #define SAFE_ADD(n) \ |
908 | JS_BEGIN_MACRO \ |
909 | size_t n_ = (n); \ |
910 | curlen += n_; \ |
911 | if (curlen < n_) \ |
912 | goto overflow; \ |
913 | JS_END_MACRO |
914 | |
915 | curlen = nchars; |
916 | if (comma) |
917 | SAFE_ADD(2); |
918 | SAFE_ADD(idstrlength + 1); |
919 | if (gsop[j]) |
920 | SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); |
921 | SAFE_ADD(vsharplength); |
922 | SAFE_ADD(vlength); |
923 | /* Account for the trailing null. */ |
924 | SAFE_ADD((outermost ? 2 : 1) + 1); |
925 | #undef SAFE_ADD |
926 | |
927 | if (curlen > (size_t)-1 / sizeof(jschar)) |
928 | goto overflow; |
929 | |
930 | /* Allocate 1 + 1 at end for closing brace and terminating 0. */ |
931 | chars = (jschar *) |
932 | realloc((ochars = chars), curlen * sizeof(jschar)); |
933 | if (!chars) { |
934 | /* Save code space on error: let JS_free ignore null vsharp. */ |
935 | JS_free(cx, vsharp); |
936 | free(ochars); |
937 | goto error; |
938 | } |
939 | |
940 | if (comma) { |
941 | chars[nchars++] = comma[0]; |
942 | chars[nchars++] = comma[1]; |
943 | } |
944 | comma = ", "; |
945 | |
946 | if (needOldStyleGetterSetter) { |
947 | js_strncpy(&chars[nchars], idstrchars, idstrlength); |
948 | nchars += idstrlength; |
949 | if (gsop[j]) { |
950 | chars[nchars++] = ' '; |
951 | gsoplength = JSSTRING_LENGTH(gsop[j]); |
952 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), |
953 | gsoplength); |
954 | nchars += gsoplength; |
955 | } |
956 | chars[nchars++] = ':'; |
957 | } else { /* New style "decompilation" */ |
958 | if (gsop[j]) { |
959 | gsoplength = JSSTRING_LENGTH(gsop[j]); |
960 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), |
961 | gsoplength); |
962 | nchars += gsoplength; |
963 | chars[nchars++] = ' '; |
964 | } |
965 | js_strncpy(&chars[nchars], idstrchars, idstrlength); |
966 | nchars += idstrlength; |
967 | /* Extraneous space after id here will be extracted later */ |
968 | chars[nchars++] = gsop[j] ? ' ' : ':'; |
969 | } |
970 | |
971 | if (vsharplength) { |
972 | js_strncpy(&chars[nchars], vsharp, vsharplength); |
973 | nchars += vsharplength; |
974 | } |
975 | js_strncpy(&chars[nchars], vchars, vlength); |
976 | nchars += vlength; |
977 | |
978 | if (vsharp) |
979 | JS_free(cx, vsharp); |
980 | } |
981 | } |
982 | |
983 | chars[nchars++] = '}'; |
984 | if (outermost) |
985 | chars[nchars++] = ')'; |
986 | chars[nchars] = 0; |
987 | |
988 | error: |
989 | js_LeaveSharpObject(cx, &ida); |
990 | |
991 | if (!ok) { |
992 | if (chars) |
993 | free(chars); |
994 | goto out; |
995 | } |
996 | |
997 | if (!chars) { |
998 | JS_ReportOutOfMemory(cx); |
999 | ok = JS_FALSE; |
1000 | goto out; |
1001 | } |
1002 | make_string: |
1003 | str = js_NewString(cx, chars, nchars); |
1004 | if (!str) { |
1005 | free(chars); |
1006 | ok = JS_FALSE; |
1007 | goto out; |
1008 | } |
1009 | *vp = STRING_TO_JSVAL(str); |
1010 | ok = JS_TRUE; |
1011 | out: |
1012 | JS_POP_TEMP_ROOT(cx, &tvr); |
1013 | return ok; |
1014 | |
1015 | overflow: |
1016 | JS_free(cx, vsharp); |
1017 | free(chars); |
1018 | chars = NULL; |
1019 | goto error; |
1020 | } |
1021 | #endif /* JS_HAS_TOSOURCE */ |
1022 | |
1023 | static JSBool |
1024 | obj_toString(JSContext *cx, uintN argc, jsval *vp) |
1025 | { |
1026 | JSObject *obj; |
1027 | jschar *chars; |
1028 | size_t nchars; |
1029 | const char *clazz, *prefix; |
1030 | JSString *str; |
1031 | |
1032 | obj = JS_THIS_OBJECT(cx, vp); |
1033 | if (!obj) |
1034 | return JS_FALSE; |
1035 | obj = js_GetWrappedObject(cx, obj); |
1036 | clazz = OBJ_GET_CLASS(cx, obj)->name; |
1037 | nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ |
1038 | chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); |
1039 | if (!chars) |
1040 | return JS_FALSE; |
1041 | |
1042 | prefix = "[object "; |
1043 | nchars = 0; |
1044 | while ((chars[nchars] = (jschar)*prefix) != 0) |
1045 | nchars++, prefix++; |
1046 | while ((chars[nchars] = (jschar)*clazz) != 0) |
1047 | nchars++, clazz++; |
1048 | chars[nchars++] = ']'; |
1049 | chars[nchars] = 0; |
1050 | |
1051 | str = js_NewString(cx, chars, nchars); |
1052 | if (!str) { |
1053 | JS_free(cx, chars); |
1054 | return JS_FALSE; |
1055 | } |
1056 | *vp = STRING_TO_JSVAL(str); |
1057 | return JS_TRUE; |
1058 | } |
1059 | |
1060 | static JSBool |
1061 | obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp) |
1062 | { |
1063 | jsval thisv; |
1064 | JSString *str; |
1065 | |
1066 | thisv = JS_THIS(cx, vp); |
1067 | if (JSVAL_IS_NULL(thisv)) |
1068 | return JS_FALSE; |
1069 | |
1070 | str = js_ValueToString(cx, thisv); |
1071 | if (!str) |
1072 | return JS_FALSE; |
1073 | |
1074 | *vp = STRING_TO_JSVAL(str); |
1075 | return JS_TRUE; |
1076 | } |
1077 | |
1078 | static JSBool |
1079 | obj_valueOf(JSContext *cx, uintN argc, jsval *vp) |
1080 | { |
1081 | *vp = JS_THIS(cx, vp); |
1082 | return !JSVAL_IS_NULL(*vp); |
1083 | } |
1084 | |
1085 | /* |
1086 | * Check whether principals subsumes scopeobj's principals, and return true |
1087 | * if so (or if scopeobj has no principals, for backward compatibility with |
1088 | * the JS API, which does not require principals), and false otherwise. |
1089 | */ |
1090 | JSBool |
1091 | js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, |
1092 | JSPrincipals *principals, JSAtom *caller) |
1093 | { |
1094 | JSSecurityCallbacks *callbacks; |
1095 | JSPrincipals *scopePrincipals; |
1096 | const char *callerstr; |
1097 | |
1098 | callbacks = JS_GetSecurityCallbacks(cx); |
1099 | if (callbacks && callbacks->findObjectPrincipals) { |
1100 | scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj); |
1101 | if (!principals || !scopePrincipals || |
1102 | !principals->subsume(principals, scopePrincipals)) { |
1103 | callerstr = js_AtomToPrintableString(cx, caller); |
1104 | if (!callerstr) |
1105 | return JS_FALSE; |
1106 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
1107 | JSMSG_BAD_INDIRECT_CALL, callerstr); |
1108 | return JS_FALSE; |
1109 | } |
1110 | } |
1111 | return JS_TRUE; |
1112 | } |
1113 | |
1114 | JSObject * |
1115 | js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) |
1116 | { |
1117 | JSClass *clasp; |
1118 | JSExtendedClass *xclasp; |
1119 | JSObject *inner; |
1120 | |
1121 | if (!scopeobj) |
1122 | goto bad; |
1123 | |
1124 | OBJ_TO_INNER_OBJECT(cx, scopeobj); |
1125 | if (!scopeobj) |
1126 | return NULL; |
1127 | |
1128 | inner = scopeobj; |
1129 | |
1130 | /* XXX This is an awful gross hack. */ |
1131 | while (scopeobj) { |
1132 | clasp = OBJ_GET_CLASS(cx, scopeobj); |
1133 | if (clasp->flags & JSCLASS_IS_EXTENDED) { |
1134 | xclasp = (JSExtendedClass*)clasp; |
1135 | if (xclasp->innerObject && |
1136 | xclasp->innerObject(cx, scopeobj) != scopeobj) { |
1137 | goto bad; |
1138 | } |
1139 | } |
1140 | |
1141 | scopeobj = OBJ_GET_PARENT(cx, scopeobj); |
1142 | } |
1143 | |
1144 | return inner; |
1145 | |
1146 | bad: |
1147 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
1148 | JSMSG_BAD_INDIRECT_CALL, caller); |
1149 | return NULL; |
1150 | } |
1151 | |
1152 | const char * |
1153 | js_ComputeFilename(JSContext *cx, JSStackFrame *caller, |
1154 | JSPrincipals *principals, uintN *linenop) |
1155 | { |
1156 | uint32 flags; |
1157 | #ifdef DEBUG |
1158 | JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); |
1159 | #endif |
1160 | |
1161 | JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals)); |
1162 | flags = JS_GetScriptFilenameFlags(caller->script); |
1163 | if ((flags & JSFILENAME_PROTECTED) && |
1164 | principals && |
1165 | strcmp(principals->codebase, "[System Principal]")) { |
1166 | *linenop = 0; |
1167 | return principals->codebase; |
1168 | } |
1169 | |
1170 | if (caller->regs && *caller->regs->pc == JSOP_EVAL) { |
1171 | JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO); |
1172 | *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH); |
1173 | } else { |
1174 | *linenop = js_FramePCToLineNumber(cx, caller); |
1175 | } |
1176 | return caller->script->filename; |
1177 | } |
1178 | |
1179 | static JSBool |
1180 | obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1181 | { |
1182 | JSStackFrame *fp, *caller; |
1183 | JSBool indirectCall; |
1184 | JSObject *scopeobj; |
1185 | JSString *str; |
1186 | const char *file; |
1187 | uintN line; |
1188 | JSPrincipals *principals; |
1189 | uint32 tcflags; |
1190 | JSScript *script; |
1191 | JSBool ok; |
1192 | #if JS_HAS_EVAL_THIS_SCOPE |
1193 | JSObject *callerScopeChain = NULL, *callerVarObj = NULL; |
1194 | JSObject *setCallerScopeChain = NULL; |
1195 | JSBool setCallerVarObj = JS_FALSE; |
1196 | #endif |
1197 | |
1198 | fp = cx->fp; |
1199 | caller = JS_GetScriptedCaller(cx, fp); |
1200 | indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); |
1201 | |
1202 | /* |
1203 | * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and |
1204 | * calls that attempt to use a non-global object as the "with" object in |
1205 | * the former indirect case. |
1206 | */ |
1207 | scopeobj = OBJ_GET_PARENT(cx, obj); |
1208 | if (scopeobj) { |
1209 | scopeobj = js_GetWrappedObject(cx, obj); |
1210 | scopeobj = OBJ_GET_PARENT(cx, scopeobj); |
1211 | } |
1212 | if (indirectCall || scopeobj) { |
1213 | uintN flags = scopeobj |
1214 | ? JSREPORT_ERROR |
1215 | : JSREPORT_STRICT | JSREPORT_WARNING; |
1216 | if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, |
1217 | JSMSG_BAD_INDIRECT_CALL, |
1218 | js_eval_str)) { |
1219 | return JS_FALSE; |
1220 | } |
1221 | } |
1222 | |
1223 | if (!JSVAL_IS_STRING(argv[0])) { |
1224 | *rval = argv[0]; |
1225 | return JS_TRUE; |
1226 | } |
1227 | |
1228 | /* |
1229 | * If the caller is a lightweight function and doesn't have a variables |
1230 | * object, then we need to provide one for the compiler to stick any |
1231 | * declared (var) variables into. |
1232 | */ |
1233 | if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) |
1234 | return JS_FALSE; |
1235 | |
1236 | /* eval no longer takes an optional trailing argument. */ |
1237 | if (argc >= 2 && |
1238 | !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, |
1239 | js_GetErrorMessage, NULL, |
1240 | JSMSG_EVAL_ARITY)) { |
1241 | return JS_FALSE; |
1242 | } |
1243 | |
1244 | /* From here on, control must exit through label out with ok set. */ |
1245 | MUST_FLOW_THROUGH("out"); |
1246 | if (!scopeobj) { |
1247 | #if JS_HAS_EVAL_THIS_SCOPE |
1248 | /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ |
1249 | if (indirectCall) { |
1250 | callerScopeChain = js_GetScopeChain(cx, caller); |
1251 | if (!callerScopeChain) { |
1252 | ok = JS_FALSE; |
1253 | goto out; |
1254 | } |
1255 | OBJ_TO_INNER_OBJECT(cx, obj); |
1256 | if (!obj) { |
1257 | ok = JS_FALSE; |
1258 | goto out; |
1259 | } |
1260 | if (obj != callerScopeChain) { |
1261 | ok = js_CheckPrincipalsAccess(cx, obj, |
1262 | caller->script->principals, |
1263 | cx->runtime->atomState.evalAtom); |
1264 | if (!ok) |
1265 | goto out; |
1266 | |
1267 | scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); |
1268 | if (!scopeobj) { |
1269 | ok = JS_FALSE; |
1270 | goto out; |
1271 | } |
1272 | |
1273 | /* Set fp->scopeChain too, for the compiler. */ |
1274 | caller->scopeChain = fp->scopeChain = scopeobj; |
1275 | |
1276 | /* Remember scopeobj so we can null its private when done. */ |
1277 | setCallerScopeChain = scopeobj; |
1278 | } |
1279 | |
1280 | callerVarObj = caller->varobj; |
1281 | if (obj != callerVarObj) { |
1282 | /* Set fp->varobj too, for the compiler. */ |
1283 | caller->varobj = fp->varobj = obj; |
1284 | setCallerVarObj = JS_TRUE; |
1285 | } |
1286 | } |
1287 | #endif |
1288 | |
1289 | /* |
1290 | * Compile using caller's current scope object. |
1291 | * |
1292 | * NB: This means that native callers (who reach this point through |
1293 | * the C API) must use the two parameter form. |
1294 | */ |
1295 | if (caller) { |
1296 | scopeobj = js_GetScopeChain(cx, caller); |
1297 | if (!scopeobj) { |
1298 | ok = JS_FALSE; |
1299 | goto out; |
1300 | } |
1301 | } |
1302 | } |
1303 | |
1304 | /* Ensure we compile this eval with the right object in the scope chain. */ |
1305 | scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); |
1306 | if (!scopeobj) { |
1307 | ok = JS_FALSE; |
1308 | goto out; |
1309 | } |
1310 | |
1311 | str = JSVAL_TO_STRING(argv[0]); |
1312 | if (caller) { |
1313 | principals = JS_EvalFramePrincipals(cx, fp, caller); |
1314 | file = js_ComputeFilename(cx, caller, principals, &line); |
1315 | } else { |
1316 | file = NULL; |
1317 | line = 0; |
1318 | principals = NULL; |
1319 | } |
1320 | |
1321 | tcflags = TCF_COMPILE_N_GO; |
1322 | if (caller) |
1323 | tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1); |
1324 | script = js_CompileScript(cx, scopeobj, caller, principals, tcflags, |
1325 | JSSTRING_CHARS(str), JSSTRING_LENGTH(str), |
1326 | NULL, file, line); |
1327 | if (!script) { |
1328 | ok = JS_FALSE; |
1329 | goto out; |
1330 | } |
1331 | |
1332 | if (argc < 2) { |
1333 | /* Execute using caller's new scope object (might be a Call object). */ |
1334 | if (caller) |
1335 | scopeobj = caller->scopeChain; |
1336 | } |
1337 | |
1338 | /* |
1339 | * Belt-and-braces: check that the lesser of eval's principals and the |
1340 | * caller's principals has access to scopeobj. |
1341 | */ |
1342 | ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, |
1343 | cx->runtime->atomState.evalAtom); |
1344 | if (ok) |
1345 | ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); |
1346 | |
1347 | script->u.nextToGC = JS_SCRIPTS_TO_GC(cx); |
1348 | JS_SCRIPTS_TO_GC(cx) = script; |
1349 | #ifdef CHECK_SCRIPT_OWNER |
1350 | script->owner = NULL; |
1351 | #endif |
1352 | |
1353 | out: |
1354 | #if JS_HAS_EVAL_THIS_SCOPE |
1355 | /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ |
1356 | if (setCallerScopeChain) { |
1357 | caller->scopeChain = callerScopeChain; |
1358 | JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); |
1359 | JS_SetPrivate(cx, setCallerScopeChain, NULL); |
1360 | } |
1361 | if (setCallerVarObj) |
1362 | caller->varobj = callerVarObj; |
1363 | #endif |
1364 | return ok; |
1365 | } |
1366 | |
1367 | #if JS_HAS_OBJ_WATCHPOINT |
1368 | |
1369 | static JSBool |
1370 | obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, |
1371 | void *closure) |
1372 | { |
1373 | JSObject *callable; |
1374 | JSSecurityCallbacks *callbacks; |
1375 | JSStackFrame *caller; |
1376 | JSPrincipals *subject, *watcher; |
1377 | JSResolvingKey key; |
1378 | JSResolvingEntry *entry; |
1379 | uint32 generation; |
1380 | jsval argv[3]; |
1381 | JSBool ok; |
1382 | |
1383 | callable = (JSObject *) closure; |
1384 | |
1385 | callbacks = JS_GetSecurityCallbacks(cx); |
1386 | if (callbacks && callbacks->findObjectPrincipals) { |
1387 | /* Skip over any obj_watch_* frames between us and the real subject. */ |
1388 | caller = JS_GetScriptedCaller(cx, cx->fp); |
1389 | if (caller) { |
1390 | /* |
1391 | * Only call the watch handler if the watcher is allowed to watch |
1392 | * the currently executing script. |
1393 | */ |
1394 | watcher = callbacks->findObjectPrincipals(cx, callable); |
1395 | subject = JS_StackFramePrincipals(cx, caller); |
1396 | |
1397 | if (watcher && subject && !watcher->subsume(watcher, subject)) { |
1398 | /* Silently don't call the watch handler. */ |
1399 | return JS_TRUE; |
1400 | } |
1401 | } |
1402 | } |
1403 | |
1404 | /* Avoid recursion on (obj, id) already being watched on cx. */ |
1405 | key.obj = obj; |
1406 | key.id = id; |
1407 | if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) |
1408 | return JS_FALSE; |
1409 | if (!entry) |
1410 | return JS_TRUE; |
1411 | generation = cx->resolvingTable->generation; |
1412 | |
1413 | argv[0] = id; |
1414 | argv[1] = old; |
1415 | argv[2] = *nvp; |
1416 | ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); |
1417 | js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); |
1418 | return ok; |
1419 | } |
1420 | |
1421 | static JSBool |
1422 | obj_watch(JSContext *cx, uintN argc, jsval *vp) |
1423 | { |
1424 | JSObject *callable; |
1425 | jsval userid, value; |
1426 | jsid propid; |
1427 | JSObject *obj; |
1428 | uintN attrs; |
1429 | |
1430 | if (argc <= 1) { |
1431 | js_ReportMissingArg(cx, vp, 1); |
1432 | return JS_FALSE; |
1433 | } |
1434 | |
1435 | callable = js_ValueToCallableObject(cx, &vp[3], 0); |
1436 | if (!callable) |
1437 | return JS_FALSE; |
1438 | |
1439 | /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ |
1440 | userid = vp[2]; |
1441 | if (!JS_ValueToId(cx, userid, &propid)) |
1442 | return JS_FALSE; |
1443 | |
1444 | obj = JS_THIS_OBJECT(cx, vp); |
1445 | if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) |
1446 | return JS_FALSE; |
1447 | if (attrs & JSPROP_READONLY) |
1448 | return JS_TRUE; |
1449 | *vp = JSVAL_VOID; |
1450 | |
1451 | if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj)) |
1452 | return JS_FALSE; |
1453 | return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); |
1454 | } |
1455 | |
1456 | static JSBool |
1457 | obj_unwatch(JSContext *cx, uintN argc, jsval *vp) |
1458 | { |
1459 | JSObject *obj; |
1460 | |
1461 | obj = JS_THIS_OBJECT(cx, vp); |
1462 | if (!obj) |
1463 | return JS_FALSE; |
1464 | *vp = JSVAL_VOID; |
1465 | return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID, |
1466 | NULL, NULL); |
1467 | } |
1468 | |
1469 | #endif /* JS_HAS_OBJ_WATCHPOINT */ |
1470 | |
1471 | /* |
1472 | * Prototype and property query methods, to complement the 'in' and |
1473 | * 'instanceof' operators. |
1474 | */ |
1475 | |
1476 | /* Proposed ECMA 15.2.4.5. */ |
1477 | static JSBool |
1478 | obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) |
1479 | { |
1480 | JSObject *obj; |
1481 | |
1482 | obj = JS_THIS_OBJECT(cx, vp); |
1483 | return obj && |
1484 | js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp); |
1485 | } |
1486 | |
1487 | JSBool |
1488 | js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc, |
1489 | jsval *vp) |
1490 | { |
1491 | jsid id; |
1492 | JSObject *obj; |
1493 | |
1494 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1495 | return JS_FALSE; |
1496 | obj = JS_THIS_OBJECT(cx, vp); |
1497 | return obj && js_HasOwnProperty(cx, lookup, obj, id, vp); |
1498 | } |
1499 | |
1500 | JSBool |
1501 | js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id, |
1502 | jsval *vp) |
1503 | { |
1504 | JSObject *obj2; |
1505 | JSProperty *prop; |
1506 | JSScopeProperty *sprop; |
1507 | |
1508 | if (!lookup(cx, obj, id, &obj2, &prop)) |
1509 | return JS_FALSE; |
1510 | if (!prop) { |
1511 | *vp = JSVAL_FALSE; |
1512 | } else if (obj2 == obj) { |
1513 | *vp = JSVAL_TRUE; |
1514 | } else { |
1515 | JSClass *clasp; |
1516 | JSExtendedClass *xclasp; |
1517 | JSObject *outer; |
1518 | |
1519 | clasp = OBJ_GET_CLASS(cx, obj2); |
1520 | if (!(clasp->flags & JSCLASS_IS_EXTENDED) || |
1521 | !(xclasp = (JSExtendedClass *) clasp)->outerObject) { |
1522 | outer = NULL; |
1523 | } else { |
1524 | outer = xclasp->outerObject(cx, obj2); |
1525 | if (!outer) |
1526 | return JS_FALSE; |
1527 | } |
1528 | if (outer == obj) { |
1529 | *vp = JSVAL_TRUE; |
1530 | } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) { |
1531 | /* |
1532 | * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a |
1533 | * delegated property makes that property appear to be direct in |
1534 | * all delegating instances of the same native class. This hack |
1535 | * avoids bloating every function instance with its own 'length' |
1536 | * (AKA 'arity') property. But it must not extend across class |
1537 | * boundaries, to avoid making hasOwnProperty lie (bug 320854). |
1538 | * |
1539 | * It's not really a hack, of course: a permanent property can't |
1540 | * be deleted, and JSPROP_SHARED means "don't allocate a slot in |
1541 | * any instance, prototype or delegating". Without a slot, and |
1542 | * without the ability to remove and recreate (with differences) |
1543 | * the property, there is no way to tell whether it is directly |
1544 | * owned, or indirectly delegated. |
1545 | */ |
1546 | sprop = (JSScopeProperty *)prop; |
1547 | *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); |
1548 | } else { |
1549 | *vp = JSVAL_FALSE; |
1550 | } |
1551 | } |
1552 | if (prop) |
1553 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
1554 | return JS_TRUE; |
1555 | } |
1556 | |
1557 | #ifdef JS_TRACER |
1558 | static int32 FASTCALL |
1559 | Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str) |
1560 | { |
1561 | jsid id; |
1562 | jsval v; |
1563 | |
1564 | if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id)) |
1565 | return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1566 | if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) |
1567 | return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1568 | JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1569 | return JSVAL_TO_BOOLEAN(v); |
1570 | } |
1571 | #endif |
1572 | |
1573 | /* Proposed ECMA 15.2.4.6. */ |
1574 | static JSBool |
1575 | obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp) |
1576 | { |
1577 | JSBool b; |
1578 | |
1579 | if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp), |
1580 | argc != 0 ? vp[2] : JSVAL_VOID, &b)) { |
1581 | return JS_FALSE; |
1582 | } |
1583 | *vp = BOOLEAN_TO_JSVAL(b); |
1584 | return JS_TRUE; |
1585 | } |
1586 | |
1587 | /* Proposed ECMA 15.2.4.7. */ |
1588 | static JSBool |
1589 | obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) |
1590 | { |
1591 | jsid id; |
1592 | JSObject *obj; |
1593 | |
1594 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1595 | return JS_FALSE; |
1596 | |
1597 | obj = JS_THIS_OBJECT(cx, vp); |
1598 | return obj && js_PropertyIsEnumerable(cx, obj, id, vp); |
1599 | } |
1600 | |
1601 | #ifdef JS_TRACER |
1602 | static int32 FASTCALL |
1603 | Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str) |
1604 | { |
1605 | jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str)); |
1606 | jsval v; |
1607 | if (!js_PropertyIsEnumerable(cx, obj, id, &v)) |
1608 | return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1609 | JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1610 | return JSVAL_TO_BOOLEAN(v); |
1611 | } |
1612 | #endif |
1613 | |
1614 | JSBool |
1615 | js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
1616 | { |
1617 | JSObject *pobj; |
1618 | uintN attrs; |
1619 | JSProperty *prop; |
1620 | JSBool ok; |
1621 | |
1622 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
1623 | return JS_FALSE; |
1624 | |
1625 | if (!prop) { |
1626 | *vp = JSVAL_FALSE; |
1627 | return JS_TRUE; |
1628 | } |
1629 | |
1630 | /* |
1631 | * XXX ECMA spec error compatible: return false unless hasOwnProperty. |
1632 | * The ECMA spec really should be fixed so propertyIsEnumerable and the |
1633 | * for..in loop agree on whether prototype properties are enumerable, |
1634 | * obviously by fixing this method (not by breaking the for..in loop!). |
1635 | * |
1636 | * We check here for shared permanent prototype properties, which should |
1637 | * be treated as if they are local to obj. They are an implementation |
1638 | * technique used to satisfy ECMA requirements; users should not be able |
1639 | * to distinguish a shared permanent proto-property from a local one. |
1640 | */ |
1641 | if (pobj != obj && |
1642 | !(OBJ_IS_NATIVE(pobj) && |
1643 | SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { |
1644 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
1645 | *vp = JSVAL_FALSE; |
1646 | return JS_TRUE; |
1647 | } |
1648 | |
1649 | ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs); |
1650 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
1651 | if (ok) |
1652 | *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); |
1653 | return ok; |
1654 | } |
1655 | |
1656 | #if JS_HAS_GETTER_SETTER |
1657 | static JSBool |
1658 | obj_defineGetter(JSContext *cx, uintN argc, jsval *vp) |
1659 | { |
1660 | jsval fval, junk; |
1661 | jsid id; |
1662 | JSObject *obj; |
1663 | uintN attrs; |
1664 | |
1665 | if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { |
1666 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
1667 | JSMSG_BAD_GETTER_OR_SETTER, |
1668 | js_getter_str); |
1669 | return JS_FALSE; |
1670 | } |
1671 | fval = vp[3]; |
1672 | |
1673 | if (!JS_ValueToId(cx, vp[2], &id)) |
1674 | return JS_FALSE; |
1675 | obj = JS_THIS_OBJECT(cx, vp); |
1676 | if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) |
1677 | return JS_FALSE; |
1678 | /* |
1679 | * Getters and setters are just like watchpoints from an access |
1680 | * control point of view. |
1681 | */ |
1682 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) |
1683 | return JS_FALSE; |
1684 | *vp = JSVAL_VOID; |
1685 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
1686 | (JSPropertyOp) JSVAL_TO_OBJECT(fval), |
1687 | JS_PropertyStub, |
1688 | JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, |
1689 | NULL); |
1690 | } |
1691 | |
1692 | static JSBool |
1693 | obj_defineSetter(JSContext *cx, uintN argc, jsval *vp) |
1694 | { |
1695 | jsval fval, junk; |
1696 | jsid id; |
1697 | JSObject *obj; |
1698 | uintN attrs; |
1699 | |
1700 | if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { |
1701 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
1702 | JSMSG_BAD_GETTER_OR_SETTER, |
1703 | js_setter_str); |
1704 | return JS_FALSE; |
1705 | } |
1706 | fval = vp[3]; |
1707 | |
1708 | if (!JS_ValueToId(cx, vp[2], &id)) |
1709 | return JS_FALSE; |
1710 | obj = JS_THIS_OBJECT(cx, vp); |
1711 | if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) |
1712 | return JS_FALSE; |
1713 | /* |
1714 | * Getters and setters are just like watchpoints from an access |
1715 | * control point of view. |
1716 | */ |
1717 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) |
1718 | return JS_FALSE; |
1719 | *vp = JSVAL_VOID; |
1720 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
1721 | JS_PropertyStub, |
1722 | (JSPropertyOp) JSVAL_TO_OBJECT(fval), |
1723 | JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, |
1724 | NULL); |
1725 | } |
1726 | |
1727 | static JSBool |
1728 | obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp) |
1729 | { |
1730 | jsid id; |
1731 | JSObject *obj, *pobj; |
1732 | JSProperty *prop; |
1733 | JSScopeProperty *sprop; |
1734 | |
1735 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1736 | return JS_FALSE; |
1737 | obj = JS_THIS_OBJECT(cx, vp); |
1738 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
1739 | return JS_FALSE; |
1740 | *vp = JSVAL_VOID; |
1741 | if (prop) { |
1742 | if (OBJ_IS_NATIVE(pobj)) { |
1743 | sprop = (JSScopeProperty *) prop; |
1744 | if (sprop->attrs & JSPROP_GETTER) |
1745 | *vp = OBJECT_TO_JSVAL(sprop->getter); |
1746 | } |
1747 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
1748 | } |
1749 | return JS_TRUE; |
1750 | } |
1751 | |
1752 | static JSBool |
1753 | obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp) |
1754 | { |
1755 | jsid id; |
1756 | JSObject *obj, *pobj; |
1757 | JSProperty *prop; |
1758 | JSScopeProperty *sprop; |
1759 | |
1760 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1761 | return JS_FALSE; |
1762 | obj = JS_THIS_OBJECT(cx, vp); |
1763 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
1764 | return JS_FALSE; |
1765 | *vp = JSVAL_VOID; |
1766 | if (prop) { |
1767 | if (OBJ_IS_NATIVE(pobj)) { |
1768 | sprop = (JSScopeProperty *) prop; |
1769 | if (sprop->attrs & JSPROP_SETTER) |
1770 | *vp = OBJECT_TO_JSVAL(sprop->setter); |
1771 | } |
1772 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
1773 | } |
1774 | return JS_TRUE; |
1775 | } |
1776 | #endif /* JS_HAS_GETTER_SETTER */ |
1777 | |
1778 | JSBool |
1779 | obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp) |
1780 | { |
1781 | JSObject *obj; |
1782 | uintN attrs; |
1783 | |
1784 | if (argc == 0) { |
1785 | js_ReportMissingArg(cx, vp, 0); |
1786 | return JS_FALSE; |
1787 | } |
1788 | |
1789 | obj = js_ValueToNonNullObject(cx, vp[2]); |
1790 | if (!obj) |
1791 | return JS_FALSE; |
1792 | vp[2] = OBJECT_TO_JSVAL(obj); |
1793 | |
1794 | return OBJ_CHECK_ACCESS(cx, obj, |
1795 | ATOM_TO_JSID(cx->runtime->atomState.protoAtom), |
1796 | JSACC_PROTO, vp, &attrs); |
1797 | } |
1798 | |
1799 | #if JS_HAS_OBJ_WATCHPOINT |
1800 | const char js_watch_str[] = "watch"; |
1801 | const char js_unwatch_str[] = "unwatch"; |
1802 | #endif |
1803 | const char js_hasOwnProperty_str[] = "hasOwnProperty"; |
1804 | const char js_isPrototypeOf_str[] = "isPrototypeOf"; |
1805 | const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; |
1806 | #if JS_HAS_GETTER_SETTER |
1807 | const char js_defineGetter_str[] = "__defineGetter__"; |
1808 | const char js_defineSetter_str[] = "__defineSetter__"; |
1809 | const char js_lookupGetter_str[] = "__lookupGetter__"; |
1810 | const char js_lookupSetter_str[] = "__lookupSetter__"; |
1811 | #endif |
1812 | |
1813 | JS_DEFINE_TRCINFO_1(obj_hasOwnProperty, |
1814 | (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0))) |
1815 | JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable, |
1816 | (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0))) |
1817 | |
1818 | static JSFunctionSpec object_methods[] = { |
1819 | #if JS_HAS_TOSOURCE |
1820 | JS_FN(js_toSource_str, obj_toSource, 0,0), |
1821 | #endif |
1822 | JS_FN(js_toString_str, obj_toString, 0,0), |
1823 | JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), |
1824 | JS_FN(js_valueOf_str, obj_valueOf, 0,0), |
1825 | #if JS_HAS_OBJ_WATCHPOINT |
1826 | JS_FN(js_watch_str, obj_watch, 2,0), |
1827 | JS_FN(js_unwatch_str, obj_unwatch, 1,0), |
1828 | #endif |
1829 | JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, |
1830 | obj_hasOwnProperty_trcinfo), |
1831 | JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), |
1832 | JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, |
1833 | obj_propertyIsEnumerable_trcinfo), |
1834 | #if JS_HAS_GETTER_SETTER |
1835 | JS_FN(js_defineGetter_str, obj_defineGetter, 2,0), |
1836 | JS_FN(js_defineSetter_str, obj_defineSetter, 2,0), |
1837 | JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), |
1838 | JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), |
1839 | #endif |
1840 | JS_FS_END |
1841 | }; |
1842 | |
1843 | static JSFunctionSpec object_static_methods[] = { |
1844 | JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0), |
1845 | JS_FS_END |
1846 | }; |
1847 | |
1848 | JSBool |
1849 | js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1850 | { |
1851 | if (argc == 0) { |
1852 | /* Trigger logic below to construct a blank object. */ |
1853 | obj = NULL; |
1854 | } else { |
1855 | /* If argv[0] is null or undefined, obj comes back null. */ |
1856 | if (!js_ValueToObject(cx, argv[0], &obj)) |
1857 | return JS_FALSE; |
1858 | } |
1859 | if (!obj) { |
1860 | JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); |
1861 | if (cx->fp->flags & JSFRAME_CONSTRUCTING) |
1862 | return JS_TRUE; |
1863 | obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); |
1864 | if (!obj) |
1865 | return JS_FALSE; |
1866 | } |
1867 | *rval = OBJECT_TO_JSVAL(obj); |
1868 | return JS_TRUE; |
1869 | } |
1870 | |
1871 | /* |
1872 | * ObjectOps and Class for with-statement stack objects. |
1873 | */ |
1874 | static JSBool |
1875 | with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
1876 | JSProperty **propp) |
1877 | { |
1878 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1879 | if (!proto) |
1880 | return js_LookupProperty(cx, obj, id, objp, propp); |
1881 | return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); |
1882 | } |
1883 | |
1884 | static JSBool |
1885 | with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
1886 | { |
1887 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1888 | if (!proto) |
1889 | return js_GetProperty(cx, obj, id, vp); |
1890 | return OBJ_GET_PROPERTY(cx, proto, id, vp); |
1891 | } |
1892 | |
1893 | static JSBool |
1894 | with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
1895 | { |
1896 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1897 | if (!proto) |
1898 | return js_SetProperty(cx, obj, id, vp); |
1899 | return OBJ_SET_PROPERTY(cx, proto, id, vp); |
1900 | } |
1901 | |
1902 | static JSBool |
1903 | with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, |
1904 | uintN *attrsp) |
1905 | { |
1906 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1907 | if (!proto) |
1908 | return js_GetAttributes(cx, obj, id, prop, attrsp); |
1909 | return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); |
1910 | } |
1911 | |
1912 | static JSBool |
1913 | with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, |
1914 | uintN *attrsp) |
1915 | { |
1916 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1917 | if (!proto) |
1918 | return js_SetAttributes(cx, obj, id, prop, attrsp); |
1919 | return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); |
1920 | } |
1921 | |
1922 | static JSBool |
1923 | with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) |
1924 | { |
1925 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1926 | if (!proto) |
1927 | return js_DeleteProperty(cx, obj, id, rval); |
1928 | return OBJ_DELETE_PROPERTY(cx, proto, id, rval); |
1929 | } |
1930 | |
1931 | static JSBool |
1932 | with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) |
1933 | { |
1934 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1935 | if (!proto) |
1936 | return js_DefaultValue(cx, obj, hint, vp); |
1937 | return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); |
1938 | } |
1939 | |
1940 | static JSBool |
1941 | with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, |
1942 | jsval *statep, jsid *idp) |
1943 | { |
1944 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1945 | if (!proto) |
1946 | return js_Enumerate(cx, obj, enum_op, statep, idp); |
1947 | return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); |
1948 | } |
1949 | |
1950 | static JSBool |
1951 | with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, |
1952 | jsval *vp, uintN *attrsp) |
1953 | { |
1954 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1955 | if (!proto) |
1956 | return js_CheckAccess(cx, obj, id, mode, vp, attrsp); |
1957 | return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); |
1958 | } |
1959 | |
1960 | static JSObject * |
1961 | with_ThisObject(JSContext *cx, JSObject *obj) |
1962 | { |
1963 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
1964 | if (!proto) |
1965 | return obj; |
1966 | return OBJ_THIS_OBJECT(cx, proto); |
1967 | } |
1968 | |
1969 | JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { |
1970 | js_NewObjectMap, js_DestroyObjectMap, |
1971 | with_LookupProperty, js_DefineProperty, |
1972 | with_GetProperty, with_SetProperty, |
1973 | with_GetAttributes, with_SetAttributes, |
1974 | with_DeleteProperty, with_DefaultValue, |
1975 | with_Enumerate, with_CheckAccess, |
1976 | with_ThisObject, NATIVE_DROP_PROPERTY, |
1977 | NULL, NULL, |
1978 | NULL, NULL, |
1979 | js_SetProtoOrParent, js_SetProtoOrParent, |
1980 | js_TraceObject, js_Clear, |
1981 | NULL, NULL |
1982 | }; |
1983 | |
1984 | static JSObjectOps * |
1985 | with_getObjectOps(JSContext *cx, JSClass *clasp) |
1986 | { |
1987 | return &js_WithObjectOps; |
1988 | } |
1989 | |
1990 | JSClass js_WithClass = { |
1991 | "With", |
1992 | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, |
1993 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
1994 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
1995 | with_getObjectOps, |
1996 | 0,0,0,0,0,0,0 |
1997 | }; |
1998 | |
1999 | JSObject * |
2000 | js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) |
2001 | { |
2002 | JSObject *obj; |
2003 | |
2004 | obj = js_NewObject(cx, &js_WithClass, proto, parent, 0); |
2005 | if (!obj) |
2006 | return NULL; |
2007 | STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp)); |
2008 | OBJ_SET_BLOCK_DEPTH(cx, obj, depth); |
2009 | return obj; |
2010 | } |
2011 | |
2012 | JSObject * |
2013 | js_NewBlockObject(JSContext *cx) |
2014 | { |
2015 | JSObject *obj; |
2016 | JSBool ok; |
2017 | |
2018 | /* |
2019 | * Null obj's proto slot so that Object.prototype.* does not pollute block |
2020 | * scopes. Make sure obj has its own scope too, since clearing proto does |
2021 | * not affect OBJ_SCOPE(obj). |
2022 | */ |
2023 | obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0); |
2024 | if (!obj) |
2025 | return NULL; |
2026 | JS_LOCK_OBJ(cx, obj); |
2027 | ok = js_GetMutableScope(cx, obj) != NULL; |
2028 | JS_UNLOCK_OBJ(cx, obj); |
2029 | if (!ok) |
2030 | return NULL; |
2031 | OBJ_CLEAR_PROTO(cx, obj); |
2032 | return obj; |
2033 | } |
2034 | |
2035 | JSObject * |
2036 | js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, |
2037 | JSStackFrame *fp) |
2038 | { |
2039 | JSObject *clone; |
2040 | |
2041 | JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass); |
2042 | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto)); |
2043 | clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0); |
2044 | if (!clone) |
2045 | return NULL; |
2046 | STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp)); |
2047 | STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH, |
2048 | OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH)); |
2049 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); |
2050 | return clone; |
2051 | } |
2052 | |
2053 | JSBool |
2054 | js_PutBlockObject(JSContext *cx, JSBool normalUnwind) |
2055 | { |
2056 | JSStackFrame *fp; |
2057 | JSObject *obj; |
2058 | uintN depth, count; |
2059 | |
2060 | /* Blocks have one fixed slot available for the first local.*/ |
2061 | JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); |
2062 | |
2063 | fp = cx->fp; |
2064 | obj = fp->scopeChain; |
2065 | JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); |
2066 | JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp); |
2067 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); |
2068 | |
2069 | /* |
2070 | * Block objects should never be exposed to scripts. Thus the clone should |
2071 | * not own the property map and rather always share it with the prototype |
2072 | * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after |
2073 | * we copy the stack slots into reserved slots. |
2074 | */ |
2075 | JS_ASSERT(OBJ_SCOPE(obj)->object != obj); |
2076 | |
2077 | /* Block objects should not have reserved slots before they are put. */ |
2078 | JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS); |
2079 | |
2080 | /* The block and its locals must be on the current stack for GC safety. */ |
2081 | depth = OBJ_BLOCK_DEPTH(cx, obj); |
2082 | count = OBJ_BLOCK_COUNT(cx, obj); |
2083 | JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp))); |
2084 | JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)); |
2085 | |
2086 | /* See comments in CheckDestructuring from jsparse.c. */ |
2087 | JS_ASSERT(count >= 1); |
2088 | |
2089 | depth += fp->script->nfixed; |
2090 | obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth]; |
2091 | if (normalUnwind && count > 1) { |
2092 | --count; |
2093 | JS_LOCK_OBJ(cx, obj); |
2094 | if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE)) |
2095 | normalUnwind = JS_FALSE; |
2096 | else |
2097 | memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); |
2098 | JS_UNLOCK_OBJ(cx, obj); |
2099 | } |
2100 | |
2101 | /* We must clear the private slot even with errors. */ |
2102 | JS_SetPrivate(cx, obj, NULL); |
2103 | fp->scopeChain = OBJ_GET_PARENT(cx, obj); |
2104 | return normalUnwind; |
2105 | } |
2106 | |
2107 | static JSBool |
2108 | block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
2109 | { |
2110 | uintN index; |
2111 | JSStackFrame *fp; |
2112 | |
2113 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); |
2114 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); |
2115 | if (!JSVAL_IS_INT(id)) |
2116 | return JS_TRUE; |
2117 | |
2118 | index = (uint16) JSVAL_TO_INT(id); |
2119 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
2120 | if (fp) { |
2121 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); |
2122 | JS_ASSERT(index < fp->script->nslots); |
2123 | *vp = fp->slots[index]; |
2124 | return JS_TRUE; |
2125 | } |
2126 | |
2127 | /* Reserve slots start with the first slot after the private. */ |
2128 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; |
2129 | return JS_GetReservedSlot(cx, obj, index, vp); |
2130 | } |
2131 | |
2132 | static JSBool |
2133 | block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
2134 | { |
2135 | uintN index; |
2136 | JSStackFrame *fp; |
2137 | |
2138 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); |
2139 | if (!JSVAL_IS_INT(id)) |
2140 | return JS_TRUE; |
2141 | |
2142 | index = (uint16) JSVAL_TO_INT(id); |
2143 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
2144 | if (fp) { |
2145 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); |
2146 | JS_ASSERT(index < fp->script->nslots); |
2147 | fp->slots[index] = *vp; |
2148 | return JS_TRUE; |
2149 | } |
2150 | |
2151 | /* Reserve slots start with the first slot after the private. */ |
2152 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; |
2153 | return JS_SetReservedSlot(cx, obj, index, *vp); |
2154 | } |
2155 | |
2156 | #if JS_HAS_XDR |
2157 | |
2158 | #define NO_PARENT_INDEX ((uint32)-1) |
2159 | |
2160 | uint32 |
2161 | FindObjectIndex(JSObjectArray *array, JSObject *obj) |
2162 | { |
2163 | size_t i; |
2164 | |
2165 | if (array) { |
2166 | i = array->length; |
2167 | do { |
2168 | |
2169 | if (array->vector[--i] == obj) |
2170 | return i; |
2171 | } while (i != 0); |
2172 | } |
2173 | |
2174 | return NO_PARENT_INDEX; |
2175 | } |
2176 | |
2177 | static JSBool |
2178 | block_xdrObject(JSXDRState *xdr, JSObject **objp) |
2179 | { |
2180 | JSContext *cx; |
2181 | uint32 parentId; |
2182 | JSObject *obj, *parent; |
2183 | uint16 depth, count, i; |
2184 | uint32 tmp; |
2185 | JSTempValueRooter tvr; |
2186 | JSScopeProperty *sprop; |
2187 | jsid propid; |
2188 | JSAtom *atom; |
2189 | int16 shortid; |
2190 | JSBool ok; |
2191 | |
2192 | cx = xdr->cx; |
2193 | #ifdef __GNUC__ |
2194 | obj = NULL; /* quell GCC overwarning */ |
2195 | #endif |
2196 | |
2197 | if (xdr->mode == JSXDR_ENCODE) { |
2198 | obj = *objp; |
2199 | parent = OBJ_GET_PARENT(cx, obj); |
2200 | parentId = (xdr->script->objectsOffset == 0) |
2201 | ? NO_PARENT_INDEX |
2202 | : FindObjectIndex(JS_SCRIPT_OBJECTS(xdr->script), parent); |
2203 | depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); |
2204 | count = (uint16)OBJ_BLOCK_COUNT(cx, obj); |
2205 | tmp = (uint32)(depth << 16) | count; |
2206 | } |
2207 | #ifdef __GNUC__ /* suppress bogus gcc warnings */ |
2208 | else count = 0; |
2209 | #endif |
2210 | |
2211 | /* First, XDR the parent atomid. */ |
2212 | if (!JS_XDRUint32(xdr, &parentId)) |
2213 | return JS_FALSE; |
2214 | |
2215 | if (xdr->mode == JSXDR_DECODE) { |
2216 | obj = js_NewBlockObject(cx); |
2217 | if (!obj) |
2218 | return JS_FALSE; |
2219 | *objp = obj; |
2220 | |
2221 | /* |
2222 | * If there's a parent id, then get the parent out of our script's |
2223 | * object array. We know that we XDR block object in outer-to-inner |
2224 | * order, which means that getting the parent now will work. |
2225 | */ |
2226 | if (parentId == NO_PARENT_INDEX) |
2227 | parent = NULL; |
2228 | else |
2229 | JS_GET_SCRIPT_OBJECT(xdr->script, parentId, parent); |
2230 | STOBJ_SET_PARENT(obj, parent); |
2231 | } |
2232 | |
2233 | JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); |
2234 | |
2235 | if (!JS_XDRUint32(xdr, &tmp)) { |
2236 | JS_POP_TEMP_ROOT(cx, &tvr); |
2237 | return JS_FALSE; |
2238 | } |
2239 | |
2240 | if (xdr->mode == JSXDR_DECODE) { |
2241 | depth = (uint16)(tmp >> 16); |
2242 | count = (uint16)tmp; |
2243 | STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)); |
2244 | } |
2245 | |
2246 | /* |
2247 | * XDR the block object's properties. We know that there are 'count' |
2248 | * properties to XDR, stored as id/shortid pairs. We do not XDR any |
2249 | * non-native properties, only those that the compiler created. |
2250 | */ |
2251 | sprop = NULL; |
2252 | ok = JS_TRUE; |
2253 | for (i = 0; i < count; i++) { |
2254 | if (xdr->mode == JSXDR_ENCODE) { |
2255 | /* Find a property to XDR. */ |
2256 | do { |
2257 | /* If sprop is NULL, this is the first property. */ |
2258 | sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; |
2259 | } while (!(sprop->flags & SPROP_HAS_SHORTID)); |
2260 | |
2261 | JS_ASSERT(sprop->getter == js_BlockClass.getProperty); |
2262 | propid = sprop->id; |
2263 | JS_ASSERT(JSID_IS_ATOM(propid)); |
2264 | atom = JSID_TO_ATOM(propid); |
2265 | shortid = sprop->shortid; |
2266 | JS_ASSERT(shortid >= 0); |
2267 | } |
2268 | |
2269 | /* XDR the real id, then the shortid. */ |
2270 | if (!js_XDRStringAtom(xdr, &atom) || |
2271 | !JS_XDRUint16(xdr, (uint16 *)&shortid)) { |
2272 | ok = JS_FALSE; |
2273 | break; |
2274 | } |
2275 | |
2276 | if (xdr->mode == JSXDR_DECODE) { |
2277 | if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), |
2278 | JSVAL_VOID, NULL, NULL, |
2279 | JSPROP_ENUMERATE | JSPROP_PERMANENT, |
2280 | SPROP_HAS_SHORTID, shortid, NULL)) { |
2281 | ok = JS_FALSE; |
2282 | break; |
2283 | } |
2284 | } |
2285 | } |
2286 | |
2287 | JS_POP_TEMP_ROOT(cx, &tvr); |
2288 | return ok; |
2289 | } |
2290 | |
2291 | #else |
2292 | # define block_xdrObject NULL |
2293 | #endif |
2294 | |
2295 | static uint32 |
2296 | block_reserveSlots(JSContext *cx, JSObject *obj) |
2297 | { |
2298 | return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0; |
2299 | } |
2300 | |
2301 | JSClass js_BlockClass = { |
2302 | "Block", |
2303 | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | |
2304 | JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), |
2305 | JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, |
2306 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
2307 | NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, block_reserveSlots |
2308 | }; |
2309 | |
2310 | JSObject* |
2311 | js_InitBlockClass(JSContext *cx, JSObject* obj) |
2312 | { |
2313 | JSObject *proto; |
2314 | |
2315 | proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, |
2316 | NULL, NULL, NULL); |
2317 | if (!proto) |
2318 | return NULL; |
2319 | |
2320 | OBJ_CLEAR_PROTO(cx, proto); |
2321 | return proto; |
2322 | } |
2323 | |
2324 | JSObject * |
2325 | js_InitEval(JSContext *cx, JSObject *obj) |
2326 | { |
2327 | /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */ |
2328 | if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom, |
2329 | obj_eval, 1, 0)) { |
2330 | return NULL; |
2331 | } |
2332 | |
2333 | return obj; |
2334 | } |
2335 | |
2336 | JSObject * |
2337 | js_InitObjectClass(JSContext *cx, JSObject *obj) |
2338 | { |
2339 | return JS_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, |
2340 | object_props, object_methods, NULL, |
2341 | object_static_methods); |
2342 | } |
2343 | |
2344 | void |
2345 | js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, |
2346 | JSClass *clasp) |
2347 | { |
2348 | map->nrefs = nrefs; |
2349 | map->ops = ops; |
2350 | map->freeslot = JSSLOT_FREE(clasp); |
2351 | } |
2352 | |
2353 | JSObjectMap * |
2354 | js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, |
2355 | JSClass *clasp, JSObject *obj) |
2356 | { |
2357 | return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); |
2358 | } |
2359 | |
2360 | void |
2361 | js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) |
2362 | { |
2363 | js_DestroyScope(cx, (JSScope *)map); |
2364 | } |
2365 | |
2366 | JSObjectMap * |
2367 | js_HoldObjectMap(JSContext *cx, JSObjectMap *map) |
2368 | { |
2369 | JS_ASSERT(map->nrefs >= 0); |
2370 | JS_ATOMIC_INCREMENT(&map->nrefs); |
2371 | return map; |
2372 | } |
2373 | |
2374 | JSObjectMap * |
2375 | js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) |
2376 | { |
2377 | JS_ASSERT(map->nrefs > 0); |
2378 | JS_ATOMIC_DECREMENT(&map->nrefs); |
2379 | if (map->nrefs == 0) { |
2380 | map->ops->destroyObjectMap(cx, map); |
2381 | return NULL; |
2382 | } |
2383 | if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) |
2384 | ((JSScope *)map)->object = NULL; |
2385 | return map; |
2386 | } |
2387 | |
2388 | static void |
2389 | FreeSlots(JSContext *cx, JSObject *obj) |
2390 | { |
2391 | if (obj->dslots) { |
2392 | JS_ASSERT((uint32)obj->dslots[-1] > JS_INITIAL_NSLOTS); |
2393 | JS_free(cx, obj->dslots - 1); |
2394 | obj->dslots = NULL; |
2395 | } |
2396 | } |
2397 | |
2398 | #define SLOTS_TO_DYNAMIC_WORDS(nslots) \ |
2399 | (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS) |
2400 | |
2401 | #define DYNAMIC_WORDS_TO_SLOTS(words) \ |
2402 | (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) |
2403 | |
2404 | JSBool |
2405 | js_ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots, |
2406 | JSBool exactAllocation) |
2407 | { |
2408 | jsval *old, *slots; |
2409 | uint32 oslots, nwords, owords, log, i; |
2410 | |
2411 | /* |
2412 | * Minimal number of dynamic slots to allocate. |
2413 | */ |
2414 | #define MIN_DYNAMIC_WORDS 4 |
2415 | |
2416 | /* |
2417 | * The limit to switch to linear allocation strategy from the power of 2 |
2418 | * growth no to waste too much memory. |
2419 | */ |
2420 | #define LINEAR_GROWTH_STEP JS_BIT(16) |
2421 | |
2422 | old = obj->dslots; |
2423 | if (nslots <= JS_INITIAL_NSLOTS) { |
2424 | if (old && |
2425 | (exactAllocation || |
2426 | SLOTS_TO_DYNAMIC_WORDS((uint32)old[-1]) != MIN_DYNAMIC_WORDS || |
2427 | nslots <= (JS_INITIAL_NSLOTS + |
2428 | JSSLOT_FREE(STOBJ_GET_CLASS(obj))) / 2)) { |
2429 | /* |
2430 | * We do not want to free dynamic slots when allocation is a hint, |
2431 | * we reached minimal allocation and almost all fixed slots are |
2432 | * used. It avoids allocating dynamic slots again when properties |
2433 | * are added to the object. |
2434 | * |
2435 | * If there were no private or reserved slots, the condition to |
2436 | * free the slots would be |
2437 | * |
2438 | * nslots <= JS_INITIAL_NSLOTS / 2 |
2439 | * |
2440 | * but to account for never removed slots before JSSLOT_FREE(class) |
2441 | * we need to subtract it from the slot counts which gives |
2442 | * |
2443 | * nslots - JSSLOT_FREE <= (JS_INITIAL_NSLOTS - JSSLOT_FREE) / 2 |
2444 | * |
2445 | * or |
2446 | * |
2447 | * nslots <= (JS_INITIAL_NSLOTS + JSSLOT_FREE) / 2 |
2448 | */ |
2449 | FreeSlots(cx, obj); |
2450 | } |
2451 | return JS_TRUE; |
2452 | } |
2453 | |
2454 | oslots = (old) ? (uint32)*--old : JS_INITIAL_NSLOTS; |
2455 | nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); |
2456 | |
2457 | if (nslots > oslots) { |
2458 | if (!exactAllocation) { |
2459 | /* |
2460 | * Round up nslots so the number of bytes in dslots array is power |
2461 | * of 2 to ensure exponential grouth. |
2462 | */ |
2463 | if (nwords <= MIN_DYNAMIC_WORDS) { |
2464 | nwords = MIN_DYNAMIC_WORDS; |
2465 | } else if (nwords < LINEAR_GROWTH_STEP) { |
2466 | JS_CEILING_LOG2(log, nwords); |
2467 | nwords = JS_BIT(log); |
2468 | } else { |
2469 | nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); |
2470 | } |
2471 | } |
2472 | slots = (jsval *)JS_realloc(cx, old, nwords * sizeof(jsval)); |
2473 | if (!slots) |
2474 | return JS_FALSE; |
2475 | } else { |
2476 | JS_ASSERT(nslots < oslots); |
2477 | if (!exactAllocation) { |
2478 | owords = DYNAMIC_WORDS_TO_SLOTS(oslots); |
2479 | if (owords <= MIN_DYNAMIC_WORDS) |
2480 | return JS_TRUE; |
2481 | if (owords < LINEAR_GROWTH_STEP * 2) { |
2482 | /* |
2483 | * Shrink only if 1/4 of slots are left and we need to grow |
2484 | * the array at least twice to reach the current capacity. It |
2485 | * prevents frequent capacity growth/shrinking when slots are |
2486 | * often removed and added. |
2487 | */ |
2488 | if (nwords > owords / 4) |
2489 | return JS_TRUE; |
2490 | JS_CEILING_LOG2(log, nwords); |
2491 | nwords = JS_BIT(log); |
2492 | if (nwords < MIN_DYNAMIC_WORDS) |
2493 | nwords = MIN_DYNAMIC_WORDS; |
2494 | } else { |
2495 | /* |
2496 | * Shrink only if we free at least 2 linear allocation |
2497 | * segments, to prevent growth/shrinking resonance. |
2498 | */ |
2499 | if (nwords > owords - LINEAR_GROWTH_STEP * 2) |
2500 | return JS_TRUE; |
2501 | nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); |
2502 | } |
2503 | } |
2504 | |
2505 | /* We avoid JS_realloc not to report a failed shrink attempt. */ |
2506 | slots = (jsval *)realloc(old, nwords * sizeof(jsval)); |
2507 | if (!slots) |
2508 | slots = old; |
2509 | } |
2510 | |
2511 | nslots = DYNAMIC_WORDS_TO_SLOTS(nwords); |
2512 | *slots++ = (jsval)nslots; |
2513 | obj->dslots = slots; |
2514 | |
2515 | /* If we're extending an allocation, initialize free slots. */ |
2516 | for (i = oslots; i < nslots; i++) |
2517 | slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID; |
2518 | |
2519 | return JS_TRUE; |
2520 | |
2521 | #undef LINEAR_GROWTH_STEP |
2522 | #undef MIN_DYNAMIC_WORDS |
2523 | } |
2524 | |
2525 | extern JSBool |
2526 | js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) |
2527 | { |
2528 | JSProtoKey key; |
2529 | JSAtom *atom; |
2530 | |
2531 | key = JSCLASS_CACHED_PROTO_KEY(clasp); |
2532 | if (key != JSProto_Null) { |
2533 | *idp = INT_TO_JSID(key); |
2534 | } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { |
2535 | *idp = INT_TO_JSID(JSProto_Object); |
2536 | } else { |
2537 | atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); |
2538 | if (!atom) |
2539 | return JS_FALSE; |
2540 | *idp = ATOM_TO_JSID(atom); |
2541 | } |
2542 | return JS_TRUE; |
2543 | } |
2544 | |
2545 | JSObject * |
2546 | js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, |
2547 | uintN objectSize) |
2548 | { |
2549 | jsid id; |
2550 | |
2551 | /* Bootstrap the ur-object, and make it the default prototype object. */ |
2552 | if (!proto) { |
2553 | if (!js_GetClassId(cx, clasp, &id)) |
2554 | return NULL; |
2555 | if (!js_GetClassPrototype(cx, parent, id, &proto)) |
2556 | return NULL; |
2557 | if (!proto && |
2558 | !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), |
2559 | &proto)) { |
2560 | return NULL; |
2561 | } |
2562 | } |
2563 | |
2564 | return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize); |
2565 | } |
2566 | |
2567 | JSObject * |
2568 | js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, |
2569 | JSObject *parent, uintN objectSize) |
2570 | { |
2571 | JSObject *obj; |
2572 | JSObjectOps *ops; |
2573 | JSObjectMap *map; |
2574 | JSClass *protoclasp; |
2575 | uint32 nslots, i; |
2576 | JSTempValueRooter tvr; |
2577 | |
2578 | #ifdef INCLUDE_MOZILLA_DTRACE |
2579 | if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) |
2580 | jsdtrace_object_create_start(cx->fp, clasp); |
2581 | #endif |
2582 | |
2583 | /* Currently only functions can have non-standard allocation size. */ |
2584 | if (clasp == &js_FunctionClass) { |
2585 | if (objectSize == 0) |
2586 | objectSize = sizeof(JSFunction); |
2587 | else |
2588 | JS_ASSERT(objectSize == sizeof(JSObject)); |
2589 | } else { |
2590 | JS_ASSERT(objectSize == 0); |
2591 | objectSize = sizeof(JSObject); |
2592 | } |
2593 | |
2594 | /* |
2595 | * Allocate an object from the GC heap and initialize all its fields before |
2596 | * doing any operation that can potentially trigger GC. |
2597 | */ |
2598 | obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); |
2599 | if (!obj) |
2600 | goto earlybad; |
2601 | |
2602 | obj->map = NULL; |
2603 | obj->dslots = NULL; |
2604 | |
2605 | /* |
2606 | * Set the class slot with the initial value of the system and delegate |
2607 | * flags set to false. |
2608 | */ |
2609 | JS_ASSERT(((jsuword) clasp & 3) == 0); |
2610 | obj->classword = jsuword(clasp); |
2611 | JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); |
2612 | JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); |
2613 | |
2614 | /* Set the proto and parent properties. */ |
2615 | STOBJ_SET_PROTO(obj, proto); |
2616 | STOBJ_SET_PARENT(obj, parent); |
2617 | |
2618 | /* Initialize the remaining fixed slots. */ |
2619 | for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i) |
2620 | obj->fslots[i] = JSVAL_VOID; |
2621 | |
2622 | #ifdef DEBUG |
2623 | memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, |
2624 | objectSize - sizeof(JSObject)); |
2625 | #endif |
2626 | |
2627 | /* |
2628 | * Root obj to prevent it from being collected out from under this call to |
2629 | * js_NewObject. There's a possibilty of GC under the objectHook call-out |
2630 | * further below. |
2631 | */ |
2632 | JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); |
2633 | |
2634 | /* Always call the class's getObjectOps hook if it has one. */ |
2635 | ops = clasp->getObjectOps |
2636 | ? clasp->getObjectOps(cx, clasp) |
2637 | : &js_ObjectOps; |
2638 | |
2639 | /* |
2640 | * Default parent to the parent of the prototype, which was set from |
2641 | * the parent of the prototype's constructor. |
2642 | */ |
2643 | if (proto && !parent) |
2644 | STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto)); |
2645 | |
2646 | /* |
2647 | * Share proto's map only if it has the same JSObjectOps, and only if |
2648 | * proto's class has the same private and reserved slots as obj's map |
2649 | * and class have. We assume that if prototype and object are of the |
2650 | * same class, they always have the same number of computed reserved |
2651 | * slots (returned via clasp->reserveSlots); otherwise, prototype and |
2652 | * object classes must have the same (null or not) reserveSlots hook. |
2653 | */ |
2654 | if (proto && |
2655 | (map = proto->map)->ops == ops && |
2656 | ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || |
2657 | (!((protoclasp->flags ^ clasp->flags) & |
2658 | (JSCLASS_HAS_PRIVATE | |
2659 | (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && |
2660 | protoclasp->reserveSlots == clasp->reserveSlots))) |
2661 | { |
2662 | /* Share the given prototype's map. */ |
2663 | obj->map = js_HoldObjectMap(cx, map); |
2664 | } else { |
2665 | map = ops->newObjectMap(cx, 1, ops, clasp, obj); |
2666 | if (!map) |
2667 | goto bad; |
2668 | obj->map = map; |
2669 | |
2670 | /* Let ops->newObjectMap set freeslot so as to reserve slots. */ |
2671 | nslots = map->freeslot; |
2672 | JS_ASSERT(nslots >= JSSLOT_PRIVATE); |
2673 | if (nslots > JS_INITIAL_NSLOTS && |
2674 | !js_ReallocSlots(cx, obj, nslots, JS_TRUE)) { |
2675 | js_DropObjectMap(cx, map, obj); |
2676 | obj->map = NULL; |
2677 | goto bad; |
2678 | } |
2679 | } |
2680 | |
2681 | if (cx->debugHooks->objectHook) { |
2682 | JS_KEEP_ATOMS(cx->runtime); |
2683 | cx->debugHooks->objectHook(cx, obj, JS_TRUE, |
2684 | cx->debugHooks->objectHookData); |
2685 | JS_UNKEEP_ATOMS(cx->runtime); |
2686 | } |
2687 | |
2688 | out: |
2689 | JS_POP_TEMP_ROOT(cx, &tvr); |
2690 | cx->weakRoots.newborn[GCX_OBJECT] = obj; |
2691 | #ifdef INCLUDE_MOZILLA_DTRACE |
2692 | if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) |
2693 | jsdtrace_object_create(cx, clasp, obj); |
2694 | if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) |
2695 | jsdtrace_object_create_done(cx->fp, clasp); |
2696 | #endif |
2697 | return obj; |
2698 | |
2699 | bad: |
2700 | obj = NULL; |
2701 | goto out; |
2702 | |
2703 | earlybad: |
2704 | #ifdef INCLUDE_MOZILLA_DTRACE |
2705 | if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) |
2706 | jsdtrace_object_create(cx, clasp, NULL); |
2707 | if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) |
2708 | jsdtrace_object_create_done(cx->fp, clasp); |
2709 | #endif |
2710 | return NULL; |
2711 | } |
2712 | |
2713 | JS_BEGIN_EXTERN_C |
2714 | |
2715 | static JSObject * |
2716 | js_InitNullClass(JSContext *cx, JSObject *obj) |
2717 | { |
2718 | JS_ASSERT(0); |
2719 | return NULL; |
2720 | } |
2721 | |
2722 | #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); |
2723 | #include "jsproto.tbl" |
2724 | #undef JS_PROTO |
2725 | |
2726 | static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { |
2727 | #define JS_PROTO(name,code,init) init, |
2728 | #include "jsproto.tbl" |
2729 | #undef JS_PROTO |
2730 | }; |
2731 | |
2732 | JS_END_EXTERN_C |
2733 | |
2734 | JSBool |
2735 | js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, |
2736 | JSObject **objp) |
2737 | { |
2738 | JSBool ok; |
2739 | JSObject *tmp, *cobj; |
2740 | JSResolvingKey rkey; |
2741 | JSResolvingEntry *rentry; |
2742 | uint32 generation; |
2743 | JSObjectOp init; |
2744 | jsval v; |
2745 | |
2746 | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) |
2747 | obj = tmp; |
2748 | if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { |
2749 | *objp = NULL; |
2750 | return JS_TRUE; |
2751 | } |
2752 | |
2753 | ok = JS_GetReservedSlot(cx, obj, key, &v); |
2754 | if (!ok) |
2755 | return JS_FALSE; |
2756 | if (!JSVAL_IS_PRIMITIVE(v)) { |
2757 | *objp = JSVAL_TO_OBJECT(v); |
2758 | return JS_TRUE; |
2759 | } |
2760 | |
2761 | rkey.obj = obj; |
2762 | rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); |
2763 | if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) |
2764 | return JS_FALSE; |
2765 | if (!rentry) { |
2766 | /* Already caching key in obj -- suppress recursion. */ |
2767 | *objp = NULL; |
2768 | return JS_TRUE; |
2769 | } |
2770 | generation = cx->resolvingTable->generation; |
2771 | |
2772 | cobj = NULL; |
2773 | init = lazy_prototype_init[key]; |
2774 | if (init) { |
2775 | if (!init(cx, obj)) { |
2776 | ok = JS_FALSE; |
2777 | } else { |
2778 | ok = JS_GetReservedSlot(cx, obj, key, &v); |
2779 | if (ok && !JSVAL_IS_PRIMITIVE(v)) |
2780 | cobj = JSVAL_TO_OBJECT(v); |
2781 | } |
2782 | } |
2783 | |
2784 | js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); |
2785 | *objp = cobj; |
2786 | return ok; |
2787 | } |
2788 | |
2789 | JSBool |
2790 | js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) |
2791 | { |
2792 | JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); |
2793 | if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) |
2794 | return JS_TRUE; |
2795 | |
2796 | return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); |
2797 | } |
2798 | |
2799 | JSBool |
2800 | js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) |
2801 | { |
2802 | JSObject *obj, *cobj, *pobj; |
2803 | JSProtoKey key; |
2804 | JSProperty *prop; |
2805 | jsval v; |
2806 | JSScopeProperty *sprop; |
2807 | |
2808 | if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { |
2809 | /* Find the topmost object in the scope chain. */ |
2810 | do { |
2811 | obj = start; |
2812 | start = OBJ_GET_PARENT(cx, obj); |
2813 | } while (start); |
2814 | } else { |
2815 | obj = cx->globalObject; |
2816 | if (!obj) { |
2817 | *vp = JSVAL_VOID; |
2818 | return JS_TRUE; |
2819 | } |
2820 | } |
2821 | |
2822 | OBJ_TO_INNER_OBJECT(cx, obj); |
2823 | if (!obj) |
2824 | return JS_FALSE; |
2825 | |
2826 | if (JSID_IS_INT(id)) { |
2827 | key = (JSProtoKey) JSID_TO_INT(id); |
2828 | JS_ASSERT(key != JSProto_Null); |
2829 | if (!js_GetClassObject(cx, obj, key, &cobj)) |
2830 | return JS_FALSE; |
2831 | if (cobj) { |
2832 | *vp = OBJECT_TO_JSVAL(cobj); |
2833 | return JS_TRUE; |
2834 | } |
2835 | id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); |
2836 | } |
2837 | |
2838 | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
2839 | if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, |
2840 | &pobj, &prop) < 0) { |
2841 | return JS_FALSE; |
2842 | } |
2843 | v = JSVAL_VOID; |
2844 | if (prop) { |
2845 | if (OBJ_IS_NATIVE(pobj)) { |
2846 | sprop = (JSScopeProperty *) prop; |
2847 | if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) { |
2848 | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); |
2849 | if (JSVAL_IS_PRIMITIVE(v)) |
2850 | v = JSVAL_VOID; |
2851 | } |
2852 | } |
2853 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
2854 | } |
2855 | *vp = v; |
2856 | return JS_TRUE; |
2857 | } |
2858 | |
2859 | JSObject * |
2860 | js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, |
2861 | JSObject *parent, uintN argc, jsval *argv) |
2862 | { |
2863 | jsid id; |
2864 | jsval cval, rval; |
2865 | JSTempValueRooter argtvr, tvr; |
2866 | JSObject *obj, *ctor; |
2867 | |
2868 | JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); |
2869 | |
2870 | if (!js_GetClassId(cx, clasp, &id) || |
2871 | !js_FindClassObject(cx, parent, id, &cval)) { |
2872 | JS_POP_TEMP_ROOT(cx, &argtvr); |
2873 | return NULL; |
2874 | } |
2875 | |
2876 | if (JSVAL_IS_PRIMITIVE(cval)) { |
2877 | js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); |
2878 | JS_POP_TEMP_ROOT(cx, &argtvr); |
2879 | return NULL; |
2880 | } |
2881 | |
2882 | /* |
2883 | * Protect cval in case a crazy getter for .prototype uproots it. After |
2884 | * this point, all control flow must exit through label out with obj set. |
2885 | */ |
2886 | JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); |
2887 | MUST_FLOW_THROUGH("out"); |
2888 | |
2889 | /* |
2890 | * If proto or parent are NULL, set them to Constructor.prototype and/or |
2891 | * Constructor.__parent__, just like JSOP_NEW does. |
2892 | */ |
2893 | ctor = JSVAL_TO_OBJECT(cval); |
2894 | if (!parent) |
2895 | parent = OBJ_GET_PARENT(cx, ctor); |
2896 | if (!proto) { |
2897 | if (!OBJ_GET_PROPERTY(cx, ctor, |
2898 | ATOM_TO_JSID(cx->runtime->atomState |
2899 | .classPrototypeAtom), |
2900 | &rval)) { |
2901 | obj = NULL; |
2902 | goto out; |
2903 | } |
2904 | if (JSVAL_IS_OBJECT(rval)) |
2905 | proto = JSVAL_TO_OBJECT(rval); |
2906 | } |
2907 | |
2908 | obj = js_NewObject(cx, clasp, proto, parent, 0); |
2909 | if (!obj) |
2910 | goto out; |
2911 | |
2912 | if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) |
2913 | goto bad; |
2914 | |
2915 | if (JSVAL_IS_PRIMITIVE(rval)) |
2916 | goto out; |
2917 | obj = JSVAL_TO_OBJECT(rval); |
2918 | |
2919 | /* |
2920 | * If the instance's class differs from what was requested, throw a type |
2921 | * error. If the given class has both the JSCLASS_HAS_PRIVATE and the |
2922 | * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its |
2923 | * private data set at this point, then the constructor was replaced and |
2924 | * we should throw a type error. |
2925 | */ |
2926 | if (OBJ_GET_CLASS(cx, obj) != clasp || |
2927 | (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | |
2928 | JSCLASS_CONSTRUCT_PROTOTYPE)) && |
2929 | !JS_GetPrivate(cx, obj))) { |
2930 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
2931 | JSMSG_WRONG_CONSTRUCTOR, clasp->name); |
2932 | goto bad; |
2933 | } |
2934 | |
2935 | out: |
2936 | JS_POP_TEMP_ROOT(cx, &tvr); |
2937 | JS_POP_TEMP_ROOT(cx, &argtvr); |
2938 | return obj; |
2939 | |
2940 | bad: |
2941 | cx->weakRoots.newborn[GCX_OBJECT] = NULL; |
2942 | obj = NULL; |
2943 | goto out; |
2944 | } |
2945 | |
2946 | void |
2947 | js_FinalizeObject(JSContext *cx, JSObject *obj) |
2948 | { |
2949 | JSObjectMap *map; |
2950 | |
2951 | /* Cope with stillborn objects that have no map. */ |
2952 | map = obj->map; |
2953 | if (!map) |
2954 | return; |
2955 | |
2956 | if (cx->debugHooks->objectHook) { |
2957 | cx->debugHooks->objectHook(cx, obj, JS_FALSE, |
2958 | cx->debugHooks->objectHookData); |
2959 | } |
2960 | |
2961 | /* Finalize obj first, in case it needs map and slots. */ |
2962 | STOBJ_GET_CLASS(obj)->finalize(cx, obj); |
2963 | |
2964 | #ifdef INCLUDE_MOZILLA_DTRACE |
2965 | if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) |
2966 | jsdtrace_object_finalize(obj); |
2967 | #endif |
2968 | |
2969 | /* Drop map and free slots. */ |
2970 | js_DropObjectMap(cx, map, obj); |
2971 | FreeSlots(cx, obj); |
2972 | } |
2973 | |
2974 | /* XXXbe if one adds props, deletes earlier props, adds more, the last added |
2975 | won't recycle the deleted props' slots. */ |
2976 | JSBool |
2977 | js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) |
2978 | { |
2979 | JSObjectMap *map; |
2980 | JSClass *clasp; |
2981 | |
2982 | map = obj->map; |
2983 | JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); |
2984 | clasp = LOCKED_OBJ_GET_CLASS(obj); |
2985 | if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { |
2986 | /* Adjust map->freeslot to include computed reserved slots, if any. */ |
2987 | map->freeslot += clasp->reserveSlots(cx, obj); |
2988 | } |
2989 | |
2990 | if (map->freeslot >= STOBJ_NSLOTS(obj) && |
2991 | !js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) { |
2992 | return JS_FALSE; |
2993 | } |
2994 | |
2995 | /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ |
2996 | JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot))); |
2997 | *slotp = map->freeslot++; |
2998 | return JS_TRUE; |
2999 | } |
3000 | |
3001 | void |
3002 | js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) |
3003 | { |
3004 | JSObjectMap *map; |
3005 | |
3006 | map = obj->map; |
3007 | JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); |
3008 | LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); |
3009 | if (map->freeslot == slot + 1) { |
3010 | map->freeslot = slot; |
3011 | |
3012 | /* When shrinking, js_ReallocSlots always returns true. */ |
3013 | js_ReallocSlots(cx, obj, slot, JS_FALSE); |
3014 | } |
3015 | } |
3016 | |
3017 | jsid |
3018 | js_CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, |
3019 | JSBool negative) |
3020 | { |
3021 | jsuint index = JS7_UNDEC(*cp++); |
3022 | jsuint oldIndex = 0; |
3023 | jsuint c = 0; |
3024 | |
3025 | if (index != 0) { |
3026 | while (JS7_ISDEC(*cp)) { |
3027 | oldIndex = index; |
3028 | c = JS7_UNDEC(*cp); |
3029 | index = 10 * index + c; |
3030 | cp++; |
3031 | } |
3032 | } |
3033 | if (cp == end && |
3034 | (oldIndex < (JSVAL_INT_MAX / 10) || |
3035 | (oldIndex == (JSVAL_INT_MAX / 10) && |
3036 | c <= (JSVAL_INT_MAX % 10)))) { |
3037 | if (negative) |
3038 | index = 0 - index; |
3039 | id = INT_TO_JSID((jsint)index); |
3040 | } |
3041 | return id; |
3042 | } |
3043 | |
3044 | static JSBool |
3045 | PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id) |
3046 | { |
3047 | JSScope *scope; |
3048 | JSScopeProperty *sprop; |
3049 | |
3050 | while (obj) { |
3051 | if (!OBJ_IS_NATIVE(obj)) { |
3052 | obj = OBJ_GET_PROTO(cx, obj); |
3053 | continue; |
3054 | } |
3055 | JS_LOCK_OBJ(cx, obj); |
3056 | scope = OBJ_SCOPE(obj); |
3057 | sprop = SCOPE_GET_PROPERTY(scope, id); |
3058 | if (sprop) { |
3059 | PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); |
3060 | SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
3061 | JS_UNLOCK_SCOPE(cx, scope); |
3062 | return JS_TRUE; |
3063 | } |
3064 | obj = LOCKED_OBJ_GET_PROTO(scope->object); |
3065 | JS_UNLOCK_SCOPE(cx, scope); |
3066 | } |
3067 | return JS_FALSE; |
3068 | } |
3069 | |
3070 | static void |
3071 | PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) |
3072 | { |
3073 | if (!OBJ_IS_DELEGATE(cx, obj)) |
3074 | return; |
3075 | |
3076 | PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id); |
3077 | while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { |
3078 | if (PurgeProtoChain(cx, obj, id)) |
3079 | return; |
3080 | } |
3081 | } |
3082 | |
3083 | JSScopeProperty * |
3084 | js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, |
3085 | JSPropertyOp getter, JSPropertyOp setter, uint32 slot, |
3086 | uintN attrs, uintN flags, intN shortid) |
3087 | { |
3088 | JSScope *scope; |
3089 | JSScopeProperty *sprop; |
3090 | |
3091 | /* |
3092 | * Purge the property cache of now-shadowed id in obj's scope chain. Do |
3093 | * this optimistically (assuming no failure below) before locking obj, so |
3094 | * we can lock the shadowed scope. |
3095 | */ |
3096 | PurgeScopeChain(cx, obj, id); |
3097 | |
3098 | JS_LOCK_OBJ(cx, obj); |
3099 | scope = js_GetMutableScope(cx, obj); |
3100 | if (!scope) { |
3101 | sprop = NULL; |
3102 | } else { |
3103 | /* Convert string indices to integers if appropriate. */ |
3104 | CHECK_FOR_STRING_INDEX(id); |
3105 | sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, |
3106 | flags, shortid); |
3107 | } |
3108 | JS_UNLOCK_OBJ(cx, obj); |
3109 | return sprop; |
3110 | } |
3111 | |
3112 | JSScopeProperty * |
3113 | js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, |
3114 | JSScopeProperty *sprop, uintN attrs, uintN mask, |
3115 | JSPropertyOp getter, JSPropertyOp setter) |
3116 | { |
3117 | JSScope *scope; |
3118 | |
3119 | JS_LOCK_OBJ(cx, obj); |
3120 | scope = js_GetMutableScope(cx, obj); |
3121 | if (!scope) { |
3122 | sprop = NULL; |
3123 | } else { |
3124 | sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, |
3125 | getter, setter); |
3126 | } |
3127 | JS_UNLOCK_OBJ(cx, obj); |
3128 | return sprop; |
3129 | } |
3130 | |
3131 | JSBool |
3132 | js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
3133 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
3134 | JSProperty **propp) |
3135 | { |
3136 | return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, |
3137 | 0, 0, propp); |
3138 | } |
3139 | |
3140 | /* |
3141 | * Backward compatibility requires allowing addProperty hooks to mutate the |
3142 | * nominal initial value of a slot-full property, while GC safety wants that |
3143 | * value to be stored before the call-out through the hook. Optimize to do |
3144 | * both while saving cycles for classes that stub their addProperty hook. |
3145 | */ |
3146 | #define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ |
3147 | JS_BEGIN_MACRO \ |
3148 | if ((clasp)->addProperty != JS_PropertyStub) { \ |
3149 | jsval nominal_ = *(vp); \ |
3150 | if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ |
3151 | cleanup; \ |
3152 | } \ |
3153 | if (*(vp) != nominal_) { \ |
3154 | if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ |
3155 | LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *(vp)); \ |
3156 | } \ |
3157 | } \ |
3158 | JS_END_MACRO |
3159 | |
3160 | JSBool |
3161 | js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
3162 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
3163 | uintN flags, intN shortid, JSProperty **propp) |
3164 | { |
3165 | JSClass *clasp; |
3166 | JSScope *scope; |
3167 | JSScopeProperty *sprop; |
3168 | |
3169 | /* Convert string indices to integers if appropriate. */ |
3170 | CHECK_FOR_STRING_INDEX(id); |
3171 | |
3172 | #if JS_HAS_GETTER_SETTER |
3173 | /* |
3174 | * If defining a getter or setter, we must check for its counterpart and |
3175 | * update the attributes and property ops. A getter or setter is really |
3176 | * only half of a property. |
3177 | */ |
3178 | sprop = NULL; |
3179 | if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { |
3180 | JSObject *pobj; |
3181 | JSProperty *prop; |
3182 | |
3183 | /* |
3184 | * If JS_THREADSAFE and id is found, js_LookupProperty returns with |
3185 | * sprop non-null and pobj locked. If pobj == obj, the property is |
3186 | * already in obj and obj has its own (mutable) scope. So if we are |
3187 | * defining a getter whose setter was already defined, or vice versa, |
3188 | * finish the job via js_ChangeScopePropertyAttributes, and refresh |
3189 | * the property cache line for (obj, id) to map sprop. |
3190 | */ |
3191 | if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) |
3192 | return JS_FALSE; |
3193 | sprop = (JSScopeProperty *) prop; |
3194 | if (sprop && |
3195 | pobj == obj && |
3196 | (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
3197 | sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, |
3198 | attrs, sprop->attrs, |
3199 | (attrs & JSPROP_GETTER) |
3200 | ? getter |
3201 | : sprop->getter, |
3202 | (attrs & JSPROP_SETTER) |
3203 | ? setter |
3204 | : sprop->setter); |
3205 | |
3206 | /* NB: obj == pobj, so we can share unlock code at the bottom. */ |
3207 | if (!sprop) |
3208 | goto bad; |
3209 | } else if (prop) { |
3210 | /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ |
3211 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
3212 | prop = NULL; |
3213 | sprop = NULL; |
3214 | } |
3215 | } |
3216 | #endif /* JS_HAS_GETTER_SETTER */ |
3217 | |
3218 | /* |
3219 | * Purge the property cache of now-shadowed id in obj's scope chain. |
3220 | * Do this early, before locking obj to avoid nesting locks. |
3221 | */ |
3222 | PurgeScopeChain(cx, obj, id); |
3223 | |
3224 | /* Lock if object locking is required by this implementation. */ |
3225 | JS_LOCK_OBJ(cx, obj); |
3226 | |
3227 | /* Use the object's class getter and setter by default. */ |
3228 | clasp = LOCKED_OBJ_GET_CLASS(obj); |
3229 | if (!getter) |
3230 | getter = clasp->getProperty; |
3231 | if (!setter) |
3232 | setter = clasp->setProperty; |
3233 | |
3234 | /* Get obj's own scope if it has one, or create a new one for obj. */ |
3235 | scope = js_GetMutableScope(cx, obj); |
3236 | if (!scope) |
3237 | goto bad; |
3238 | |
3239 | if (!sprop) { |
3240 | /* Add a new property, or replace an existing one of the same id. */ |
3241 | if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) |
3242 | attrs |= JSPROP_SHARED; |
3243 | sprop = js_AddScopeProperty(cx, scope, id, getter, setter, |
3244 | SPROP_INVALID_SLOT, attrs, flags, |
3245 | shortid); |
3246 | if (!sprop) |
3247 | goto bad; |
3248 | } |
3249 | |
3250 | /* Store value before calling addProperty, in case the latter GC's. */ |
3251 | if (SPROP_HAS_VALID_SLOT(sprop, scope)) |
3252 | LOCKED_OBJ_WRITE_BARRIER(cx, obj, sprop->slot, value); |
3253 | |
3254 | /* XXXbe called with lock held */ |
3255 | ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, |
3256 | js_RemoveScopeProperty(cx, scope, id); |
3257 | goto bad); |
3258 | |
3259 | if (propp) |
3260 | *propp = (JSProperty *) sprop; |
3261 | else |
3262 | JS_UNLOCK_OBJ(cx, obj); |
3263 | return JS_TRUE; |
3264 | |
3265 | bad: |
3266 | JS_UNLOCK_OBJ(cx, obj); |
3267 | return JS_FALSE; |
3268 | } |
3269 | |
3270 | /* |
3271 | * Given pc pointing after a property accessing bytecode, return true if the |
3272 | * access is "object-detecting" in the sense used by web scripts, e.g., when |
3273 | * checking whether document.all is defined. |
3274 | */ |
3275 | static JSBool |
3276 | Detecting(JSContext *cx, jsbytecode *pc) |
3277 | { |
3278 | JSScript *script; |
3279 | jsbytecode *endpc; |
3280 | JSOp op; |
3281 | JSAtom *atom; |
3282 | |
3283 | if (!cx->fp) |
3284 | return JS_FALSE; |
3285 | script = cx->fp->script; |
3286 | for (endpc = script->code + script->length; |
3287 | pc < endpc; |
3288 | pc += js_CodeSpec[op].length) { |
3289 | /* General case: a branch or equality op follows the access. */ |
3290 | op = (JSOp) *pc; |
3291 | if (js_CodeSpec[op].format & JOF_DETECTING) |
3292 | return JS_TRUE; |
3293 | |
3294 | switch (op) { |
3295 | case JSOP_NULL: |
3296 | /* |
3297 | * Special case #1: handle (document.all == null). Don't sweat |
3298 | * about JS1.2's revision of the equality operators here. |
3299 | */ |
3300 | if (++pc < endpc) |
3301 | return *pc == JSOP_EQ || *pc == JSOP_NE; |
3302 | return JS_FALSE; |
3303 | |
3304 | case JSOP_NAME: |
3305 | /* |
3306 | * Special case #2: handle (document.all == undefined). Don't |
3307 | * worry about someone redefining undefined, which was added by |
3308 | * Edition 3, so is read/write for backward compatibility. |
3309 | */ |
3310 | GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); |
3311 | if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && |
3312 | (pc += js_CodeSpec[op].length) < endpc) { |
3313 | op = (JSOp) *pc; |
3314 | return op == JSOP_EQ || op == JSOP_NE || |
3315 | op == JSOP_STRICTEQ || op == JSOP_STRICTNE; |
3316 | } |
3317 | return JS_FALSE; |
3318 | |
3319 | default: |
3320 | /* |
3321 | * At this point, anything but an extended atom index prefix means |
3322 | * we're not detecting. |