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