1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* ***** BEGIN LICENSE BLOCK *****
4
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 1.1 (the "License"); you may not use this file except in compliance with
8
* the License. You may obtain a copy of the License at
9
* http://www.mozilla.org/MPL/
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
16
* The Original Code is Mozilla Communicator client code, released
19
* The Initial Developer of the Original Code is
20
* Netscape Communications Corporation.
21
* Portions created by the Initial Developer are Copyright (C) 1998
22
* the Initial Developer. All Rights Reserved.
26
* Alternatively, the contents of this file may be used under the terms of
27
* either of the GNU General Public License Version 2 or later (the "GPL"),
28
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
* in which case the provisions of the GPL or the LGPL are applicable instead
30
* of those above. If you wish to allow use of your version of this file only
31
* under the terms of either the GPL or the LGPL, and not to allow others to
32
* use your version of this file under the terms of the MPL, indicate your
33
* decision by deleting the provisions above and replace them with the notice
34
* and other provisions required by the GPL or the LGPL. If you do not delete
35
* the provisions above, a recipient may use your version of this file under
36
* the terms of any one of the MPL, the GPL or the LGPL.
38
* ***** END LICENSE BLOCK ***** */
41
* JS object implementation.
47
#include "jsarena.h" /* Added by JSIFY */
48
#include "jsutil.h" /* Added by JSIFY */
49
#include "jshash.h" /* Added by JSIFY */
69
#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
72
#define NATIVE_DROP_PROPERTY js_DropProperty
75
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
77
#define NATIVE_DROP_PROPERTY NULL
84
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
85
js_NewObjectMap, js_DestroyObjectMap,
86
#if defined JS_THREADSAFE && defined DEBUG
87
_js_LookupProperty, js_DefineProperty,
89
js_LookupProperty, js_DefineProperty,
91
js_GetProperty, js_SetProperty,
92
js_GetAttributes, js_SetAttributes,
93
js_DeleteProperty, js_DefaultValue,
94
js_Enumerate, js_CheckAccess,
95
NULL, NATIVE_DROP_PROPERTY,
96
js_Call, js_Construct,
98
js_SetProtoOrParent, js_SetProtoOrParent,
100
js_GetRequiredSlot, js_SetRequiredSlot
107
JSClass js_ObjectClass = {
110
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
111
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
112
JSCLASS_NO_OPTIONAL_MEMBERS
115
#if JS_HAS_OBJ_PROTO_PROP
118
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
121
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
124
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
126
static JSPropertySpec object_props[] = {
127
/* These two must come first; see object_props[slot].name usage below. */
128
{js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
129
obj_getSlot, obj_setSlot},
130
{js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
131
obj_getSlot, obj_setSlot},
132
{js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount},
136
/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
137
#define JSSLOT_COUNT 2
140
ReportStrictSlot(JSContext *cx, uint32 slot)
142
if (slot == JSSLOT_PROTO)
144
return JS_ReportErrorFlagsAndNumber(cx,
145
JSREPORT_WARNING | JSREPORT_STRICT,
146
js_GetErrorMessage, NULL,
147
JSMSG_DEPRECATED_USAGE,
148
object_props[slot].name);
152
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
158
slot = (uint32) JSVAL_TO_INT(id);
159
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
161
if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
162
id = (jsid)cx->runtime->atomState.protoAtom;
165
id = (jsid)cx->runtime->atomState.parentAtom;
168
if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
170
*vp = OBJ_GET_SLOT(cx, obj, slot);
175
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
181
if (!JSVAL_IS_OBJECT(*vp))
183
pobj = JSVAL_TO_OBJECT(*vp);
184
slot = (uint32) JSVAL_TO_INT(id);
185
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
188
/* __parent__ is readonly and permanent, only __proto__ may be set. */
189
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
192
return js_SetProtoOrParent(cx, obj, slot, pobj);
196
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
202
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
205
/* Get the number of properties to enumerate. */
206
iter_state = JSVAL_NULL;
207
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
211
if (!JSVAL_IS_INT(num_properties)) {
216
*vp = num_properties;
219
if (iter_state != JSVAL_NULL)
220
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
224
#else /* !JS_HAS_OBJ_PROTO_PROP */
226
#define object_props NULL
228
#endif /* !JS_HAS_OBJ_PROTO_PROP */
231
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
234
JSObject *obj2, *oldproto;
235
JSScope *scope, *newscope;
238
* Serialize all proto and parent setting in order to detect cycles.
239
* We nest locks in this function, and only here, in the following orders:
241
* (1) rt->setSlotLock < pobj's scope lock;
242
* rt->setSlotLock < pobj's proto-or-parent's scope lock;
243
* rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
245
* (2) rt->setSlotLock < obj's scope lock < pobj's scope lock.
247
* We avoid AB-BA deadlock by restricting obj from being on pobj's parent
248
* or proto chain (pobj may already be on obj's parent or proto chain; it
249
* could be moving up or down). We finally order obj with respect to pobj
250
* at the bottom of this routine (just before releasing rt->setSlotLock),
251
* by making pobj be obj's prototype or parent.
253
* After we have set the slot and released rt->setSlotLock, another call
254
* to js_SetProtoOrParent could nest locks according to the first order
255
* list above, but it cannot deadlock with any other thread. For there
256
* to be a deadlock, other parts of the engine would have to nest scope
257
* locks in the opposite order. XXXbe ensure they don't!
262
JS_ACQUIRE_LOCK(rt->setSlotLock);
263
while (rt->setSlotBusy) {
264
jsrefcount saveDepth;
266
/* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
267
JS_RELEASE_LOCK(rt->setSlotLock);
268
saveDepth = JS_SuspendRequest(cx);
269
JS_ACQUIRE_LOCK(rt->setSlotLock);
271
JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
272
JS_RELEASE_LOCK(rt->setSlotLock);
273
JS_ResumeRequest(cx, saveDepth);
274
JS_ACQUIRE_LOCK(rt->setSlotLock);
276
rt->setSlotBusy = JS_TRUE;
277
JS_RELEASE_LOCK(rt->setSlotLock);
279
#define SET_SLOT_DONE(rt) \
281
JS_ACQUIRE_LOCK((rt)->setSlotLock); \
282
(rt)->setSlotBusy = JS_FALSE; \
283
JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \
284
JS_RELEASE_LOCK((rt)->setSlotLock); \
289
#define SET_SLOT_DONE(rt) /* nothing */
297
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
299
#if JS_HAS_OBJ_PROTO_PROP
300
object_props[slot].name
302
(slot == JSSLOT_PROTO) ? js_proto_str
308
obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
311
if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
312
/* Check to see whether obj shares its prototype's scope. */
313
JS_LOCK_OBJ(cx, obj);
314
scope = OBJ_SCOPE(obj);
315
oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
316
if (oldproto && OBJ_SCOPE(oldproto) == scope) {
317
/* Either obj needs a new empty scope, or it should share pobj's. */
319
!OBJ_IS_NATIVE(pobj) ||
320
OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
322
* With no proto and no scope of its own, obj is truly empty.
324
* If pobj is not native, obj needs its own empty scope -- it
325
* should not continue to share oldproto's scope once oldproto
326
* is not on obj's prototype chain. That would put properties
327
* from oldproto's scope ahead of properties defined by pobj,
330
* If pobj's class differs from oldproto's, we may need a new
331
* scope to handle differences in private and reserved slots,
332
* so we suboptimally but safely make one.
334
scope = js_GetMutableScope(cx, obj);
336
JS_UNLOCK_OBJ(cx, obj);
340
} else if (OBJ_SCOPE(pobj) != scope) {
343
* We are about to nest scope locks. Help jslock.c:ShareScope
344
* keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
345
* avoiding deadlock, by recording scope in rt->setSlotScope.
347
if (scope->ownercx) {
348
JS_ASSERT(scope->ownercx == cx);
349
rt->setSlotScope = scope;
353
/* We can't deadlock because we checked for cycles above (2). */
354
JS_LOCK_OBJ(cx, pobj);
355
newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
356
obj->map = &newscope->map;
357
js_DropObjectMap(cx, &scope->map, obj);
358
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
361
rt->setSlotScope = NULL;
365
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
366
JS_UNLOCK_SCOPE(cx, scope);
368
OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
377
JS_STATIC_DLL_CALLBACK(JSHashNumber)
378
js_hash_object(const void *key)
380
return (JSHashNumber)key >> JSVAL_TAGBITS;
384
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
386
JSSharpObjectMap *map;
389
JSHashEntry **hep, *he;
395
#if JS_HAS_GETTER_SETTER
402
map = &cx->sharpObjectMap;
404
hash = js_hash_object(obj);
405
hep = JS_HashTableRawLookup(table, hash, obj);
409
he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
411
JS_ReportOutOfMemory(cx);
414
ida = JS_Enumerate(cx, obj);
418
for (i = 0, length = ida->length; i < length; i++) {
420
#if JS_HAS_GETTER_SETTER
421
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
425
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
427
if (OBJ_IS_NATIVE(obj2) &&
428
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
430
if (attrs & JSPROP_GETTER)
431
val = (jsval) ((JSScopeProperty*)prop)->getter;
432
if (attrs & JSPROP_SETTER) {
433
if (val != JSVAL_NULL) {
434
/* Mark the getter, then set val to setter. */
435
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
439
val = (jsval) ((JSScopeProperty*)prop)->setter;
442
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
445
OBJ_DROP_PROPERTY(cx, obj2, prop);
448
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
452
if (!JSVAL_IS_PRIMITIVE(val) &&
453
!MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
459
JS_DestroyIdArray(cx, ida);
463
sharpid = (jsatomid) he->value;
465
sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
466
he->value = (void *) sharpid;
476
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
479
JSSharpObjectMap *map;
483
JSHashEntry *he, **hep;
488
/* Set to null in case we return an early error. */
490
map = &cx->sharpObjectMap;
493
table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
494
JS_CompareValues, NULL, NULL);
496
JS_ReportOutOfMemory(cx);
503
if (map->depth == 0) {
504
he = MarkSharpObjects(cx, obj, &ida);
507
JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
509
JS_DestroyIdArray(cx, ida);
513
hash = js_hash_object(obj);
514
hep = JS_HashTableRawLookup(table, hash, obj);
518
* It's possible that the value of a property has changed from the
519
* first time the object's properties are traversed (when the property
520
* ids are entered into the hash table) to the second (when they are
521
* converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
525
he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
527
JS_ReportOutOfMemory(cx);
536
sharpid = (jsatomid) he->value;
540
len = JS_snprintf(buf, sizeof buf, "#%u%c",
541
sharpid >> SHARP_ID_SHIFT,
542
(sharpid & SHARP_BIT) ? '#' : '=');
543
*sp = js_InflateString(cx, buf, len);
546
JS_DestroyIdArray(cx, ida);
553
if ((sharpid & SHARP_BIT) == 0) {
555
ida = JS_Enumerate(cx, obj);
572
/* Clean up the sharpObjectMap table on outermost error. */
573
if (map->depth == 0) {
575
JS_HashTableDestroy(map->table);
582
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
584
JSSharpObjectMap *map;
587
map = &cx->sharpObjectMap;
588
JS_ASSERT(map->depth > 0);
589
if (--map->depth == 0) {
591
JS_HashTableDestroy(map->table);
597
JS_DestroyIdArray(cx, ida);
603
#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */
605
#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
607
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
610
JSBool ok, outermost;
613
jschar *chars, *ochars, *vsharp;
614
const jschar *idstrchars, *vchars;
615
size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
617
jsint i, j, length, valcnt;
619
#if JS_HAS_GETTER_SETTER
627
JSString *idstr, *valstr, *str;
630
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
631
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
636
* obj_toString for 1.2 calls toSource, and doesn't want the extra parens
639
outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
640
he = js_EnterSharpObject(cx, obj, &ida, &chars);
645
* We didn't enter -- obj is already "sharp", meaning we've visited it
646
* already in our depth first search, and therefore chars contains a
647
* string of the form "#n#".
650
#if JS_HAS_SHARP_VARS
651
nchars = js_strlen(chars);
664
/* If outermost, allocate 4 + 1 for "({})" and the terminator. */
665
chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
670
chars[nchars++] = '(';
672
/* js_EnterSharpObject returned a string of the form "#n=" in chars. */
674
nchars = js_strlen(chars);
676
realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
683
* No need for parentheses around the whole shebang, because #n=
684
* unambiguously begins an object initializer, and never a block
687
outermost = JS_FALSE;
691
chars[nchars++] = '{';
695
for (i = 0, length = ida->length; i < length; i++) {
696
/* Get strings for id and value and GC-root them via argv. */
699
#if JS_HAS_GETTER_SETTER
701
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
706
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
708
OBJ_DROP_PROPERTY(cx, obj2, prop);
711
if (OBJ_IS_NATIVE(obj2) &&
712
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
713
if (attrs & JSPROP_GETTER) {
714
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
715
#ifdef OLD_GETTER_SETTER
717
ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
720
ATOM_TO_STRING(cx->runtime->atomState.getAtom);
724
if (attrs & JSPROP_SETTER) {
725
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
726
#ifdef OLD_GETTER_SETTER
728
ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
731
ATOM_TO_STRING(cx->runtime->atomState.setAtom);
738
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
740
OBJ_DROP_PROPERTY(cx, obj2, prop);
743
#else /* !JS_HAS_GETTER_SETTER */
747
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
749
#endif /* !JS_HAS_GETTER_SETTER */
754
/* Convert id to a jsval and then to a string. */
755
atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
756
id = ID_TO_VALUE(id);
757
idstr = js_ValueToString(cx, id);
762
argv[0] = STRING_TO_JSVAL(idstr);
765
* If id is a string that's a reserved identifier, or else id is not
766
* an identifier at all, then it needs to be quoted.
768
if (atom && (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))) {
769
idstr = js_QuoteString(cx, idstr, (jschar)'\'');
774
argv[0] = STRING_TO_JSVAL(idstr);
776
idstrchars = JSSTRING_CHARS(idstr);
777
idstrlength = JSSTRING_LENGTH(idstr);
779
for (j = 0; j < valcnt; j++) {
780
/* Convert val[j] to its canonical source form. */
781
valstr = js_ValueToSource(cx, val[j]);
786
argv[1+j] = STRING_TO_JSVAL(valstr);
787
vchars = JSSTRING_CHARS(valstr);
788
vlength = JSSTRING_LENGTH(valstr);
790
#ifndef OLD_GETTER_SETTER
791
/* Remove 'function ' from beginning of valstr. */
793
int n = strlen(js_function_str) + 1;
799
/* If val[j] is a non-sharp object, consider sharpening it. */
802
#if JS_HAS_SHARP_VARS
803
if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
804
he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
812
vlength = js_strlen(vchars);
815
vsharplength = js_strlen(vsharp);
818
js_LeaveSharpObject(cx, NULL);
823
/* Allocate 1 + 1 at end for closing brace and terminating 0. */
825
realloc((ochars = chars),
826
(nchars + (comma ? 2 : 0) +
828
(gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
829
vsharplength + vlength +
830
(outermost ? 2 : 1) + 1) * sizeof(jschar));
832
/* Save code space on error: let JS_free ignore null vsharp. */
839
chars[nchars++] = comma[0];
840
chars[nchars++] = comma[1];
844
#ifdef OLD_GETTER_SETTER
845
js_strncpy(&chars[nchars], idstrchars, idstrlength);
846
nchars += idstrlength;
848
chars[nchars++] = ' ';
849
gsoplength = JSSTRING_LENGTH(gsop[j]);
850
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
851
nchars += gsoplength;
853
chars[nchars++] = ':';
856
gsoplength = JSSTRING_LENGTH(gsop[j]);
857
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
858
nchars += gsoplength;
859
chars[nchars++] = ' ';
861
js_strncpy(&chars[nchars], idstrchars, idstrlength);
862
nchars += idstrlength;
864
chars[nchars++] = ':';
867
js_strncpy(&chars[nchars], vsharp, vsharplength);
868
nchars += vsharplength;
870
js_strncpy(&chars[nchars], vchars, vlength);
878
chars[nchars++] = '}';
880
chars[nchars++] = ')';
884
js_LeaveSharpObject(cx, &ida);
893
JS_ReportOutOfMemory(cx);
897
str = js_NewString(cx, chars, nchars, 0);
902
*rval = STRING_TO_JSVAL(str);
905
#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
908
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
913
const char *clazz, *prefix;
916
#if JS_HAS_INITIALIZERS
917
if (cx->version == JSVERSION_1_2)
918
return js_obj_toSource(cx, obj, argc, argv, rval);
921
clazz = OBJ_GET_CLASS(cx, obj)->name;
922
nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
923
chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
929
while ((chars[nchars] = (jschar)*prefix) != 0)
931
while ((chars[nchars] = (jschar)*clazz) != 0)
933
chars[nchars++] = ']';
936
str = js_NewString(cx, chars, nchars, 0);
941
*rval = STRING_TO_JSVAL(str);
946
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
948
*rval = OBJECT_TO_JSVAL(obj);
953
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
955
JSStackFrame *fp, *caller;
961
JSPrincipals *principals;
964
#if JS_HAS_EVAL_THIS_SCOPE
965
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
966
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
970
caller = JS_GetScriptedCaller(cx, fp);
971
indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
973
if (JSVERSION_IS_ECMA(cx->version) &&
975
!JS_ReportErrorFlagsAndNumber(cx,
976
JSREPORT_WARNING | JSREPORT_STRICT,
977
js_GetErrorMessage, NULL,
978
JSMSG_BAD_INDIRECT_CALL,
983
if (!JSVAL_IS_STRING(argv[0])) {
988
#if JS_HAS_SCRIPT_OBJECT
990
* Script.prototype.compile/exec and Object.prototype.eval all take an
991
* optional trailing argument that overrides the scope object.
995
if (!js_ValueToObject(cx, argv[1], &scopeobj))
997
argv[1] = OBJECT_TO_JSVAL(scopeobj);
1002
#if JS_HAS_EVAL_THIS_SCOPE
1003
/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1005
callerScopeChain = caller->scopeChain;
1006
if (obj != callerScopeChain) {
1007
scopeobj = js_NewObject(cx, &js_WithClass, obj,
1012
/* Set fp->scopeChain too, for the compiler. */
1013
caller->scopeChain = fp->scopeChain = scopeobj;
1014
setCallerScopeChain = JS_TRUE;
1017
callerVarObj = caller->varobj;
1018
if (obj != callerVarObj) {
1019
/* Set fp->varobj too, for the compiler. */
1020
caller->varobj = fp->varobj = obj;
1021
setCallerVarObj = JS_TRUE;
1024
/* From here on, control must exit through label out with ok set. */
1027
#if JS_BUG_EVAL_THIS_SCOPE
1028
/* An old version used the object in which eval was found for scope. */
1031
/* Compile using caller's current scope object. */
1033
scopeobj = caller->scopeChain;
1037
str = JSVAL_TO_STRING(argv[0]);
1039
file = caller->script->filename;
1040
line = js_PCToLineNumber(cx, caller->script, caller->pc);
1041
principals = JS_EvalFramePrincipals(cx, fp, caller);
1048
/* XXXbe set only for the compiler, which does not currently test it */
1049
fp->flags |= JSFRAME_EVAL;
1050
script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1051
JSSTRING_CHARS(str),
1052
JSSTRING_LENGTH(str),
1059
#if !JS_BUG_EVAL_THIS_SCOPE
1060
#if JS_HAS_SCRIPT_OBJECT
1064
/* Execute using caller's new scope object (might be a Call object). */
1066
scopeobj = caller->scopeChain;
1069
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1070
JS_DestroyScript(cx, script);
1073
#if JS_HAS_EVAL_THIS_SCOPE
1074
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1075
if (setCallerScopeChain)
1076
caller->scopeChain = callerScopeChain;
1077
if (setCallerVarObj)
1078
caller->varobj = callerVarObj;
1083
JS_STATIC_DLL_CALLBACK(const void *)
1084
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
1086
JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
1091
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
1092
resolving_HashKey(JSDHashTable *table, const void *ptr)
1094
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
1096
return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
1099
JS_PUBLIC_API(JSBool)
1100
resolving_MatchEntry(JSDHashTable *table,
1101
const JSDHashEntryHdr *hdr,
1104
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
1105
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
1107
return entry->key.obj == key->obj && entry->key.id == key->id;
1110
static const JSDHashTableOps resolving_dhash_ops = {
1115
resolving_MatchEntry,
1116
JS_DHashMoveEntryStub,
1117
JS_DHashClearEntryStub,
1118
JS_DHashFinalizeStub,
1123
StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1124
JSResolvingEntry **entryp)
1126
JSDHashTable *table;
1127
JSResolvingEntry *entry;
1129
table = cx->resolvingTable;
1131
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
1132
sizeof(JSResolvingEntry),
1136
cx->resolvingTable = table;
1139
entry = (JSResolvingEntry *)
1140
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
1144
if (entry->flags & flag) {
1145
/* An entry for (key, flag) exists already -- dampen recursion. */
1148
/* Fill in key if we were the first to add entry, then set flag. */
1149
if (!entry->key.obj)
1151
entry->flags |= flag;
1157
JS_ReportOutOfMemory(cx);
1162
StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1163
JSResolvingEntry *entry, uint32 generation)
1165
JSDHashTable *table;
1168
* Clear flag from entry->flags and return early if other flags remain.
1169
* We must take care to re-lookup entry if the table has changed since
1170
* it was found by StartResolving.
1172
table = cx->resolvingTable;
1173
if (table->generation != generation) {
1174
entry = (JSResolvingEntry *)
1175
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
1177
entry->flags &= ~flag;
1182
* Do a raw remove only if fewer entries were removed than would cause
1183
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
1184
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
1185
* compressing or shrinking the table as needed.
1187
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
1188
JS_DHashTableRawRemove(table, &entry->hdr);
1190
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
1193
#if JS_HAS_OBJ_WATCHPOINT
1196
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1200
JSResolvingEntry *entry;
1206
/* Avoid recursion on (obj, id) already being watched on cx. */
1209
if (!StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1213
generation = cx->resolvingTable->generation;
1215
funobj = (JSObject *) closure;
1219
ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
1220
StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1225
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1228
jsval userid, value;
1232
fun = js_ValueToFunction(cx, &argv[1], 0);
1235
argv[1] = OBJECT_TO_JSVAL(fun->object);
1237
/* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1239
if (!JS_ValueToId(cx, userid, &propid))
1242
if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1244
if (attrs & JSPROP_READONLY)
1246
return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
1250
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1252
return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1255
#endif /* JS_HAS_OBJ_WATCHPOINT */
1257
#if JS_HAS_NEW_OBJ_METHODS
1259
* Prototype and property query methods, to complement the 'in' and
1260
* 'instanceof' operators.
1263
/* Proposed ECMA 15.2.4.5. */
1265
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1271
JSScopeProperty *sprop;
1273
if (!JS_ValueToId(cx, argv[0], &id))
1275
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1278
*rval = JSVAL_FALSE;
1279
} else if (obj2 == obj) {
1281
} else if (OBJ_IS_NATIVE(obj2)) {
1282
sprop = (JSScopeProperty *)prop;
1283
*rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1285
*rval = JSVAL_FALSE;
1288
OBJ_DROP_PROPERTY(cx, obj2, prop);
1292
/* Proposed ECMA 15.2.4.6. */
1294
obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1299
if (!js_IsDelegate(cx, obj, *argv, &b))
1301
*rval = BOOLEAN_TO_JSVAL(b);
1305
/* Proposed ECMA 15.2.4.7. */
1307
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1316
if (!JS_ValueToId(cx, argv[0], &id))
1319
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1323
*rval = JSVAL_FALSE;
1328
* XXX ECMA spec error compatible: return false unless hasOwnProperty.
1329
* The ECMA spec really should be fixed so propertyIsEnumerable and the
1330
* for..in loop agree on whether prototype properties are enumerable,
1331
* obviously by fixing this method (not by breaking the for..in loop!).
1333
* We check here for shared permanent prototype properties, which should
1334
* be treated as if they are local to obj. They are an implementation
1335
* technique used to satisfy ECMA requirements; users should not be able
1336
* to distinguish a shared permanent proto-property from a local one.
1339
!(OBJ_IS_NATIVE(obj2) &&
1340
SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1341
OBJ_DROP_PROPERTY(cx, obj2, prop);
1342
*rval = JSVAL_FALSE;
1346
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1347
OBJ_DROP_PROPERTY(cx, obj2, prop);
1349
*rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1352
#endif /* JS_HAS_NEW_OBJ_METHODS */
1354
#if JS_HAS_GETTER_SETTER
1356
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1365
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1366
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1367
JSMSG_BAD_GETTER_OR_SETTER,
1372
if (!JS_ValueToId(cx, argv[0], &id))
1374
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
1377
* Getters and setters are just like watchpoints from an access
1378
* control point of view.
1380
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1382
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1383
(JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1384
JSPROP_GETTER | JSPROP_SHARED, NULL);
1388
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1397
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1398
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1399
JSMSG_BAD_GETTER_OR_SETTER,
1404
if (!JS_ValueToId(cx, argv[0], &id))
1406
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
1409
* Getters and setters are just like watchpoints from an access
1410
* control point of view.
1412
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1414
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1415
NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1416
JSPROP_SETTER | JSPROP_SHARED, NULL);
1420
obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1425
JSScopeProperty *sprop;
1427
if (!JS_ValueToId(cx, argv[0], &id))
1429
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
1432
if (sprop->attrs & JSPROP_GETTER)
1433
*rval = OBJECT_TO_JSVAL(sprop->getter);
1434
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
1440
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1445
JSScopeProperty *sprop;
1447
if (!JS_ValueToId(cx, argv[0], &id))
1449
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
1452
if (sprop->attrs & JSPROP_SETTER)
1453
*rval = OBJECT_TO_JSVAL(sprop->setter);
1454
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
1458
#endif /* JS_HAS_GETTER_SETTER */
1460
#if JS_HAS_OBJ_WATCHPOINT
1461
const char js_watch_str[] = "watch";
1462
const char js_unwatch_str[] = "unwatch";
1464
#if JS_HAS_NEW_OBJ_METHODS
1465
const char js_hasOwnProperty_str[] = "hasOwnProperty";
1466
const char js_isPrototypeOf_str[] = "isPrototypeOf";
1467
const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1469
#if JS_HAS_GETTER_SETTER
1470
const char js_defineGetter_str[] = "__defineGetter__";
1471
const char js_defineSetter_str[] = "__defineSetter__";
1472
const char js_lookupGetter_str[] = "__lookupGetter__";
1473
const char js_lookupSetter_str[] = "__lookupSetter__";
1476
static JSFunctionSpec object_methods[] = {
1478
{js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA},
1480
{js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1481
{js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1482
{js_valueOf_str, obj_valueOf, 0,0,0},
1483
{js_eval_str, obj_eval, 1,0,0},
1484
#if JS_HAS_OBJ_WATCHPOINT
1485
{js_watch_str, obj_watch, 2,0,0},
1486
{js_unwatch_str, obj_unwatch, 1,0,0},
1488
#if JS_HAS_NEW_OBJ_METHODS
1489
{js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
1490
{js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
1491
{js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1493
#if JS_HAS_GETTER_SETTER
1494
{js_defineGetter_str, obj_defineGetter, 2,0,0},
1495
{js_defineSetter_str, obj_defineSetter, 2,0,0},
1496
{js_lookupGetter_str, obj_lookupGetter, 1,0,0},
1497
{js_lookupSetter_str, obj_lookupSetter, 1,0,0},
1503
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1506
/* Trigger logic below to construct a blank object. */
1509
/* If argv[0] is null or undefined, obj comes back null. */
1510
if (!js_ValueToObject(cx, argv[0], &obj))
1514
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1515
if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1517
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1521
*rval = OBJECT_TO_JSVAL(obj);
1526
* ObjectOps and Class for with-statement stack objects.
1529
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1531
#if defined JS_THREADSAFE && defined DEBUG
1532
, const char *file, uintN line
1537
JSScopeProperty *sprop;
1540
proto = OBJ_GET_PROTO(cx, obj);
1542
return js_LookupProperty(cx, obj, id, objp, propp);
1543
if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
1547
* Check whether id names an argument or local variable in an active
1548
* function. If so, pretend we didn't find it, so that the real arg or
1549
* var property can be found in the function's call object, later on in
1550
* the scope chain. But skip unshared arg and var properties -- those
1551
* result when a script explicitly sets a function "static" property of
1552
* the same name. See jsinterp.c:SetFunctionSlot.
1554
* XXX blame pre-ECMA reflection of function args and vars as properties
1556
if ((sprop = (JSScopeProperty *) *propp) &&
1557
(proto = *objp, OBJ_IS_NATIVE(proto)) &&
1558
(sprop->getter == js_GetArgument ||
1559
sprop->getter == js_GetLocalVariable) &&
1560
(sprop->attrs & JSPROP_SHARED)) {
1561
JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
1562
for (fp = cx->fp; fp && (!fp->fun || fp->fun->native); fp = fp->down)
1564
if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
1565
OBJ_DROP_PROPERTY(cx, proto, *propp);
1574
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1576
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1578
return js_GetProperty(cx, obj, id, vp);
1579
return OBJ_GET_PROPERTY(cx, proto, id, vp);
1583
with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1585
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1587
return js_SetProperty(cx, obj, id, vp);
1588
return OBJ_SET_PROPERTY(cx, proto, id, vp);
1592
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1595
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1597
return js_GetAttributes(cx, obj, id, prop, attrsp);
1598
return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1602
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1605
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1607
return js_SetAttributes(cx, obj, id, prop, attrsp);
1608
return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1612
with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1614
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1616
return js_DeleteProperty(cx, obj, id, rval);
1617
return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1621
with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1623
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1625
return js_DefaultValue(cx, obj, hint, vp);
1626
return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1630
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1631
jsval *statep, jsid *idp)
1633
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1635
return js_Enumerate(cx, obj, enum_op, statep, idp);
1636
return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1640
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1641
jsval *vp, uintN *attrsp)
1643
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1645
return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1646
return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1650
with_ThisObject(JSContext *cx, JSObject *obj)
1652
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1655
return OBJ_THIS_OBJECT(cx, proto);
1658
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1659
js_NewObjectMap, js_DestroyObjectMap,
1660
with_LookupProperty, js_DefineProperty,
1661
with_GetProperty, with_SetProperty,
1662
with_GetAttributes, with_SetAttributes,
1663
with_DeleteProperty, with_DefaultValue,
1664
with_Enumerate, with_CheckAccess,
1665
with_ThisObject, NATIVE_DROP_PROPERTY,
1668
js_SetProtoOrParent, js_SetProtoOrParent,
1673
static JSObjectOps *
1674
with_getObjectOps(JSContext *cx, JSClass *clasp)
1676
return &js_WithObjectOps;
1679
JSClass js_WithClass = {
1682
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1683
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
1688
#if JS_HAS_OBJ_PROTO_PROP
1690
With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1692
JSObject *parent, *proto;
1695
if (!JS_ReportErrorFlagsAndNumber(cx,
1696
JSREPORT_WARNING | JSREPORT_STRICT,
1697
js_GetErrorMessage, NULL,
1698
JSMSG_DEPRECATED_USAGE,
1699
js_WithClass.name)) {
1703
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1704
obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
1707
*rval = OBJECT_TO_JSVAL(obj);
1710
parent = cx->fp->scopeChain;
1712
if (!js_ValueToObject(cx, argv[0], &proto))
1714
v = OBJECT_TO_JSVAL(proto);
1715
if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1718
if (!js_ValueToObject(cx, argv[1], &parent))
1722
v = OBJECT_TO_JSVAL(parent);
1723
return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1728
js_InitObjectClass(JSContext *cx, JSObject *obj)
1733
#if JS_HAS_SHARP_VARS
1734
JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1737
proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1738
object_props, object_methods, NULL, NULL);
1742
#if JS_HAS_OBJ_PROTO_PROP
1743
if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1744
NULL, NULL, NULL, NULL)) {
1749
/* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1750
if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
1754
if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
1755
eval, NULL, NULL, 0, NULL)) {
1763
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1768
map->nslots = JS_INITIAL_NSLOTS;
1769
map->freeslot = JSSLOT_FREE(clasp);
1773
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1774
JSClass *clasp, JSObject *obj)
1776
return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1780
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1782
js_DestroyScope(cx, (JSScope *)map);
1786
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1788
JS_ASSERT(map->nrefs >= 0);
1789
JS_ATOMIC_INCREMENT(&map->nrefs);
1794
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
1796
JS_ASSERT(map->nrefs > 0);
1797
JS_ATOMIC_DECREMENT(&map->nrefs);
1798
if (map->nrefs == 0) {
1799
map->ops->destroyObjectMap(cx, map);
1802
if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
1803
((JSScope *)map)->object = NULL;
1808
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
1812
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
1814
JSObject *obj, *ctor;
1821
/* Allocate an object from the GC heap and zero it. */
1822
obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
1826
/* Bootstrap the ur-object, and make it the default prototype object. */
1828
if (!GetClassPrototype(cx, parent, clasp->name, &proto))
1830
if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
1834
/* Always call the class's getObjectOps hook if it has one. */
1835
ops = clasp->getObjectOps
1836
? clasp->getObjectOps(cx, clasp)
1840
* Share proto's map only if it has the same JSObjectOps, and only if
1841
* proto's class has the same private and reserved slots, as obj's map
1845
(map = proto->map)->ops == ops &&
1846
((clasp->flags ^ OBJ_GET_CLASS(cx, proto)->flags) &
1847
(JSCLASS_HAS_PRIVATE |
1848
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT)))
1850
/* Default parent to the parent of the prototype's constructor. */
1852
if (!OBJ_GET_PROPERTY(cx, proto,
1853
(jsid)cx->runtime->atomState.constructorAtom,
1857
if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
1858
parent = OBJ_GET_PARENT(cx, ctor);
1861
/* Share the given prototype's map. */
1862
obj->map = js_HoldObjectMap(cx, map);
1864
/* Ensure that obj starts with the minimum slots for clasp. */
1865
nslots = JS_INITIAL_NSLOTS;
1867
/* Leave parent alone. Allocate a new map for obj. */
1868
map = ops->newObjectMap(cx, 1, ops, clasp, obj);
1873
/* Let ops->newObjectMap set nslots so as to reserve slots. */
1874
nslots = map->nslots;
1877
/* Allocate a slots vector, with a -1'st element telling its length. */
1878
newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
1880
js_DropObjectMap(cx, obj->map, obj);
1884
newslots[0] = nslots;
1887
/* Set the proto, parent, and class properties. */
1888
newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
1889
newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
1890
newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
1892
/* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
1893
for (i = JSSLOT_CLASS + 1; i < nslots; i++)
1894
newslots[i] = JSVAL_VOID;
1896
/* Store newslots after initializing all of 'em, just in case. */
1897
obj->slots = newslots;
1899
if (cx->runtime->objectHook)
1900
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
1905
cx->newborn[GCX_OBJECT] = NULL;
1910
FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
1915
JSScopeProperty *sprop;
1917
atom = js_Atomize(cx, name, strlen(name), 0);
1921
if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) {
1922
/* Find the topmost object in the scope chain. */
1925
scope = OBJ_GET_PARENT(cx, obj);
1928
obj = cx->globalObject;
1935
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
1942
JS_ASSERT(OBJ_IS_NATIVE(pobj));
1943
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
1944
*vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
1945
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
1950
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1951
JSObject *parent, uintN argc, jsval *argv)
1954
JSObject *obj, *ctor;
1956
if (!FindConstructor(cx, parent, clasp->name, &cval))
1958
if (JSVAL_IS_PRIMITIVE(cval)) {
1959
js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
1964
* If proto or parent are NULL, set them to Constructor.prototype and/or
1965
* Constructor.__parent__, just like JSOP_NEW does.
1967
ctor = JSVAL_TO_OBJECT(cval);
1969
parent = OBJ_GET_PARENT(cx, ctor);
1971
if (!OBJ_GET_PROPERTY(cx, ctor,
1972
(jsid)cx->runtime->atomState.classPrototypeAtom,
1976
if (JSVAL_IS_OBJECT(rval))
1977
proto = JSVAL_TO_OBJECT(rval);
1980
obj = js_NewObject(cx, clasp, proto, parent);
1984
if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
1986
return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
1988
cx->newborn[GCX_OBJECT] = NULL;
1993
js_FinalizeObject(JSContext *cx, JSObject *obj)
1997
/* Cope with stillborn objects that have no map. */
2001
JS_ASSERT(obj->slots);
2003
if (cx->runtime->objectHook)
2004
cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
2006
/* Remove all watchpoints with weak links to obj. */
2007
JS_ClearWatchPointsForObject(cx, obj);
2010
* Finalize obj first, in case it needs map and slots. Optimized to use
2011
* LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
2012
* obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
2013
* we're called from the GC. Only the GC should call js_FinalizeObject,
2014
* and no other threads run JS (and possibly racing to update obj->slots)
2015
* while the GC is running.
2017
LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
2019
/* Drop map and free slots. */
2020
js_DropObjectMap(cx, map, obj);
2022
JS_free(cx, obj->slots - 1);
2026
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
2027
won't recycle the deleted props' slots. */
2029
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
2037
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2038
nslots = map->nslots;
2039
if (map->freeslot >= nslots) {
2040
nslots = map->freeslot;
2041
JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2042
nslots += (nslots + 1) / 2;
2044
nbytes = (nslots + 1) * sizeof(jsval);
2045
#if defined _MSC_VER && _MSC_VER <= 800
2046
if (nbytes > 60000U) {
2047
JS_ReportOutOfMemory(cx);
2052
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
2055
for (i = 1 + newslots[0]; i <= nslots; i++)
2056
newslots[i] = JSVAL_VOID;
2057
newslots[0] = map->nslots = nslots;
2058
obj->slots = newslots + 1;
2062
obj->slots[map->freeslot] = JSVAL_VOID;
2064
*slotp = map->freeslot++;
2069
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2076
OBJ_CHECK_SLOT(obj, slot);
2077
obj->slots[slot] = JSVAL_VOID;
2079
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2080
if (map->freeslot == slot + 1)
2081
map->freeslot = slot;
2082
nslots = map->nslots;
2083
if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2084
nslots = map->freeslot;
2085
nslots += nslots / 2;
2086
if (nslots < JS_INITIAL_NSLOTS)
2087
nslots = JS_INITIAL_NSLOTS;
2088
nbytes = (nslots + 1) * sizeof(jsval);
2089
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
2092
newslots[0] = map->nslots = nslots;
2093
obj->slots = newslots + 1;
2097
#if JS_BUG_EMPTY_INDEX_ZERO
2098
#define CHECK_FOR_EMPTY_INDEX(id) \
2100
if (JSSTRING_LENGTH(_str) == 0) \
2104
#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
2107
/* JSVAL_INT_MAX as a string */
2108
#define JSVAL_INT_MAX_STRING "1073741823"
2110
#define CHECK_FOR_FUNNY_INDEX(id) \
2112
if (!JSVAL_IS_INT(id)) { \
2113
JSAtom *atom_ = (JSAtom *)id; \
2114
JSString *str_ = ATOM_TO_STRING(atom_); \
2115
const jschar *cp_ = str_->chars; \
2116
JSBool negative_ = (*cp_ == '-'); \
2117
if (negative_) cp_++; \
2118
if (JS7_ISDEC(*cp_) && \
2119
str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
2120
id = CheckForFunnyIndex(id, cp_, negative_); \
2122
CHECK_FOR_EMPTY_INDEX(id); \
2128
CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
2130
jsuint index = JS7_UNDEC(*cp++);
2131
jsuint oldIndex = 0;
2135
while (JS7_ISDEC(*cp)) {
2138
index = 10 * index + c;
2143
(oldIndex < (JSVAL_INT_MAX / 10) ||
2144
(oldIndex == (JSVAL_INT_MAX / 10) &&
2145
c <= (JSVAL_INT_MAX % 10)))) {
2148
id = INT_TO_JSVAL((jsint)index);
2154
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2155
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2156
uintN attrs, uintN flags, intN shortid)
2159
JSScopeProperty *sprop;
2161
JS_LOCK_OBJ(cx, obj);
2162
scope = js_GetMutableScope(cx, obj);
2167
* Handle old bug that took empty string as zero index. Also convert
2168
* string indices to integers if appropriate.
2170
CHECK_FOR_FUNNY_INDEX(id);
2171
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2174
JS_UNLOCK_OBJ(cx, obj);
2179
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2180
JSScopeProperty *sprop, uintN attrs, uintN mask,
2181
JSPropertyOp getter, JSPropertyOp setter)
2185
JS_LOCK_OBJ(cx, obj);
2186
scope = js_GetMutableScope(cx, obj);
2190
sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2193
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2197
JS_UNLOCK_OBJ(cx, obj);
2202
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2203
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2206
return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2211
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2212
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2213
uintN flags, intN shortid, JSProperty **propp)
2217
JSScopeProperty *sprop;
2220
* Handle old bug that took empty string as zero index. Also convert
2221
* string indices to integers if appropriate.
2223
CHECK_FOR_FUNNY_INDEX(id);
2225
#if JS_HAS_GETTER_SETTER
2227
* If defining a getter or setter, we must check for its counterpart and
2228
* update the attributes and property ops. A getter or setter is really
2229
* only half of a property.
2231
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2235
* If JS_THREADSAFE and id is found, js_LookupProperty returns with
2236
* sprop non-null and pobj locked. If pobj == obj, the property is
2237
* already in obj and obj has its own (mutable) scope. So if we are
2238
* defining a getter whose setter was already defined, or vice versa,
2239
* finish the job via js_ChangeScopePropertyAttributes, and refresh
2240
* the property cache line for (obj, id) to map sprop.
2242
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
2246
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2247
sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
2248
attrs, sprop->attrs,
2249
(attrs & JSPROP_GETTER)
2252
(attrs & JSPROP_SETTER)
2256
/* NB: obj == pobj, so we can share unlock code at the bottom. */
2263
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2264
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
2268
#endif /* JS_HAS_GETTER_SETTER */
2270
/* Lock if object locking is required by this implementation. */
2271
JS_LOCK_OBJ(cx, obj);
2273
/* Use the object's class getter and setter by default. */
2274
clasp = LOCKED_OBJ_GET_CLASS(obj);
2276
getter = clasp->getProperty;
2278
setter = clasp->setProperty;
2280
/* Get obj's own scope if it has one, or create a new one for obj. */
2281
scope = js_GetMutableScope(cx, obj);
2285
/* Add the property to scope, or replace an existing one of the same id. */
2286
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2287
attrs |= JSPROP_SHARED;
2288
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2289
SPROP_INVALID_SLOT, attrs, flags, shortid);
2293
/* XXXbe called with lock held */
2294
if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
2295
(void) js_RemoveScopeProperty(cx, scope, id);
2299
if (SPROP_HAS_VALID_SLOT(sprop, scope))
2300
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2302
#if JS_HAS_GETTER_SETTER
2305
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2307
*propp = (JSProperty *) sprop;
2309
JS_UNLOCK_OBJ(cx, obj);
2313
JS_UNLOCK_OBJ(cx, obj);
2317
#if defined JS_THREADSAFE && defined DEBUG
2318
JS_FRIEND_API(JSBool)
2319
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2320
JSProperty **propp, const char *file, uintN line)
2322
JS_FRIEND_API(JSBool)
2323
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2327
JSObject *start, *obj2, *proto;
2329
JSScopeProperty *sprop;
2331
JSResolveOp resolve;
2333
JSResolvingEntry *entry;
2335
JSNewResolveOp newresolve;
2341
* Handle old bug that took empty string as zero index. Also convert
2342
* string indices to integers if appropriate.
2344
CHECK_FOR_FUNNY_INDEX(id);
2346
/* Search scopes starting with obj and following the prototype link. */
2349
JS_LOCK_OBJ(cx, obj);
2350
SET_OBJ_INFO(obj, file, line);
2351
scope = OBJ_SCOPE(obj);
2352
if (scope->object == obj) {
2353
sprop = SCOPE_GET_PROPERTY(scope, id);
2355
/* Shared prototype scope: try resolve before lookup. */
2359
/* Try obj's class resolve hook if id was not found in obj's scope. */
2361
clasp = LOCKED_OBJ_GET_CLASS(obj);
2362
resolve = clasp->resolve;
2363
if (resolve != JS_ResolveStub) {
2364
/* Avoid recursion on (obj, id) already being resolved on cx. */
2369
* Once we have successfully added an entry for (obj, key) to
2370
* cx->resolvingTable, control must go through cleanup: before
2371
* returning. But note that JS_DHASH_ADD may find an existing
2372
* entry, in which case we bail to suppress runaway recursion.
2374
if (!StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2375
JS_UNLOCK_OBJ(cx, obj);
2379
/* Already resolving id in obj -- dampen recursion. */
2380
JS_UNLOCK_OBJ(cx, obj);
2383
generation = cx->resolvingTable->generation;
2385
/* Null *propp here so we can test it at cleanup: safely. */
2388
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2389
newresolve = (JSNewResolveOp)resolve;
2391
if (cx->fp && cx->fp->pc) {
2392
format = js_CodeSpec[*cx->fp->pc].format;
2393
if ((format & JOF_MODEMASK) != JOF_NAME)
2394
flags |= JSRESOLVE_QUALIFIED;
2395
if ((format & JOF_ASSIGNING) ||
2396
(cx->fp->flags & JSFRAME_ASSIGNING)) {
2397
flags |= JSRESOLVE_ASSIGNING;
2400
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2403
JS_UNLOCK_OBJ(cx, obj);
2405
/* Protect id and all atoms from a GC nested in resolve. */
2406
JS_KEEP_ATOMS(cx->runtime);
2407
ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2408
JS_UNKEEP_ATOMS(cx->runtime);
2412
JS_LOCK_OBJ(cx, obj);
2413
SET_OBJ_INFO(obj, file, line);
2415
/* Resolved: juggle locks and lookup id again. */
2417
JS_UNLOCK_OBJ(cx, obj);
2418
JS_LOCK_OBJ(cx, obj2);
2420
scope = OBJ_SCOPE(obj2);
2421
if (!MAP_IS_NATIVE(&scope->map)) {
2422
/* Whoops, newresolve handed back a foreign obj2. */
2423
JS_ASSERT(obj2 != obj);
2424
JS_UNLOCK_OBJ(cx, obj2);
2425
ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2428
JS_LOCK_OBJ(cx, obj2);
2431
* Require that obj2 have its own scope now, as we
2432
* do for old-style resolve. If it doesn't, then
2433
* id was not truly resolved, and we'll find it in
2434
* the proto chain, or miss it if obj2's proto is
2435
* not on obj's proto chain. That last case is a
2438
if (scope->object == obj2)
2439
sprop = SCOPE_GET_PROPERTY(scope, id);
2441
if (obj2 != obj && !sprop) {
2442
JS_UNLOCK_OBJ(cx, obj2);
2443
JS_LOCK_OBJ(cx, obj);
2448
* Old resolve always requires id re-lookup if obj owns
2449
* its scope after resolve returns.
2451
JS_UNLOCK_OBJ(cx, obj);
2452
ok = resolve(cx, obj, ID_TO_VALUE(id));
2455
JS_LOCK_OBJ(cx, obj);
2456
SET_OBJ_INFO(obj, file, line);
2457
scope = OBJ_SCOPE(obj);
2458
JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2459
if (scope->object == obj)
2460
sprop = SCOPE_GET_PROPERTY(scope, id);
2464
StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2471
JS_ASSERT(OBJ_SCOPE(obj) == scope);
2472
*objp = scope->object; /* XXXbe hide in jsscope.[ch] */
2474
*propp = (JSProperty *) sprop;
2478
proto = LOCKED_OBJ_GET_PROTO(obj);
2479
JS_UNLOCK_OBJ(cx, obj);
2482
if (!OBJ_IS_NATIVE(proto))
2483
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2493
JS_FRIEND_API(JSBool)
2494
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2498
JSObject *obj, *pobj, *lastobj;
2499
JSScopeProperty *sprop;
2503
obj = cx->fp->scopeChain;
2505
/* Try the property cache and return immediately on cache hit. */
2506
if (OBJ_IS_NATIVE(obj)) {
2507
JS_LOCK_OBJ(cx, obj);
2508
PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2510
JS_ASSERT(OBJ_IS_NATIVE(obj));
2513
*propp = (JSProperty *) sprop;
2516
JS_UNLOCK_OBJ(cx, obj);
2519
/* If cache miss, take the slow path. */
2520
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2523
if (OBJ_IS_NATIVE(pobj)) {
2524
sprop = (JSScopeProperty *) prop;
2525
PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2533
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2542
js_FindIdentifierBase(JSContext *cx, jsid id)
2544
JSObject *obj, *pobj;
2548
* Look for id's property along the "with" statement chain and the
2549
* statically-linked scope chain.
2551
if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
2554
OBJ_DROP_PROPERTY(cx, pobj, prop);
2559
* Use the top-level scope from the scope chain, which won't end in the
2560
* same scope as cx->globalObject for cross-context function calls.
2565
* Property not found. Give a strict warning if binding an undeclared
2566
* top-level variable.
2568
if (JS_HAS_STRICT_OPTION(cx)) {
2569
JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
2570
if (!JS_ReportErrorFlagsAndNumber(cx,
2571
JSREPORT_WARNING | JSREPORT_STRICT,
2572
js_GetErrorMessage, NULL,
2573
JSMSG_UNDECLARED_VAR,
2574
JS_GetStringBytes(str))) {
2582
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2585
JSScopeProperty *sprop;
2590
* Handle old bug that took empty string as zero index. Also convert
2591
* string indices to integers if appropriate.
2593
CHECK_FOR_FUNNY_INDEX(id);
2595
if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
2600
#if JS_BUG_NULL_INDEX_PROPS
2601
/* Indexed properties defaulted to null in old versions. */
2602
default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
2606
default_val = JSVAL_VOID;
2610
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
2614
* Give a strict warning if foo.bar is evaluated by a script for an
2615
* object foo with no property named 'bar'.
2617
if (JS_HAS_STRICT_OPTION(cx) &&
2618
*vp == default_val &&
2619
cx->fp && cx->fp->pc &&
2620
(*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
2622
jsbytecode *pc, *endpc;
2625
/* Kludge to allow (typeof foo == "undefined") tests. */
2626
JS_ASSERT(cx->fp->script);
2628
pc += js_CodeSpec[*pc].length;
2629
endpc = cx->fp->script->code + cx->fp->script->length;
2630
while (pc < endpc) {
2631
if (*pc == JSOP_TYPEOF)
2633
if (*pc != JSOP_GROUP)
2638
/* Ok, bad undefined property reference: whine about it. */
2639
str = js_DecompileValueGenerator(cx, JS_FALSE, ID_TO_VALUE(id),
2642
!JS_ReportErrorFlagsAndNumber(cx,
2643
JSREPORT_WARNING|JSREPORT_STRICT,
2644
js_GetErrorMessage, NULL,
2645
JSMSG_UNDEFINED_PROP,
2646
JS_GetStringBytes(str))) {
2653
if (!OBJ_IS_NATIVE(obj2)) {
2654
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
2655
return OBJ_GET_PROPERTY(cx, obj2, id, vp);
2658
/* Unlock obj2 before calling getter, relock after to avoid deadlock. */
2659
scope = OBJ_SCOPE(obj2);
2661
if (slot != SPROP_INVALID_SLOT) {
2662
JS_ASSERT(slot < obj2->map->freeslot);
2663
*vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
2665
/* If sprop has a stub getter, we're done. */
2672
JS_UNLOCK_SCOPE(cx, scope);
2673
if (!SPROP_GET(cx, sprop, obj, obj2, vp))
2675
JS_LOCK_SCOPE(cx, scope);
2677
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2678
LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
2679
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
2683
JS_UNLOCK_SCOPE(cx, scope);
2688
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2691
JSScopeProperty *sprop;
2696
JSPropertyOp getter, setter;
2701
* Handle old bug that took empty string as zero index. Also convert
2702
* string indices to integers if appropriate.
2704
CHECK_FOR_FUNNY_INDEX(id);
2706
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
2709
if (sprop && !OBJ_IS_NATIVE(pobj)) {
2710
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
2715
* Now either sprop is null, meaning id was not found in obj or one of its
2716
* prototypes; or sprop is non-null, meaning id was found in pobj's scope.
2717
* If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
2718
* is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
2719
* (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
2720
* because it is cheaper).
2722
attrs = JSPROP_ENUMERATE;
2725
clasp = OBJ_GET_CLASS(cx, obj);
2726
getter = clasp->getProperty;
2727
setter = clasp->setProperty;
2731
* Set scope for use below. It was locked by js_LookupProperty, and
2732
* we know pobj owns it (i.e., scope->object == pobj). Therefore we
2733
* optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
2735
scope = OBJ_SCOPE(pobj);
2737
attrs = sprop->attrs;
2738
if ((attrs & JSPROP_READONLY) ||
2739
(SCOPE_IS_SEALED(scope) && pobj == obj)) {
2740
JS_UNLOCK_SCOPE(cx, scope);
2741
if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
2743
goto read_only_error;
2748
* We found id in a prototype object: prepare to share or shadow.
2749
* NB: Thanks to the immutable, garbage-collected property tree
2750
* maintained by jsscope.c in cx->runtime, we needn't worry about
2751
* sprop going away behind our back after we've unlocked scope.
2753
JS_UNLOCK_SCOPE(cx, scope);
2755
/* Don't clone a shared prototype property. */
2756
if (attrs & JSPROP_SHARED)
2757
return SPROP_SET(cx, sprop, obj, pobj, vp);
2759
/* Restore attrs to the ECMA default for new properties. */
2760
attrs = JSPROP_ENUMERATE;
2763
* Preserve the shortid, getter, and setter when shadowing any
2764
* property that has a shortid. An old API convention requires
2765
* that the property's getter and setter functions receive the
2766
* shortid, not id, when they are called on the shadow we are
2767
* about to create in obj's scope.
2769
if (sprop->flags & SPROP_HAS_SHORTID) {
2770
flags = SPROP_HAS_SHORTID;
2771
shortid = sprop->shortid;
2772
getter = sprop->getter;
2773
setter = sprop->setter;
2777
* Forget we found the proto-property now that we've copied any
2778
* needed member values.
2782
#ifdef __GNUC__ /* suppress bogus gcc warnings */
2789
if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
2790
goto read_only_error;
2792
/* Find or make a property descriptor with the right heritage. */
2793
JS_LOCK_OBJ(cx, obj);
2794
scope = js_GetMutableScope(cx, obj);
2796
JS_UNLOCK_OBJ(cx, obj);
2799
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2800
attrs |= JSPROP_SHARED;
2801
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2802
SPROP_INVALID_SLOT, attrs, flags, shortid);
2804
JS_UNLOCK_SCOPE(cx, scope);
2808
/* XXXbe called with obj locked */
2809
if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
2810
(void) js_RemoveScopeProperty(cx, scope, id);
2811
JS_UNLOCK_SCOPE(cx, scope);
2815
/* Initialize new property value (passed to setter) to undefined. */
2816
if (SPROP_HAS_VALID_SLOT(sprop, scope))
2817
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
2819
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2822
/* Get the current property value from its slot. */
2824
if (slot != SPROP_INVALID_SLOT) {
2825
JS_ASSERT(slot < obj->map->freeslot);
2826
pval = LOCKED_OBJ_GET_SLOT(obj, slot);
2828
/* If sprop has a stub setter, keep scope locked and just store *vp. */
2833
/* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
2834
JS_UNLOCK_SCOPE(cx, scope);
2836
/* Let the setter modify vp before copying from it to obj->slots[slot]. */
2837
if (!SPROP_SET(cx, sprop, obj, obj, vp))
2840
/* Relock obj's scope until we are done with sprop. */
2841
JS_LOCK_SCOPE(cx, scope);
2844
* Check whether sprop is still around (was not deleted), and whether it
2845
* has a slot (it may never have had one, or we may have lost a race with
2846
* someone who cleared scope).
2848
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2851
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
2853
JS_UNLOCK_SCOPE(cx, scope);
2857
JSString *str = js_DecompileValueGenerator(cx,
2862
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2864
JS_GetStringBytes(str));
2871
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2875
JSScopeProperty *sprop;
2879
if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2885
if (!OBJ_IS_NATIVE(obj)) {
2886
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2887
OBJ_DROP_PROPERTY(cx, obj, prop);
2891
sprop = (JSScopeProperty *)prop;
2892
*attrsp = sprop->attrs;
2894
OBJ_DROP_PROPERTY(cx, obj, prop);
2899
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2903
JSScopeProperty *sprop;
2907
if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2911
if (!OBJ_IS_NATIVE(obj)) {
2912
ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2913
OBJ_DROP_PROPERTY(cx, obj, prop);
2917
sprop = (JSScopeProperty *)prop;
2918
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2920
~(JSPROP_GETTER | JSPROP_SETTER), 0,
2921
sprop->getter, sprop->setter);
2923
OBJ_DROP_PROPERTY(cx, obj, prop);
2924
return (sprop != NULL);
2928
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
2930
#if JS_HAS_PROP_DELETE
2934
JSScopeProperty *sprop;
2939
*rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
2942
* Handle old bug that took empty string as zero index. Also convert
2943
* string indices to integers if appropriate.
2945
CHECK_FOR_FUNNY_INDEX(id);
2947
if (!js_LookupProperty(cx, obj, id, &proto, &prop))
2949
if (!prop || proto != obj) {
2951
* If the property was found in a native prototype, check whether it's
2952
* shared and permanent. Such a property stands for direct properties
2953
* in all delegating objects, matching ECMA semantics without bloating
2954
* each delegating object.
2957
if (OBJ_IS_NATIVE(proto)) {
2958
sprop = (JSScopeProperty *)prop;
2959
if (SPROP_IS_SHARED_PERMANENT(sprop))
2960
*rval = JSVAL_FALSE;
2962
OBJ_DROP_PROPERTY(cx, proto, prop);
2963
if (*rval == JSVAL_FALSE)
2968
* If no property, or the property comes unshared or impermanent from
2969
* a prototype, call the class's delProperty hook, passing rval as the
2972
return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
2976
sprop = (JSScopeProperty *)prop;
2977
if (sprop->attrs & JSPROP_PERMANENT) {
2978
OBJ_DROP_PROPERTY(cx, obj, prop);
2979
if (JSVERSION_IS_ECMA(cx->version)) {
2980
*rval = JSVAL_FALSE;
2983
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
2984
ID_TO_VALUE(id), NULL);
2986
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2987
JSMSG_PERMANENT, JS_GetStringBytes(str));
2992
/* XXXbe called with obj locked */
2993
if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
2995
OBJ_DROP_PROPERTY(cx, obj, prop);
2999
scope = OBJ_SCOPE(obj);
3000
if (SPROP_HAS_VALID_SLOT(sprop, scope))
3001
GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3003
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3004
ok = js_RemoveScopeProperty(cx, scope, id);
3005
OBJ_DROP_PROPERTY(cx, obj, prop);
3008
#else /* !JS_HAS_PROP_DELETE */
3010
jsval null = JSVAL_NULL;
3013
return js_SetProperty(cx, obj, id, &null);
3015
#endif /* !JS_HAS_PROP_DELETE */
3019
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3024
v = OBJECT_TO_JSVAL(obj);
3028
* Propagate the exception if js_TryMethod finds an appropriate
3029
* method, and calling that method returned failure.
3031
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3035
if (!JSVAL_IS_PRIMITIVE(v)) {
3036
if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3040
* JS1.2 never failed (except for malloc failure) to convert an
3041
* object to a string. ECMA requires an error if both toString
3042
* and valueOf fail to produce a primitive value.
3044
if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
3045
char *bytes = JS_smprintf("[object %s]",
3046
OBJ_GET_CLASS(cx, obj)->name);
3049
str = JS_NewString(cx, bytes, strlen(bytes));
3054
v = STRING_TO_JSVAL(str);
3061
if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3063
if (!JSVAL_IS_PRIMITIVE(v)) {
3064
JSType type = JS_TypeOfValue(cx, v);
3066
(type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3069
/* Don't convert to string (source object literal) for JS1.2. */
3070
if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
3072
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3078
if (!JSVAL_IS_PRIMITIVE(v)) {
3079
/* Avoid recursive death through js_DecompileValueGenerator. */
3080
if (hint == JSTYPE_STRING) {
3081
str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3087
*vp = OBJECT_TO_JSVAL(obj);
3088
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3090
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3091
JSMSG_CANT_CONVERT_TO,
3092
JS_GetStringBytes(str),
3093
(hint == JSTYPE_VOID)
3095
: js_type_str[hint]);
3105
js_NewIdArray(JSContext *cx, jsint length)
3110
JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3112
ida->length = length;
3117
js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
3120
JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3122
ida->length = length;
3126
/* Private type used to iterate over all properties of a native JS object */
3127
typedef struct JSNativeIteratorState {
3128
jsint next_index; /* index into jsid array */
3129
JSIdArray *ida; /* All property ids in enumeration */
3130
} JSNativeIteratorState;
3133
* This function is used to enumerate the properties of native JSObjects
3134
* and those host objects that do not define a JSNewEnumerateOp-style iterator
3138
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3139
jsval *statep, jsid *idp)
3141
JSObject *proto_obj;
3143
JSEnumerateOp enumerate;
3144
JSScopeProperty *sprop, *lastProp;
3148
JSNativeIteratorState *state;
3150
clasp = OBJ_GET_CLASS(cx, obj);
3151
enumerate = clasp->enumerate;
3152
if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3153
return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3157
case JSENUMERATE_INIT:
3158
if (!enumerate(cx, obj))
3163
* The set of all property ids is pre-computed when the iterator
3164
* is initialized so as to avoid problems with properties being
3165
* deleted during the iteration.
3167
JS_LOCK_OBJ(cx, obj);
3168
scope = OBJ_SCOPE(obj);
3171
* If this object shares a scope with its prototype, don't enumerate
3172
* its properties. Otherwise they will be enumerated a second time
3173
* when the prototype object is enumerated.
3175
proto_obj = OBJ_GET_PROTO(cx, obj);
3176
if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
3177
ida = js_NewIdArray(cx, 0);
3179
JS_UNLOCK_OBJ(cx, obj);
3183
/* Object has a private scope; Enumerate all props in scope. */
3184
for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3185
sprop = sprop->parent) {
3186
if ((sprop->attrs & JSPROP_ENUMERATE) &&
3187
!(sprop->flags & SPROP_IS_ALIAS) &&
3188
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3189
SCOPE_HAS_PROPERTY(scope, sprop))) {
3193
ida = js_NewIdArray(cx, length);
3195
JS_UNLOCK_OBJ(cx, obj);
3199
for (sprop = lastProp; sprop; sprop = sprop->parent) {
3200
if ((sprop->attrs & JSPROP_ENUMERATE) &&
3201
!(sprop->flags & SPROP_IS_ALIAS) &&
3202
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3203
SCOPE_HAS_PROPERTY(scope, sprop))) {
3205
ida->vector[--i] = sprop->id;
3209
JS_UNLOCK_OBJ(cx, obj);
3211
state = (JSNativeIteratorState *)
3212
JS_malloc(cx, sizeof(JSNativeIteratorState));
3214
JS_DestroyIdArray(cx, ida);
3218
state->next_index = 0;
3219
*statep = PRIVATE_TO_JSVAL(state);
3221
*idp = INT_TO_JSVAL(length);
3224
case JSENUMERATE_NEXT:
3225
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3227
length = ida->length;
3228
if (state->next_index != length) {
3229
*idp = ida->vector[state->next_index++];
3233
/* Fall through ... */
3235
case JSENUMERATE_DESTROY:
3236
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3237
JS_DestroyIdArray(cx, state->ida);
3239
*statep = JSVAL_NULL;
3248
*statep = JSVAL_NULL;
3253
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3254
jsval *vp, uintN *attrsp)
3258
JSScopeProperty *sprop;
3262
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3267
clasp = OBJ_GET_CLASS(cx, obj);
3268
return !clasp->checkAccess ||
3269
clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3271
if (!OBJ_IS_NATIVE(pobj)) {
3272
OBJ_DROP_PROPERTY(cx, pobj, prop);
3273
return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3275
sprop = (JSScopeProperty *)prop;
3276
*vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3277
? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3279
*attrsp = sprop->attrs;
3280
clasp = LOCKED_OBJ_GET_CLASS(obj);
3281
if (clasp->checkAccess) {
3282
JS_UNLOCK_OBJ(cx, pobj);
3283
ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3284
JS_LOCK_OBJ(cx, pobj);
3288
OBJ_DROP_PROPERTY(cx, pobj, prop);
3292
#ifdef JS_THREADSAFE
3294
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3296
JS_UNLOCK_OBJ(cx, obj);
3301
ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
3304
* The decompiler may need to access the args of the function in
3305
* progress rather than the one we had hoped to call.
3306
* So we switch the cx->fp to the frame below us. We stick the
3307
* current frame in the dormantFrameChain to protect it from gc.
3310
JSStackFrame *fp = cx->fp;
3312
JS_ASSERT(!fp->dormantNext);
3313
fp->dormantNext = cx->dormantFrameChain;
3314
cx->dormantFrameChain = fp;
3318
js_ReportIsNotFunction(cx, vp, flags);
3321
JS_ASSERT(cx->dormantFrameChain == fp);
3322
cx->dormantFrameChain = fp->dormantNext;
3323
fp->dormantNext = NULL;
3329
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3333
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3335
ReportIsNotFunction(cx, &argv[-2], 0);
3338
return clasp->call(cx, obj, argc, argv, rval);
3342
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3347
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3348
if (!clasp->construct) {
3349
ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3352
return clasp->construct(cx, obj, argc, argv, rval);
3356
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3360
clasp = OBJ_GET_CLASS(cx, obj);
3361
if (clasp->hasInstance)
3362
return clasp->hasInstance(cx, obj, v, bp);
3368
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3373
if (JSVAL_IS_PRIMITIVE(v))
3375
obj2 = JSVAL_TO_OBJECT(v);
3376
while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
3386
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
3388
return GetClassPrototype(cx, NULL, name, protop);
3392
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
3398
if (!FindConstructor(cx, scope, name, &v))
3400
if (JSVAL_IS_FUNCTION(cx, v)) {
3401
ctor = JSVAL_TO_OBJECT(v);
3402
if (!OBJ_GET_PROPERTY(cx, ctor,
3403
(jsid)cx->runtime->atomState.classPrototypeAtom,
3408
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
3413
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
3417
* Use the given attributes for the prototype property of the constructor,
3418
* as user-defined constructors have a DontEnum | DontDelete prototype (it
3419
* may be reset), while native or "system" constructors require DontEnum |
3420
* ReadOnly | DontDelete.
3422
if (!OBJ_DEFINE_PROPERTY(cx, ctor,
3423
(jsid)cx->runtime->atomState.classPrototypeAtom,
3424
OBJECT_TO_JSVAL(proto), NULL, NULL,
3430
* ECMA says that Object.prototype.constructor, or f.prototype.constructor
3431
* for a user-defined function f, is DontEnum.
3433
return OBJ_DEFINE_PROPERTY(cx, proto,
3434
(jsid)cx->runtime->atomState.constructorAtom,
3435
OBJECT_TO_JSVAL(ctor), NULL, NULL,
3440
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
3444
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
3446
} else if (JSVAL_IS_OBJECT(v)) {
3447
obj = JSVAL_TO_OBJECT(v);
3448
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
3450
if (JSVAL_IS_OBJECT(v))
3451
obj = JSVAL_TO_OBJECT(v);
3453
if (JSVAL_IS_STRING(v)) {
3454
obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
3455
} else if (JSVAL_IS_INT(v)) {
3456
obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
3457
} else if (JSVAL_IS_DOUBLE(v)) {
3458
obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
3460
JS_ASSERT(JSVAL_IS_BOOLEAN(v));
3461
obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
3471
js_ValueToNonNullObject(JSContext *cx, jsval v)
3476
if (!js_ValueToObject(cx, v, &obj))
3479
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
3481
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3482
JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
3489
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
3491
#if JS_HAS_VALUEOF_HINT
3494
argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
3495
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
3498
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
3504
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
3505
uintN argc, jsval *argv, jsval *rval)
3507
JSErrorReporter older;
3512
* Report failure only if an appropriate method was found, and calling it
3513
* returned failure. We propagate failure in this case to make exceptions
3516
older = JS_SetErrorReporter(cx, NULL);
3517
if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&
3518
!JSVAL_IS_PRIMITIVE(fval)) {
3519
ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
3523
JS_SetErrorReporter(cx, older);
3529
#include "jsxdrapi.h"
3532
js_XDRObject(JSXDRState *xdr, JSObject **objp)
3536
const char *className;
3537
uint32 classId, classDef;
3542
if (xdr->mode == JSXDR_ENCODE) {
3543
clasp = OBJ_GET_CLASS(cx, *objp);
3544
className = clasp->name;
3545
classId = JS_XDRFindClassIdByName(xdr, className);
3546
classDef = !classId;
3547
if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
3552
clasp = NULL; /* quell GCC overwarning */
3555
/* XDR a flag word followed (if true) by the class name. */
3556
if (!JS_XDRUint32(xdr, &classDef))
3558
if (classDef && !JS_XDRCString(xdr, (char **) &className))
3561
/* From here on, return through out: to free className if it was set. */
3562
ok = JS_XDRUint32(xdr, &classId);
3566
if (xdr->mode != JSXDR_ENCODE) {
3568
ok = js_GetClassPrototype(cx, className, &proto);
3571
clasp = OBJ_GET_CLASS(cx, proto);
3572
ok = JS_XDRRegisterClass(xdr, clasp, &classId);
3576
clasp = JS_XDRFindClassById(xdr, classId);
3579
JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
3580
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3581
JSMSG_CANT_FIND_CLASS, numBuf);
3588
if (!clasp->xdrObject) {
3589
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3590
JSMSG_CANT_XDR_CLASS, clasp->name);
3593
ok = clasp->xdrObject(xdr, objp);
3596
if (xdr->mode != JSXDR_ENCODE && className)
3597
JS_free(cx, (void *)className);
3601
#endif /* JS_HAS_XDR */
3603
#ifdef DEBUG_brendan
3608
uint32 js_entry_count_max;
3609
uint32 js_entry_count_sum;
3610
double js_entry_count_sqsum;
3611
uint32 js_entry_count_hist[11];
3614
MeterEntryCount(uintN count)
3617
js_entry_count_sum += count;
3618
js_entry_count_sqsum += (double)count * count;
3619
if (count > js_entry_count_max)
3620
js_entry_count_max = count;
3622
js_entry_count_hist[JS_MIN(count, 10)]++;
3626
js_DumpScopeMeters(JSRuntime *rt)
3630
logfp = fopen("/tmp/scope.stats", "a");
3633
double mean = 0., var = 0., sigma = 0.;
3634
double nscopes = rt->liveScopes;
3635
double nentrys = js_entry_count_sum;
3636
if (nscopes > 0 && nentrys >= 0) {
3637
mean = nentrys / nscopes;
3638
var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
3639
if (var < 0.0 || nscopes <= 1)
3642
var /= nscopes * (nscopes - 1);
3644
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
3645
sigma = (var != 0.) ? sqrt(var) : 0.;
3649
"scopes %g entries %g mean %g sigma %g max %u",
3650
nscopes, nentrys, mean, sigma, js_entry_count_max);
3653
fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
3654
js_entry_count_hist[0], js_entry_count_hist[1],
3655
js_entry_count_hist[2], js_entry_count_hist[3],
3656
js_entry_count_hist[4], js_entry_count_hist[5],
3657
js_entry_count_hist[6], js_entry_count_hist[7],
3658
js_entry_count_hist[8], js_entry_count_hist[9],
3659
js_entry_count_hist[10]);
3660
js_entry_count_sum = js_entry_count_max = 0;
3661
js_entry_count_sqsum = 0;
3662
memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
3666
#endif /* DEBUG_brendan */
3669
js_Mark(JSContext *cx, JSObject *obj, void *arg)
3672
JSScopeProperty *sprop;
3675
JS_ASSERT(OBJ_IS_NATIVE(obj));
3676
scope = OBJ_SCOPE(obj);
3677
#ifdef DEBUG_brendan
3678
if (scope->object == obj)
3679
MeterEntryCount(scope->entryCount);
3682
JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
3683
SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
3685
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3686
if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
3688
MARK_SCOPE_PROPERTY(sprop);
3689
if (!JSVAL_IS_INT(sprop->id))
3690
GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
3692
#if JS_HAS_GETTER_SETTER
3693
if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3694
#ifdef GC_MARK_DEBUG
3696
JSAtom *atom = (JSAtom *)sprop->id;
3697
const char *id = (atom && ATOM_IS_STRING(atom))
3698
? JS_GetStringBytes(ATOM_TO_STRING(atom))
3702
if (sprop->attrs & JSPROP_GETTER) {
3703
#ifdef GC_MARK_DEBUG
3704
JS_snprintf(buf, sizeof buf, "%s %s",
3708
JSVAL_TO_GCTHING((jsval) sprop->getter),
3712
if (sprop->attrs & JSPROP_SETTER) {
3713
#ifdef GC_MARK_DEBUG
3714
JS_snprintf(buf, sizeof buf, "%s %s",
3718
JSVAL_TO_GCTHING((jsval) sprop->setter),
3723
#endif /* JS_HAS_GETTER_SETTER */
3726
/* No one runs while the GC is running, so we can use LOCKED_... here. */
3727
clasp = LOCKED_OBJ_GET_CLASS(obj);
3729
(void) clasp->mark(cx, obj, arg);
3731
if (scope->object != obj) {
3733
* An unmutated object that shares a prototype's scope. We can't tell
3734
* how many slots are allocated and in use at obj->slots by looking at
3735
* scope, so we get obj->slots' length from its -1'st element.
3737
return (uint32) obj->slots[-1];
3739
return JS_MIN(obj->map->freeslot, obj->map->nslots);
3743
js_Clear(JSContext *cx, JSObject *obj)
3747
JSScopeProperty *sprop;
3751
* Clear our scope and the property cache of all obj's properties only if
3752
* obj owns the scope (i.e., not if obj is unmutated and therefore sharing
3753
* its prototype's scope). NB: we do not clear any reserved slots lying
3754
* below JSSLOT_FREE(clasp).
3756
JS_LOCK_OBJ(cx, obj);
3757
scope = OBJ_SCOPE(obj);
3758
if (scope->object == obj) {
3759
/* Clear the property cache before we clear the scope. */
3761
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3762
if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3763
SCOPE_HAS_PROPERTY(scope, sprop)) {
3764
PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
3768
/* Now that we're done using scope->lastProp/table, clear scope. */
3769
js_ClearScope(cx, scope);
3771
/* Clear slot values and reset freeslot so we're consistent. */
3772
i = scope->map.nslots;
3773
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
3775
obj->slots[i] = JSVAL_VOID;
3776
scope->map.freeslot = n;
3778
JS_UNLOCK_OBJ(cx, obj);
3782
js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
3786
JS_LOCK_OBJ(cx, obj);
3787
v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
3788
JS_UNLOCK_OBJ(cx, obj);
3793
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
3795
uint32 nslots, rlimit, i;
3799
JS_LOCK_OBJ(cx, obj);
3800
nslots = (uint32) obj->slots[-1];
3801
if (slot >= nslots) {
3802
clasp = LOCKED_OBJ_GET_CLASS(obj);
3803
rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp);
3804
JS_ASSERT(slot < rlimit);
3805
if (rlimit > nslots)
3808
newslots = (jsval *)
3809
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
3811
JS_UNLOCK_OBJ(cx, obj);
3814
for (i = 1 + newslots[0]; i <= rlimit; i++)
3815
newslots[i] = JSVAL_VOID;
3816
newslots[0] = nslots;
3817
if (OBJ_SCOPE(obj)->object == obj)
3818
obj->map->nslots = nslots;
3819
obj->slots = newslots + 1;
3822
obj->slots[slot] = v;
3823
JS_UNLOCK_OBJ(cx, obj);
3828
/* Routines to print out values during debugging. */
3830
void printChar(jschar *cp) {
3831
fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
3833
fputc(*cp++, stderr);
3835
fputc('\n', stderr);
3838
void printString(JSString *str) {
3841
fprintf(stderr, "string (0x%p) \"", (void *)str);
3842
s = JSSTRING_CHARS(str);
3843
for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
3844
fputc(s[i], stderr);
3846
fputc('\n', stderr);
3849
void printVal(JSContext *cx, jsval val);
3851
void printObj(JSContext *cx, JSObject *jsobj) {
3856
fprintf(stderr, "object 0x%p\n", (void *)jsobj);
3857
clasp = OBJ_GET_CLASS(cx, jsobj);
3858
fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
3859
for (i=0; i < jsobj->map->nslots; i++) {
3860
fprintf(stderr, "slot %3d ", i);
3861
val = jsobj->slots[i];
3862
if (JSVAL_IS_OBJECT(val))
3863
fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
3869
void printVal(JSContext *cx, jsval val) {
3870
fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
3871
if (JSVAL_IS_NULL(val)) {
3872
fprintf(stderr, "null\n");
3873
} else if (JSVAL_IS_VOID(val)) {
3874
fprintf(stderr, "undefined\n");
3875
} else if (JSVAL_IS_OBJECT(val)) {
3876
printObj(cx, JSVAL_TO_OBJECT(val));
3877
} else if (JSVAL_IS_INT(val)) {
3878
fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
3879
} else if (JSVAL_IS_STRING(val)) {
3880
printString(JSVAL_TO_STRING(val));
3881
} else if (JSVAL_IS_DOUBLE(val)) {
3882
fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
3884
JS_ASSERT(JSVAL_IS_BOOLEAN(val));
3885
fprintf(stderr, "(boolean) %s\n",
3886
JSVAL_TO_BOOLEAN(val) ? "true" : "false");
3891
void printId(JSContext *cx, jsid id) {
3892
fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
3893
printVal(cx, ID_TO_VALUE(id));
3896
void printAtom(JSAtom *atom) {
3897
printString(ATOM_TO_STRING(atom));