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 (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
160
id = (jsid)cx->runtime->atomState.protoAtom;
163
id = (jsid)cx->runtime->atomState.parentAtom;
166
if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
168
*vp = OBJ_GET_SLOT(cx, obj, slot);
173
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
179
if (!JSVAL_IS_OBJECT(*vp))
181
pobj = JSVAL_TO_OBJECT(*vp);
182
slot = (uint32) JSVAL_TO_INT(id);
183
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
186
/* __parent__ is readonly and permanent, only __proto__ may be set. */
187
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
190
return js_SetProtoOrParent(cx, obj, slot, pobj);
194
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
200
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
203
/* Get the number of properties to enumerate. */
204
iter_state = JSVAL_NULL;
205
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
209
if (!JSVAL_IS_INT(num_properties)) {
214
*vp = num_properties;
217
if (iter_state != JSVAL_NULL)
218
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
222
#else /* !JS_HAS_OBJ_PROTO_PROP */
224
#define object_props NULL
226
#endif /* !JS_HAS_OBJ_PROTO_PROP */
229
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
232
JSObject *obj2, *oldproto;
233
JSScope *scope, *newscope;
236
* Serialize all proto and parent setting in order to detect cycles.
237
* We nest locks in this function, and only here, in the following orders:
239
* (1) rt->setSlotLock < pobj's scope lock;
240
* rt->setSlotLock < pobj's proto-or-parent's scope lock;
241
* rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
243
* (2) rt->setSlotLock < obj's scope lock < pobj's scope lock.
245
* We avoid AB-BA deadlock by restricting obj from being on pobj's parent
246
* or proto chain (pobj may already be on obj's parent or proto chain; it
247
* could be moving up or down). We finally order obj with respect to pobj
248
* at the bottom of this routine (just before releasing rt->setSlotLock),
249
* by making pobj be obj's prototype or parent.
251
* After we have set the slot and released rt->setSlotLock, another call
252
* to js_SetProtoOrParent could nest locks according to the first order
253
* list above, but it cannot deadlock with any other thread. For there
254
* to be a deadlock, other parts of the engine would have to nest scope
255
* locks in the opposite order. XXXbe ensure they don't!
260
JS_ACQUIRE_LOCK(rt->setSlotLock);
261
while (rt->setSlotBusy) {
262
jsrefcount saveDepth;
264
/* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
265
JS_RELEASE_LOCK(rt->setSlotLock);
266
saveDepth = JS_SuspendRequest(cx);
267
JS_ACQUIRE_LOCK(rt->setSlotLock);
269
JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
270
JS_RELEASE_LOCK(rt->setSlotLock);
271
JS_ResumeRequest(cx, saveDepth);
272
JS_ACQUIRE_LOCK(rt->setSlotLock);
274
rt->setSlotBusy = JS_TRUE;
275
JS_RELEASE_LOCK(rt->setSlotLock);
277
#define SET_SLOT_DONE(rt) \
279
JS_ACQUIRE_LOCK((rt)->setSlotLock); \
280
(rt)->setSlotBusy = JS_FALSE; \
281
JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \
282
JS_RELEASE_LOCK((rt)->setSlotLock); \
287
#define SET_SLOT_DONE(rt) /* nothing */
295
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
297
#if JS_HAS_OBJ_PROTO_PROP
298
object_props[slot].name
300
(slot == JSSLOT_PROTO) ? js_proto_str
306
obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
309
if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
310
/* Check to see whether obj shares its prototype's scope. */
311
JS_LOCK_OBJ(cx, obj);
312
scope = OBJ_SCOPE(obj);
313
oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
314
if (oldproto && OBJ_SCOPE(oldproto) == scope) {
315
/* Either obj needs a new empty scope, or it should share pobj's. */
317
!OBJ_IS_NATIVE(pobj) ||
318
OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
320
* With no proto and no scope of its own, obj is truly empty.
322
* If pobj is not native, obj needs its own empty scope -- it
323
* should not continue to share oldproto's scope once oldproto
324
* is not on obj's prototype chain. That would put properties
325
* from oldproto's scope ahead of properties defined by pobj,
328
* If pobj's class differs from oldproto's, we may need a new
329
* scope to handle differences in private and reserved slots,
330
* so we suboptimally but safely make one.
332
scope = js_GetMutableScope(cx, obj);
334
JS_UNLOCK_OBJ(cx, obj);
338
} else if (OBJ_SCOPE(pobj) != scope) {
341
* We are about to nest scope locks. Help jslock.c:ShareScope
342
* keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
343
* avoiding deadlock, by recording scope in rt->setSlotScope.
345
if (scope->ownercx) {
346
JS_ASSERT(scope->ownercx == cx);
347
rt->setSlotScope = scope;
351
/* We can't deadlock because we checked for cycles above (2). */
352
JS_LOCK_OBJ(cx, pobj);
353
newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
354
obj->map = &newscope->map;
355
js_DropObjectMap(cx, &scope->map, obj);
356
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
359
rt->setSlotScope = NULL;
363
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
364
JS_UNLOCK_SCOPE(cx, scope);
366
OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
375
JS_STATIC_DLL_CALLBACK(JSHashNumber)
376
js_hash_object(const void *key)
378
return (JSHashNumber)key >> JSVAL_TAGBITS;
382
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
384
JSSharpObjectMap *map;
387
JSHashEntry **hep, *he;
393
#if JS_HAS_GETTER_SETTER
400
map = &cx->sharpObjectMap;
402
hash = js_hash_object(obj);
403
hep = JS_HashTableRawLookup(table, hash, obj);
407
he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
409
JS_ReportOutOfMemory(cx);
412
ida = JS_Enumerate(cx, obj);
416
for (i = 0, length = ida->length; i < length; i++) {
418
#if JS_HAS_GETTER_SETTER
419
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
423
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
425
if (OBJ_IS_NATIVE(obj2) &&
426
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
428
if (attrs & JSPROP_GETTER)
429
val = (jsval) ((JSScopeProperty*)prop)->getter;
430
if (attrs & JSPROP_SETTER) {
431
if (val != JSVAL_NULL) {
432
/* Mark the getter, then set val to setter. */
433
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
437
val = (jsval) ((JSScopeProperty*)prop)->setter;
440
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
443
OBJ_DROP_PROPERTY(cx, obj2, prop);
446
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
450
if (!JSVAL_IS_PRIMITIVE(val) &&
451
!MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
457
JS_DestroyIdArray(cx, ida);
461
sharpid = (jsatomid) he->value;
463
sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
464
he->value = (void *) sharpid;
474
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
477
JSSharpObjectMap *map;
481
JSHashEntry *he, **hep;
486
/* Set to null in case we return an early error. */
488
map = &cx->sharpObjectMap;
491
table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
492
JS_CompareValues, NULL, NULL);
494
JS_ReportOutOfMemory(cx);
501
if (map->depth == 0) {
502
he = MarkSharpObjects(cx, obj, &ida);
505
JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
507
JS_DestroyIdArray(cx, ida);
511
hash = js_hash_object(obj);
512
hep = JS_HashTableRawLookup(table, hash, obj);
516
* It's possible that the value of a property has changed from the
517
* first time the object's properties are traversed (when the property
518
* ids are entered into the hash table) to the second (when they are
519
* converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
523
he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
525
JS_ReportOutOfMemory(cx);
534
sharpid = (jsatomid) he->value;
538
len = JS_snprintf(buf, sizeof buf, "#%u%c",
539
sharpid >> SHARP_ID_SHIFT,
540
(sharpid & SHARP_BIT) ? '#' : '=');
541
*sp = js_InflateString(cx, buf, len);
544
JS_DestroyIdArray(cx, ida);
551
if ((sharpid & SHARP_BIT) == 0) {
553
ida = JS_Enumerate(cx, obj);
570
/* Clean up the sharpObjectMap table on outermost error. */
571
if (map->depth == 0) {
573
JS_HashTableDestroy(map->table);
580
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
582
JSSharpObjectMap *map;
585
map = &cx->sharpObjectMap;
586
JS_ASSERT(map->depth > 0);
587
if (--map->depth == 0) {
589
JS_HashTableDestroy(map->table);
595
JS_DestroyIdArray(cx, ida);
601
#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */
603
#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
605
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
608
JSBool ok, outermost;
611
jschar *chars, *ochars, *vsharp;
612
const jschar *idstrchars, *vchars;
613
size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
615
jsint i, j, length, valcnt;
617
#if JS_HAS_GETTER_SETTER
625
JSString *idstr, *valstr, *str;
628
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
629
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
634
* obj_toString for 1.2 calls toSource, and doesn't want the extra parens
637
outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
638
he = js_EnterSharpObject(cx, obj, &ida, &chars);
643
* We didn't enter -- obj is already "sharp", meaning we've visited it
644
* already in our depth first search, and therefore chars contains a
645
* string of the form "#n#".
648
#if JS_HAS_SHARP_VARS
649
nchars = js_strlen(chars);
662
/* If outermost, allocate 4 + 1 for "({})" and the terminator. */
663
chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
668
chars[nchars++] = '(';
670
/* js_EnterSharpObject returned a string of the form "#n=" in chars. */
672
nchars = js_strlen(chars);
674
realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
681
* No need for parentheses around the whole shebang, because #n=
682
* unambiguously begins an object initializer, and never a block
685
outermost = JS_FALSE;
689
#ifdef DUMP_CALL_TABLE
690
if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
691
const char *classname = OBJ_GET_CLASS(cx, obj)->name;
692
size_t classnchars = strlen(classname);
693
static const char classpropid[] = "C";
695
size_t onchars = nchars;
697
/* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
698
classnchars += sizeof classpropid - 1 + 2 + 2;
702
/* 2 for the braces, 1 for the terminator */
704
realloc((ochars = chars),
705
(nchars + classnchars + 2 + 1) * sizeof(jschar));
711
chars[nchars++] = '{'; /* 1 from the 2 braces */
712
for (cp = classpropid; *cp; cp++)
713
chars[nchars++] = (jschar) *cp;
714
chars[nchars++] = ':';
715
chars[nchars++] = ' '; /* 2 for ': ' */
716
chars[nchars++] = '"';
717
for (cp = classname; *cp; cp++)
718
chars[nchars++] = (jschar) *cp;
719
chars[nchars++] = '"'; /* 2 quotes */
721
chars[nchars++] = ',';
722
chars[nchars++] = ' '; /* 2 for ', ' */
725
JS_ASSERT(nchars - onchars == 1 + classnchars);
728
chars[nchars++] = '{';
732
for (i = 0, length = ida->length; i < length; i++) {
733
/* Get strings for id and value and GC-root them via argv. */
736
#if JS_HAS_GETTER_SETTER
738
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
743
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
745
OBJ_DROP_PROPERTY(cx, obj2, prop);
748
if (OBJ_IS_NATIVE(obj2) &&
749
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
750
if (attrs & JSPROP_GETTER) {
751
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
752
#ifdef OLD_GETTER_SETTER
754
ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
757
ATOM_TO_STRING(cx->runtime->atomState.getAtom);
761
if (attrs & JSPROP_SETTER) {
762
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
763
#ifdef OLD_GETTER_SETTER
765
ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
768
ATOM_TO_STRING(cx->runtime->atomState.setAtom);
775
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
777
OBJ_DROP_PROPERTY(cx, obj2, prop);
780
#else /* !JS_HAS_GETTER_SETTER */
784
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
786
#endif /* !JS_HAS_GETTER_SETTER */
791
/* Convert id to a jsval and then to a string. */
792
atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
793
id = ID_TO_VALUE(id);
794
idstr = js_ValueToString(cx, id);
799
argv[0] = STRING_TO_JSVAL(idstr);
802
* If id is a string that's a reserved identifier, or else id is not
803
* an identifier at all, then it needs to be quoted. Also, negative
804
* integer ids must be quoted.
807
? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))
808
: JSVAL_TO_INT(id) < 0) {
809
idstr = js_QuoteString(cx, idstr, (jschar)'\'');
814
argv[0] = STRING_TO_JSVAL(idstr);
816
idstrchars = JSSTRING_CHARS(idstr);
817
idstrlength = JSSTRING_LENGTH(idstr);
819
for (j = 0; j < valcnt; j++) {
820
/* Convert val[j] to its canonical source form. */
821
valstr = js_ValueToSource(cx, val[j]);
826
argv[1+j] = STRING_TO_JSVAL(valstr);
827
vchars = JSSTRING_CHARS(valstr);
828
vlength = JSSTRING_LENGTH(valstr);
830
#ifndef OLD_GETTER_SETTER
831
/* Remove 'function ' from beginning of valstr. */
833
int n = strlen(js_function_str) + 1;
839
/* If val[j] is a non-sharp object, consider sharpening it. */
842
#if JS_HAS_SHARP_VARS
843
if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
844
he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
852
vlength = js_strlen(vchars);
855
vsharplength = js_strlen(vsharp);
858
js_LeaveSharpObject(cx, NULL);
863
/* Allocate 1 + 1 at end for closing brace and terminating 0. */
865
realloc((ochars = chars),
866
(nchars + (comma ? 2 : 0) +
868
(gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
869
vsharplength + vlength +
870
(outermost ? 2 : 1) + 1) * sizeof(jschar));
872
/* Save code space on error: let JS_free ignore null vsharp. */
879
chars[nchars++] = comma[0];
880
chars[nchars++] = comma[1];
884
#ifdef OLD_GETTER_SETTER
885
js_strncpy(&chars[nchars], idstrchars, idstrlength);
886
nchars += idstrlength;
888
chars[nchars++] = ' ';
889
gsoplength = JSSTRING_LENGTH(gsop[j]);
890
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
891
nchars += gsoplength;
893
chars[nchars++] = ':';
896
gsoplength = JSSTRING_LENGTH(gsop[j]);
897
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
898
nchars += gsoplength;
899
chars[nchars++] = ' ';
901
js_strncpy(&chars[nchars], idstrchars, idstrlength);
902
nchars += idstrlength;
904
chars[nchars++] = ':';
907
js_strncpy(&chars[nchars], vsharp, vsharplength);
908
nchars += vsharplength;
910
js_strncpy(&chars[nchars], vchars, vlength);
915
#ifdef DUMP_CALL_TABLE
916
if (outermost && nchars >= js_LogCallToSourceLimit)
922
chars[nchars++] = '}';
924
chars[nchars++] = ')';
928
js_LeaveSharpObject(cx, &ida);
937
JS_ReportOutOfMemory(cx);
941
str = js_NewString(cx, chars, nchars, 0);
946
*rval = STRING_TO_JSVAL(str);
949
#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
952
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
957
const char *clazz, *prefix;
960
#if JS_HAS_INITIALIZERS
961
if (cx->version == JSVERSION_1_2)
962
return js_obj_toSource(cx, obj, argc, argv, rval);
965
clazz = OBJ_GET_CLASS(cx, obj)->name;
966
nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
967
chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
973
while ((chars[nchars] = (jschar)*prefix) != 0)
975
while ((chars[nchars] = (jschar)*clazz) != 0)
977
chars[nchars++] = ']';
980
str = js_NewString(cx, chars, nchars, 0);
985
*rval = STRING_TO_JSVAL(str);
990
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
992
*rval = OBJECT_TO_JSVAL(obj);
997
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
999
JSStackFrame *fp, *caller;
1000
JSBool indirectCall;
1005
JSPrincipals *principals;
1008
#if JS_HAS_EVAL_THIS_SCOPE
1009
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1010
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
1014
caller = JS_GetScriptedCaller(cx, fp);
1015
indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
1017
if (JSVERSION_IS_ECMA(cx->version) &&
1019
!JS_ReportErrorFlagsAndNumber(cx,
1020
JSREPORT_WARNING | JSREPORT_STRICT,
1021
js_GetErrorMessage, NULL,
1022
JSMSG_BAD_INDIRECT_CALL,
1027
if (!JSVAL_IS_STRING(argv[0])) {
1032
#if JS_HAS_SCRIPT_OBJECT
1034
* Script.prototype.compile/exec and Object.prototype.eval all take an
1035
* optional trailing argument that overrides the scope object.
1039
if (!js_ValueToObject(cx, argv[1], &scopeobj))
1041
argv[1] = OBJECT_TO_JSVAL(scopeobj);
1046
#if JS_HAS_EVAL_THIS_SCOPE
1047
/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1049
callerScopeChain = caller->scopeChain;
1050
if (obj != callerScopeChain) {
1051
scopeobj = js_NewObject(cx, &js_WithClass, obj,
1056
/* Set fp->scopeChain too, for the compiler. */
1057
caller->scopeChain = fp->scopeChain = scopeobj;
1058
setCallerScopeChain = JS_TRUE;
1061
callerVarObj = caller->varobj;
1062
if (obj != callerVarObj) {
1063
/* Set fp->varobj too, for the compiler. */
1064
caller->varobj = fp->varobj = obj;
1065
setCallerVarObj = JS_TRUE;
1068
/* From here on, control must exit through label out with ok set. */
1071
#if JS_BUG_EVAL_THIS_SCOPE
1072
/* An old version used the object in which eval was found for scope. */
1075
/* Compile using caller's current scope object. */
1077
scopeobj = caller->scopeChain;
1081
str = JSVAL_TO_STRING(argv[0]);
1083
file = caller->script->filename;
1084
line = js_PCToLineNumber(cx, caller->script, caller->pc);
1085
principals = JS_EvalFramePrincipals(cx, fp, caller);
1092
fp->flags |= JSFRAME_EVAL;
1093
script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1094
JSSTRING_CHARS(str),
1095
JSSTRING_LENGTH(str),
1102
#if !JS_BUG_EVAL_THIS_SCOPE
1103
#if JS_HAS_SCRIPT_OBJECT
1107
/* Execute using caller's new scope object (might be a Call object). */
1109
scopeobj = caller->scopeChain;
1112
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1113
JS_DestroyScript(cx, script);
1116
#if JS_HAS_EVAL_THIS_SCOPE
1117
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1118
if (setCallerScopeChain)
1119
caller->scopeChain = callerScopeChain;
1120
if (setCallerVarObj)
1121
caller->varobj = callerVarObj;
1126
#if JS_HAS_OBJ_WATCHPOINT
1129
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1133
JSResolvingEntry *entry;
1139
/* Avoid recursion on (obj, id) already being watched on cx. */
1142
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1146
generation = cx->resolvingTable->generation;
1148
funobj = (JSObject *) closure;
1152
ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
1153
js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1158
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1162
jsval userid, value;
1166
if (JSVAL_IS_FUNCTION(cx, argv[1])) {
1167
funobj = JSVAL_TO_OBJECT(argv[1]);
1169
fun = js_ValueToFunction(cx, &argv[1], 0);
1172
funobj = fun->object;
1174
argv[1] = OBJECT_TO_JSVAL(funobj);
1176
/* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1178
if (!JS_ValueToId(cx, userid, &propid))
1181
if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1183
if (attrs & JSPROP_READONLY)
1185
return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, funobj);
1189
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1191
return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1194
#endif /* JS_HAS_OBJ_WATCHPOINT */
1196
#if JS_HAS_NEW_OBJ_METHODS
1198
* Prototype and property query methods, to complement the 'in' and
1199
* 'instanceof' operators.
1202
/* Proposed ECMA 15.2.4.5. */
1204
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1210
JSScopeProperty *sprop;
1212
if (!JS_ValueToId(cx, argv[0], &id))
1214
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1217
*rval = JSVAL_FALSE;
1218
} else if (obj2 == obj) {
1220
} else if (OBJ_IS_NATIVE(obj2)) {
1221
sprop = (JSScopeProperty *)prop;
1222
*rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1224
*rval = JSVAL_FALSE;
1227
OBJ_DROP_PROPERTY(cx, obj2, prop);
1231
/* Proposed ECMA 15.2.4.6. */
1233
obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1238
if (!js_IsDelegate(cx, obj, *argv, &b))
1240
*rval = BOOLEAN_TO_JSVAL(b);
1244
/* Proposed ECMA 15.2.4.7. */
1246
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1255
if (!JS_ValueToId(cx, argv[0], &id))
1258
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1262
*rval = JSVAL_FALSE;
1267
* XXX ECMA spec error compatible: return false unless hasOwnProperty.
1268
* The ECMA spec really should be fixed so propertyIsEnumerable and the
1269
* for..in loop agree on whether prototype properties are enumerable,
1270
* obviously by fixing this method (not by breaking the for..in loop!).
1272
* We check here for shared permanent prototype properties, which should
1273
* be treated as if they are local to obj. They are an implementation
1274
* technique used to satisfy ECMA requirements; users should not be able
1275
* to distinguish a shared permanent proto-property from a local one.
1278
!(OBJ_IS_NATIVE(obj2) &&
1279
SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1280
OBJ_DROP_PROPERTY(cx, obj2, prop);
1281
*rval = JSVAL_FALSE;
1285
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1286
OBJ_DROP_PROPERTY(cx, obj2, prop);
1288
*rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1291
#endif /* JS_HAS_NEW_OBJ_METHODS */
1293
#if JS_HAS_GETTER_SETTER
1295
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1303
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1304
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1305
JSMSG_BAD_GETTER_OR_SETTER,
1310
if (!JS_ValueToId(cx, argv[0], &id))
1312
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1315
* Getters and setters are just like watchpoints from an access
1316
* control point of view.
1318
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1320
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1321
(JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1322
JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1327
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1335
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1336
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1337
JSMSG_BAD_GETTER_OR_SETTER,
1342
if (!JS_ValueToId(cx, argv[0], &id))
1344
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1347
* Getters and setters are just like watchpoints from an access
1348
* control point of view.
1350
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1352
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1353
NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1354
JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1359
obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1365
JSScopeProperty *sprop;
1367
if (!JS_ValueToId(cx, argv[0], &id))
1369
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1372
if (OBJ_IS_NATIVE(pobj)) {
1373
sprop = (JSScopeProperty *) prop;
1374
if (sprop->attrs & JSPROP_GETTER)
1375
*rval = OBJECT_TO_JSVAL(sprop->getter);
1377
OBJ_DROP_PROPERTY(cx, pobj, prop);
1383
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1389
JSScopeProperty *sprop;
1391
if (!JS_ValueToId(cx, argv[0], &id))
1393
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1396
if (OBJ_IS_NATIVE(pobj)) {
1397
sprop = (JSScopeProperty *) prop;
1398
if (sprop->attrs & JSPROP_SETTER)
1399
*rval = OBJECT_TO_JSVAL(sprop->setter);
1401
OBJ_DROP_PROPERTY(cx, pobj, prop);
1405
#endif /* JS_HAS_GETTER_SETTER */
1407
#if JS_HAS_OBJ_WATCHPOINT
1408
const char js_watch_str[] = "watch";
1409
const char js_unwatch_str[] = "unwatch";
1411
#if JS_HAS_NEW_OBJ_METHODS
1412
const char js_hasOwnProperty_str[] = "hasOwnProperty";
1413
const char js_isPrototypeOf_str[] = "isPrototypeOf";
1414
const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1416
#if JS_HAS_GETTER_SETTER
1417
const char js_defineGetter_str[] = "__defineGetter__";
1418
const char js_defineSetter_str[] = "__defineSetter__";
1419
const char js_lookupGetter_str[] = "__lookupGetter__";
1420
const char js_lookupSetter_str[] = "__lookupSetter__";
1423
static JSFunctionSpec object_methods[] = {
1425
{js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA},
1427
{js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1428
{js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1429
{js_valueOf_str, obj_valueOf, 0,0,0},
1430
{js_eval_str, obj_eval, 1,0,0},
1431
#if JS_HAS_OBJ_WATCHPOINT
1432
{js_watch_str, obj_watch, 2,0,0},
1433
{js_unwatch_str, obj_unwatch, 1,0,0},
1435
#if JS_HAS_NEW_OBJ_METHODS
1436
{js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
1437
{js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
1438
{js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1440
#if JS_HAS_GETTER_SETTER
1441
{js_defineGetter_str, obj_defineGetter, 2,0,0},
1442
{js_defineSetter_str, obj_defineSetter, 2,0,0},
1443
{js_lookupGetter_str, obj_lookupGetter, 1,0,0},
1444
{js_lookupSetter_str, obj_lookupSetter, 1,0,0},
1450
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1453
/* Trigger logic below to construct a blank object. */
1456
/* If argv[0] is null or undefined, obj comes back null. */
1457
if (!js_ValueToObject(cx, argv[0], &obj))
1461
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1462
if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1464
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1468
*rval = OBJECT_TO_JSVAL(obj);
1473
* ObjectOps and Class for with-statement stack objects.
1476
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1478
#if defined JS_THREADSAFE && defined DEBUG
1479
, const char *file, uintN line
1484
JSScopeProperty *sprop;
1487
proto = OBJ_GET_PROTO(cx, obj);
1489
return js_LookupProperty(cx, obj, id, objp, propp);
1490
if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
1494
* Check whether id names an argument or local variable in an active
1495
* function. If so, pretend we didn't find it, so that the real arg or
1496
* var property can be found in the function's call object, later on in
1497
* the scope chain. But skip unshared arg and var properties -- those
1498
* result when a script explicitly sets a function "static" property of
1499
* the same name. See jsinterp.c:SetFunctionSlot.
1501
* XXX blame pre-ECMA reflection of function args and vars as properties
1503
if ((sprop = (JSScopeProperty *) *propp) &&
1504
(proto = *objp, OBJ_IS_NATIVE(proto)) &&
1505
(sprop->getter == js_GetArgument ||
1506
sprop->getter == js_GetLocalVariable) &&
1507
(sprop->attrs & JSPROP_SHARED)) {
1508
JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
1509
for (fp = cx->fp; fp && (!fp->fun || !fp->fun->interpreted);
1513
if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
1514
OBJ_DROP_PROPERTY(cx, proto, *propp);
1523
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1525
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1527
return js_GetProperty(cx, obj, id, vp);
1528
return OBJ_GET_PROPERTY(cx, proto, id, vp);
1532
with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1534
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1536
return js_SetProperty(cx, obj, id, vp);
1537
return OBJ_SET_PROPERTY(cx, proto, id, vp);
1541
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1544
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1546
return js_GetAttributes(cx, obj, id, prop, attrsp);
1547
return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1551
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1554
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1556
return js_SetAttributes(cx, obj, id, prop, attrsp);
1557
return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1561
with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1563
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1565
return js_DeleteProperty(cx, obj, id, rval);
1566
return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1570
with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1572
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1574
return js_DefaultValue(cx, obj, hint, vp);
1575
return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1579
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1580
jsval *statep, jsid *idp)
1582
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1584
return js_Enumerate(cx, obj, enum_op, statep, idp);
1585
return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1589
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1590
jsval *vp, uintN *attrsp)
1592
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1594
return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1595
return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1599
with_ThisObject(JSContext *cx, JSObject *obj)
1601
JSObject *proto = OBJ_GET_PROTO(cx, obj);
1604
return OBJ_THIS_OBJECT(cx, proto);
1607
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1608
js_NewObjectMap, js_DestroyObjectMap,
1609
with_LookupProperty, js_DefineProperty,
1610
with_GetProperty, with_SetProperty,
1611
with_GetAttributes, with_SetAttributes,
1612
with_DeleteProperty, with_DefaultValue,
1613
with_Enumerate, with_CheckAccess,
1614
with_ThisObject, NATIVE_DROP_PROPERTY,
1617
js_SetProtoOrParent, js_SetProtoOrParent,
1622
static JSObjectOps *
1623
with_getObjectOps(JSContext *cx, JSClass *clasp)
1625
return &js_WithObjectOps;
1628
JSClass js_WithClass = {
1631
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1632
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
1637
#if JS_HAS_OBJ_PROTO_PROP
1639
With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1641
JSObject *parent, *proto;
1644
if (!JS_ReportErrorFlagsAndNumber(cx,
1645
JSREPORT_WARNING | JSREPORT_STRICT,
1646
js_GetErrorMessage, NULL,
1647
JSMSG_DEPRECATED_USAGE,
1648
js_WithClass.name)) {
1652
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1653
obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
1656
*rval = OBJECT_TO_JSVAL(obj);
1659
parent = cx->fp->scopeChain;
1661
if (!js_ValueToObject(cx, argv[0], &proto))
1663
v = OBJECT_TO_JSVAL(proto);
1664
if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1667
if (!js_ValueToObject(cx, argv[1], &parent))
1671
v = OBJECT_TO_JSVAL(parent);
1672
return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1677
js_InitObjectClass(JSContext *cx, JSObject *obj)
1682
#if JS_HAS_SHARP_VARS
1683
JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1686
proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1687
object_props, object_methods, NULL, NULL);
1691
#if JS_HAS_OBJ_PROTO_PROP
1692
if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1693
NULL, NULL, NULL, NULL)) {
1698
/* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1699
if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
1703
if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
1704
eval, NULL, NULL, 0, NULL)) {
1712
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1717
map->nslots = JS_INITIAL_NSLOTS;
1718
map->freeslot = JSSLOT_FREE(clasp);
1722
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1723
JSClass *clasp, JSObject *obj)
1725
return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1729
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1731
js_DestroyScope(cx, (JSScope *)map);
1735
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1737
JS_ASSERT(map->nrefs >= 0);
1738
JS_ATOMIC_INCREMENT(&map->nrefs);
1743
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
1745
JS_ASSERT(map->nrefs > 0);
1746
JS_ATOMIC_DECREMENT(&map->nrefs);
1747
if (map->nrefs == 0) {
1748
map->ops->destroyObjectMap(cx, map);
1751
if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
1752
((JSScope *)map)->object = NULL;
1757
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
1761
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
1763
JSObject *obj, *ctor;
1766
JSClass *protoclasp;
1771
/* Allocate an object from the GC heap and zero it. */
1772
obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
1776
/* Bootstrap the ur-object, and make it the default prototype object. */
1778
if (!GetClassPrototype(cx, parent, clasp->name, &proto))
1780
if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
1784
/* Always call the class's getObjectOps hook if it has one. */
1785
ops = clasp->getObjectOps
1786
? clasp->getObjectOps(cx, clasp)
1790
* Share proto's map only if it has the same JSObjectOps, and only if
1791
* proto's class has the same private and reserved slots as obj's map
1792
* and class have. We assume that if prototype and object are of the
1793
* same class, they always have the same number of computed reserved
1794
* slots (returned via clasp->reserveSlots); otherwise, prototype and
1795
* object classes must have the same (null or not) reserveSlots hook.
1798
(map = proto->map)->ops == ops &&
1799
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
1800
(!((protoclasp->flags ^ clasp->flags) &
1801
(JSCLASS_HAS_PRIVATE |
1802
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
1803
protoclasp->reserveSlots == clasp->reserveSlots)))
1805
/* Default parent to the parent of the prototype's constructor. */
1807
if (!OBJ_GET_PROPERTY(cx, proto,
1808
(jsid)cx->runtime->atomState.constructorAtom,
1812
if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
1813
parent = OBJ_GET_PARENT(cx, ctor);
1816
/* Share the given prototype's map. */
1817
obj->map = js_HoldObjectMap(cx, map);
1819
/* Ensure that obj starts with the minimum slots for clasp. */
1820
nslots = JS_INITIAL_NSLOTS;
1822
/* Leave parent alone. Allocate a new map for obj. */
1823
map = ops->newObjectMap(cx, 1, ops, clasp, obj);
1828
/* Let ops->newObjectMap set nslots so as to reserve slots. */
1829
nslots = map->nslots;
1832
/* Allocate a slots vector, with a -1'st element telling its length. */
1833
newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
1835
js_DropObjectMap(cx, obj->map, obj);
1839
newslots[0] = nslots;
1842
/* Set the proto, parent, and class properties. */
1843
newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
1844
newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
1845
newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
1847
/* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
1848
for (i = JSSLOT_CLASS + 1; i < nslots; i++)
1849
newslots[i] = JSVAL_VOID;
1851
/* Store newslots after initializing all of 'em, just in case. */
1852
obj->slots = newslots;
1854
if (cx->runtime->objectHook) {
1855
JS_KEEP_ATOMS(cx->runtime);
1856
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
1857
JS_UNKEEP_ATOMS(cx->runtime);
1863
cx->newborn[GCX_OBJECT] = NULL;
1868
FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
1871
JSObject *obj, *pobj;
1873
JSScopeProperty *sprop;
1875
atom = js_Atomize(cx, name, strlen(name), 0);
1879
if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
1880
/* Find the topmost object in the scope chain. */
1883
start = OBJ_GET_PARENT(cx, obj);
1886
obj = cx->globalObject;
1893
JS_ASSERT(OBJ_IS_NATIVE(obj));
1894
if (!js_LookupPropertyWithFlags(cx, obj, (jsid)atom, JSRESOLVE_CLASSNAME,
1896
#if defined JS_THREADSAFE && defined DEBUG
1897
, __FILE__, __LINE__
1907
JS_ASSERT(OBJ_IS_NATIVE(pobj));
1908
sprop = (JSScopeProperty *) prop;
1909
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
1910
*vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
1911
OBJ_DROP_PROPERTY(cx, pobj, prop);
1916
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1917
JSObject *parent, uintN argc, jsval *argv)
1920
JSObject *obj, *ctor;
1922
if (!FindConstructor(cx, parent, clasp->name, &cval))
1924
if (JSVAL_IS_PRIMITIVE(cval)) {
1925
js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
1930
* If proto or parent are NULL, set them to Constructor.prototype and/or
1931
* Constructor.__parent__, just like JSOP_NEW does.
1933
ctor = JSVAL_TO_OBJECT(cval);
1935
parent = OBJ_GET_PARENT(cx, ctor);
1937
if (!OBJ_GET_PROPERTY(cx, ctor,
1938
(jsid)cx->runtime->atomState.classPrototypeAtom,
1942
if (JSVAL_IS_OBJECT(rval))
1943
proto = JSVAL_TO_OBJECT(rval);
1946
obj = js_NewObject(cx, clasp, proto, parent);
1950
if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
1952
return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
1954
cx->newborn[GCX_OBJECT] = NULL;
1959
js_FinalizeObject(JSContext *cx, JSObject *obj)
1963
/* Cope with stillborn objects that have no map. */
1967
JS_ASSERT(obj->slots);
1969
if (cx->runtime->objectHook)
1970
cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
1972
/* Remove all watchpoints with weak links to obj. */
1973
JS_ClearWatchPointsForObject(cx, obj);
1976
* Finalize obj first, in case it needs map and slots. Optimized to use
1977
* LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
1978
* obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
1979
* we're called from the GC. Only the GC should call js_FinalizeObject,
1980
* and no other threads run JS (and possibly racing to update obj->slots)
1981
* while the GC is running.
1983
LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
1985
/* Drop map and free slots. */
1986
js_DropObjectMap(cx, map, obj);
1988
JS_free(cx, obj->slots - 1);
1992
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
1993
won't recycle the deleted props' slots. */
1995
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
2003
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2004
clasp = LOCKED_OBJ_GET_CLASS(obj);
2005
if (map->freeslot == JSSLOT_FREE(clasp)) {
2006
/* Adjust map->freeslot to include computed reserved slots, if any. */
2007
if (clasp->reserveSlots)
2008
map->freeslot += clasp->reserveSlots(cx, obj);
2010
nslots = map->nslots;
2011
if (map->freeslot >= nslots) {
2012
nslots = map->freeslot;
2013
JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2014
nslots += (nslots + 1) / 2;
2016
newslots = (jsval *)
2017
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2020
for (i = 1 + newslots[0]; i <= nslots; i++)
2021
newslots[i] = JSVAL_VOID;
2022
newslots[0] = map->nslots = nslots;
2023
obj->slots = newslots + 1;
2027
obj->slots[map->freeslot] = JSVAL_VOID;
2029
*slotp = map->freeslot++;
2034
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2040
OBJ_CHECK_SLOT(obj, slot);
2041
obj->slots[slot] = JSVAL_VOID;
2043
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2044
if (map->freeslot == slot + 1)
2045
map->freeslot = slot;
2046
nslots = map->nslots;
2047
if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2048
nslots = map->freeslot;
2049
nslots += nslots / 2;
2050
if (nslots < JS_INITIAL_NSLOTS)
2051
nslots = JS_INITIAL_NSLOTS;
2053
newslots = (jsval *)
2054
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2057
newslots[0] = map->nslots = nslots;
2058
obj->slots = newslots + 1;
2062
#if JS_BUG_EMPTY_INDEX_ZERO
2063
#define CHECK_FOR_EMPTY_INDEX(id) \
2065
if (JSSTRING_LENGTH(_str) == 0) \
2069
#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
2072
/* JSVAL_INT_MAX as a string */
2073
#define JSVAL_INT_MAX_STRING "1073741823"
2075
#define CHECK_FOR_FUNNY_INDEX(id) \
2077
if (!JSVAL_IS_INT(id)) { \
2078
JSAtom *atom_ = (JSAtom *)id; \
2079
JSString *str_ = ATOM_TO_STRING(atom_); \
2080
const jschar *cp_ = str_->chars; \
2081
JSBool negative_ = (*cp_ == '-'); \
2082
if (negative_) cp_++; \
2083
if (JS7_ISDEC(*cp_) && \
2084
str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
2085
id = CheckForFunnyIndex(id, cp_, negative_); \
2087
CHECK_FOR_EMPTY_INDEX(id); \
2093
CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
2095
jsuint index = JS7_UNDEC(*cp++);
2096
jsuint oldIndex = 0;
2100
while (JS7_ISDEC(*cp)) {
2103
index = 10 * index + c;
2108
(oldIndex < (JSVAL_INT_MAX / 10) ||
2109
(oldIndex == (JSVAL_INT_MAX / 10) &&
2110
c <= (JSVAL_INT_MAX % 10)))) {
2113
id = INT_TO_JSVAL((jsint)index);
2119
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2120
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2121
uintN attrs, uintN flags, intN shortid)
2124
JSScopeProperty *sprop;
2126
JS_LOCK_OBJ(cx, obj);
2127
scope = js_GetMutableScope(cx, obj);
2132
* Handle old bug that took empty string as zero index. Also convert
2133
* string indices to integers if appropriate.
2135
CHECK_FOR_FUNNY_INDEX(id);
2136
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2139
JS_UNLOCK_OBJ(cx, obj);
2144
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2145
JSScopeProperty *sprop, uintN attrs, uintN mask,
2146
JSPropertyOp getter, JSPropertyOp setter)
2150
JS_LOCK_OBJ(cx, obj);
2151
scope = js_GetMutableScope(cx, obj);
2155
sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2158
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2162
JS_UNLOCK_OBJ(cx, obj);
2167
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2168
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2171
return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2176
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2177
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2178
uintN flags, intN shortid, JSProperty **propp)
2183
JSScopeProperty *sprop;
2186
* Handle old bug that took empty string as zero index. Also convert
2187
* string indices to integers if appropriate.
2189
CHECK_FOR_FUNNY_INDEX(id);
2191
#if JS_HAS_GETTER_SETTER
2193
* If defining a getter or setter, we must check for its counterpart and
2194
* update the attributes and property ops. A getter or setter is really
2195
* only half of a property.
2197
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2201
* If JS_THREADSAFE and id is found, js_LookupProperty returns with
2202
* sprop non-null and pobj locked. If pobj == obj, the property is
2203
* already in obj and obj has its own (mutable) scope. So if we are
2204
* defining a getter whose setter was already defined, or vice versa,
2205
* finish the job via js_ChangeScopePropertyAttributes, and refresh
2206
* the property cache line for (obj, id) to map sprop.
2208
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2210
sprop = (JSScopeProperty *) prop;
2213
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2214
sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
2215
attrs, sprop->attrs,
2216
(attrs & JSPROP_GETTER)
2219
(attrs & JSPROP_SETTER)
2223
/* NB: obj == pobj, so we can share unlock code at the bottom. */
2230
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2231
OBJ_DROP_PROPERTY(cx, pobj, prop);
2235
#endif /* JS_HAS_GETTER_SETTER */
2237
/* Lock if object locking is required by this implementation. */
2238
JS_LOCK_OBJ(cx, obj);
2240
/* Use the object's class getter and setter by default. */
2241
clasp = LOCKED_OBJ_GET_CLASS(obj);
2243
getter = clasp->getProperty;
2245
setter = clasp->setProperty;
2247
/* Get obj's own scope if it has one, or create a new one for obj. */
2248
scope = js_GetMutableScope(cx, obj);
2252
/* Add the property to scope, or replace an existing one of the same id. */
2253
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2254
attrs |= JSPROP_SHARED;
2255
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2256
SPROP_INVALID_SLOT, attrs, flags, shortid);
2260
/* XXXbe called with lock held */
2261
if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
2262
(void) js_RemoveScopeProperty(cx, scope, id);
2266
if (SPROP_HAS_VALID_SLOT(sprop, scope))
2267
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2269
#if JS_HAS_GETTER_SETTER
2272
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2274
*propp = (JSProperty *) sprop;
2276
JS_UNLOCK_OBJ(cx, obj);
2280
JS_UNLOCK_OBJ(cx, obj);
2285
* Given pc pointing after a property accessing bytecode, return true if the
2286
* access is a "object-detecting" in the sense used by web pages, e.g., when
2287
* checking whether document.all is defined.
2290
Detecting(JSContext *cx, jsbytecode *pc)
2297
script = cx->fp->script;
2298
for (endpc = script->code + script->length; pc < endpc; pc++) {
2299
/* General case: a branch or equality op follows the access. */
2301
if (js_CodeSpec[op].format & JOF_DETECTING)
2305
* Special case #1: handle (document.all == null). Don't sweat about
2306
* JS1.2's revision of the equality operators here.
2308
if (op == JSOP_NULL) {
2310
return *pc == JSOP_EQ || *pc == JSOP_NE;
2315
* Special case #2: handle (document.all == undefined). Don't worry
2316
* about someone redefining undefined, which was added by Edition 3,
2317
* so was read/write for backward compatibility.
2319
if (op == JSOP_NAME) {
2320
atom = GET_ATOM(cx, script, pc);
2321
if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2322
(pc += js_CodeSpec[op].length) < endpc) {
2324
return op == JSOP_EQ || op == JSOP_NE ||
2325
op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
2330
/* At this point, anything but grouping means we're not detecting. */
2331
if (op != JSOP_GROUP)
2338
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2339
JSObject **objp, JSProperty **propp
2340
#if defined JS_THREADSAFE && defined DEBUG
2341
, const char *file, uintN line
2345
JSObject *start, *obj2, *proto;
2347
JSScopeProperty *sprop;
2349
JSResolveOp resolve;
2351
JSResolvingEntry *entry;
2353
JSNewResolveOp newresolve;
2355
const JSCodeSpec *cs;
2360
* Handle old bug that took empty string as zero index. Also convert
2361
* string indices to integers if appropriate.
2363
CHECK_FOR_FUNNY_INDEX(id);
2365
/* Search scopes starting with obj and following the prototype link. */
2368
JS_LOCK_OBJ(cx, obj);
2369
SET_OBJ_INFO(obj, file, line);
2370
scope = OBJ_SCOPE(obj);
2371
if (scope->object == obj) {
2372
sprop = SCOPE_GET_PROPERTY(scope, id);
2374
/* Shared prototype scope: try resolve before lookup. */
2378
/* Try obj's class resolve hook if id was not found in obj's scope. */
2380
clasp = LOCKED_OBJ_GET_CLASS(obj);
2381
resolve = clasp->resolve;
2382
if (resolve != JS_ResolveStub) {
2383
/* Avoid recursion on (obj, id) already being resolved on cx. */
2388
* Once we have successfully added an entry for (obj, key) to
2389
* cx->resolvingTable, control must go through cleanup: before
2390
* returning. But note that JS_DHASH_ADD may find an existing
2391
* entry, in which case we bail to suppress runaway recursion.
2393
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2394
JS_UNLOCK_OBJ(cx, obj);
2398
/* Already resolving id in obj -- dampen recursion. */
2399
JS_UNLOCK_OBJ(cx, obj);
2402
generation = cx->resolvingTable->generation;
2404
/* Null *propp here so we can test it at cleanup: safely. */
2407
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2408
newresolve = (JSNewResolveOp)resolve;
2409
if (cx->fp && (pc = cx->fp->pc)) {
2410
cs = &js_CodeSpec[*pc];
2411
format = cs->format;
2412
if ((format & JOF_MODEMASK) != JOF_NAME)
2413
flags |= JSRESOLVE_QUALIFIED;
2414
if ((format & JOF_ASSIGNING) ||
2415
(cx->fp->flags & JSFRAME_ASSIGNING)) {
2416
flags |= JSRESOLVE_ASSIGNING;
2419
if (Detecting(cx, pc))
2420
flags |= JSRESOLVE_DETECTING;
2422
if (format & JOF_DECLARING)
2423
flags |= JSRESOLVE_DECLARING;
2425
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2428
JS_UNLOCK_OBJ(cx, obj);
2430
/* Protect id and all atoms from a GC nested in resolve. */
2431
JS_KEEP_ATOMS(cx->runtime);
2432
ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2433
JS_UNKEEP_ATOMS(cx->runtime);
2437
JS_LOCK_OBJ(cx, obj);
2438
SET_OBJ_INFO(obj, file, line);
2440
/* Resolved: juggle locks and lookup id again. */
2442
JS_UNLOCK_OBJ(cx, obj);
2443
JS_LOCK_OBJ(cx, obj2);
2445
scope = OBJ_SCOPE(obj2);
2446
if (!MAP_IS_NATIVE(&scope->map)) {
2447
/* Whoops, newresolve handed back a foreign obj2. */
2448
JS_ASSERT(obj2 != obj);
2449
JS_UNLOCK_OBJ(cx, obj2);
2450
ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2453
JS_LOCK_OBJ(cx, obj2);
2456
* Require that obj2 have its own scope now, as we
2457
* do for old-style resolve. If it doesn't, then
2458
* id was not truly resolved, and we'll find it in
2459
* the proto chain, or miss it if obj2's proto is
2460
* not on obj's proto chain. That last case is a
2463
if (scope->object == obj2)
2464
sprop = SCOPE_GET_PROPERTY(scope, id);
2466
if (obj2 != obj && !sprop) {
2467
JS_UNLOCK_OBJ(cx, obj2);
2468
JS_LOCK_OBJ(cx, obj);
2473
* Old resolve always requires id re-lookup if obj owns
2474
* its scope after resolve returns.
2476
JS_UNLOCK_OBJ(cx, obj);
2477
ok = resolve(cx, obj, ID_TO_VALUE(id));
2480
JS_LOCK_OBJ(cx, obj);
2481
SET_OBJ_INFO(obj, file, line);
2482
scope = OBJ_SCOPE(obj);
2483
JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2484
if (scope->object == obj)
2485
sprop = SCOPE_GET_PROPERTY(scope, id);
2489
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2496
JS_ASSERT(OBJ_SCOPE(obj) == scope);
2497
*objp = scope->object; /* XXXbe hide in jsscope.[ch] */
2499
*propp = (JSProperty *) sprop;
2503
proto = LOCKED_OBJ_GET_PROTO(obj);
2504
JS_UNLOCK_OBJ(cx, obj);
2507
if (!OBJ_IS_NATIVE(proto))
2508
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2518
#if defined JS_THREADSAFE && defined DEBUG
2519
JS_FRIEND_API(JSBool)
2520
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2521
JSProperty **propp, const char *file, uintN line)
2523
return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp, file, line);
2526
JS_FRIEND_API(JSBool)
2527
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2530
return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
2534
JS_FRIEND_API(JSBool)
2535
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2539
JSObject *obj, *pobj, *lastobj;
2540
JSScopeProperty *sprop;
2544
obj = cx->fp->scopeChain;
2546
/* Try the property cache and return immediately on cache hit. */
2547
if (OBJ_IS_NATIVE(obj)) {
2548
JS_LOCK_OBJ(cx, obj);
2549
PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2551
JS_ASSERT(OBJ_IS_NATIVE(obj));
2554
*propp = (JSProperty *) sprop;
2557
JS_UNLOCK_OBJ(cx, obj);
2560
/* If cache miss, take the slow path. */
2561
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2564
if (OBJ_IS_NATIVE(pobj)) {
2565
sprop = (JSScopeProperty *) prop;
2566
PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2574
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2583
js_FindIdentifierBase(JSContext *cx, jsid id)
2585
JSObject *obj, *pobj;
2589
* Look for id's property along the "with" statement chain and the
2590
* statically-linked scope chain.
2592
if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
2595
OBJ_DROP_PROPERTY(cx, pobj, prop);
2600
* Use the top-level scope from the scope chain, which won't end in the
2601
* same scope as cx->globalObject for cross-context function calls.
2606
* Property not found. Give a strict warning if binding an undeclared
2607
* top-level variable.
2609
if (JS_HAS_STRICT_OPTION(cx)) {
2610
JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
2611
if (!JS_ReportErrorFlagsAndNumber(cx,
2612
JSREPORT_WARNING | JSREPORT_STRICT,
2613
js_GetErrorMessage, NULL,
2614
JSMSG_UNDECLARED_VAR,
2615
JS_GetStringBytes(str))) {
2623
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2628
JSScopeProperty *sprop;
2632
* Handle old bug that took empty string as zero index. Also convert
2633
* string indices to integers if appropriate.
2635
CHECK_FOR_FUNNY_INDEX(id);
2637
if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
2642
#if JS_BUG_NULL_INDEX_PROPS
2643
/* Indexed properties defaulted to null in old versions. */
2644
default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
2648
default_val = JSVAL_VOID;
2652
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
2656
* Give a strict warning if foo.bar is evaluated by a script for an
2657
* object foo with no property named 'bar'.
2659
if (JS_HAS_STRICT_OPTION(cx) &&
2660
*vp == default_val &&
2661
cx->fp && cx->fp->pc &&
2662
(*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
2667
/* Kludge to allow (typeof foo == "undefined") tests. */
2668
JS_ASSERT(cx->fp->script);
2670
pc += js_CodeSpec[*pc].length;
2671
if (Detecting(cx, pc))
2674
/* Ok, bad undefined property reference: whine about it. */
2675
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
2676
ID_TO_VALUE(id), NULL);
2678
!JS_ReportErrorFlagsAndNumber(cx,
2679
JSREPORT_WARNING|JSREPORT_STRICT,
2680
js_GetErrorMessage, NULL,
2681
JSMSG_UNDEFINED_PROP,
2682
JS_GetStringBytes(str))) {
2689
if (!OBJ_IS_NATIVE(obj2)) {
2690
OBJ_DROP_PROPERTY(cx, obj2, prop);
2691
return OBJ_GET_PROPERTY(cx, obj2, id, vp);
2694
/* Unlock obj2 before calling getter, relock after to avoid deadlock. */
2695
scope = OBJ_SCOPE(obj2);
2696
sprop = (JSScopeProperty *) prop;
2698
if (slot != SPROP_INVALID_SLOT) {
2699
JS_ASSERT(slot < obj2->map->freeslot);
2700
*vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
2702
/* If sprop has a stub getter, we're done. */
2709
JS_UNLOCK_SCOPE(cx, scope);
2710
if (!SPROP_GET(cx, sprop, obj, obj2, vp))
2712
JS_LOCK_SCOPE(cx, scope);
2714
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2715
LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
2716
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
2720
JS_UNLOCK_SCOPE(cx, scope);
2725
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2729
JSScopeProperty *sprop;
2734
JSPropertyOp getter, setter;
2739
* Handle old bug that took empty string as zero index. Also convert
2740
* string indices to integers if appropriate.
2742
CHECK_FOR_FUNNY_INDEX(id);
2744
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2747
if (prop && !OBJ_IS_NATIVE(pobj)) {
2748
OBJ_DROP_PROPERTY(cx, pobj, prop);
2751
sprop = (JSScopeProperty *) prop;
2754
* Now either sprop is null, meaning id was not found in obj or one of its
2755
* prototypes; or sprop is non-null, meaning id was found in pobj's scope.
2756
* If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
2757
* is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
2758
* (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
2759
* because it is cheaper).
2761
attrs = JSPROP_ENUMERATE;
2764
clasp = OBJ_GET_CLASS(cx, obj);
2765
getter = clasp->getProperty;
2766
setter = clasp->setProperty;
2770
* Set scope for use below. It was locked by js_LookupProperty, and
2771
* we know pobj owns it (i.e., scope->object == pobj). Therefore we
2772
* optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
2774
scope = OBJ_SCOPE(pobj);
2776
attrs = sprop->attrs;
2777
if ((attrs & JSPROP_READONLY) ||
2778
(SCOPE_IS_SEALED(scope) && pobj == obj)) {
2779
JS_UNLOCK_SCOPE(cx, scope);
2780
if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
2782
goto read_only_error;
2787
* We found id in a prototype object: prepare to share or shadow.
2788
* NB: Thanks to the immutable, garbage-collected property tree
2789
* maintained by jsscope.c in cx->runtime, we needn't worry about
2790
* sprop going away behind our back after we've unlocked scope.
2792
JS_UNLOCK_SCOPE(cx, scope);
2794
/* Don't clone a shared prototype property. */
2795
if (attrs & JSPROP_SHARED)
2796
return SPROP_SET(cx, sprop, obj, pobj, vp);
2798
/* Restore attrs to the ECMA default for new properties. */
2799
attrs = JSPROP_ENUMERATE;
2802
* Preserve the shortid, getter, and setter when shadowing any
2803
* property that has a shortid. An old API convention requires
2804
* that the property's getter and setter functions receive the
2805
* shortid, not id, when they are called on the shadow we are
2806
* about to create in obj's scope.
2808
if (sprop->flags & SPROP_HAS_SHORTID) {
2809
flags = SPROP_HAS_SHORTID;
2810
shortid = sprop->shortid;
2811
getter = sprop->getter;
2812
setter = sprop->setter;
2816
* Forget we found the proto-property now that we've copied any
2817
* needed member values.
2821
#ifdef __GNUC__ /* suppress bogus gcc warnings */
2828
if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
2829
goto read_only_error;
2831
/* Find or make a property descriptor with the right heritage. */
2832
JS_LOCK_OBJ(cx, obj);
2833
scope = js_GetMutableScope(cx, obj);
2835
JS_UNLOCK_OBJ(cx, obj);
2838
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2839
attrs |= JSPROP_SHARED;
2840
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2841
SPROP_INVALID_SLOT, attrs, flags, shortid);
2843
JS_UNLOCK_SCOPE(cx, scope);
2847
/* XXXbe called with obj locked */
2848
if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
2849
(void) js_RemoveScopeProperty(cx, scope, id);
2850
JS_UNLOCK_SCOPE(cx, scope);
2854
/* Initialize new property value (passed to setter) to undefined. */
2855
if (SPROP_HAS_VALID_SLOT(sprop, scope))
2856
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
2858
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2861
/* Get the current property value from its slot. */
2863
if (slot != SPROP_INVALID_SLOT) {
2864
JS_ASSERT(slot < obj->map->freeslot);
2865
pval = LOCKED_OBJ_GET_SLOT(obj, slot);
2867
/* If sprop has a stub setter, keep scope locked and just store *vp. */
2872
/* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
2873
JS_UNLOCK_SCOPE(cx, scope);
2875
/* Let the setter modify vp before copying from it to obj->slots[slot]. */
2876
if (!SPROP_SET(cx, sprop, obj, obj, vp))
2879
/* Relock obj's scope until we are done with sprop. */
2880
JS_LOCK_SCOPE(cx, scope);
2883
* Check whether sprop is still around (was not deleted), and whether it
2884
* has a slot (it may never have had one, or we may have lost a race with
2885
* someone who cleared scope).
2887
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2890
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
2892
JS_UNLOCK_SCOPE(cx, scope);
2896
JSString *str = js_DecompileValueGenerator(cx,
2901
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2903
JS_GetStringBytes(str));
2910
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2914
JSScopeProperty *sprop;
2918
if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2924
if (!OBJ_IS_NATIVE(obj)) {
2925
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2926
OBJ_DROP_PROPERTY(cx, obj, prop);
2930
sprop = (JSScopeProperty *)prop;
2931
*attrsp = sprop->attrs;
2933
OBJ_DROP_PROPERTY(cx, obj, prop);
2938
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2942
JSScopeProperty *sprop;
2946
if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2950
if (!OBJ_IS_NATIVE(obj)) {
2951
ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2952
OBJ_DROP_PROPERTY(cx, obj, prop);
2956
sprop = (JSScopeProperty *)prop;
2957
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2959
~(JSPROP_GETTER | JSPROP_SETTER), 0,
2960
sprop->getter, sprop->setter);
2962
OBJ_DROP_PROPERTY(cx, obj, prop);
2963
return (sprop != NULL);
2967
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
2969
#if JS_HAS_PROP_DELETE
2973
JSScopeProperty *sprop;
2978
*rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
2981
* Handle old bug that took empty string as zero index. Also convert
2982
* string indices to integers if appropriate.
2984
CHECK_FOR_FUNNY_INDEX(id);
2986
if (!js_LookupProperty(cx, obj, id, &proto, &prop))
2988
if (!prop || proto != obj) {
2990
* If the property was found in a native prototype, check whether it's
2991
* shared and permanent. Such a property stands for direct properties
2992
* in all delegating objects, matching ECMA semantics without bloating
2993
* each delegating object.
2996
if (OBJ_IS_NATIVE(proto)) {
2997
sprop = (JSScopeProperty *)prop;
2998
if (SPROP_IS_SHARED_PERMANENT(sprop))
2999
*rval = JSVAL_FALSE;
3001
OBJ_DROP_PROPERTY(cx, proto, prop);
3002
if (*rval == JSVAL_FALSE)
3007
* If no property, or the property comes unshared or impermanent from
3008
* a prototype, call the class's delProperty hook, passing rval as the
3011
return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3015
sprop = (JSScopeProperty *)prop;
3016
if (sprop->attrs & JSPROP_PERMANENT) {
3017
OBJ_DROP_PROPERTY(cx, obj, prop);
3018
if (JSVERSION_IS_ECMA(cx->version)) {
3019
*rval = JSVAL_FALSE;
3022
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3023
ID_TO_VALUE(id), NULL);
3025
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3026
JSMSG_PERMANENT, JS_GetStringBytes(str));
3031
/* XXXbe called with obj locked */
3032
if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3034
OBJ_DROP_PROPERTY(cx, obj, prop);
3038
scope = OBJ_SCOPE(obj);
3039
if (SPROP_HAS_VALID_SLOT(sprop, scope))
3040
GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3042
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3043
ok = js_RemoveScopeProperty(cx, scope, id);
3044
OBJ_DROP_PROPERTY(cx, obj, prop);
3047
#else /* !JS_HAS_PROP_DELETE */
3049
jsval null = JSVAL_NULL;
3052
return js_SetProperty(cx, obj, id, &null);
3054
#endif /* !JS_HAS_PROP_DELETE */
3058
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3063
v = OBJECT_TO_JSVAL(obj);
3067
* Propagate the exception if js_TryMethod finds an appropriate
3068
* method, and calling that method returned failure.
3070
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3074
if (!JSVAL_IS_PRIMITIVE(v)) {
3075
if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3079
* JS1.2 never failed (except for malloc failure) to convert an
3080
* object to a string. ECMA requires an error if both toString
3081
* and valueOf fail to produce a primitive value.
3083
if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
3084
char *bytes = JS_smprintf("[object %s]",
3085
OBJ_GET_CLASS(cx, obj)->name);
3088
str = JS_NewString(cx, bytes, strlen(bytes));
3093
v = STRING_TO_JSVAL(str);
3100
if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3102
if (!JSVAL_IS_PRIMITIVE(v)) {
3103
JSType type = JS_TypeOfValue(cx, v);
3105
(type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3108
/* Don't convert to string (source object literal) for JS1.2. */
3109
if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
3111
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3117
if (!JSVAL_IS_PRIMITIVE(v)) {
3118
/* Avoid recursive death through js_DecompileValueGenerator. */
3119
if (hint == JSTYPE_STRING) {
3120
str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3126
*vp = OBJECT_TO_JSVAL(obj);
3127
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3129
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3130
JSMSG_CANT_CONVERT_TO,
3131
JS_GetStringBytes(str),
3132
(hint == JSTYPE_VOID)
3134
: js_type_str[hint]);
3144
js_NewIdArray(JSContext *cx, jsint length)
3149
JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3151
ida->length = length;
3156
js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
3159
JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3161
ida->length = length;
3165
/* Private type used to iterate over all properties of a native JS object */
3166
typedef struct JSNativeIteratorState {
3167
jsint next_index; /* index into jsid array */
3168
JSIdArray *ida; /* All property ids in enumeration */
3169
} JSNativeIteratorState;
3172
* This function is used to enumerate the properties of native JSObjects
3173
* and those host objects that do not define a JSNewEnumerateOp-style iterator
3177
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3178
jsval *statep, jsid *idp)
3180
JSObject *proto_obj;
3182
JSEnumerateOp enumerate;
3183
JSScopeProperty *sprop, *lastProp;
3187
JSNativeIteratorState *state;
3189
clasp = OBJ_GET_CLASS(cx, obj);
3190
enumerate = clasp->enumerate;
3191
if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3192
return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3196
case JSENUMERATE_INIT:
3197
if (!enumerate(cx, obj))
3202
* The set of all property ids is pre-computed when the iterator
3203
* is initialized so as to avoid problems with properties being
3204
* deleted during the iteration.
3206
JS_LOCK_OBJ(cx, obj);
3207
scope = OBJ_SCOPE(obj);
3210
* If this object shares a scope with its prototype, don't enumerate
3211
* its properties. Otherwise they will be enumerated a second time
3212
* when the prototype object is enumerated.
3214
proto_obj = OBJ_GET_PROTO(cx, obj);
3215
if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
3216
ida = js_NewIdArray(cx, 0);
3218
JS_UNLOCK_OBJ(cx, obj);
3222
/* Object has a private scope; Enumerate all props in scope. */
3223
for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3224
sprop = sprop->parent) {
3226
#ifdef DUMP_CALL_TABLE
3227
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3229
(sprop->attrs & JSPROP_ENUMERATE)) &&
3230
!(sprop->flags & SPROP_IS_ALIAS) &&
3231
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3232
SCOPE_HAS_PROPERTY(scope, sprop))) {
3236
ida = js_NewIdArray(cx, length);
3238
JS_UNLOCK_OBJ(cx, obj);
3242
for (sprop = lastProp; sprop; sprop = sprop->parent) {
3244
#ifdef DUMP_CALL_TABLE
3245
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3247
(sprop->attrs & JSPROP_ENUMERATE)) &&
3248
!(sprop->flags & SPROP_IS_ALIAS) &&
3249
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3250
SCOPE_HAS_PROPERTY(scope, sprop))) {
3252
ida->vector[--i] = sprop->id;
3256
JS_UNLOCK_OBJ(cx, obj);
3258
state = (JSNativeIteratorState *)
3259
JS_malloc(cx, sizeof(JSNativeIteratorState));
3261
JS_DestroyIdArray(cx, ida);
3265
state->next_index = 0;
3266
*statep = PRIVATE_TO_JSVAL(state);
3268
*idp = INT_TO_JSVAL(length);
3271
case JSENUMERATE_NEXT:
3272
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3274
length = ida->length;
3275
if (state->next_index != length) {
3276
*idp = ida->vector[state->next_index++];
3280
/* Fall through ... */
3282
case JSENUMERATE_DESTROY:
3283
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3284
JS_DestroyIdArray(cx, state->ida);
3286
*statep = JSVAL_NULL;
3295
*statep = JSVAL_NULL;
3300
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3301
jsval *vp, uintN *attrsp)
3305
JSScopeProperty *sprop;
3309
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3314
clasp = OBJ_GET_CLASS(cx, obj);
3315
return !clasp->checkAccess ||
3316
clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3318
if (!OBJ_IS_NATIVE(pobj)) {
3319
OBJ_DROP_PROPERTY(cx, pobj, prop);
3320
return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3322
sprop = (JSScopeProperty *)prop;
3323
*vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3324
? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3326
*attrsp = sprop->attrs;
3327
clasp = LOCKED_OBJ_GET_CLASS(obj);
3328
if (clasp->checkAccess) {
3329
JS_UNLOCK_OBJ(cx, pobj);
3330
ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3331
JS_LOCK_OBJ(cx, pobj);
3335
OBJ_DROP_PROPERTY(cx, pobj, prop);
3339
#ifdef JS_THREADSAFE
3341
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3343
JS_UNLOCK_OBJ(cx, obj);
3348
ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
3351
* The decompiler may need to access the args of the function in
3352
* progress rather than the one we had hoped to call.
3353
* So we switch the cx->fp to the frame below us. We stick the
3354
* current frame in the dormantFrameChain to protect it from gc.
3357
JSStackFrame *fp = cx->fp;
3359
JS_ASSERT(!fp->dormantNext);
3360
fp->dormantNext = cx->dormantFrameChain;
3361
cx->dormantFrameChain = fp;
3365
js_ReportIsNotFunction(cx, vp, flags);
3368
JS_ASSERT(cx->dormantFrameChain == fp);
3369
cx->dormantFrameChain = fp->dormantNext;
3370
fp->dormantNext = NULL;
3377
GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
3382
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
3384
if (!OBJ_GET_PROPERTY(cx, obj,
3385
(jsid)cx->runtime->atomState.ExecutionContextAtom,
3389
if (JSVAL_IS_PRIMITIVE(xcval)) {
3390
JS_ReportError(cx, "invalid ExecutionContext in global object");
3393
if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
3394
(jsid)cx->runtime->atomState.currentAtom,
3403
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3407
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3410
JSObject *callee, *args;
3411
jsval fval, nargv[3];
3414
callee = JSVAL_TO_OBJECT(argv[-2]);
3415
if (!OBJ_GET_PROPERTY(cx, callee,
3416
(jsid)cx->runtime->atomState.callAtom,
3420
if (JSVAL_IS_FUNCTION(cx, fval)) {
3421
if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
3423
args = js_GetArgsObject(cx, cx->fp);
3426
nargv[0] = OBJECT_TO_JSVAL(obj);
3427
nargv[1] = OBJECT_TO_JSVAL(args);
3428
return js_InternalCall(cx, callee, fval, 3, nargv, rval);
3430
if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
3432
ok = js_Call(cx, obj, argc, argv, rval);
3433
argv[-2] = OBJECT_TO_JSVAL(callee);
3437
ReportIsNotFunction(cx, &argv[-2], 0);
3440
return clasp->call(cx, obj, argc, argv, rval);
3444
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3449
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3450
if (!clasp->construct) {
3452
JSObject *callee, *args;
3453
jsval cval, nargv[2];
3456
callee = JSVAL_TO_OBJECT(argv[-2]);
3457
if (!OBJ_GET_PROPERTY(cx, callee,
3458
(jsid)cx->runtime->atomState.constructAtom,
3462
if (JSVAL_IS_FUNCTION(cx, cval)) {
3463
if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
3465
args = js_GetArgsObject(cx, cx->fp);
3468
nargv[0] = OBJECT_TO_JSVAL(args);
3469
return js_InternalCall(cx, callee, cval, 2, nargv, rval);
3471
if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
3473
ok = js_Call(cx, obj, argc, argv, rval);
3474
argv[-2] = OBJECT_TO_JSVAL(callee);
3478
ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3481
return clasp->construct(cx, obj, argc, argv, rval);
3485
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3489
clasp = OBJ_GET_CLASS(cx, obj);
3490
if (clasp->hasInstance)
3491
return clasp->hasInstance(cx, obj, v, bp);
3496
if (!OBJ_GET_PROPERTY(cx, obj,
3497
(jsid)cx->runtime->atomState.hasInstanceAtom,
3501
if (JSVAL_IS_FUNCTION(cx, fval)) {
3502
return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
3503
js_ValueToBoolean(cx, rval, bp);
3512
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3517
if (JSVAL_IS_PRIMITIVE(v))
3519
obj2 = JSVAL_TO_OBJECT(v);
3520
while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
3530
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
3532
return GetClassPrototype(cx, NULL, name, protop);
3536
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
3542
if (!FindConstructor(cx, scope, name, &v))
3544
if (JSVAL_IS_FUNCTION(cx, v)) {
3545
ctor = JSVAL_TO_OBJECT(v);
3546
if (!OBJ_GET_PROPERTY(cx, ctor,
3547
(jsid)cx->runtime->atomState.classPrototypeAtom,
3552
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
3557
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
3561
* Use the given attributes for the prototype property of the constructor,
3562
* as user-defined constructors have a DontDelete prototype (which may be
3563
* reset), while native or "system" constructors have DontEnum | ReadOnly |
3566
if (!OBJ_DEFINE_PROPERTY(cx, ctor,
3567
(jsid)cx->runtime->atomState.classPrototypeAtom,
3568
OBJECT_TO_JSVAL(proto), NULL, NULL,
3574
* ECMA says that Object.prototype.constructor, or f.prototype.constructor
3575
* for a user-defined function f, is DontEnum.
3577
return OBJ_DEFINE_PROPERTY(cx, proto,
3578
(jsid)cx->runtime->atomState.constructorAtom,
3579
OBJECT_TO_JSVAL(ctor), NULL, NULL,
3584
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
3588
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
3590
} else if (JSVAL_IS_OBJECT(v)) {
3591
obj = JSVAL_TO_OBJECT(v);
3592
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
3594
if (JSVAL_IS_OBJECT(v))
3595
obj = JSVAL_TO_OBJECT(v);
3597
if (JSVAL_IS_STRING(v)) {
3598
obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
3599
} else if (JSVAL_IS_INT(v)) {
3600
obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
3601
} else if (JSVAL_IS_DOUBLE(v)) {
3602
obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
3604
JS_ASSERT(JSVAL_IS_BOOLEAN(v));
3605
obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
3615
js_ValueToNonNullObject(JSContext *cx, jsval v)
3620
if (!js_ValueToObject(cx, v, &obj))
3623
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
3625
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3626
JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
3633
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
3635
#if JS_HAS_VALUEOF_HINT
3638
argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
3639
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
3642
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
3648
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
3649
uintN argc, jsval *argv, jsval *rval)
3651
JSErrorReporter older;
3656
* Report failure only if an appropriate method was found, and calling it
3657
* returned failure. We propagate failure in this case to make exceptions
3660
older = JS_SetErrorReporter(cx, NULL);
3661
if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval)) {
3662
JS_ClearPendingException(cx);
3664
} else if (!JSVAL_IS_PRIMITIVE(fval)) {
3665
ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
3667
JS_ClearPendingException(cx);
3671
JS_SetErrorReporter(cx, older);
3677
#include "jsxdrapi.h"
3680
js_XDRObject(JSXDRState *xdr, JSObject **objp)
3684
const char *className;
3685
uint32 classId, classDef;
3690
if (xdr->mode == JSXDR_ENCODE) {
3691
clasp = OBJ_GET_CLASS(cx, *objp);
3692
className = clasp->name;
3693
classId = JS_XDRFindClassIdByName(xdr, className);
3694
classDef = !classId;
3695
if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
3700
clasp = NULL; /* quell GCC overwarning */
3703
/* XDR a flag word followed (if true) by the class name. */
3704
if (!JS_XDRUint32(xdr, &classDef))
3706
if (classDef && !JS_XDRCString(xdr, (char **) &className))
3709
/* From here on, return through out: to free className if it was set. */
3710
ok = JS_XDRUint32(xdr, &classId);
3714
if (xdr->mode != JSXDR_ENCODE) {
3716
ok = js_GetClassPrototype(cx, className, &proto);
3719
clasp = OBJ_GET_CLASS(cx, proto);
3720
ok = JS_XDRRegisterClass(xdr, clasp, &classId);
3724
clasp = JS_XDRFindClassById(xdr, classId);
3727
JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
3728
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3729
JSMSG_CANT_FIND_CLASS, numBuf);
3736
if (!clasp->xdrObject) {
3737
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3738
JSMSG_CANT_XDR_CLASS, clasp->name);
3741
ok = clasp->xdrObject(xdr, objp);
3744
if (xdr->mode != JSXDR_ENCODE && className)
3745
JS_free(cx, (void *)className);
3749
#endif /* JS_HAS_XDR */
3751
#ifdef DEBUG_brendan
3756
uint32 js_entry_count_max;
3757
uint32 js_entry_count_sum;
3758
double js_entry_count_sqsum;
3759
uint32 js_entry_count_hist[11];
3762
MeterEntryCount(uintN count)
3765
js_entry_count_sum += count;
3766
js_entry_count_sqsum += (double)count * count;
3767
if (count > js_entry_count_max)
3768
js_entry_count_max = count;
3770
js_entry_count_hist[JS_MIN(count, 10)]++;
3774
js_DumpScopeMeters(JSRuntime *rt)
3778
logfp = fopen("/tmp/scope.stats", "a");
3781
double mean = 0., var = 0., sigma = 0.;
3782
double nscopes = rt->liveScopes;
3783
double nentrys = js_entry_count_sum;
3784
if (nscopes > 0 && nentrys >= 0) {
3785
mean = nentrys / nscopes;
3786
var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
3787
if (var < 0.0 || nscopes <= 1)
3790
var /= nscopes * (nscopes - 1);
3792
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
3793
sigma = (var != 0.) ? sqrt(var) : 0.;
3797
"scopes %g entries %g mean %g sigma %g max %u",
3798
nscopes, nentrys, mean, sigma, js_entry_count_max);
3801
fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
3802
js_entry_count_hist[0], js_entry_count_hist[1],
3803
js_entry_count_hist[2], js_entry_count_hist[3],
3804
js_entry_count_hist[4], js_entry_count_hist[5],
3805
js_entry_count_hist[6], js_entry_count_hist[7],
3806
js_entry_count_hist[8], js_entry_count_hist[9],
3807
js_entry_count_hist[10]);
3808
js_entry_count_sum = js_entry_count_max = 0;
3809
js_entry_count_sqsum = 0;
3810
memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
3814
#endif /* DEBUG_brendan */
3817
js_Mark(JSContext *cx, JSObject *obj, void *arg)
3820
JSScopeProperty *sprop;
3823
JS_ASSERT(OBJ_IS_NATIVE(obj));
3824
scope = OBJ_SCOPE(obj);
3825
#ifdef DEBUG_brendan
3826
if (scope->object == obj)
3827
MeterEntryCount(scope->entryCount);
3830
JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
3831
SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
3833
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3834
if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
3836
MARK_SCOPE_PROPERTY(sprop);
3837
if (!JSVAL_IS_INT(sprop->id))
3838
GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
3840
#if JS_HAS_GETTER_SETTER
3841
if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3842
#ifdef GC_MARK_DEBUG
3844
JSAtom *atom = (JSAtom *)sprop->id;
3845
const char *id = (atom && ATOM_IS_STRING(atom))
3846
? JS_GetStringBytes(ATOM_TO_STRING(atom))
3850
if (sprop->attrs & JSPROP_GETTER) {
3851
#ifdef GC_MARK_DEBUG
3852
JS_snprintf(buf, sizeof buf, "%s %s",
3856
JSVAL_TO_GCTHING((jsval) sprop->getter),
3860
if (sprop->attrs & JSPROP_SETTER) {
3861
#ifdef GC_MARK_DEBUG
3862
JS_snprintf(buf, sizeof buf, "%s %s",
3866
JSVAL_TO_GCTHING((jsval) sprop->setter),
3871
#endif /* JS_HAS_GETTER_SETTER */
3874
/* No one runs while the GC is running, so we can use LOCKED_... here. */
3875
clasp = LOCKED_OBJ_GET_CLASS(obj);
3877
(void) clasp->mark(cx, obj, arg);
3879
if (scope->object != obj) {
3881
* An unmutated object that shares a prototype's scope. We can't tell
3882
* how many slots are allocated and in use at obj->slots by looking at
3883
* scope, so we get obj->slots' length from its -1'st element.
3885
return (uint32) obj->slots[-1];
3887
return JS_MIN(scope->map.freeslot, scope->map.nslots);
3891
js_Clear(JSContext *cx, JSObject *obj)
3895
JSScopeProperty *sprop;
3899
* Clear our scope and the property cache of all obj's properties only if
3900
* obj owns the scope (i.e., not if obj is unmutated and therefore sharing
3901
* its prototype's scope). NB: we do not clear any reserved slots lying
3902
* below JSSLOT_FREE(clasp).
3904
JS_LOCK_OBJ(cx, obj);
3905
scope = OBJ_SCOPE(obj);
3906
if (scope->object == obj) {
3907
/* Clear the property cache before we clear the scope. */
3909
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3910
if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3911
SCOPE_HAS_PROPERTY(scope, sprop)) {
3912
PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
3916
/* Now that we're done using scope->lastProp/table, clear scope. */
3917
js_ClearScope(cx, scope);
3919
/* Clear slot values and reset freeslot so we're consistent. */
3920
i = scope->map.nslots;
3921
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
3923
obj->slots[i] = JSVAL_VOID;
3924
scope->map.freeslot = n;
3926
JS_UNLOCK_OBJ(cx, obj);
3930
js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
3934
JS_LOCK_OBJ(cx, obj);
3935
v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
3936
JS_UNLOCK_OBJ(cx, obj);
3941
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
3948
JS_LOCK_OBJ(cx, obj);
3949
scope = OBJ_SCOPE(obj);
3950
nslots = (uint32) obj->slots[-1];
3951
if (slot >= nslots) {
3953
* At this point, obj may or may not own scope. If some path calls
3954
* js_GetMutableScope but does not add a slot-owning property, then
3955
* scope->object == obj but nslots will be nominal. If obj shares a
3956
* prototype's scope, then we cannot update scope->map here, but we
3957
* must update obj->slots[-1] when we grow obj->slots.
3959
* See js_Mark, before the last return, where we make a special case
3960
* for unmutated (scope->object != obj) objects.
3962
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
3963
clasp = LOCKED_OBJ_GET_CLASS(obj);
3964
nslots = JSSLOT_FREE(clasp);
3965
if (clasp->reserveSlots)
3966
nslots += clasp->reserveSlots(cx, obj);
3967
JS_ASSERT(slot < nslots);
3969
newslots = (jsval *)
3970
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
3972
JS_UNLOCK_SCOPE(cx, scope);
3975
for (i = 1 + newslots[0]; i <= nslots; i++)
3976
newslots[i] = JSVAL_VOID;
3977
if (scope->object == obj)
3978
scope->map.nslots = nslots;
3979
newslots[0] = nslots;
3980
obj->slots = newslots + 1;
3983
/* Whether or not we grew nslots, we may need to advance freeslot. */
3984
if (scope->object == obj && slot >= scope->map.freeslot)
3985
scope->map.freeslot = slot + 1;
3987
obj->slots[slot] = v;
3988
JS_UNLOCK_SCOPE(cx, scope);
3994
/* Routines to print out values during debugging. */
3996
void printChar(jschar *cp) {
3997
fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
3999
fputc(*cp++, stderr);
4001
fputc('\n', stderr);
4004
void printString(JSString *str) {
4007
fprintf(stderr, "string (0x%p) \"", (void *)str);
4008
s = JSSTRING_CHARS(str);
4009
for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
4010
fputc(s[i], stderr);
4012
fputc('\n', stderr);
4015
void printVal(JSContext *cx, jsval val);
4017
void printObj(JSContext *cx, JSObject *jsobj) {
4022
fprintf(stderr, "object 0x%p\n", (void *)jsobj);
4023
clasp = OBJ_GET_CLASS(cx, jsobj);
4024
fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
4025
for (i=0; i < jsobj->map->nslots; i++) {
4026
fprintf(stderr, "slot %3d ", i);
4027
val = jsobj->slots[i];
4028
if (JSVAL_IS_OBJECT(val))
4029
fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
4035
void printVal(JSContext *cx, jsval val) {
4036
fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
4037
if (JSVAL_IS_NULL(val)) {
4038
fprintf(stderr, "null\n");
4039
} else if (JSVAL_IS_VOID(val)) {
4040
fprintf(stderr, "undefined\n");
4041
} else if (JSVAL_IS_OBJECT(val)) {
4042
printObj(cx, JSVAL_TO_OBJECT(val));
4043
} else if (JSVAL_IS_INT(val)) {
4044
fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
4045
} else if (JSVAL_IS_STRING(val)) {
4046
printString(JSVAL_TO_STRING(val));
4047
} else if (JSVAL_IS_DOUBLE(val)) {
4048
fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
4050
JS_ASSERT(JSVAL_IS_BOOLEAN(val));
4051
fprintf(stderr, "(boolean) %s\n",
4052
JSVAL_TO_BOOLEAN(val) ? "true" : "false");
4057
void printId(JSContext *cx, jsid id) {
4058
fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
4059
printVal(cx, ID_TO_VALUE(id));
4062
void printAtom(JSAtom *atom) {
4063
printString(ATOM_TO_STRING(atom));