Parent Directory
|
Revision Log
Upgrade to SpiderMonkey from Firefox 3.5.3.
1 | siliconforks | 332 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | siliconforks | 460 | * vim: set ts=8 sw=4 et tw=79: |
3 | siliconforks | 332 | * |
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 | siliconforks | 399 | #include "jsbuiltins.h" |
59 | siliconforks | 332 | #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 | siliconforks | 460 | #include "jsstaticcheck.h" |
73 | siliconforks | 332 | #include "jsstr.h" |
74 | siliconforks | 460 | #include "jstracer.h" |
75 | #include "jsdbgapi.h" | ||
76 | siliconforks | 332 | |
77 | #if JS_HAS_GENERATORS | ||
78 | #include "jsiter.h" | ||
79 | #endif | ||
80 | |||
81 | #if JS_HAS_XML_SUPPORT | ||
82 | #include "jsxml.h" | ||
83 | #endif | ||
84 | |||
85 | #if JS_HAS_XDR | ||
86 | #include "jsxdrapi.h" | ||
87 | #endif | ||
88 | |||
89 | #ifdef INCLUDE_MOZILLA_DTRACE | ||
90 | #include "jsdtracef.h" | ||
91 | #endif | ||
92 | |||
93 | #include "jsautooplen.h" | ||
94 | |||
95 | #ifdef JS_THREADSAFE | ||
96 | #define NATIVE_DROP_PROPERTY js_DropProperty | ||
97 | |||
98 | extern void | ||
99 | js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); | ||
100 | #else | ||
101 | #define NATIVE_DROP_PROPERTY NULL | ||
102 | #endif | ||
103 | |||
104 | JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { | ||
105 | siliconforks | 460 | NULL, |
106 | siliconforks | 332 | js_LookupProperty, js_DefineProperty, |
107 | js_GetProperty, js_SetProperty, | ||
108 | js_GetAttributes, js_SetAttributes, | ||
109 | js_DeleteProperty, js_DefaultValue, | ||
110 | js_Enumerate, js_CheckAccess, | ||
111 | NULL, NATIVE_DROP_PROPERTY, | ||
112 | js_Call, js_Construct, | ||
113 | siliconforks | 460 | js_HasInstance, js_TraceObject, |
114 | js_Clear, js_GetRequiredSlot, | ||
115 | js_SetRequiredSlot | ||
116 | siliconforks | 332 | }; |
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 | |||
173 | slot = (uint32) JSVAL_TO_INT(id); | ||
174 | if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { | ||
175 | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); | ||
176 | mode = JSACC_PROTO; | ||
177 | } else { | ||
178 | propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); | ||
179 | mode = JSACC_PARENT; | ||
180 | } | ||
181 | |||
182 | /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ | ||
183 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) | ||
184 | return JS_FALSE; | ||
185 | |||
186 | pobj = JSVAL_TO_OBJECT(*vp); | ||
187 | if (pobj) { | ||
188 | clasp = OBJ_GET_CLASS(cx, pobj); | ||
189 | if (clasp == &js_CallClass || clasp == &js_BlockClass) { | ||
190 | /* Censor activations and lexical scopes per ECMA-262. */ | ||
191 | *vp = JSVAL_NULL; | ||
192 | siliconforks | 460 | } else { |
193 | /* | ||
194 | * DeclEnv only exists as a parent for a Call object which we | ||
195 | * censor. So it cannot escape to scripts. | ||
196 | */ | ||
197 | JS_ASSERT(clasp != &js_DeclEnvClass); | ||
198 | if (pobj->map->ops->thisObject) { | ||
199 | pobj = pobj->map->ops->thisObject(cx, pobj); | ||
200 | siliconforks | 332 | if (!pobj) |
201 | return JS_FALSE; | ||
202 | *vp = OBJECT_TO_JSVAL(pobj); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | return JS_TRUE; | ||
207 | } | ||
208 | |||
209 | static JSBool | ||
210 | obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
211 | { | ||
212 | JSObject *pobj; | ||
213 | uint32 slot; | ||
214 | jsid propid; | ||
215 | uintN attrs; | ||
216 | |||
217 | if (!JSVAL_IS_OBJECT(*vp)) | ||
218 | return JS_TRUE; | ||
219 | pobj = JSVAL_TO_OBJECT(*vp); | ||
220 | |||
221 | if (pobj) { | ||
222 | /* | ||
223 | * Innerize pobj here to avoid sticking unwanted properties on the | ||
224 | * outer object. This ensures that any with statements only grant | ||
225 | * access to the inner object. | ||
226 | */ | ||
227 | OBJ_TO_INNER_OBJECT(cx, pobj); | ||
228 | if (!pobj) | ||
229 | return JS_FALSE; | ||
230 | } | ||
231 | slot = (uint32) JSVAL_TO_INT(id); | ||
232 | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) | ||
233 | return JS_FALSE; | ||
234 | |||
235 | /* __parent__ is readonly and permanent, only __proto__ may be set. */ | ||
236 | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); | ||
237 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, | ||
238 | (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, | ||
239 | &attrs)) { | ||
240 | return JS_FALSE; | ||
241 | } | ||
242 | |||
243 | siliconforks | 460 | return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE); |
244 | siliconforks | 332 | } |
245 | |||
246 | static JSBool | ||
247 | obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
248 | { | ||
249 | jsval iter_state; | ||
250 | jsid num_properties; | ||
251 | JSBool ok; | ||
252 | |||
253 | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) | ||
254 | return JS_FALSE; | ||
255 | |||
256 | /* Get the number of properties to enumerate. */ | ||
257 | iter_state = JSVAL_NULL; | ||
258 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); | ||
259 | if (!ok) | ||
260 | goto out; | ||
261 | |||
262 | if (!JSVAL_IS_INT(num_properties)) { | ||
263 | JS_ASSERT(0); | ||
264 | *vp = JSVAL_ZERO; | ||
265 | goto out; | ||
266 | } | ||
267 | *vp = num_properties; | ||
268 | |||
269 | out: | ||
270 | if (iter_state != JSVAL_NULL) | ||
271 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); | ||
272 | return ok; | ||
273 | } | ||
274 | |||
275 | #else /* !JS_HAS_OBJ_PROTO_PROP */ | ||
276 | |||
277 | #define object_props NULL | ||
278 | |||
279 | #endif /* !JS_HAS_OBJ_PROTO_PROP */ | ||
280 | |||
281 | JSBool | ||
282 | siliconforks | 460 | js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj, |
283 | JSBool checkForCycles) | ||
284 | siliconforks | 332 | { |
285 | siliconforks | 460 | JS_ASSERT(slot == JSSLOT_PARENT || slot == JSSLOT_PROTO); |
286 | JS_ASSERT_IF(!checkForCycles, obj != pobj); | ||
287 | siliconforks | 332 | |
288 | siliconforks | 460 | if (slot == JSSLOT_PROTO) { |
289 | if (OBJ_IS_NATIVE(obj)) { | ||
290 | JS_LOCK_OBJ(cx, obj); | ||
291 | bool ok = !!js_GetMutableScope(cx, obj); | ||
292 | siliconforks | 332 | JS_UNLOCK_OBJ(cx, obj); |
293 | siliconforks | 460 | if (!ok) |
294 | return JS_FALSE; | ||
295 | siliconforks | 332 | } |
296 | siliconforks | 460 | |
297 | /* | ||
298 | * Regenerate property cache shape ids for all of the scopes along the | ||
299 | * old prototype chain to invalidate their property cache entries, in | ||
300 | * case any entries were filled by looking up starting from obj. | ||
301 | */ | ||
302 | JSObject *oldproto = obj; | ||
303 | while (oldproto && OBJ_IS_NATIVE(oldproto)) { | ||
304 | JS_LOCK_OBJ(cx, oldproto); | ||
305 | JSScope *scope = OBJ_SCOPE(oldproto); | ||
306 | js_MakeScopeShapeUnique(cx, scope); | ||
307 | JSObject *tmp = STOBJ_GET_PROTO(scope->object); | ||
308 | JS_UNLOCK_OBJ(cx, oldproto); | ||
309 | oldproto = tmp; | ||
310 | } | ||
311 | siliconforks | 332 | } |
312 | |||
313 | siliconforks | 460 | if (!pobj || !checkForCycles) { |
314 | if (slot == JSSLOT_PROTO) | ||
315 | STOBJ_SET_PROTO(obj, pobj); | ||
316 | else | ||
317 | STOBJ_SET_PARENT(obj, pobj); | ||
318 | } else { | ||
319 | /* | ||
320 | * Use the GC machinery to serialize access to all objects on the | ||
321 | * prototype or parent chain. | ||
322 | */ | ||
323 | JSSetSlotRequest ssr; | ||
324 | ssr.obj = obj; | ||
325 | ssr.pobj = pobj; | ||
326 | ssr.slot = (uint16) slot; | ||
327 | ssr.cycle = false; | ||
328 | siliconforks | 332 | |
329 | siliconforks | 460 | JSRuntime *rt = cx->runtime; |
330 | siliconforks | 332 | JS_LOCK_GC(rt); |
331 | siliconforks | 460 | ssr.next = rt->setSlotRequests; |
332 | rt->setSlotRequests = &ssr; | ||
333 | for (;;) { | ||
334 | js_GC(cx, GC_SET_SLOT_REQUEST); | ||
335 | JS_UNLOCK_GC(rt); | ||
336 | if (!rt->setSlotRequests) | ||
337 | break; | ||
338 | JS_LOCK_GC(rt); | ||
339 | } | ||
340 | siliconforks | 332 | |
341 | siliconforks | 460 | if (ssr.cycle) { |
342 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
343 | JSMSG_CYCLIC_VALUE, | ||
344 | siliconforks | 332 | #if JS_HAS_OBJ_PROTO_PROP |
345 | object_props[slot].name | ||
346 | #else | ||
347 | (slot == JSSLOT_PROTO) ? js_proto_str | ||
348 | : js_parent_str | ||
349 | #endif | ||
350 | ); | ||
351 | siliconforks | 460 | return JS_FALSE; |
352 | siliconforks | 332 | } |
353 | } | ||
354 | return JS_TRUE; | ||
355 | } | ||
356 | |||
357 | static JSHashNumber | ||
358 | js_hash_object(const void *key) | ||
359 | { | ||
360 | return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; | ||
361 | } | ||
362 | |||
363 | static JSHashEntry * | ||
364 | MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) | ||
365 | { | ||
366 | JSSharpObjectMap *map; | ||
367 | JSHashTable *table; | ||
368 | JSHashNumber hash; | ||
369 | JSHashEntry **hep, *he; | ||
370 | jsatomid sharpid; | ||
371 | JSIdArray *ida; | ||
372 | JSBool ok; | ||
373 | jsint i, length; | ||
374 | jsid id; | ||
375 | #if JS_HAS_GETTER_SETTER | ||
376 | JSObject *obj2; | ||
377 | JSProperty *prop; | ||
378 | uintN attrs; | ||
379 | #endif | ||
380 | jsval val; | ||
381 | |||
382 | JS_CHECK_RECURSION(cx, return NULL); | ||
383 | |||
384 | map = &cx->sharpObjectMap; | ||
385 | siliconforks | 460 | JS_ASSERT(map->depth >= 1); |
386 | siliconforks | 332 | table = map->table; |
387 | hash = js_hash_object(obj); | ||
388 | hep = JS_HashTableRawLookup(table, hash, obj); | ||
389 | he = *hep; | ||
390 | if (!he) { | ||
391 | sharpid = 0; | ||
392 | he = JS_HashTableRawAdd(table, hep, hash, obj, | ||
393 | JS_UINT32_TO_PTR(sharpid)); | ||
394 | if (!he) { | ||
395 | JS_ReportOutOfMemory(cx); | ||
396 | return NULL; | ||
397 | } | ||
398 | |||
399 | ida = JS_Enumerate(cx, obj); | ||
400 | if (!ida) | ||
401 | return NULL; | ||
402 | |||
403 | ok = JS_TRUE; | ||
404 | for (i = 0, length = ida->length; i < length; i++) { | ||
405 | id = ida->vector[i]; | ||
406 | #if JS_HAS_GETTER_SETTER | ||
407 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); | ||
408 | if (!ok) | ||
409 | break; | ||
410 | if (!prop) | ||
411 | continue; | ||
412 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); | ||
413 | if (ok) { | ||
414 | if (OBJ_IS_NATIVE(obj2) && | ||
415 | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { | ||
416 | siliconforks | 460 | JSScopeProperty *sprop = (JSScopeProperty *) prop; |
417 | siliconforks | 332 | val = JSVAL_NULL; |
418 | if (attrs & JSPROP_GETTER) | ||
419 | siliconforks | 460 | val = js_CastAsObjectJSVal(sprop->getter); |
420 | siliconforks | 332 | if (attrs & JSPROP_SETTER) { |
421 | if (val != JSVAL_NULL) { | ||
422 | /* Mark the getter, then set val to setter. */ | ||
423 | ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), | ||
424 | NULL) | ||
425 | != NULL); | ||
426 | } | ||
427 | siliconforks | 460 | val = js_CastAsObjectJSVal(sprop->setter); |
428 | siliconforks | 332 | } |
429 | } else { | ||
430 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); | ||
431 | } | ||
432 | } | ||
433 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
434 | #else | ||
435 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); | ||
436 | #endif | ||
437 | if (!ok) | ||
438 | break; | ||
439 | if (!JSVAL_IS_PRIMITIVE(val) && | ||
440 | !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { | ||
441 | ok = JS_FALSE; | ||
442 | break; | ||
443 | } | ||
444 | } | ||
445 | if (!ok || !idap) | ||
446 | JS_DestroyIdArray(cx, ida); | ||
447 | if (!ok) | ||
448 | return NULL; | ||
449 | } else { | ||
450 | sharpid = JS_PTR_TO_UINT32(he->value); | ||
451 | if (sharpid == 0) { | ||
452 | sharpid = ++map->sharpgen << SHARP_ID_SHIFT; | ||
453 | he->value = JS_UINT32_TO_PTR(sharpid); | ||
454 | } | ||
455 | ida = NULL; | ||
456 | } | ||
457 | if (idap) | ||
458 | *idap = ida; | ||
459 | return he; | ||
460 | } | ||
461 | |||
462 | JSHashEntry * | ||
463 | js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, | ||
464 | jschar **sp) | ||
465 | { | ||
466 | JSSharpObjectMap *map; | ||
467 | JSHashTable *table; | ||
468 | JSIdArray *ida; | ||
469 | JSHashNumber hash; | ||
470 | JSHashEntry *he, **hep; | ||
471 | jsatomid sharpid; | ||
472 | char buf[20]; | ||
473 | size_t len; | ||
474 | |||
475 | siliconforks | 460 | if (!JS_CHECK_OPERATION_LIMIT(cx)) |
476 | siliconforks | 332 | return NULL; |
477 | |||
478 | /* Set to null in case we return an early error. */ | ||
479 | *sp = NULL; | ||
480 | map = &cx->sharpObjectMap; | ||
481 | table = map->table; | ||
482 | if (!table) { | ||
483 | table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, | ||
484 | JS_CompareValues, NULL, NULL); | ||
485 | if (!table) { | ||
486 | JS_ReportOutOfMemory(cx); | ||
487 | return NULL; | ||
488 | } | ||
489 | map->table = table; | ||
490 | JS_KEEP_ATOMS(cx->runtime); | ||
491 | } | ||
492 | |||
493 | /* From this point the control must flow either through out: or bad:. */ | ||
494 | ida = NULL; | ||
495 | if (map->depth == 0) { | ||
496 | siliconforks | 460 | /* |
497 | * Although MarkSharpObjects tries to avoid invoking getters, | ||
498 | * it ends up doing so anyway under some circumstances; for | ||
499 | * example, if a wrapped object has getters, the wrapper will | ||
500 | * prevent MarkSharpObjects from recognizing them as such. | ||
501 | * This could lead to js_LeaveSharpObject being called while | ||
502 | * MarkSharpObjects is still working. | ||
503 | * | ||
504 | * Increment map->depth while we call MarkSharpObjects, to | ||
505 | * ensure that such a call doesn't free the hash table we're | ||
506 | * still using. | ||
507 | */ | ||
508 | ++map->depth; | ||
509 | siliconforks | 332 | he = MarkSharpObjects(cx, obj, &ida); |
510 | siliconforks | 460 | --map->depth; |
511 | siliconforks | 332 | if (!he) |
512 | goto bad; | ||
513 | JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); | ||
514 | if (!idap) { | ||
515 | JS_DestroyIdArray(cx, ida); | ||
516 | ida = NULL; | ||
517 | } | ||
518 | } else { | ||
519 | hash = js_hash_object(obj); | ||
520 | hep = JS_HashTableRawLookup(table, hash, obj); | ||
521 | he = *hep; | ||
522 | |||
523 | /* | ||
524 | * It's possible that the value of a property has changed from the | ||
525 | * first time the object's properties are traversed (when the property | ||
526 | * ids are entered into the hash table) to the second (when they are | ||
527 | * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not | ||
528 | * idempotent. | ||
529 | */ | ||
530 | if (!he) { | ||
531 | he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); | ||
532 | if (!he) { | ||
533 | JS_ReportOutOfMemory(cx); | ||
534 | goto bad; | ||
535 | } | ||
536 | sharpid = 0; | ||
537 | goto out; | ||
538 | } | ||
539 | } | ||
540 | |||
541 | sharpid = JS_PTR_TO_UINT32(he->value); | ||
542 | if (sharpid != 0) { | ||
543 | len = JS_snprintf(buf, sizeof buf, "#%u%c", | ||
544 | sharpid >> SHARP_ID_SHIFT, | ||
545 | (sharpid & SHARP_BIT) ? '#' : '='); | ||
546 | *sp = js_InflateString(cx, buf, &len); | ||
547 | if (!*sp) { | ||
548 | if (ida) | ||
549 | JS_DestroyIdArray(cx, ida); | ||
550 | goto bad; | ||
551 | } | ||
552 | } | ||
553 | |||
554 | out: | ||
555 | JS_ASSERT(he); | ||
556 | if ((sharpid & SHARP_BIT) == 0) { | ||
557 | if (idap && !ida) { | ||
558 | ida = JS_Enumerate(cx, obj); | ||
559 | if (!ida) { | ||
560 | if (*sp) { | ||
561 | JS_free(cx, *sp); | ||
562 | *sp = NULL; | ||
563 | } | ||
564 | goto bad; | ||
565 | } | ||
566 | } | ||
567 | map->depth++; | ||
568 | } | ||
569 | |||
570 | if (idap) | ||
571 | *idap = ida; | ||
572 | return he; | ||
573 | |||
574 | bad: | ||
575 | /* Clean up the sharpObjectMap table on outermost error. */ | ||
576 | if (map->depth == 0) { | ||
577 | JS_UNKEEP_ATOMS(cx->runtime); | ||
578 | map->sharpgen = 0; | ||
579 | JS_HashTableDestroy(map->table); | ||
580 | map->table = NULL; | ||
581 | } | ||
582 | return NULL; | ||
583 | } | ||
584 | |||
585 | void | ||
586 | js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) | ||
587 | { | ||
588 | JSSharpObjectMap *map; | ||
589 | JSIdArray *ida; | ||
590 | |||
591 | map = &cx->sharpObjectMap; | ||
592 | JS_ASSERT(map->depth > 0); | ||
593 | if (--map->depth == 0) { | ||
594 | JS_UNKEEP_ATOMS(cx->runtime); | ||
595 | map->sharpgen = 0; | ||
596 | JS_HashTableDestroy(map->table); | ||
597 | map->table = NULL; | ||
598 | } | ||
599 | if (idap) { | ||
600 | ida = *idap; | ||
601 | if (ida) { | ||
602 | JS_DestroyIdArray(cx, ida); | ||
603 | *idap = NULL; | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | |||
608 | static intN | ||
609 | gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) | ||
610 | { | ||
611 | JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key, | ||
612 | "sharp table entry"); | ||
613 | return JS_DHASH_NEXT; | ||
614 | } | ||
615 | |||
616 | void | ||
617 | js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map) | ||
618 | { | ||
619 | JS_ASSERT(map->depth > 0); | ||
620 | JS_ASSERT(map->table); | ||
621 | |||
622 | /* | ||
623 | * During recursive calls to MarkSharpObjects a non-native object or | ||
624 | * object with a custom getProperty method can potentially return an | ||
625 | * unrooted value or even cut from the object graph an argument of one of | ||
626 | * MarkSharpObjects recursive invocations. So we must protect map->table | ||
627 | * entries against GC. | ||
628 | * | ||
629 | * We can not simply use JSTempValueRooter to mark the obj argument of | ||
630 | * MarkSharpObjects during recursion as we have to protect *all* entries | ||
631 | * in JSSharpObjectMap including those that contains otherwise unreachable | ||
632 | * objects just allocated through custom getProperty. Otherwise newer | ||
633 | * allocations can re-use the address of an object stored in the hashtable | ||
634 | * confusing js_EnterSharpObject. So to address the problem we simply | ||
635 | * mark all objects from map->table. | ||
636 | * | ||
637 | * An alternative "proper" solution is to use JSTempValueRooter in | ||
638 | * MarkSharpObjects with code to remove during finalization entries | ||
639 | * with otherwise unreachable objects. But this is way too complex | ||
640 | * to justify spending efforts. | ||
641 | */ | ||
642 | JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc); | ||
643 | } | ||
644 | |||
645 | #if JS_HAS_TOSOURCE | ||
646 | static JSBool | ||
647 | obj_toSource(JSContext *cx, uintN argc, jsval *vp) | ||
648 | { | ||
649 | JSBool ok, outermost; | ||
650 | JSObject *obj; | ||
651 | JSHashEntry *he; | ||
652 | JSIdArray *ida; | ||
653 | jschar *chars, *ochars, *vsharp; | ||
654 | const jschar *idstrchars, *vchars; | ||
655 | size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; | ||
656 | const char *comma; | ||
657 | jsint i, j, length, valcnt; | ||
658 | jsid id; | ||
659 | #if JS_HAS_GETTER_SETTER | ||
660 | JSObject *obj2; | ||
661 | JSProperty *prop; | ||
662 | uintN attrs; | ||
663 | #endif | ||
664 | jsval *val; | ||
665 | jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; | ||
666 | JSTempValueRooter tvr; | ||
667 | JSString *gsopold[2]; | ||
668 | JSString *gsop[2]; | ||
669 | JSString *idstr, *valstr, *str; | ||
670 | |||
671 | JS_CHECK_RECURSION(cx, return JS_FALSE); | ||
672 | |||
673 | MUST_FLOW_THROUGH("out"); | ||
674 | JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr); | ||
675 | |||
676 | /* If outermost, we need parentheses to be an expression, not a block. */ | ||
677 | outermost = (cx->sharpObjectMap.depth == 0); | ||
678 | obj = JS_THIS_OBJECT(cx, vp); | ||
679 | if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) { | ||
680 | ok = JS_FALSE; | ||
681 | goto out; | ||
682 | } | ||
683 | if (IS_SHARP(he)) { | ||
684 | /* | ||
685 | * We didn't enter -- obj is already "sharp", meaning we've visited it | ||
686 | * already in our depth first search, and therefore chars contains a | ||
687 | * string of the form "#n#". | ||
688 | */ | ||
689 | JS_ASSERT(!ida); | ||
690 | #if JS_HAS_SHARP_VARS | ||
691 | nchars = js_strlen(chars); | ||
692 | #else | ||
693 | chars[0] = '{'; | ||
694 | chars[1] = '}'; | ||
695 | chars[2] = 0; | ||
696 | nchars = 2; | ||
697 | #endif | ||
698 | goto make_string; | ||
699 | } | ||
700 | JS_ASSERT(ida); | ||
701 | ok = JS_TRUE; | ||
702 | |||
703 | if (!chars) { | ||
704 | /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ | ||
705 | chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); | ||
706 | nchars = 0; | ||
707 | if (!chars) | ||
708 | goto error; | ||
709 | if (outermost) | ||
710 | chars[nchars++] = '('; | ||
711 | } else { | ||
712 | /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ | ||
713 | MAKE_SHARP(he); | ||
714 | nchars = js_strlen(chars); | ||
715 | chars = (jschar *) | ||
716 | realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); | ||
717 | if (!chars) { | ||
718 | free(ochars); | ||
719 | goto error; | ||
720 | } | ||
721 | if (outermost) { | ||
722 | /* | ||
723 | * No need for parentheses around the whole shebang, because #n= | ||
724 | * unambiguously begins an object initializer, and never a block | ||
725 | * statement. | ||
726 | */ | ||
727 | outermost = JS_FALSE; | ||
728 | } | ||
729 | } | ||
730 | |||
731 | chars[nchars++] = '{'; | ||
732 | |||
733 | comma = NULL; | ||
734 | |||
735 | /* | ||
736 | * We have four local roots for cooked and raw value GC safety. Hoist the | ||
737 | * "localroot + 2" out of the loop using the val local, which refers to | ||
738 | * the raw (unconverted, "uncooked") values. | ||
739 | */ | ||
740 | val = localroot + 2; | ||
741 | |||
742 | for (i = 0, length = ida->length; i < length; i++) { | ||
743 | JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; | ||
744 | |||
745 | /* Get strings for id and value and GC-root them via vp. */ | ||
746 | id = ida->vector[i]; | ||
747 | |||
748 | #if JS_HAS_GETTER_SETTER | ||
749 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); | ||
750 | if (!ok) | ||
751 | goto error; | ||
752 | #endif | ||
753 | |||
754 | /* | ||
755 | * Convert id to a jsval and then to a string. Decide early whether we | ||
756 | * prefer get/set or old getter/setter syntax. | ||
757 | */ | ||
758 | idstr = js_ValueToString(cx, ID_TO_VALUE(id)); | ||
759 | if (!idstr) { | ||
760 | ok = JS_FALSE; | ||
761 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
762 | goto error; | ||
763 | } | ||
764 | *vp = STRING_TO_JSVAL(idstr); /* local root */ | ||
765 | idIsLexicalIdentifier = js_IsIdentifier(idstr); | ||
766 | needOldStyleGetterSetter = | ||
767 | !idIsLexicalIdentifier || | ||
768 | js_CheckKeyword(JSSTRING_CHARS(idstr), | ||
769 | JSSTRING_LENGTH(idstr)) != TOK_EOF; | ||
770 | |||
771 | #if JS_HAS_GETTER_SETTER | ||
772 | |||
773 | valcnt = 0; | ||
774 | if (prop) { | ||
775 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); | ||
776 | if (!ok) { | ||
777 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
778 | goto error; | ||
779 | } | ||
780 | if (OBJ_IS_NATIVE(obj2) && | ||
781 | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { | ||
782 | siliconforks | 460 | JSScopeProperty *sprop = (JSScopeProperty *) prop; |
783 | siliconforks | 332 | if (attrs & JSPROP_GETTER) { |
784 | siliconforks | 460 | val[valcnt] = js_CastAsObjectJSVal(sprop->getter); |
785 | siliconforks | 332 | gsopold[valcnt] = |
786 | ATOM_TO_STRING(cx->runtime->atomState.getterAtom); | ||
787 | gsop[valcnt] = | ||
788 | ATOM_TO_STRING(cx->runtime->atomState.getAtom); | ||
789 | |||
790 | valcnt++; | ||
791 | } | ||
792 | if (attrs & JSPROP_SETTER) { | ||
793 | siliconforks | 460 | val[valcnt] = js_CastAsObjectJSVal(sprop->setter); |
794 | siliconforks | 332 | gsopold[valcnt] = |
795 | ATOM_TO_STRING(cx->runtime->atomState.setterAtom); | ||
796 | gsop[valcnt] = | ||
797 | ATOM_TO_STRING(cx->runtime->atomState.setAtom); | ||
798 | |||
799 | valcnt++; | ||
800 | } | ||
801 | } else { | ||
802 | valcnt = 1; | ||
803 | gsop[0] = NULL; | ||
804 | gsopold[0] = NULL; | ||
805 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); | ||
806 | } | ||
807 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
808 | } | ||
809 | |||
810 | #else /* !JS_HAS_GETTER_SETTER */ | ||
811 | |||
812 | /* | ||
813 | * We simplify the source code at the price of minor dead code bloat in | ||
814 | * the ECMA version (for testing only, see jsversion.h). The null | ||
815 | * default values in gsop[j] suffice to disable non-ECMA getter and | ||
816 | * setter code. | ||
817 | */ | ||
818 | valcnt = 1; | ||
819 | gsop[0] = NULL; | ||
820 | gsopold[0] = NULL; | ||
821 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); | ||
822 | |||
823 | #endif /* !JS_HAS_GETTER_SETTER */ | ||
824 | |||
825 | if (!ok) | ||
826 | goto error; | ||
827 | |||
828 | /* | ||
829 | * If id is a string that's not an identifier, then it needs to be | ||
830 | * quoted. Also, negative integer ids must be quoted. | ||
831 | */ | ||
832 | if (JSID_IS_ATOM(id) | ||
833 | ? !idIsLexicalIdentifier | ||
834 | : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { | ||
835 | idstr = js_QuoteString(cx, idstr, (jschar)'\''); | ||
836 | if (!idstr) { | ||
837 | ok = JS_FALSE; | ||
838 | goto error; | ||
839 | } | ||
840 | *vp = STRING_TO_JSVAL(idstr); /* local root */ | ||
841 | } | ||
842 | JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength); | ||
843 | |||
844 | for (j = 0; j < valcnt; j++) { | ||
845 | /* Convert val[j] to its canonical source form. */ | ||
846 | valstr = js_ValueToSource(cx, val[j]); | ||
847 | if (!valstr) { | ||
848 | ok = JS_FALSE; | ||
849 | goto error; | ||
850 | } | ||
851 | localroot[j] = STRING_TO_JSVAL(valstr); /* local root */ | ||
852 | JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength); | ||
853 | |||
854 | if (vchars[0] == '#') | ||
855 | needOldStyleGetterSetter = JS_TRUE; | ||
856 | |||
857 | if (needOldStyleGetterSetter) | ||
858 | gsop[j] = gsopold[j]; | ||
859 | |||
860 | /* If val[j] is a non-sharp object, consider sharpening it. */ | ||
861 | vsharp = NULL; | ||
862 | vsharplength = 0; | ||
863 | #if JS_HAS_SHARP_VARS | ||
864 | if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { | ||
865 | he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, | ||
866 | &vsharp); | ||
867 | if (!he) { | ||
868 | ok = JS_FALSE; | ||
869 | goto error; | ||
870 | } | ||
871 | if (IS_SHARP(he)) { | ||
872 | vchars = vsharp; | ||
873 | vlength = js_strlen(vchars); | ||
874 | needOldStyleGetterSetter = JS_TRUE; | ||
875 | gsop[j] = gsopold[j]; | ||
876 | } else { | ||
877 | if (vsharp) { | ||
878 | vsharplength = js_strlen(vsharp); | ||
879 | MAKE_SHARP(he); | ||
880 | needOldStyleGetterSetter = JS_TRUE; | ||
881 | gsop[j] = gsopold[j]; | ||
882 | } | ||
883 | js_LeaveSharpObject(cx, NULL); | ||
884 | } | ||
885 | } | ||
886 | #endif | ||
887 | |||
888 | #ifndef OLD_GETTER_SETTER | ||
889 | /* | ||
890 | * Remove '(function ' from the beginning of valstr and ')' from the | ||
891 | * end so that we can put "get" in front of the function definition. | ||
892 | */ | ||
893 | if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && | ||
894 | !needOldStyleGetterSetter) { | ||
895 | JSFunction *fun = JS_ValueToFunction(cx, val[j]); | ||
896 | const jschar *start = vchars; | ||
897 | const jschar *end = vchars + vlength; | ||
898 | |||
899 | uint8 parenChomp = 0; | ||
900 | if (vchars[0] == '(') { | ||
901 | vchars++; | ||
902 | parenChomp = 1; | ||
903 | } | ||
904 | |||
905 | /* | ||
906 | * Try to jump "getter" or "setter" keywords, if we suspect | ||
907 | * they might appear here. This code can be confused by people | ||
908 | * defining Function.prototype.toString, so let's be cautious. | ||
909 | */ | ||
910 | if (JSFUN_GETTER_TEST(fun->flags) || | ||
911 | JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */ | ||
912 | const jschar *tmp = js_strchr_limit(vchars, ' ', end); | ||
913 | if (tmp) | ||
914 | vchars = tmp + 1; | ||
915 | } | ||
916 | |||
917 | /* Try to jump "function" keyword. */ | ||
918 | if (vchars) | ||
919 | vchars = js_strchr_limit(vchars, ' ', end); | ||
920 | |||
921 | if (vchars) { | ||
922 | if (*vchars == ' ') | ||
923 | vchars++; | ||
924 | vlength = end - vchars - parenChomp; | ||
925 | } else { | ||
926 | gsop[j] = NULL; | ||
927 | vchars = start; | ||
928 | } | ||
929 | } | ||
930 | #else | ||
931 | needOldStyleGetterSetter = JS_TRUE; | ||
932 | gsop[j] = gsopold[j]; | ||
933 | #endif | ||
934 | |||
935 | #define SAFE_ADD(n) \ | ||
936 | JS_BEGIN_MACRO \ | ||
937 | size_t n_ = (n); \ | ||
938 | curlen += n_; \ | ||
939 | if (curlen < n_) \ | ||
940 | goto overflow; \ | ||
941 | JS_END_MACRO | ||
942 | |||
943 | curlen = nchars; | ||
944 | if (comma) | ||
945 | SAFE_ADD(2); | ||
946 | SAFE_ADD(idstrlength + 1); | ||
947 | if (gsop[j]) | ||
948 | SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); | ||
949 | SAFE_ADD(vsharplength); | ||
950 | SAFE_ADD(vlength); | ||
951 | /* Account for the trailing null. */ | ||
952 | SAFE_ADD((outermost ? 2 : 1) + 1); | ||
953 | #undef SAFE_ADD | ||
954 | |||
955 | if (curlen > (size_t)-1 / sizeof(jschar)) | ||
956 | goto overflow; | ||
957 | |||
958 | /* Allocate 1 + 1 at end for closing brace and terminating 0. */ | ||
959 | chars = (jschar *) | ||
960 | realloc((ochars = chars), curlen * sizeof(jschar)); | ||
961 | if (!chars) { | ||
962 | /* Save code space on error: let JS_free ignore null vsharp. */ | ||
963 | JS_free(cx, vsharp); | ||
964 | free(ochars); | ||
965 | goto error; | ||
966 | } | ||
967 | |||
968 | if (comma) { | ||
969 | chars[nchars++] = comma[0]; | ||
970 | chars[nchars++] = comma[1]; | ||
971 | } | ||
972 | comma = ", "; | ||
973 | |||
974 | if (needOldStyleGetterSetter) { | ||
975 | js_strncpy(&chars[nchars], idstrchars, idstrlength); | ||
976 | nchars += idstrlength; | ||
977 | if (gsop[j]) { | ||
978 | chars[nchars++] = ' '; | ||
979 | gsoplength = JSSTRING_LENGTH(gsop[j]); | ||
980 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), | ||
981 | gsoplength); | ||
982 | nchars += gsoplength; | ||
983 | } | ||
984 | chars[nchars++] = ':'; | ||
985 | } else { /* New style "decompilation" */ | ||
986 | if (gsop[j]) { | ||
987 | gsoplength = JSSTRING_LENGTH(gsop[j]); | ||
988 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), | ||
989 | gsoplength); | ||
990 | nchars += gsoplength; | ||
991 | chars[nchars++] = ' '; | ||
992 | } | ||
993 | js_strncpy(&chars[nchars], idstrchars, idstrlength); | ||
994 | nchars += idstrlength; | ||
995 | /* Extraneous space after id here will be extracted later */ | ||
996 | chars[nchars++] = gsop[j] ? ' ' : ':'; | ||
997 | } | ||
998 | |||
999 | if (vsharplength) { | ||
1000 | js_strncpy(&chars[nchars], vsharp, vsharplength); | ||
1001 | nchars += vsharplength; | ||
1002 | } | ||
1003 | js_strncpy(&chars[nchars], vchars, vlength); | ||
1004 | nchars += vlength; | ||
1005 | |||
1006 | if (vsharp) | ||
1007 | JS_free(cx, vsharp); | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | chars[nchars++] = '}'; | ||
1012 | if (outermost) | ||
1013 | chars[nchars++] = ')'; | ||
1014 | chars[nchars] = 0; | ||
1015 | |||
1016 | error: | ||
1017 | js_LeaveSharpObject(cx, &ida); | ||
1018 | |||
1019 | if (!ok) { | ||
1020 | if (chars) | ||
1021 | free(chars); | ||
1022 | goto out; | ||
1023 | } | ||
1024 | |||
1025 | if (!chars) { | ||
1026 | JS_ReportOutOfMemory(cx); | ||
1027 | ok = JS_FALSE; | ||
1028 | goto out; | ||
1029 | } | ||
1030 | make_string: | ||
1031 | str = js_NewString(cx, chars, nchars); | ||
1032 | if (!str) { | ||
1033 | free(chars); | ||
1034 | ok = JS_FALSE; | ||
1035 | goto out; | ||
1036 | } | ||
1037 | *vp = STRING_TO_JSVAL(str); | ||
1038 | ok = JS_TRUE; | ||
1039 | out: | ||
1040 | JS_POP_TEMP_ROOT(cx, &tvr); | ||
1041 | return ok; | ||
1042 | |||
1043 | overflow: | ||
1044 | JS_free(cx, vsharp); | ||
1045 | free(chars); | ||
1046 | chars = NULL; | ||
1047 | goto error; | ||
1048 | } | ||
1049 | #endif /* JS_HAS_TOSOURCE */ | ||
1050 | |||
1051 | static JSBool | ||
1052 | obj_toString(JSContext *cx, uintN argc, jsval *vp) | ||
1053 | { | ||
1054 | JSObject *obj; | ||
1055 | jschar *chars; | ||
1056 | size_t nchars; | ||
1057 | const char *clazz, *prefix; | ||
1058 | JSString *str; | ||
1059 | |||
1060 | obj = JS_THIS_OBJECT(cx, vp); | ||
1061 | if (!obj) | ||
1062 | return JS_FALSE; | ||
1063 | obj = js_GetWrappedObject(cx, obj); | ||
1064 | clazz = OBJ_GET_CLASS(cx, obj)->name; | ||
1065 | nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ | ||
1066 | chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); | ||
1067 | if (!chars) | ||
1068 | return JS_FALSE; | ||
1069 | |||
1070 | prefix = "[object "; | ||
1071 | nchars = 0; | ||
1072 | while ((chars[nchars] = (jschar)*prefix) != 0) | ||
1073 | nchars++, prefix++; | ||
1074 | while ((chars[nchars] = (jschar)*clazz) != 0) | ||
1075 | nchars++, clazz++; | ||
1076 | chars[nchars++] = ']'; | ||
1077 | chars[nchars] = 0; | ||
1078 | |||
1079 | str = js_NewString(cx, chars, nchars); | ||
1080 | if (!str) { | ||
1081 | JS_free(cx, chars); | ||
1082 | return JS_FALSE; | ||
1083 | } | ||
1084 | *vp = STRING_TO_JSVAL(str); | ||
1085 | return JS_TRUE; | ||
1086 | } | ||
1087 | |||
1088 | static JSBool | ||
1089 | obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp) | ||
1090 | { | ||
1091 | jsval thisv; | ||
1092 | JSString *str; | ||
1093 | |||
1094 | thisv = JS_THIS(cx, vp); | ||
1095 | if (JSVAL_IS_NULL(thisv)) | ||
1096 | return JS_FALSE; | ||
1097 | |||
1098 | str = js_ValueToString(cx, thisv); | ||
1099 | if (!str) | ||
1100 | return JS_FALSE; | ||
1101 | |||
1102 | *vp = STRING_TO_JSVAL(str); | ||
1103 | return JS_TRUE; | ||
1104 | } | ||
1105 | |||
1106 | static JSBool | ||
1107 | obj_valueOf(JSContext *cx, uintN argc, jsval *vp) | ||
1108 | { | ||
1109 | *vp = JS_THIS(cx, vp); | ||
1110 | return !JSVAL_IS_NULL(*vp); | ||
1111 | } | ||
1112 | |||
1113 | siliconforks | 460 | #ifdef JS_TRACER |
1114 | static jsval FASTCALL | ||
1115 | Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint) | ||
1116 | { | ||
1117 | return OBJECT_TO_JSVAL(obj); | ||
1118 | } | ||
1119 | #endif | ||
1120 | |||
1121 | siliconforks | 332 | /* |
1122 | * Check whether principals subsumes scopeobj's principals, and return true | ||
1123 | * if so (or if scopeobj has no principals, for backward compatibility with | ||
1124 | * the JS API, which does not require principals), and false otherwise. | ||
1125 | */ | ||
1126 | JSBool | ||
1127 | js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, | ||
1128 | JSPrincipals *principals, JSAtom *caller) | ||
1129 | { | ||
1130 | JSSecurityCallbacks *callbacks; | ||
1131 | JSPrincipals *scopePrincipals; | ||
1132 | const char *callerstr; | ||
1133 | |||
1134 | callbacks = JS_GetSecurityCallbacks(cx); | ||
1135 | if (callbacks && callbacks->findObjectPrincipals) { | ||
1136 | scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj); | ||
1137 | if (!principals || !scopePrincipals || | ||
1138 | !principals->subsume(principals, scopePrincipals)) { | ||
1139 | callerstr = js_AtomToPrintableString(cx, caller); | ||
1140 | if (!callerstr) | ||
1141 | return JS_FALSE; | ||
1142 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
1143 | JSMSG_BAD_INDIRECT_CALL, callerstr); | ||
1144 | return JS_FALSE; | ||
1145 | } | ||
1146 | } | ||
1147 | return JS_TRUE; | ||
1148 | } | ||
1149 | |||
1150 | JSObject * | ||
1151 | js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) | ||
1152 | { | ||
1153 | JSClass *clasp; | ||
1154 | JSExtendedClass *xclasp; | ||
1155 | JSObject *inner; | ||
1156 | |||
1157 | if (!scopeobj) | ||
1158 | goto bad; | ||
1159 | |||
1160 | OBJ_TO_INNER_OBJECT(cx, scopeobj); | ||
1161 | if (!scopeobj) | ||
1162 | return NULL; | ||
1163 | |||
1164 | inner = scopeobj; | ||
1165 | |||
1166 | /* XXX This is an awful gross hack. */ | ||
1167 | while (scopeobj) { | ||
1168 | clasp = OBJ_GET_CLASS(cx, scopeobj); | ||
1169 | if (clasp->flags & JSCLASS_IS_EXTENDED) { | ||
1170 | xclasp = (JSExtendedClass*)clasp; | ||
1171 | if (xclasp->innerObject && | ||
1172 | xclasp->innerObject(cx, scopeobj) != scopeobj) { | ||
1173 | goto bad; | ||
1174 | } | ||
1175 | } | ||
1176 | |||
1177 | scopeobj = OBJ_GET_PARENT(cx, scopeobj); | ||
1178 | } | ||
1179 | |||
1180 | return inner; | ||
1181 | |||
1182 | bad: | ||
1183 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
1184 | JSMSG_BAD_INDIRECT_CALL, caller); | ||
1185 | return NULL; | ||
1186 | } | ||
1187 | |||
1188 | const char * | ||
1189 | js_ComputeFilename(JSContext *cx, JSStackFrame *caller, | ||
1190 | JSPrincipals *principals, uintN *linenop) | ||
1191 | { | ||
1192 | uint32 flags; | ||
1193 | #ifdef DEBUG | ||
1194 | JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); | ||
1195 | #endif | ||
1196 | |||
1197 | JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals)); | ||
1198 | flags = JS_GetScriptFilenameFlags(caller->script); | ||
1199 | if ((flags & JSFILENAME_PROTECTED) && | ||
1200 | principals && | ||
1201 | strcmp(principals->codebase, "[System Principal]")) { | ||
1202 | *linenop = 0; | ||
1203 | return principals->codebase; | ||
1204 | } | ||
1205 | |||
1206 | siliconforks | 460 | jsbytecode *pc = caller->regs->pc; |
1207 | if (caller->regs && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) { | ||
1208 | JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); | ||
1209 | *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH); | ||
1210 | siliconforks | 332 | } else { |
1211 | siliconforks | 399 | *linenop = js_FramePCToLineNumber(cx, caller); |
1212 | siliconforks | 332 | } |
1213 | return caller->script->filename; | ||
1214 | } | ||
1215 | |||
1216 | siliconforks | 460 | #ifndef EVAL_CACHE_CHAIN_LIMIT |
1217 | # define EVAL_CACHE_CHAIN_LIMIT 4 | ||
1218 | #endif | ||
1219 | |||
1220 | static inline JSScript ** | ||
1221 | EvalCacheHash(JSContext *cx, JSString *str) | ||
1222 | { | ||
1223 | const jschar *s; | ||
1224 | size_t n; | ||
1225 | uint32 h; | ||
1226 | |||
1227 | JSSTRING_CHARS_AND_LENGTH(str, s, n); | ||
1228 | if (n > 100) | ||
1229 | n = 100; | ||
1230 | for (h = 0; n; s++, n--) | ||
1231 | h = JS_ROTATE_LEFT32(h, 4) ^ *s; | ||
1232 | |||
1233 | h *= JS_GOLDEN_RATIO; | ||
1234 | h >>= 32 - JS_EVAL_CACHE_SHIFT; | ||
1235 | return &JS_SCRIPTS_TO_GC(cx)[h]; | ||
1236 | } | ||
1237 | |||
1238 | siliconforks | 399 | static JSBool |
1239 | obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
1240 | siliconforks | 332 | { |
1241 | JSStackFrame *fp, *caller; | ||
1242 | JSBool indirectCall; | ||
1243 | siliconforks | 460 | uint32 tcflags; |
1244 | JSPrincipals *principals; | ||
1245 | siliconforks | 332 | const char *file; |
1246 | uintN line; | ||
1247 | siliconforks | 460 | JSString *str; |
1248 | siliconforks | 332 | JSScript *script; |
1249 | JSBool ok; | ||
1250 | siliconforks | 460 | JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */ |
1251 | siliconforks | 332 | #if JS_HAS_EVAL_THIS_SCOPE |
1252 | JSObject *callerScopeChain = NULL, *callerVarObj = NULL; | ||
1253 | JSObject *setCallerScopeChain = NULL; | ||
1254 | JSBool setCallerVarObj = JS_FALSE; | ||
1255 | #endif | ||
1256 | |||
1257 | siliconforks | 460 | fp = js_GetTopStackFrame(cx); |
1258 | caller = js_GetScriptedCaller(cx, fp); | ||
1259 | siliconforks | 332 | indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); |
1260 | |||
1261 | /* | ||
1262 | siliconforks | 460 | * This call to js_GetWrappedObject is safe because of the security checks |
1263 | * we do below. However, the control flow below is confusing, so we double | ||
1264 | * check. There are two cases: | ||
1265 | * - Direct call: This object is never used. So unwrapping can't hurt. | ||
1266 | * - Indirect call: If this object isn't already the scope chain (which | ||
1267 | * we're guaranteed to be allowed to access) then we do a security | ||
1268 | * check. | ||
1269 | */ | ||
1270 | obj = js_GetWrappedObject(cx, obj); | ||
1271 | |||
1272 | /* | ||
1273 | siliconforks | 332 | * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and |
1274 | * calls that attempt to use a non-global object as the "with" object in | ||
1275 | * the former indirect case. | ||
1276 | */ | ||
1277 | siliconforks | 460 | { |
1278 | JSObject *parent = OBJ_GET_PARENT(cx, obj); | ||
1279 | if (indirectCall || parent) { | ||
1280 | uintN flags = parent | ||
1281 | ? JSREPORT_ERROR | ||
1282 | : JSREPORT_STRICT | JSREPORT_WARNING; | ||
1283 | if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, | ||
1284 | JSMSG_BAD_INDIRECT_CALL, | ||
1285 | js_eval_str)) { | ||
1286 | return JS_FALSE; | ||
1287 | } | ||
1288 | siliconforks | 332 | } |
1289 | } | ||
1290 | |||
1291 | if (!JSVAL_IS_STRING(argv[0])) { | ||
1292 | *rval = argv[0]; | ||
1293 | return JS_TRUE; | ||
1294 | } | ||
1295 | |||
1296 | /* | ||
1297 | * If the caller is a lightweight function and doesn't have a variables | ||
1298 | * object, then we need to provide one for the compiler to stick any | ||
1299 | * declared (var) variables into. | ||
1300 | */ | ||
1301 | siliconforks | 460 | if (caller && !caller->varobj && !js_GetCallObject(cx, caller)) |
1302 | siliconforks | 332 | return JS_FALSE; |
1303 | |||
1304 | siliconforks | 460 | /* Accept an optional trailing argument that overrides the scope object. */ |
1305 | JSObject *scopeobj = NULL; | ||
1306 | if (argc >= 2) { | ||
1307 | if (!js_ValueToObject(cx, argv[1], &scopeobj)) | ||
1308 | return JS_FALSE; | ||
1309 | argv[1] = OBJECT_TO_JSVAL(scopeobj); | ||
1310 | siliconforks | 332 | } |
1311 | |||
1312 | /* From here on, control must exit through label out with ok set. */ | ||
1313 | MUST_FLOW_THROUGH("out"); | ||
1314 | if (!scopeobj) { | ||
1315 | #if JS_HAS_EVAL_THIS_SCOPE | ||
1316 | /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ | ||
1317 | if (indirectCall) { | ||
1318 | callerScopeChain = js_GetScopeChain(cx, caller); | ||
1319 | if (!callerScopeChain) { | ||
1320 | ok = JS_FALSE; | ||
1321 | goto out; | ||
1322 | } | ||
1323 | OBJ_TO_INNER_OBJECT(cx, obj); | ||
1324 | if (!obj) { | ||
1325 | ok = JS_FALSE; | ||
1326 | goto out; | ||
1327 | } | ||
1328 | if (obj != callerScopeChain) { | ||
1329 | ok = js_CheckPrincipalsAccess(cx, obj, | ||
1330 | siliconforks | 460 | JS_StackFramePrincipals(cx, caller), |
1331 | siliconforks | 332 | cx->runtime->atomState.evalAtom); |
1332 | if (!ok) | ||
1333 | goto out; | ||
1334 | |||
1335 | scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); | ||
1336 | if (!scopeobj) { | ||
1337 | ok = JS_FALSE; | ||
1338 | goto out; | ||
1339 | } | ||
1340 | |||
1341 | /* Set fp->scopeChain too, for the compiler. */ | ||
1342 | caller->scopeChain = fp->scopeChain = scopeobj; | ||
1343 | |||
1344 | /* Remember scopeobj so we can null its private when done. */ | ||
1345 | setCallerScopeChain = scopeobj; | ||
1346 | } | ||
1347 | |||
1348 | callerVarObj = caller->varobj; | ||
1349 | if (obj != callerVarObj) { | ||
1350 | /* Set fp->varobj too, for the compiler. */ | ||
1351 | caller->varobj = fp->varobj = obj; | ||
1352 | setCallerVarObj = JS_TRUE; | ||
1353 | } | ||
1354 | } | ||
1355 | #endif | ||
1356 | |||
1357 | /* | ||
1358 | * Compile using caller's current scope object. | ||
1359 | * | ||
1360 | * NB: This means that native callers (who reach this point through | ||
1361 | * the C API) must use the two parameter form. | ||
1362 | */ | ||
1363 | if (caller) { | ||
1364 | scopeobj = js_GetScopeChain(cx, caller); | ||
1365 | if (!scopeobj) { | ||
1366 | ok = JS_FALSE; | ||
1367 | goto out; | ||
1368 | } | ||
1369 | } | ||
1370 | siliconforks | 460 | } else { |
1371 | scopeobj = js_GetWrappedObject(cx, scopeobj); | ||
1372 | OBJ_TO_INNER_OBJECT(cx, scopeobj); | ||
1373 | if (!scopeobj) { | ||
1374 | ok = JS_FALSE; | ||
1375 | goto out; | ||
1376 | } | ||
1377 | ok = js_CheckPrincipalsAccess(cx, scopeobj, | ||
1378 | JS_StackFramePrincipals(cx, caller), | ||
1379 | cx->runtime->atomState.evalAtom); | ||
1380 | if (!ok) | ||
1381 | goto out; | ||
1382 | |||
1383 | scopeobj = js_NewWithObject(cx, scopeobj, | ||
1384 | JS_GetGlobalForObject(cx, scopeobj), -1); | ||
1385 | if (!scopeobj) { | ||
1386 | ok = JS_FALSE; | ||
1387 | goto out; | ||
1388 | } | ||
1389 | argv[1] = OBJECT_TO_JSVAL(scopeobj); | ||
1390 | siliconforks | 332 | } |
1391 | |||
1392 | /* Ensure we compile this eval with the right object in the scope chain. */ | ||
1393 | scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); | ||
1394 | if (!scopeobj) { | ||
1395 | ok = JS_FALSE; | ||
1396 | goto out; | ||
1397 | } | ||
1398 | |||
1399 | siliconforks | 460 | tcflags = TCF_COMPILE_N_GO; |
1400 | siliconforks | 332 | if (caller) { |
1401 | siliconforks | 460 | tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1); |
1402 | siliconforks | 332 | principals = JS_EvalFramePrincipals(cx, fp, caller); |
1403 | file = js_ComputeFilename(cx, caller, principals, &line); | ||
1404 | } else { | ||
1405 | siliconforks | 460 | principals = NULL; |
1406 | siliconforks | 332 | file = NULL; |
1407 | line = 0; | ||
1408 | } | ||
1409 | |||
1410 | siliconforks | 460 | str = JSVAL_TO_STRING(argv[0]); |
1411 | script = NULL; | ||
1412 | |||
1413 | /* Cache local eval scripts indexed by source qualified by scope. */ | ||
1414 | bucket = EvalCacheHash(cx, str); | ||
1415 | if (caller->fun) { | ||
1416 | uintN count = 0; | ||
1417 | JSScript **scriptp = bucket; | ||
1418 | |||
1419 | EVAL_CACHE_METER(probe); | ||
1420 | while ((script = *scriptp) != NULL) { | ||
1421 | if ((script->flags & JSSF_SAVED_CALLER_FUN) && | ||
1422 | script->version == cx->version && | ||
1423 | (script->principals == principals || | ||
1424 | (principals->subsume(principals, script->principals) && | ||
1425 | script->principals->subsume(script->principals, principals)))) { | ||
1426 | /* | ||
1427 | * Get the prior (cache-filling) eval's saved caller function. | ||
1428 | * See JSCompiler::compileScript in jsparse.cpp. | ||
1429 | */ | ||
1430 | JSFunction *fun; | ||
1431 | JS_GET_SCRIPT_FUNCTION(script, 0, fun); | ||
1432 | |||
1433 | if (fun == caller->fun) { | ||
1434 | /* | ||
1435 | * Get the source string passed for safekeeping in the | ||
1436 | * atom map by the prior eval to JSCompiler::compileScript. | ||
1437 | */ | ||
1438 | JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); | ||
1439 | |||
1440 | if (src == str || js_EqualStrings(src, str)) { | ||
1441 | /* | ||
1442 | * Source matches, qualify by comparing scopeobj to the | ||
1443 | * COMPILE_N_GO-memoized parent of the first literal | ||
1444 | * function or regexp object if any. If none, then this | ||
1445 | * script has no compiled-in dependencies on the prior | ||
1446 | * eval's scopeobj. | ||
1447 | */ | ||
1448 | JSObjectArray *objarray = JS_SCRIPT_OBJECTS(script); | ||
1449 | int i = 1; | ||
1450 | if (objarray->length == 1) { | ||
1451 | if (script->regexpsOffset != 0) { | ||
1452 | objarray = JS_SCRIPT_REGEXPS(script); | ||
1453 | i = 0; | ||
1454 | } else { | ||
1455 | EVAL_CACHE_METER(noscope); | ||
1456 | i = -1; | ||
1457 | } | ||
1458 | } | ||
1459 | if (i < 0 || | ||
1460 | STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) { | ||
1461 | EVAL_CACHE_METER(hit); | ||
1462 | *scriptp = script->u.nextToGC; | ||
1463 | script->u.nextToGC = NULL; | ||
1464 | break; | ||
1465 | } | ||
1466 | } | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1470 | if (++count == EVAL_CACHE_CHAIN_LIMIT) { | ||
1471 | script = NULL; | ||
1472 | break; | ||
1473 | } | ||
1474 | EVAL_CACHE_METER(step); | ||
1475 | scriptp = &script->u.nextToGC; | ||
1476 | } | ||
1477 | } | ||
1478 | |||
1479 | siliconforks | 332 | if (!script) { |
1480 | siliconforks | 460 | script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, |
1481 | JSSTRING_CHARS(str), JSSTRING_LENGTH(str), | ||
1482 | NULL, file, line, str); | ||
1483 | if (!script) { | ||
1484 | ok = JS_FALSE; | ||
1485 | goto out; | ||
1486 | } | ||
1487 | siliconforks | 332 | } |
1488 | |||
1489 | if (argc < 2) { | ||
1490 | /* Execute using caller's new scope object (might be a Call object). */ | ||
1491 | if (caller) | ||
1492 | scopeobj = caller->scopeChain; | ||
1493 | } | ||
1494 | |||
1495 | /* | ||
1496 | * Belt-and-braces: check that the lesser of eval's principals and the | ||
1497 | * caller's principals has access to scopeobj. | ||
1498 | */ | ||
1499 | ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, | ||
1500 | cx->runtime->atomState.evalAtom); | ||
1501 | if (ok) | ||
1502 | ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); | ||
1503 | |||
1504 | siliconforks | 460 | script->u.nextToGC = *bucket; |
1505 | *bucket = script; | ||
1506 | siliconforks | 332 | #ifdef CHECK_SCRIPT_OWNER |
1507 | script->owner = NULL; | ||
1508 | #endif | ||
1509 | |||
1510 | out: | ||
1511 | #if JS_HAS_EVAL_THIS_SCOPE | ||
1512 | /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ | ||
1513 | if (setCallerScopeChain) { | ||
1514 | caller->scopeChain = callerScopeChain; | ||
1515 | JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); | ||
1516 | JS_SetPrivate(cx, setCallerScopeChain, NULL); | ||
1517 | } | ||
1518 | if (setCallerVarObj) | ||
1519 | caller->varobj = callerVarObj; | ||
1520 | #endif | ||
1521 | return ok; | ||
1522 | } | ||
1523 | |||
1524 | #if JS_HAS_OBJ_WATCHPOINT | ||
1525 | |||
1526 | static JSBool | ||
1527 | obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, | ||
1528 | void *closure) | ||
1529 | { | ||
1530 | JSObject *callable; | ||
1531 | JSSecurityCallbacks *callbacks; | ||
1532 | JSStackFrame *caller; | ||
1533 | JSPrincipals *subject, *watcher; | ||
1534 | JSResolvingKey key; | ||
1535 | JSResolvingEntry *entry; | ||
1536 | uint32 generation; | ||
1537 | jsval argv[3]; | ||
1538 | JSBool ok; | ||
1539 | |||
1540 | callable = (JSObject *) closure; | ||
1541 | |||
1542 | callbacks = JS_GetSecurityCallbacks(cx); | ||
1543 | if (callbacks && callbacks->findObjectPrincipals) { | ||
1544 | /* Skip over any obj_watch_* frames between us and the real subject. */ | ||
1545 | siliconforks | 460 | caller = js_GetScriptedCaller(cx, NULL); |
1546 | siliconforks | 332 | if (caller) { |
1547 | /* | ||
1548 | * Only call the watch handler if the watcher is allowed to watch | ||
1549 | * the currently executing script. | ||
1550 | */ | ||
1551 | watcher = callbacks->findObjectPrincipals(cx, callable); | ||
1552 | subject = JS_StackFramePrincipals(cx, caller); | ||
1553 | |||
1554 | if (watcher && subject && !watcher->subsume(watcher, subject)) { | ||
1555 | /* Silently don't call the watch handler. */ | ||
1556 | return JS_TRUE; | ||
1557 | } | ||
1558 | } | ||
1559 | } | ||
1560 | |||
1561 | /* Avoid recursion on (obj, id) already being watched on cx. */ | ||
1562 | key.obj = obj; | ||
1563 | key.id = id; | ||
1564 | if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) | ||
1565 | return JS_FALSE; | ||
1566 | if (!entry) | ||
1567 | return JS_TRUE; | ||
1568 | generation = cx->resolvingTable->generation; | ||
1569 | |||
1570 | argv[0] = id; | ||
1571 | argv[1] = old; | ||
1572 | argv[2] = *nvp; | ||
1573 | ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); | ||
1574 | js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); | ||
1575 | return ok; | ||
1576 | } | ||
1577 | |||
1578 | static JSBool | ||
1579 | obj_watch(JSContext *cx, uintN argc, jsval *vp) | ||
1580 | { | ||
1581 | JSObject *callable; | ||
1582 | jsval userid, value; | ||
1583 | jsid propid; | ||
1584 | JSObject *obj; | ||
1585 | uintN attrs; | ||
1586 | |||
1587 | if (argc <= 1) { | ||
1588 | js_ReportMissingArg(cx, vp, 1); | ||
1589 | return JS_FALSE; | ||
1590 | } | ||
1591 | |||
1592 | callable = js_ValueToCallableObject(cx, &vp[3], 0); | ||
1593 | if (!callable) | ||
1594 | return JS_FALSE; | ||
1595 | |||
1596 | /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ | ||
1597 | userid = vp[2]; | ||
1598 | if (!JS_ValueToId(cx, userid, &propid)) | ||
1599 | return JS_FALSE; | ||
1600 | |||
1601 | obj = JS_THIS_OBJECT(cx, vp); | ||
1602 | if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) | ||
1603 | return JS_FALSE; | ||
1604 | if (attrs & JSPROP_READONLY) | ||
1605 | return JS_TRUE; | ||
1606 | *vp = JSVAL_VOID; | ||
1607 | |||
1608 | if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj)) | ||
1609 | return JS_FALSE; | ||
1610 | return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); | ||
1611 | } | ||
1612 | |||
1613 | static JSBool | ||
1614 | obj_unwatch(JSContext *cx, uintN argc, jsval *vp) | ||
1615 | { | ||
1616 | JSObject *obj; | ||
1617 | |||
1618 | obj = JS_THIS_OBJECT(cx, vp); | ||
1619 | if (!obj) | ||
1620 | return JS_FALSE; | ||
1621 | *vp = JSVAL_VOID; | ||
1622 | return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID, | ||
1623 | NULL, NULL); | ||
1624 | } | ||
1625 | |||
1626 | #endif /* JS_HAS_OBJ_WATCHPOINT */ | ||
1627 | |||
1628 | /* | ||
1629 | * Prototype and property query methods, to complement the 'in' and | ||
1630 | * 'instanceof' operators. | ||
1631 | */ | ||
1632 | |||
1633 | /* Proposed ECMA 15.2.4.5. */ | ||
1634 | siliconforks | 399 | static JSBool |
1635 | obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp) | ||
1636 | siliconforks | 332 | { |
1637 | JSObject *obj; | ||
1638 | |||
1639 | obj = JS_THIS_OBJECT(cx, vp); | ||
1640 | return obj && | ||
1641 | js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp); | ||
1642 | } | ||
1643 | |||
1644 | JSBool | ||
1645 | js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc, | ||
1646 | jsval *vp) | ||
1647 | { | ||
1648 | jsid id; | ||
1649 | JSObject *obj; | ||
1650 | |||
1651 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | ||
1652 | return JS_FALSE; | ||
1653 | obj = JS_THIS_OBJECT(cx, vp); | ||
1654 | return obj && js_HasOwnProperty(cx, lookup, obj, id, vp); | ||
1655 | } | ||
1656 | |||
1657 | JSBool | ||
1658 | js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id, | ||
1659 | jsval *vp) | ||
1660 | { | ||
1661 | JSObject *obj2; | ||
1662 | JSProperty *prop; | ||
1663 | JSScopeProperty *sprop; | ||
1664 | |||
1665 | if (!lookup(cx, obj, id, &obj2, &prop)) | ||
1666 | return JS_FALSE; | ||
1667 | if (!prop) { | ||
1668 | *vp = JSVAL_FALSE; | ||
1669 | } else if (obj2 == obj) { | ||
1670 | *vp = JSVAL_TRUE; | ||
1671 | } else { | ||
1672 | JSClass *clasp; | ||
1673 | JSExtendedClass *xclasp; | ||
1674 | JSObject *outer; | ||
1675 | |||
1676 | clasp = OBJ_GET_CLASS(cx, obj2); | ||
1677 | if (!(clasp->flags & JSCLASS_IS_EXTENDED) || | ||
1678 | !(xclasp = (JSExtendedClass *) clasp)->outerObject) { | ||
1679 | outer = NULL; | ||
1680 | } else { | ||
1681 | outer = xclasp->outerObject(cx, obj2); | ||
1682 | if (!outer) | ||
1683 | return JS_FALSE; | ||
1684 | } | ||
1685 | if (outer == obj) { | ||
1686 | *vp = JSVAL_TRUE; | ||
1687 | } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) { | ||
1688 | /* | ||
1689 | * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a | ||
1690 | * delegated property makes that property appear to be direct in | ||
1691 | * all delegating instances of the same native class. This hack | ||
1692 | * avoids bloating every function instance with its own 'length' | ||
1693 | * (AKA 'arity') property. But it must not extend across class | ||
1694 | * boundaries, to avoid making hasOwnProperty lie (bug 320854). | ||
1695 | * | ||
1696 | * It's not really a hack, of course: a permanent property can't | ||
1697 | * be deleted, and JSPROP_SHARED means "don't allocate a slot in | ||
1698 | * any instance, prototype or delegating". Without a slot, and | ||
1699 | * without the ability to remove and recreate (with differences) | ||
1700 | * the property, there is no way to tell whether it is directly | ||
1701 | * owned, or indirectly delegated. | ||
1702 | */ | ||
1703 | sprop = (JSScopeProperty *)prop; | ||
1704 | *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); | ||
1705 | } else { | ||
1706 | *vp = JSVAL_FALSE; | ||
1707 | } | ||
1708 | } | ||
1709 | if (prop) | ||
1710 | OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
1711 | return JS_TRUE; | ||
1712 | } | ||
1713 | |||
1714 | siliconforks | 399 | #ifdef JS_TRACER |
1715 | siliconforks | 460 | static JSBool FASTCALL |
1716 | siliconforks | 399 | Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str) |
1717 | { | ||
1718 | jsid id; | ||
1719 | jsval v; | ||
1720 | |||
1721 | siliconforks | 460 | if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) || |
1722 | !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) { | ||
1723 | js_SetBuiltinError(cx); | ||
1724 | siliconforks | 399 | return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1725 | siliconforks | 460 | } |
1726 | |||
1727 | siliconforks | 399 | JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1728 | return JSVAL_TO_BOOLEAN(v); | ||
1729 | } | ||
1730 | #endif | ||
1731 | |||
1732 | siliconforks | 332 | /* Proposed ECMA 15.2.4.6. */ |
1733 | static JSBool | ||
1734 | obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp) | ||
1735 | { | ||
1736 | JSBool b; | ||
1737 | |||
1738 | if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp), | ||
1739 | argc != 0 ? vp[2] : JSVAL_VOID, &b)) { | ||
1740 | return JS_FALSE; | ||
1741 | } | ||
1742 | *vp = BOOLEAN_TO_JSVAL(b); | ||
1743 | return JS_TRUE; | ||
1744 | } | ||
1745 | |||
1746 | /* Proposed ECMA 15.2.4.7. */ | ||
1747 | siliconforks | 399 | static JSBool |
1748 | obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) | ||
1749 | siliconforks | 332 | { |
1750 | jsid id; | ||
1751 | JSObject *obj; | ||
1752 | |||
1753 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | ||
1754 | return JS_FALSE; | ||
1755 | |||
1756 | obj = JS_THIS_OBJECT(cx, vp); | ||
1757 | return obj && js_PropertyIsEnumerable(cx, obj, id, vp); | ||
1758 | } | ||
1759 | |||
1760 | siliconforks | 399 | #ifdef JS_TRACER |
1761 | siliconforks | 460 | static JSBool FASTCALL |
1762 | siliconforks | 399 | Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str) |
1763 | { | ||
1764 | jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str)); | ||
1765 | jsval v; | ||
1766 | siliconforks | 460 | |
1767 | if (!js_PropertyIsEnumerable(cx, obj, id, &v)) { | ||
1768 | js_SetBuiltinError(cx); | ||
1769 | siliconforks | 399 | return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1770 | siliconforks | 460 | } |
1771 | |||
1772 | siliconforks | 399 | JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1773 | return JSVAL_TO_BOOLEAN(v); | ||
1774 | } | ||
1775 | #endif | ||
1776 | |||
1777 | siliconforks | 332 | JSBool |
1778 | js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp) | ||
1779 | { | ||
1780 | JSObject *pobj; | ||
1781 | uintN attrs; | ||
1782 | JSProperty *prop; | ||
1783 | JSBool ok; | ||
1784 | |||
1785 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | ||
1786 | return JS_FALSE; | ||
1787 | |||
1788 | if (!prop) { | ||
1789 | *vp = JSVAL_FALSE; | ||
1790 | return JS_TRUE; | ||
1791 | } | ||
1792 | |||
1793 | /* | ||
1794 | * XXX ECMA spec error compatible: return false unless hasOwnProperty. | ||
1795 | * The ECMA spec really should be fixed so propertyIsEnumerable and the | ||
1796 | * for..in loop agree on whether prototype properties are enumerable, | ||
1797 | * obviously by fixing this method (not by breaking the for..in loop!). | ||
1798 | * | ||
1799 | * We check here for shared permanent prototype properties, which should | ||
1800 | * be treated as if they are local to obj. They are an implementation | ||
1801 | * technique used to satisfy ECMA requirements; users should not be able | ||
1802 | * to distinguish a shared permanent proto-property from a local one. | ||
1803 | */ | ||
1804 | if (pobj != obj && | ||
1805 | !(OBJ_IS_NATIVE(pobj) && | ||
1806 | SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { | ||
1807 | OBJ_DROP_PROPERTY(cx, pobj, prop); | ||
1808 | *vp = JSVAL_FALSE; | ||
1809 | return JS_TRUE; | ||
1810 | } | ||
1811 | |||
1812 | ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs); | ||
1813 | OBJ_DROP_PROPERTY(cx, pobj, prop); | ||
1814 | if (ok) | ||
1815 | *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); | ||
1816 | return ok; | ||
1817 | } | ||
1818 | |||
1819 | #if JS_HAS_GETTER_SETTER | ||
1820 | siliconforks | 460 | JS_FRIEND_API(JSBool) |
1821 | js_obj_defineGetter(JSContext *cx, uintN argc, jsval *vp) | ||
1822 | siliconforks | 332 | { |
1823 | jsval fval, junk; | ||
1824 | jsid id; | ||
1825 | JSObject *obj; | ||
1826 | uintN attrs; | ||
1827 | |||
1828 | if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { | ||
1829 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
1830 | JSMSG_BAD_GETTER_OR_SETTER, | ||
1831 | js_getter_str); | ||
1832 | return JS_FALSE; | ||
1833 | } | ||
1834 | fval = vp[3]; | ||
1835 | |||
1836 | if (!JS_ValueToId(cx, vp[2], &id)) | ||
1837 | return JS_FALSE; | ||
1838 | obj = JS_THIS_OBJECT(cx, vp); | ||
1839 | if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) | ||
1840 | return JS_FALSE; | ||
1841 | /* | ||
1842 | * Getters and setters are just like watchpoints from an access | ||
1843 | * control point of view. | ||
1844 | */ | ||
1845 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) | ||
1846 | return JS_FALSE; | ||
1847 | *vp = JSVAL_VOID; | ||
1848 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, | ||
1849 | siliconforks | 460 | js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), |
1850 | siliconforks | 332 | JS_PropertyStub, |
1851 | JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, | ||
1852 | NULL); | ||
1853 | } | ||
1854 | |||
1855 | siliconforks | 460 | JS_FRIEND_API(JSBool) |
1856 | js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp) | ||
1857 | siliconforks | 332 | { |
1858 | jsval fval, junk; | ||
1859 | jsid id; | ||
1860 | JSObject *obj; | ||
1861 | uintN attrs; | ||
1862 | |||
1863 | if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) { | ||
1864 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
1865 | JSMSG_BAD_GETTER_OR_SETTER, | ||
1866 | js_setter_str); | ||
1867 | return JS_FALSE; | ||
1868 | } | ||
1869 | fval = vp[3]; | ||
1870 | |||
1871 | if (!JS_ValueToId(cx, vp[2], &id)) | ||
1872 | return JS_FALSE; | ||
1873 | obj = JS_THIS_OBJECT(cx, vp); | ||
1874 | if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) | ||
1875 | return JS_FALSE; | ||
1876 | /* | ||
1877 | * Getters and setters are just like watchpoints from an access | ||
1878 | * control point of view. | ||
1879 | */ | ||
1880 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) | ||
1881 | return JS_FALSE; | ||
1882 | *vp = JSVAL_VOID; | ||
1883 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, | ||
1884 | JS_PropertyStub, | ||
1885 | siliconforks | 460 | js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), |
1886 | siliconforks | 332 | JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, |
1887 | NULL); | ||
1888 | } | ||
1889 | |||
1890 | static JSBool | ||
1891 | obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp) | ||
1892 | { | ||
1893 | jsid id; | ||
1894 | JSObject *obj, *pobj; | ||
1895 | JSProperty *prop; | ||
1896 | JSScopeProperty *sprop; | ||
1897 | |||
1898 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | ||
1899 | return JS_FALSE; | ||
1900 | obj = JS_THIS_OBJECT(cx, vp); | ||
1901 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | ||
1902 | return JS_FALSE; | ||
1903 | *vp = JSVAL_VOID; | ||
1904 | if (prop) { | ||
1905 | if (OBJ_IS_NATIVE(pobj)) { | ||
1906 | sprop = (JSScopeProperty *) prop; | ||
1907 | if (sprop->attrs & JSPROP_GETTER) | ||
1908 | siliconforks | 460 | *vp = js_CastAsObjectJSVal(sprop->getter); |
1909 | siliconforks | 332 | } |
1910 | OBJ_DROP_PROPERTY(cx, pobj, prop); | ||
1911 | } | ||
1912 | return JS_TRUE; | ||
1913 | } | ||
1914 | |||
1915 | static JSBool | ||
1916 | obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp) | ||
1917 | { | ||
1918 | jsid id; | ||
1919 | JSObject *obj, *pobj; | ||
1920 | JSProperty *prop; | ||
1921 | JSScopeProperty *sprop; | ||
1922 | |||
1923 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | ||
1924 | return JS_FALSE; | ||
1925 | obj = JS_THIS_OBJECT(cx, vp); | ||
1926 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | ||
1927 | return JS_FALSE; | ||
1928 | *vp = JSVAL_VOID; | ||
1929 | if (prop) { | ||
1930 | if (OBJ_IS_NATIVE(pobj)) { | ||
1931 | sprop = (JSScopeProperty *) prop; | ||
1932 | if (sprop->attrs & JSPROP_SETTER) | ||
1933 | siliconforks | 460 | *vp = js_CastAsObjectJSVal(sprop->setter); |
1934 | siliconforks | 332 | } |
1935 | OBJ_DROP_PROPERTY(cx, pobj, prop); | ||
1936 | } | ||
1937 | return JS_TRUE; | ||
1938 | } | ||
1939 | #endif /* JS_HAS_GETTER_SETTER */ | ||
1940 | |||
1941 | JSBool | ||
1942 | obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp) | ||
1943 | { | ||
1944 | JSObject *obj; | ||
1945 | uintN attrs; | ||
1946 | |||
1947 | if (argc == 0) { | ||
1948 | js_ReportMissingArg(cx, vp, 0); | ||
1949 | return JS_FALSE; | ||
1950 | } | ||
1951 | |||
1952 | obj = js_ValueToNonNullObject(cx, vp[2]); | ||
1953 | if (!obj) | ||
1954 | return JS_FALSE; | ||
1955 | vp[2] = OBJECT_TO_JSVAL(obj); | ||
1956 | |||
1957 | return OBJ_CHECK_ACCESS(cx, obj, | ||
1958 | ATOM_TO_JSID(cx->runtime->atomState.protoAtom), | ||
1959 | JSACC_PROTO, vp, &attrs); | ||
1960 | } | ||
1961 | |||
1962 | #if JS_HAS_OBJ_WATCHPOINT | ||
1963 | const char js_watch_str[] = "watch"; | ||
1964 | const char js_unwatch_str[] = "unwatch"; | ||
1965 | #endif | ||
1966 | const char js_hasOwnProperty_str[] = "hasOwnProperty"; | ||
1967 | const char js_isPrototypeOf_str[] = "isPrototypeOf"; | ||
1968 | const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; | ||
1969 | #if JS_HAS_GETTER_SETTER | ||
1970 | const char js_defineGetter_str[] = "__defineGetter__"; | ||
1971 | const char js_defineSetter_str[] = "__defineSetter__"; | ||
1972 | const char js_lookupGetter_str[] = "__lookupGetter__"; | ||
1973 | const char js_lookupSetter_str[] = "__lookupSetter__"; | ||
1974 | #endif | ||
1975 | |||
1976 | siliconforks | 460 | JS_DEFINE_TRCINFO_1(obj_valueOf, |
1977 | (3, (static, JSVAL, Object_p_valueOf, CONTEXT, THIS, STRING, 0, 0))) | ||
1978 | siliconforks | 399 | JS_DEFINE_TRCINFO_1(obj_hasOwnProperty, |
1979 | siliconforks | 460 | (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0))) |
1980 | siliconforks | 399 | JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable, |
1981 | siliconforks | 460 | (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0))) |
1982 | siliconforks | 399 | |
1983 | siliconforks | 332 | static JSFunctionSpec object_methods[] = { |
1984 | #if JS_HAS_TOSOURCE | ||
1985 | JS_FN(js_toSource_str, obj_toSource, 0,0), | ||
1986 | #endif | ||
1987 | JS_FN(js_toString_str, obj_toString, 0,0), | ||
1988 | JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), | ||
1989 | siliconforks | 460 | JS_TN(js_valueOf_str, obj_valueOf, 0,0, |
1990 | obj_valueOf_trcinfo), | ||
1991 | siliconforks | 332 | #if JS_HAS_OBJ_WATCHPOINT |
1992 | JS_FN(js_watch_str, obj_watch, 2,0), | ||
1993 | JS_FN(js_unwatch_str, obj_unwatch, 1,0), | ||
1994 | #endif | ||
1995 | siliconforks | 399 | JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, |
1996 | obj_hasOwnProperty_trcinfo), | ||
1997 | siliconforks | 332 | JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), |
1998 | siliconforks | 399 | JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, |
1999 | obj_propertyIsEnumerable_trcinfo), | ||
2000 | siliconforks | 332 | #if JS_HAS_GETTER_SETTER |
2001 | siliconforks | 460 | JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), |
2002 | JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), | ||
2003 | siliconforks | 332 | JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), |
2004 | JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), | ||
2005 | #endif | ||
2006 | JS_FS_END | ||
2007 | }; | ||
2008 | |||
2009 | static JSFunctionSpec object_static_methods[] = { | ||
2010 | siliconforks | 399 | JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0), |
2011 | siliconforks | 332 | JS_FS_END |
2012 | }; | ||
2013 | |||
2014 | JSBool | ||
2015 | js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | ||
2016 | { | ||
2017 | if (argc == 0) { | ||
2018 | /* Trigger logic below to construct a blank object. */ | ||
2019 | obj = NULL; | ||
2020 | } else { | ||
2021 | /* If argv[0] is null or undefined, obj comes back null. */ | ||
2022 | if (!js_ValueToObject(cx, argv[0], &obj)) | ||
2023 | return JS_FALSE; | ||
2024 | } | ||
2025 | if (!obj) { | ||
2026 | JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); | ||
2027 | siliconforks | 460 | if (JS_IsConstructing(cx)) |
2028 | siliconforks | 332 | return JS_TRUE; |
2029 | obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); | ||
2030 | if (!obj) | ||
2031 | return JS_FALSE; | ||
2032 | } | ||
2033 | *rval = OBJECT_TO_JSVAL(obj); | ||
2034 | return JS_TRUE; | ||
2035 | } | ||
2036 | |||
2037 | siliconforks | 460 | static inline bool |
2038 | InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, | ||
2039 | JSObjectOps* ops) | ||
2040 | { | ||
2041 | JS_ASSERT(OPS_IS_NATIVE(ops)); | ||
2042 | JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); | ||
2043 | |||
2044 | JSClass* protoclasp; | ||
2045 | JSClass* clasp = OBJ_GET_CLASS(cx, obj); | ||
2046 | |||
2047 | /* | ||
2048 | * Share proto's map only if it has the same JSObjectOps, and only if | ||
2049 | * proto's class has the same private and reserved slots as obj's map | ||
2050 | * and class have. We assume that if prototype and object are of the | ||
2051 | * same class, they always have the same number of computed reserved | ||
2052 | * slots (returned via clasp->reserveSlots); otherwise, prototype and | ||
2053 | * object classes must have the same (null or not) reserveSlots hook. | ||
2054 | */ | ||
2055 | if (proto && | ||
2056 | proto->map->ops == ops && | ||
2057 | ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || | ||
2058 | (!((protoclasp->flags ^ clasp->flags) & | ||
2059 | (JSCLASS_HAS_PRIVATE | | ||
2060 | (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && | ||
2061 | protoclasp->reserveSlots == clasp->reserveSlots))) | ||
2062 | { | ||
2063 | js_HoldScope(OBJ_SCOPE(proto)); | ||
2064 | obj->map = proto->map; | ||
2065 | return true; | ||
2066 | } | ||
2067 | |||
2068 | JSScope *scope = js_NewScope(cx, ops, clasp, obj); | ||
2069 | if (!scope) | ||
2070 | goto bad; | ||
2071 | |||
2072 | /* Let js_NewScope set freeslot so as to reserve slots. */ | ||
2073 | JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); | ||
2074 | if (scope->freeslot > JS_INITIAL_NSLOTS && | ||
2075 | !js_ReallocSlots(cx, obj, scope->freeslot, JS_TRUE)) { | ||
2076 | js_DestroyScope(cx, scope); | ||
2077 | goto bad; | ||
2078 | } | ||
2079 | obj->map = &scope->map; | ||
2080 | return true; | ||
2081 | |||
2082 | bad: | ||
2083 | /* Ensure that the map field is initialized for GC. */ | ||
2084 | obj->map = NULL; | ||
2085 | return false; | ||
2086 | } | ||
2087 | |||
2088 | #ifdef JS_TRACER | ||
2089 | |||
2090 | static inline JSObject* | ||
2091 | NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, JSObject *parent) | ||
2092 | { | ||
2093 | JS_ASSERT(JS_ON_TRACE(cx)); | ||
2094 | JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); | ||
2095 | if (!obj) | ||
2096 | return NULL; | ||
2097 | |||
2098 | obj->classword = jsuword(clasp); | ||
2099 | obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); | ||
2100 | obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); | ||
2101 | for (unsigned i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) | ||
2102 | obj->fslots[i] = JSVAL_VOID; | ||
2103 | |||
2104 | obj->dslots = NULL; | ||
2105 | return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; | ||
2106 | } | ||
2107 | |||
2108 | JSObject* FASTCALL | ||
2109 | js_Object_tn(JSContext* cx, JSObject* proto) | ||
2110 | { | ||
2111 | return NewNativeObject(cx, &js_ObjectClass, proto, JSVAL_TO_OBJECT(proto->fslots[JSSLOT_PARENT])); | ||
2112 | } | ||
2113 | |||
2114 | JS_DEFINE_TRCINFO_1(js_Object, | ||
2115 | (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0))) | ||
2116 | |||
2117 | JSObject* FASTCALL | ||
2118 | js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor) | ||
2119 | { | ||
2120 | JS_ASSERT(HAS_FUNCTION_CLASS(ctor)); | ||
2121 | |||
2122 | JSAtom *atom = cx->runtime->atomState.classPrototypeAtom; | ||
2123 | |||
2124 | JSScope *scope = OBJ_SCOPE(ctor); | ||
2125 | #ifdef JS_THREADSAFE | ||
2126 | if (scope->title.ownercx != cx) | ||
2127 | return NULL; | ||
2128 | #endif | ||
2129 | if (scope->object != ctor) { | ||
2130 | scope = js_GetMutableScope(cx, ctor); | ||
2131 | if (!scope) | ||
2132 | return NULL; | ||
2133 | } | ||
2134 | |||
2135 | JSScopeProperty *sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); | ||
2136 | jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE; | ||
2137 | |||
2138 | JSObject *proto; | ||
2139 | if (!JSVAL_IS_PRIMITIVE(pval)) { | ||
2140 | /* An object in ctor.prototype, let's use it as the new instance's proto. */ | ||
2141 | proto = JSVAL_TO_OBJECT(pval); | ||
2142 | } else if (pval == JSVAL_HOLE) { | ||
2143 | /* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */ | ||
2144 | proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor), 0); | ||
2145 | if (!proto) | ||
2146 | return NULL; | ||
2147 | if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) | ||
2148 | return NULL; | ||
2149 | } else { | ||
2150 | /* Primitive value in .prototype means we use Object.prototype for proto. */ | ||
2151 | if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]), | ||
2152 | INT_TO_JSID(JSProto_Object), &proto)) { | ||
2153 | return NULL; | ||
2154 | } | ||
2155 | } | ||
2156 | |||
2157 | return NewNativeObject(cx, clasp, proto, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT])); | ||
2158 | } | ||
2159 | |||
2160 | JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0) | ||
2161 | |||
2162 | #else /* !JS_TRACER */ | ||
2163 | |||
2164 | # define js_Object_trcinfo NULL | ||
2165 | |||
2166 | #endif /* !JS_TRACER */ | ||
2167 | |||
2168 | siliconforks | 332 | /* |
2169 | siliconforks | 460 | * Given pc pointing after a property accessing bytecode, return true if the |
2170 | * access is "object-detecting" in the sense used by web scripts, e.g., when | ||
2171 | * checking whether document.all is defined. | ||
2172 | */ | ||
2173 | static JS_REQUIRES_STACK JSBool | ||
2174 | Detecting(JSContext *cx, jsbytecode *pc) | ||
2175 | { | ||
2176 | JSScript *script; | ||
2177 | jsbytecode *endpc; | ||
2178 | JSOp op; | ||
2179 | JSAtom *atom; | ||
2180 | |||
2181 | script = cx->fp->script; | ||
2182 | endpc = script->code + script->length; | ||
2183 | for (;; pc += js_CodeSpec[op].length) { | ||
2184 | JS_ASSERT(pc < endpc); | ||
2185 | |||
2186 | /* General case: a branch or equality op follows the access. */ | ||
2187 | op = js_GetOpcode(cx, script, pc); | ||
2188 | if (js_CodeSpec[op].format & JOF_DETECTING) | ||
2189 | return JS_TRUE; | ||
2190 | |||
2191 | switch (op) { | ||
2192 | case JSOP_NULL: | ||
2193 | /* | ||
2194 | * Special case #1: handle (document.all == null). Don't sweat | ||
2195 | * about JS1.2's revision of the equality operators here. | ||
2196 | */ | ||
2197 | if (++pc < endpc) { | ||
2198 | op = js_GetOpcode(cx, script, pc); | ||
2199 | return *pc == JSOP_EQ || *pc == JSOP_NE; | ||
2200 | } | ||
2201 | return JS_FALSE; | ||
2202 | |||
2203 | case JSOP_NAME: | ||
2204 | /* | ||
2205 | * Special case #2: handle (document.all == undefined). Don't | ||
2206 | * worry about someone redefining undefined, which was added by | ||
2207 | * Edition 3, so is read/write for backward compatibility. | ||
2208 | */ | ||
2209 | GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); | ||
2210 | if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && | ||
2211 | (pc += js_CodeSpec[op].length) < endpc) { | ||
2212 | op = js_GetOpcode(cx, script, pc); | ||
2213 | return op == JSOP_EQ || op == JSOP_NE || | ||
2214 | op == JSOP_STRICTEQ || op == JSOP_STRICTNE; | ||
2215 | } | ||
2216 | return JS_FALSE; | ||
2217 | |||
2218 | default: | ||
2219 | /* | ||
2220 | * At this point, anything but an extended atom index prefix means | ||
2221 | * we're not detecting. | ||
2222 | */ | ||
2223 | if (!(js_CodeSpec[op].format & JOF_INDEXBASE)) | ||
2224 | return JS_FALSE; | ||
2225 | break; | ||
2226 | } | ||
2227 | } | ||
2228 | } | ||
2229 | |||
2230 | /* | ||
2231 | * Infer lookup flags from the currently executing bytecode. This does | ||
2232 | * not attempt to infer JSRESOLVE_WITH, because the current bytecode | ||
2233 | * does not indicate whether we are in a with statement. Return defaultFlags | ||
2234 | * if a currently executing bytecode cannot be determined. | ||
2235 | */ | ||
2236 | static uintN | ||
2237 | InferFlags(JSContext *cx, uintN defaultFlags) | ||
2238 | { | ||
2239 | JSStackFrame *fp; | ||
2240 | jsbytecode *pc; | ||
2241 | const JSCodeSpec *cs; | ||
2242 | uint32 format; | ||
2243 | uintN flags = 0; | ||
2244 | |||
2245 | fp = js_GetTopStackFrame(cx); | ||
2246 | if (!fp || !fp->regs) | ||
2247 | return defaultFlags; | ||
2248 | pc = fp->regs->pc; | ||
2249 | cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)]; | ||
2250 | format = cs->format; | ||
2251 | if (JOF_MODE(format) != JOF_NAME) | ||
2252 | flags |= JSRESOLVE_QUALIFIED; | ||
2253 | if ((format & (JOF_SET | JOF_FOR)) || | ||
2254 | (fp->flags & JSFRAME_ASSIGNING)) { | ||
2255 | flags |= JSRESOLVE_ASSIGNING; | ||
2256 | } else { | ||
2257 | pc += cs->length; | ||
2258 | if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc)) | ||
2259 | flags |= JSRESOLVE_DETECTING; | ||
2260 | } | ||
2261 | if (format & JOF_DECLARING) | ||
2262 | flags |= JSRESOLVE_DECLARING; | ||
2263 | return flags; | ||
2264 | } | ||
2265 | |||
2266 | /* | ||
2267 | siliconforks | 332 | * ObjectOps and Class for with-statement stack objects. |
2268 | */ | ||
2269 | static JSBool | ||
2270 | with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, | ||
2271 | JSProperty **propp) | ||
2272 | { | ||
2273 | siliconforks | 460 | /* Fixes bug 463997 */ |
2274 | uintN flags = cx->resolveFlags; | ||
2275 | if (flags == JSRESOLVE_INFER) | ||
2276 | flags = InferFlags(cx, flags); | ||
2277 | flags |= JSRESOLVE_WITH; | ||
2278 | JSAutoResolveFlags rf(cx, flags); | ||
2279 | siliconforks | 332 | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2280 | if (!proto) | ||
2281 | return js_LookupProperty(cx, obj, id, objp, propp); | ||
2282 | return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); | ||
2283 | } | ||
2284 | |||
2285 | static JSBool | ||
2286 | with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) | ||
2287 | { | ||
2288 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2289 | if (!proto) | ||
2290 | return js_GetProperty(cx, obj, id, vp); | ||
2291 | return OBJ_GET_PROPERTY(cx, proto, id, vp); | ||
2292 | } | ||
2293 | |||
2294 | static JSBool | ||
2295 | with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) | ||
2296 | { | ||
2297 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2298 | if (!proto) | ||
2299 | return js_SetProperty(cx, obj, id, vp); | ||
2300 | return OBJ_SET_PROPERTY(cx, proto, id, vp); | ||
2301 | } | ||
2302 | |||
2303 | static JSBool | ||
2304 | with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, | ||
2305 | uintN *attrsp) | ||
2306 | { | ||
2307 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2308 | if (!proto) | ||
2309 | return js_GetAttributes(cx, obj, id, prop, attrsp); | ||
2310 | return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); | ||
2311 | } | ||
2312 | |||
2313 | static JSBool | ||
2314 | with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, | ||
2315 | uintN *attrsp) | ||
2316 | { | ||
2317 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2318 | if (!proto) | ||
2319 | return js_SetAttributes(cx, obj, id, prop, attrsp); | ||
2320 | return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); | ||
2321 | } | ||
2322 | |||
2323 | static JSBool | ||
2324 | with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) | ||
2325 | { | ||
2326 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2327 | if (!proto) | ||
2328 | return js_DeleteProperty(cx, obj, id, rval); | ||
2329 | return OBJ_DELETE_PROPERTY(cx, proto, id, rval); | ||
2330 | } | ||
2331 | |||
2332 | static JSBool | ||
2333 | with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) | ||
2334 | { | ||
2335 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2336 | if (!proto) | ||
2337 | return js_DefaultValue(cx, obj, hint, vp); | ||
2338 | return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); | ||
2339 | } | ||
2340 | |||
2341 | static JSBool | ||
2342 | with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, | ||
2343 | jsval *statep, jsid *idp) | ||
2344 | { | ||
2345 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2346 | if (!proto) | ||
2347 | return js_Enumerate(cx, obj, enum_op, statep, idp); | ||
2348 | return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); | ||
2349 | } | ||
2350 | |||
2351 | static JSBool | ||
2352 | with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, | ||
2353 | jsval *vp, uintN *attrsp) | ||
2354 | { | ||
2355 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2356 | if (!proto) | ||
2357 | return js_CheckAccess(cx, obj, id, mode, vp, attrsp); | ||
2358 | return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); | ||
2359 | } | ||
2360 | |||
2361 | static JSObject * | ||
2362 | with_ThisObject(JSContext *cx, JSObject *obj) | ||
2363 | { | ||
2364 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | ||
2365 | if (!proto) | ||
2366 | return obj; | ||
2367 | return OBJ_THIS_OBJECT(cx, proto); | ||
2368 | } | ||
2369 | |||
2370 | JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { | ||
2371 | siliconforks | 460 | NULL, |
2372 | siliconforks | 332 | with_LookupProperty, js_DefineProperty, |
2373 | with_GetProperty, with_SetProperty, | ||
2374 | with_GetAttributes, with_SetAttributes, | ||
2375 | with_DeleteProperty, with_DefaultValue, | ||
2376 | with_Enumerate, with_CheckAccess, | ||
2377 | with_ThisObject, NATIVE_DROP_PROPERTY, | ||
2378 | NULL, NULL, | ||
2379 | siliconforks | 460 | NULL, js_TraceObject, |
2380 | js_Clear, NULL, | ||
2381 | NULL | ||
2382 | siliconforks | 332 | }; |
2383 | |||
2384 | static JSObjectOps * | ||
2385 | with_getObjectOps(JSContext *cx, JSClass *clasp) | ||
2386 | { | ||
2387 | return &js_WithObjectOps; | ||
2388 | } | ||
2389 | |||
2390 | JSClass js_WithClass = { | ||
2391 | "With", | ||
2392 | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, | ||
2393 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | ||
2394 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | ||
2395 | with_getObjectOps, | ||
2396 | 0,0,0,0,0,0,0 | ||
2397 | }; | ||
2398 | |||
2399 | siliconforks | 460 | JS_REQUIRES_STACK JSObject * |
2400 | siliconforks | 332 | js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) |
2401 | { | ||
2402 | JSObject *obj; | ||
2403 | |||
2404 | obj = js_NewObject(cx, &js_WithClass, proto, parent, 0); | ||
2405 | if (!obj) | ||
2406 | return NULL; | ||
2407 | STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp)); | ||
2408 | OBJ_SET_BLOCK_DEPTH(cx, obj, depth); | ||
2409 | return obj; | ||
2410 | } | ||
2411 | |||
2412 | JSObject * | ||
2413 | js_NewBlockObject(JSContext *cx) | ||
2414 | { | ||
2415 | /* | ||
2416 | * Null obj's proto slot so that Object.prototype.* does not pollute block | ||
2417 | siliconforks | 460 | * scopes and to give the block object its own scope. |
2418 | siliconforks | 332 | */ |
2419 | siliconforks | 460 | JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, |
2420 | NULL, NULL, 0); | ||
2421 | JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj)); | ||
2422 | return blockObj; | ||
2423 | siliconforks | 332 | } |
2424 | |||
2425 | JSObject * | ||
2426 | js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, | ||
2427 | JSStackFrame *fp) | ||
2428 | { | ||
2429 | JSObject *clone; | ||
2430 | |||
2431 | JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass); | ||
2432 | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto)); | ||
2433 | clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0); | ||
2434 | if (!clone) | ||
2435 | return NULL; | ||
2436 | STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp)); | ||
2437 | STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH, | ||
2438 | OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH)); | ||
2439 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); | ||
2440 | siliconforks | 460 | JS_ASSERT(OBJ_SCOPE(clone)->object == proto); |
2441 | siliconforks | 332 | return clone; |
2442 | } | ||
2443 | |||
2444 | siliconforks | 460 | JS_REQUIRES_STACK JSBool |
2445 | siliconforks | 332 | js_PutBlockObject(JSContext *cx, JSBool normalUnwind) |
2446 | { | ||
2447 | JSStackFrame *fp; | ||
2448 | JSObject *obj; | ||
2449 | uintN depth, count; | ||
2450 | |||
2451 | /* Blocks have one fixed slot available for the first local.*/ | ||
2452 | JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2); | ||
2453 | |||
2454 | fp = cx->fp; | ||
2455 | obj = fp->scopeChain; | ||
2456 | JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); | ||
2457 | JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp); | ||
2458 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); | ||
2459 | |||
2460 | /* | ||
2461 | * Block objects should never be exposed to scripts. Thus the clone should | ||
2462 | * not own the property map and rather always share it with the prototype | ||
2463 | siliconforks | 460 | * object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after |
2464 | siliconforks | 332 | * we copy the stack slots into reserved slots. |
2465 | */ | ||
2466 | JS_ASSERT(OBJ_SCOPE(obj)->object != obj); | ||
2467 | |||
2468 | /* Block objects should not have reserved slots before they are put. */ | ||
2469 | JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS); | ||
2470 | |||
2471 | /* The block and its locals must be on the current stack for GC safety. */ | ||
2472 | depth = OBJ_BLOCK_DEPTH(cx, obj); | ||
2473 | count = OBJ_BLOCK_COUNT(cx, obj); | ||
2474 | JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp))); | ||
2475 | JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)); | ||
2476 | |||
2477 | /* See comments in CheckDestructuring from jsparse.c. */ | ||
2478 | JS_ASSERT(count >= 1); | ||
2479 | |||
2480 | depth += fp->script->nfixed; | ||
2481 | obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth]; | ||
2482 | if (normalUnwind && count > 1) { | ||
2483 | --count; | ||
2484 | JS_LOCK_OBJ(cx, obj); | ||
2485 | if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE)) | ||
2486 | normalUnwind = JS_FALSE; | ||
2487 | else | ||
2488 | memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); | ||
2489 | JS_UNLOCK_OBJ(cx, obj); | ||
2490 | } | ||
2491 | |||
2492 | /* We must clear the private slot even with errors. */ | ||
2493 | JS_SetPrivate(cx, obj, NULL); | ||
2494 | fp->scopeChain = OBJ_GET_PARENT(cx, obj); | ||
2495 | return normalUnwind; | ||
2496 | } | ||
2497 | |||
2498 | static JSBool | ||
2499 | block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2500 | { | ||
2501 | uintN index; | ||
2502 | JSStackFrame *fp; | ||
2503 | |||
2504 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); | ||
2505 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); | ||
2506 | if (!JSVAL_IS_INT(id)) | ||
2507 | return JS_TRUE; | ||
2508 | |||
2509 | index = (uint16) JSVAL_TO_INT(id); | ||
2510 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); | ||
2511 | if (fp) { | ||
2512 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); | ||
2513 | JS_ASSERT(index < fp->script->nslots); | ||
2514 | *vp = fp->slots[index]; | ||
2515 | return JS_TRUE; | ||
2516 | } | ||
2517 | |||
2518 | /* Reserve slots start with the first slot after the private. */ | ||
2519 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; | ||
2520 | return JS_GetReservedSlot(cx, obj, index, vp); | ||
2521 | } | ||
2522 | |||
2523 | static JSBool | ||
2524 | block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | ||
2525 | { | ||
2526 | uintN index; | ||
2527 | JSStackFrame *fp; | ||
2528 | |||
2529 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); | ||
2530 | if (!JSVAL_IS_INT(id)) | ||
2531 | return JS_TRUE; | ||
2532 | |||
2533 | index = (uint16) JSVAL_TO_INT(id); | ||
2534 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); | ||
2535 | if (fp) { | ||
2536 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); | ||
2537 | JS_ASSERT(index < fp->script->nslots); | ||
2538 | fp->slots[index] = *vp; | ||
2539 | return JS_TRUE; | ||
2540 | } | ||
2541 | |||
2542 | /* Reserve slots start with the first slot after the private. */ | ||
2543 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; | ||
2544 | return JS_SetReservedSlot(cx, obj, index, *vp); | ||
2545 | } | ||
2546 | |||
2547 | #if JS_HAS_XDR | ||
2548 | |||
2549 | #define NO_PARENT_INDEX ((uint32)-1) | ||
2550 | |||
2551 |