Parent Directory
|
Revision Log
|
Patch
revision 506 by siliconforks, Sat Sep 26 23:15:22 2009 UTC | revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC | |
---|---|---|
# | Line 1 | Line 1 |
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | * vim: set ts=8 sw=4 et tw=79: | * vim: set ts=8 sw=4 et tw=79: |
3 | * | * |
4 | * ***** BEGIN LICENSE BLOCK ***** | * ***** BEGIN LICENSE BLOCK ***** |
# | Line 41 | Line 41 |
41 | /* | /* |
42 | * JS object implementation. | * JS object implementation. |
43 | */ | */ |
#include "jsstddef.h" | ||
44 | #include <stdlib.h> | #include <stdlib.h> |
45 | #include <string.h> | #include <string.h> |
46 | #include "jstypes.h" | #include "jstypes.h" |
47 | #include "jsstdint.h" | |
48 | #include "jsarena.h" /* Added by JSIFY */ | #include "jsarena.h" /* Added by JSIFY */ |
49 | #include "jsbit.h" | #include "jsbit.h" |
50 | #include "jsutil.h" /* Added by JSIFY */ | #include "jsutil.h" /* Added by JSIFY */ |
# | Line 69 | Line 69 |
69 | #include "jsparse.h" | #include "jsparse.h" |
70 | #include "jsscope.h" | #include "jsscope.h" |
71 | #include "jsscript.h" | #include "jsscript.h" |
72 | #include "jsscriptinlines.h" | |
73 | #include "jsstaticcheck.h" | #include "jsstaticcheck.h" |
74 | #include "jsstr.h" | #include "jsstr.h" |
75 | #include "jstracer.h" | #include "jstracer.h" |
# | Line 90 | Line 91 |
91 | #include "jsdtracef.h" | #include "jsdtracef.h" |
92 | #endif | #endif |
93 | ||
94 | #include "jsatominlines.h" | |
95 | #include "jsobjinlines.h" | |
96 | #include "jsscriptinlines.h" | |
97 | ||
98 | #include "jsautooplen.h" | #include "jsautooplen.h" |
99 | ||
100 | #ifdef JS_THREADSAFE | #ifdef JS_THREADSAFE |
# | Line 111 | Line 116 |
116 | NULL, NATIVE_DROP_PROPERTY, | NULL, NATIVE_DROP_PROPERTY, |
117 | js_Call, js_Construct, | js_Call, js_Construct, |
118 | js_HasInstance, js_TraceObject, | js_HasInstance, js_TraceObject, |
119 | js_Clear, js_GetRequiredSlot, | js_Clear |
js_SetRequiredSlot | ||
120 | }; | }; |
121 | ||
122 | JSClass js_ObjectClass = { | JSClass js_ObjectClass = { |
123 | js_Object_str, | js_Object_str, |
124 | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), |
125 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
126 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, |
127 | JSCLASS_NO_OPTIONAL_MEMBERS | JSCLASS_NO_OPTIONAL_MEMBERS |
128 | }; | }; |
129 | ||
# | Line 179 | Line 183 |
183 | mode = JSACC_PARENT; | mode = JSACC_PARENT; |
184 | } | } |
185 | ||
186 | /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ | /* Let obj->checkAccess get the slot's value, based on the access mode. */ |
187 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) | if (!obj->checkAccess(cx, propid, mode, vp, &attrs)) |
188 | return JS_FALSE; | return JS_FALSE; |
189 | ||
190 | pobj = JSVAL_TO_OBJECT(*vp); | pobj = JSVAL_TO_OBJECT(*vp); |
# | Line 234 | Line 238 |
238 | ||
239 | /* __parent__ is readonly and permanent, only __proto__ may be set. */ | /* __parent__ is readonly and permanent, only __proto__ may be set. */ |
240 | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); | propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); |
241 | if (!OBJ_CHECK_ACCESS(cx, obj, propid, | if (!obj->checkAccess(cx, propid, (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, &attrs)) |
(JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, | ||
&attrs)) { | ||
242 | return JS_FALSE; | return JS_FALSE; |
} | ||
243 | ||
244 | return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE); | return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE); |
245 | } | } |
# | Line 253 | Line 254 |
254 | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) | if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) |
255 | return JS_FALSE; | return JS_FALSE; |
256 | ||
/* Get the number of properties to enumerate. */ | ||
257 | iter_state = JSVAL_NULL; | iter_state = JSVAL_NULL; |
258 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); | JSAutoEnumStateRooter tvr(cx, obj, &iter_state); |
259 | ||
260 | /* Get the number of properties to enumerate. */ | |
261 | ok = obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties); | |
262 | if (!ok) | if (!ok) |
263 | goto out; | goto out; |
264 | ||
# | Line 267 | Line 270 |
270 | *vp = num_properties; | *vp = num_properties; |
271 | ||
272 | out: | out: |
273 | if (iter_state != JSVAL_NULL) | if (!JSVAL_IS_NULL(iter_state)) |
274 | ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); | ok &= obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0); |
275 | return ok; | return ok; |
276 | } | } |
277 | ||
# | Line 297 | Line 300 |
300 | /* | /* |
301 | * Regenerate property cache shape ids for all of the scopes along the | * Regenerate property cache shape ids for all of the scopes along the |
302 | * old prototype chain to invalidate their property cache entries, in | * old prototype chain to invalidate their property cache entries, in |
303 | * case any entries were filled by looking up starting from obj. | * case any entries were filled by looking up through obj. |
304 | */ | */ |
305 | JSObject *oldproto = obj; | JSObject *oldproto = obj; |
306 | while (oldproto && OBJ_IS_NATIVE(oldproto)) { | while (oldproto && OBJ_IS_NATIVE(oldproto)) { |
307 | JS_LOCK_OBJ(cx, oldproto); | JS_LOCK_OBJ(cx, oldproto); |
308 | JSScope *scope = OBJ_SCOPE(oldproto); | JSScope *scope = OBJ_SCOPE(oldproto); |
309 | js_MakeScopeShapeUnique(cx, scope); | scope->protoShapeChange(cx); |
310 | JSObject *tmp = STOBJ_GET_PROTO(scope->object); | JSObject *tmp = STOBJ_GET_PROTO(oldproto); |
311 | JS_UNLOCK_OBJ(cx, oldproto); | JS_UNLOCK_OBJ(cx, oldproto); |
312 | oldproto = tmp; | oldproto = tmp; |
313 | } | } |
# | Line 312 | Line 315 |
315 | ||
316 | if (!pobj || !checkForCycles) { | if (!pobj || !checkForCycles) { |
317 | if (slot == JSSLOT_PROTO) | if (slot == JSSLOT_PROTO) |
318 | STOBJ_SET_PROTO(obj, pobj); | obj->setProto(pobj); |
319 | else | else |
320 | STOBJ_SET_PARENT(obj, pobj); | obj->setParent(pobj); |
321 | } else { | } else { |
322 | /* | /* |
323 | * Use the GC machinery to serialize access to all objects on the | * Use the GC machinery to serialize access to all objects on the |
# | Line 404 | Line 407 |
407 | for (i = 0, length = ida->length; i < length; i++) { | for (i = 0, length = ida->length; i < length; i++) { |
408 | id = ida->vector[i]; | id = ida->vector[i]; |
409 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
410 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); | ok = obj->lookupProperty(cx, id, &obj2, &prop); |
411 | if (!ok) | if (!ok) |
412 | break; | break; |
413 | if (!prop) | if (!prop) |
414 | continue; | continue; |
415 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); | ok = obj2->getAttributes(cx, id, prop, &attrs); |
416 | if (ok) { | if (ok) { |
417 | if (OBJ_IS_NATIVE(obj2) && | if (OBJ_IS_NATIVE(obj2) && |
418 | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { | (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
# | Line 427 | Line 430 |
430 | val = js_CastAsObjectJSVal(sprop->setter); | val = js_CastAsObjectJSVal(sprop->setter); |
431 | } | } |
432 | } else { | } else { |
433 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); | ok = obj->getProperty(cx, id, &val); |
434 | } | } |
435 | } | } |
436 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
437 | #else | #else |
438 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val); | ok = obj->getProperty(cx, id, &val); |
439 | #endif | #endif |
440 | if (!ok) | if (!ok) |
441 | break; | break; |
# | Line 524 | Line 527 |
527 | * It's possible that the value of a property has changed from the | * It's possible that the value of a property has changed from the |
528 | * first time the object's properties are traversed (when the property | * first time the object's properties are traversed (when the property |
529 | * ids are entered into the hash table) to the second (when they are | * ids are entered into the hash table) to the second (when they are |
530 | * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not | * converted to strings), i.e., the JSObject::getProperty() call is not |
531 | * idempotent. | * idempotent. |
532 | */ | */ |
533 | if (!he) { | if (!he) { |
# | Line 558 | Line 561 |
561 | ida = JS_Enumerate(cx, obj); | ida = JS_Enumerate(cx, obj); |
562 | if (!ida) { | if (!ida) { |
563 | if (*sp) { | if (*sp) { |
564 | JS_free(cx, *sp); | cx->free(*sp); |
565 | *sp = NULL; | *sp = NULL; |
566 | } | } |
567 | goto bad; | goto bad; |
# | Line 702 | Line 705 |
705 | ||
706 | if (!chars) { | if (!chars) { |
707 | /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ | /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ |
708 | chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); | chars = (jschar *) js_malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); |
709 | nchars = 0; | nchars = 0; |
710 | if (!chars) | if (!chars) |
711 | goto error; | goto error; |
# | Line 713 | Line 716 |
716 | MAKE_SHARP(he); | MAKE_SHARP(he); |
717 | nchars = js_strlen(chars); | nchars = js_strlen(chars); |
718 | chars = (jschar *) | chars = (jschar *) |
719 | realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); | js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); |
720 | if (!chars) { | if (!chars) { |
721 | free(ochars); | js_free(ochars); |
722 | goto error; | goto error; |
723 | } | } |
724 | if (outermost) { | if (outermost) { |
# | Line 746 | Line 749 |
749 | id = ida->vector[i]; | id = ida->vector[i]; |
750 | ||
751 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
752 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); | ok = obj->lookupProperty(cx, id, &obj2, &prop); |
753 | if (!ok) | if (!ok) |
754 | goto error; | goto error; |
755 | #endif | #endif |
# | Line 758 | Line 761 |
761 | idstr = js_ValueToString(cx, ID_TO_VALUE(id)); | idstr = js_ValueToString(cx, ID_TO_VALUE(id)); |
762 | if (!idstr) { | if (!idstr) { |
763 | ok = JS_FALSE; | ok = JS_FALSE; |
764 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
765 | goto error; | goto error; |
766 | } | } |
767 | *vp = STRING_TO_JSVAL(idstr); /* local root */ | *vp = STRING_TO_JSVAL(idstr); /* local root */ |
768 | idIsLexicalIdentifier = js_IsIdentifier(idstr); | idIsLexicalIdentifier = js_IsIdentifier(idstr); |
769 | needOldStyleGetterSetter = | needOldStyleGetterSetter = |
770 | !idIsLexicalIdentifier || | !idIsLexicalIdentifier || |
771 | js_CheckKeyword(JSSTRING_CHARS(idstr), | js_CheckKeyword(idstr->chars(), idstr->length()) != TOK_EOF; |
JSSTRING_LENGTH(idstr)) != TOK_EOF; | ||
772 | ||
773 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
774 | ||
775 | valcnt = 0; | valcnt = 0; |
776 | if (prop) { | if (prop) { |
777 | ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); | ok = obj2->getAttributes(cx, id, prop, &attrs); |
778 | if (!ok) { | if (!ok) { |
779 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
780 | goto error; | goto error; |
781 | } | } |
782 | if (OBJ_IS_NATIVE(obj2) && | if (OBJ_IS_NATIVE(obj2) && |
# | Line 802 | Line 804 |
804 | valcnt = 1; | valcnt = 1; |
805 | gsop[0] = NULL; | gsop[0] = NULL; |
806 | gsopold[0] = NULL; | gsopold[0] = NULL; |
807 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); | ok = obj->getProperty(cx, id, &val[0]); |
808 | } | } |
809 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
810 | } | } |
811 | ||
812 | #else /* !JS_HAS_GETTER_SETTER */ | #else /* !JS_HAS_GETTER_SETTER */ |
# | Line 818 | Line 820 |
820 | valcnt = 1; | valcnt = 1; |
821 | gsop[0] = NULL; | gsop[0] = NULL; |
822 | gsopold[0] = NULL; | gsopold[0] = NULL; |
823 | ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); | ok = obj->getProperty(cx, id, &val[0]); |
824 | ||
825 | #endif /* !JS_HAS_GETTER_SETTER */ | #endif /* !JS_HAS_GETTER_SETTER */ |
826 | ||
# | Line 839 | Line 841 |
841 | } | } |
842 | *vp = STRING_TO_JSVAL(idstr); /* local root */ | *vp = STRING_TO_JSVAL(idstr); /* local root */ |
843 | } | } |
844 | JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength); | idstr->getCharsAndLength(idstrchars, idstrlength); |
845 | ||
846 | for (j = 0; j < valcnt; j++) { | for (j = 0; j < valcnt; j++) { |
847 | /* Convert val[j] to its canonical source form. */ | /* Convert val[j] to its canonical source form. */ |
# | Line 849 | Line 851 |
851 | goto error; | goto error; |
852 | } | } |
853 | localroot[j] = STRING_TO_JSVAL(valstr); /* local root */ | localroot[j] = STRING_TO_JSVAL(valstr); /* local root */ |
854 | JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength); | valstr->getCharsAndLength(vchars, vlength); |
855 | ||
856 | if (vchars[0] == '#') | if (vchars[0] == '#') |
857 | needOldStyleGetterSetter = JS_TRUE; | needOldStyleGetterSetter = JS_TRUE; |
# | Line 945 | Line 947 |
947 | SAFE_ADD(2); | SAFE_ADD(2); |
948 | SAFE_ADD(idstrlength + 1); | SAFE_ADD(idstrlength + 1); |
949 | if (gsop[j]) | if (gsop[j]) |
950 | SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); | SAFE_ADD(gsop[j]->length() + 1); |
951 | SAFE_ADD(vsharplength); | SAFE_ADD(vsharplength); |
952 | SAFE_ADD(vlength); | SAFE_ADD(vlength); |
953 | /* Account for the trailing null. */ | /* Account for the trailing null. */ |
# | Line 957 | Line 959 |
959 | ||
960 | /* Allocate 1 + 1 at end for closing brace and terminating 0. */ | /* Allocate 1 + 1 at end for closing brace and terminating 0. */ |
961 | chars = (jschar *) | chars = (jschar *) |
962 | realloc((ochars = chars), curlen * sizeof(jschar)); | js_realloc((ochars = chars), curlen * sizeof(jschar)); |
963 | if (!chars) { | if (!chars) { |
964 | /* Save code space on error: let JS_free ignore null vsharp. */ | /* Save code space on error: let JS_free ignore null vsharp. */ |
965 | JS_free(cx, vsharp); | cx->free(vsharp); |
966 | free(ochars); | js_free(ochars); |
967 | goto error; | goto error; |
968 | } | } |
969 | ||
# | Line 976 | Line 978 |
978 | nchars += idstrlength; | nchars += idstrlength; |
979 | if (gsop[j]) { | if (gsop[j]) { |
980 | chars[nchars++] = ' '; | chars[nchars++] = ' '; |
981 | gsoplength = JSSTRING_LENGTH(gsop[j]); | gsoplength = gsop[j]->length(); |
982 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), | js_strncpy(&chars[nchars], gsop[j]->chars(), |
983 | gsoplength); | gsoplength); |
984 | nchars += gsoplength; | nchars += gsoplength; |
985 | } | } |
986 | chars[nchars++] = ':'; | chars[nchars++] = ':'; |
987 | } else { /* New style "decompilation" */ | } else { /* New style "decompilation" */ |
988 | if (gsop[j]) { | if (gsop[j]) { |
989 | gsoplength = JSSTRING_LENGTH(gsop[j]); | gsoplength = gsop[j]->length(); |
990 | js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), | js_strncpy(&chars[nchars], gsop[j]->chars(), |
991 | gsoplength); | gsoplength); |
992 | nchars += gsoplength; | nchars += gsoplength; |
993 | chars[nchars++] = ' '; | chars[nchars++] = ' '; |
# | Line 1004 | Line 1006 |
1006 | nchars += vlength; | nchars += vlength; |
1007 | ||
1008 | if (vsharp) | if (vsharp) |
1009 | JS_free(cx, vsharp); | cx->free(vsharp); |
1010 | } | } |
1011 | } | } |
1012 | ||
# | Line 1018 | Line 1020 |
1020 | ||
1021 | if (!ok) { | if (!ok) { |
1022 | if (chars) | if (chars) |
1023 | free(chars); | js_free(chars); |
1024 | goto out; | goto out; |
1025 | } | } |
1026 | ||
# | Line 1030 | Line 1032 |
1032 | make_string: | make_string: |
1033 | str = js_NewString(cx, chars, nchars); | str = js_NewString(cx, chars, nchars); |
1034 | if (!str) { | if (!str) { |
1035 | free(chars); | js_free(chars); |
1036 | ok = JS_FALSE; | ok = JS_FALSE; |
1037 | goto out; | goto out; |
1038 | } | } |
# | Line 1041 | Line 1043 |
1043 | return ok; | return ok; |
1044 | ||
1045 | overflow: | overflow: |
1046 | JS_free(cx, vsharp); | cx->free(vsharp); |
1047 | free(chars); | js_free(chars); |
1048 | chars = NULL; | chars = NULL; |
1049 | goto error; | goto error; |
1050 | } | } |
# | Line 1063 | Line 1065 |
1065 | obj = js_GetWrappedObject(cx, obj); | obj = js_GetWrappedObject(cx, obj); |
1066 | clazz = OBJ_GET_CLASS(cx, obj)->name; | clazz = OBJ_GET_CLASS(cx, obj)->name; |
1067 | nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ | nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ |
1068 | chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); | chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar)); |
1069 | if (!chars) | if (!chars) |
1070 | return JS_FALSE; | return JS_FALSE; |
1071 | ||
# | Line 1078 | Line 1080 |
1080 | ||
1081 | str = js_NewString(cx, chars, nchars); | str = js_NewString(cx, chars, nchars); |
1082 | if (!str) { | if (!str) { |
1083 | JS_free(cx, chars); | cx->free(chars); |
1084 | return JS_FALSE; | return JS_FALSE; |
1085 | } | } |
1086 | *vp = STRING_TO_JSVAL(str); | *vp = STRING_TO_JSVAL(str); |
# | Line 1203 | Line 1205 |
1205 | return principals->codebase; | return principals->codebase; |
1206 | } | } |
1207 | ||
1208 | jsbytecode *pc = caller->regs->pc; | if (caller->regs && js_GetOpcode(cx, caller->script, caller->regs->pc) == JSOP_EVAL) { |
1209 | if (caller->regs && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) { | JS_ASSERT(js_GetOpcode(cx, caller->script, caller->regs->pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); |
1210 | JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); | *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH); |
*linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH); | ||
1211 | } else { | } else { |
1212 | *linenop = js_FramePCToLineNumber(cx, caller); | *linenop = js_FramePCToLineNumber(cx, caller); |
1213 | } | } |
# | Line 1224 | Line 1225 |
1225 | size_t n; | size_t n; |
1226 | uint32 h; | uint32 h; |
1227 | ||
1228 | JSSTRING_CHARS_AND_LENGTH(str, s, n); | str->getCharsAndLength(s, n); |
1229 | if (n > 100) | if (n > 100) |
1230 | n = 100; | n = 100; |
1231 | for (h = 0; n; s++, n--) | for (h = 0; n; s++, n--) |
# | Line 1257 | Line 1258 |
1258 | fp = js_GetTopStackFrame(cx); | fp = js_GetTopStackFrame(cx); |
1259 | caller = js_GetScriptedCaller(cx, fp); | caller = js_GetScriptedCaller(cx, fp); |
1260 | indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); | indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); |
1261 | uintN staticLevel = caller->script->staticLevel + 1; | |
1262 | ||
1263 | /* | /* |
1264 | * This call to js_GetWrappedObject is safe because of the security checks | * This call to js_GetWrappedObject is safe because of the security checks |
# | Line 1398 | Line 1400 |
1400 | ||
1401 | tcflags = TCF_COMPILE_N_GO; | tcflags = TCF_COMPILE_N_GO; |
1402 | if (caller) { | if (caller) { |
1403 | tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1); | tcflags |= TCF_PUT_STATIC_LEVEL(staticLevel); |
1404 | principals = JS_EvalFramePrincipals(cx, fp, caller); | principals = JS_EvalFramePrincipals(cx, fp, caller); |
1405 | file = js_ComputeFilename(cx, caller, principals, &line); | file = js_ComputeFilename(cx, caller, principals, &line); |
1406 | } else { | } else { |
# | Line 1412 | Line 1414 |
1414 | ||
1415 | /* Cache local eval scripts indexed by source qualified by scope. */ | /* Cache local eval scripts indexed by source qualified by scope. */ |
1416 | bucket = EvalCacheHash(cx, str); | bucket = EvalCacheHash(cx, str); |
1417 | if (caller->fun) { | if (!indirectCall && argc == 1 && caller->fun) { |
1418 | uintN count = 0; | uintN count = 0; |
1419 | JSScript **scriptp = bucket; | JSScript **scriptp = bucket; |
1420 | ||
1421 | EVAL_CACHE_METER(probe); | EVAL_CACHE_METER(probe); |
1422 | while ((script = *scriptp) != NULL) { | while ((script = *scriptp) != NULL) { |
1423 | if ((script->flags & JSSF_SAVED_CALLER_FUN) && | if ((script->flags & JSSF_SAVED_CALLER_FUN) && |
1424 | script->staticLevel == staticLevel && | |
1425 | script->version == cx->version && | script->version == cx->version && |
1426 | (script->principals == principals || | (script->principals == principals || |
1427 | (principals->subsume(principals, script->principals) && | (principals->subsume(principals, script->principals) && |
# | Line 1428 | Line 1431 |
1431 | * See JSCompiler::compileScript in jsparse.cpp. | * See JSCompiler::compileScript in jsparse.cpp. |
1432 | */ | */ |
1433 | JSFunction *fun; | JSFunction *fun; |
1434 | JS_GET_SCRIPT_FUNCTION(script, 0, fun); | fun = script->getFunction(0); |
1435 | ||
1436 | if (fun == caller->fun) { | if (fun == caller->fun) { |
1437 | /* | /* |
# | Line 1445 | Line 1448 |
1448 | * script has no compiled-in dependencies on the prior | * script has no compiled-in dependencies on the prior |
1449 | * eval's scopeobj. | * eval's scopeobj. |
1450 | */ | */ |
1451 | JSObjectArray *objarray = JS_SCRIPT_OBJECTS(script); | JSObjectArray *objarray = script->objects(); |
1452 | int i = 1; | int i = 1; |
1453 | if (objarray->length == 1) { | if (objarray->length == 1) { |
1454 | if (script->regexpsOffset != 0) { | if (script->regexpsOffset != 0) { |
1455 | objarray = JS_SCRIPT_REGEXPS(script); | objarray = script->regexps(); |
1456 | i = 0; | i = 0; |
1457 | } else { | } else { |
1458 | EVAL_CACHE_METER(noscope); | EVAL_CACHE_METER(noscope); |
# | Line 1458 | Line 1461 |
1461 | } | } |
1462 | if (i < 0 || | if (i < 0 || |
1463 | STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) { | STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) { |
1464 | JS_ASSERT(staticLevel == script->staticLevel); | |
1465 | EVAL_CACHE_METER(hit); | EVAL_CACHE_METER(hit); |
1466 | *scriptp = script->u.nextToGC; | *scriptp = script->u.nextToGC; |
1467 | script->u.nextToGC = NULL; | script->u.nextToGC = NULL; |
# | Line 1478 | Line 1482 |
1482 | ||
1483 | if (!script) { | if (!script) { |
1484 | script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, | script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, |
1485 | JSSTRING_CHARS(str), JSSTRING_LENGTH(str), | str->chars(), str->length(), |
1486 | NULL, file, line, str); | NULL, file, line, str); |
1487 | if (!script) { | if (!script) { |
1488 | ok = JS_FALSE; | ok = JS_FALSE; |
# | Line 1513 | Line 1517 |
1517 | if (setCallerScopeChain) { | if (setCallerScopeChain) { |
1518 | caller->scopeChain = callerScopeChain; | caller->scopeChain = callerScopeChain; |
1519 | JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); | JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); |
1520 | JS_SetPrivate(cx, setCallerScopeChain, NULL); | setCallerScopeChain->setPrivate(NULL); |
1521 | } | } |
1522 | if (setCallerVarObj) | if (setCallerVarObj) |
1523 | caller->varobj = callerVarObj; | caller->varobj = callerVarObj; |
# | Line 1599 | Line 1603 |
1603 | return JS_FALSE; | return JS_FALSE; |
1604 | ||
1605 | obj = JS_THIS_OBJECT(cx, vp); | obj = JS_THIS_OBJECT(cx, vp); |
1606 | if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) | if (!obj || !obj->checkAccess(cx, propid, JSACC_WATCH, &value, &attrs)) |
1607 | return JS_FALSE; | return JS_FALSE; |
1608 | if (attrs & JSPROP_READONLY) | if (attrs & JSPROP_READONLY) |
1609 | return JS_TRUE; | return JS_TRUE; |
# | Line 1707 | Line 1711 |
1711 | } | } |
1712 | } | } |
1713 | if (prop) | if (prop) |
1714 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
1715 | return JS_TRUE; | return JS_TRUE; |
1716 | } | } |
1717 | ||
# | Line 1782 | Line 1786 |
1786 | JSProperty *prop; | JSProperty *prop; |
1787 | JSBool ok; | JSBool ok; |
1788 | ||
1789 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj->lookupProperty(cx, id, &pobj, &prop)) |
1790 | return JS_FALSE; | return JS_FALSE; |
1791 | ||
1792 | if (!prop) { | if (!prop) { |
# | Line 1804 | Line 1808 |
1808 | if (pobj != obj && | if (pobj != obj && |
1809 | !(OBJ_IS_NATIVE(pobj) && | !(OBJ_IS_NATIVE(pobj) && |
1810 | SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { | SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { |
1811 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
1812 | *vp = JSVAL_FALSE; | *vp = JSVAL_FALSE; |
1813 | return JS_TRUE; | return JS_TRUE; |
1814 | } | } |
1815 | ||
1816 | ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs); | ok = pobj->getAttributes(cx, id, prop, &attrs); |
1817 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
1818 | if (ok) | if (ok) |
1819 | *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); | *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); |
1820 | return ok; | return ok; |
# | Line 1842 | Line 1846 |
1846 | * Getters and setters are just like watchpoints from an access | * Getters and setters are just like watchpoints from an access |
1847 | * control point of view. | * control point of view. |
1848 | */ | */ |
1849 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) | if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs)) |
1850 | return JS_FALSE; | return JS_FALSE; |
1851 | *vp = JSVAL_VOID; | *vp = JSVAL_VOID; |
1852 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, | return obj->defineProperty(cx, id, JSVAL_VOID, |
1853 | js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), | js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub, |
1854 | JS_PropertyStub, | JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED); |
JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, | ||
NULL); | ||
1855 | } | } |
1856 | ||
1857 | JS_FRIEND_API(JSBool) | JS_FRIEND_API(JSBool) |
# | Line 1877 | Line 1879 |
1879 | * Getters and setters are just like watchpoints from an access | * Getters and setters are just like watchpoints from an access |
1880 | * control point of view. | * control point of view. |
1881 | */ | */ |
1882 | if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) | if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs)) |
1883 | return JS_FALSE; | return JS_FALSE; |
1884 | *vp = JSVAL_VOID; | *vp = JSVAL_VOID; |
1885 | return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, | return obj->defineProperty(cx, id, JSVAL_VOID, |
1886 | JS_PropertyStub, | JS_PropertyStub, js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), |
1887 | js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), | JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED); |
JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, | ||
NULL); | ||
1888 | } | } |
1889 | ||
1890 | static JSBool | static JSBool |
# | Line 1898 | Line 1898 |
1898 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1899 | return JS_FALSE; | return JS_FALSE; |
1900 | obj = JS_THIS_OBJECT(cx, vp); | obj = JS_THIS_OBJECT(cx, vp); |
1901 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop)) |
1902 | return JS_FALSE; | return JS_FALSE; |
1903 | *vp = JSVAL_VOID; | *vp = JSVAL_VOID; |
1904 | if (prop) { | if (prop) { |
# | Line 1907 | Line 1907 |
1907 | if (sprop->attrs & JSPROP_GETTER) | if (sprop->attrs & JSPROP_GETTER) |
1908 | *vp = js_CastAsObjectJSVal(sprop->getter); | *vp = js_CastAsObjectJSVal(sprop->getter); |
1909 | } | } |
1910 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
1911 | } | } |
1912 | return JS_TRUE; | return JS_TRUE; |
1913 | } | } |
# | Line 1923 | Line 1923 |
1923 | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) | if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id)) |
1924 | return JS_FALSE; | return JS_FALSE; |
1925 | obj = JS_THIS_OBJECT(cx, vp); | obj = JS_THIS_OBJECT(cx, vp); |
1926 | if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop)) |
1927 | return JS_FALSE; | return JS_FALSE; |
1928 | *vp = JSVAL_VOID; | *vp = JSVAL_VOID; |
1929 | if (prop) { | if (prop) { |
# | Line 1932 | Line 1932 |
1932 | if (sprop->attrs & JSPROP_SETTER) | if (sprop->attrs & JSPROP_SETTER) |
1933 | *vp = js_CastAsObjectJSVal(sprop->setter); | *vp = js_CastAsObjectJSVal(sprop->setter); |
1934 | } | } |
1935 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
1936 | } | } |
1937 | return JS_TRUE; | return JS_TRUE; |
1938 | } | } |
# | Line 1954 | Line 1954 |
1954 | return JS_FALSE; | return JS_FALSE; |
1955 | vp[2] = OBJECT_TO_JSVAL(obj); | vp[2] = OBJECT_TO_JSVAL(obj); |
1956 | ||
1957 | return OBJ_CHECK_ACCESS(cx, obj, | return obj->checkAccess(cx, ATOM_TO_JSID(cx->runtime->atomState.protoAtom), |
ATOM_TO_JSID(cx->runtime->atomState.protoAtom), | ||
1958 | JSACC_PROTO, vp, &attrs); | JSACC_PROTO, vp, &attrs); |
1959 | } | } |
1960 | ||
# | Line 1986 | Line 1985 |
1985 | #endif | #endif |
1986 | JS_FN(js_toString_str, obj_toString, 0,0), | JS_FN(js_toString_str, obj_toString, 0,0), |
1987 | JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), | JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), |
1988 | JS_TN(js_valueOf_str, obj_valueOf, 0,0, | JS_TN(js_valueOf_str, obj_valueOf, 0,0, &obj_valueOf_trcinfo), |
obj_valueOf_trcinfo), | ||
1989 | #if JS_HAS_OBJ_WATCHPOINT | #if JS_HAS_OBJ_WATCHPOINT |
1990 | JS_FN(js_watch_str, obj_watch, 2,0), | JS_FN(js_watch_str, obj_watch, 2,0), |
1991 | JS_FN(js_unwatch_str, obj_unwatch, 1,0), | JS_FN(js_unwatch_str, obj_unwatch, 1,0), |
1992 | #endif | #endif |
1993 | JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, | JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, &obj_hasOwnProperty_trcinfo), |
obj_hasOwnProperty_trcinfo), | ||
1994 | JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), | JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), |
1995 | JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, | JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, &obj_propertyIsEnumerable_trcinfo), |
obj_propertyIsEnumerable_trcinfo), | ||
1996 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
1997 | JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), | JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), |
1998 | JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), | JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), |
# | Line 2011 | Line 2007 |
2007 | JS_FS_END | JS_FS_END |
2008 | }; | }; |
2009 | ||
2010 | static bool | |
2011 | AllocSlots(JSContext *cx, JSObject *obj, size_t nslots); | |
2012 | ||
2013 | static inline bool | |
2014 | InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops) | |
2015 | { | |
2016 | JS_ASSERT(OPS_IS_NATIVE(ops)); | |
2017 | JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); | |
2018 | ||
2019 | /* Share proto's emptyScope only if obj is similar to proto. */ | |
2020 | JSClass *clasp = OBJ_GET_CLASS(cx, obj); | |
2021 | JSScope *scope; | |
2022 | if (proto && OBJ_IS_NATIVE(proto) && | |
2023 | (scope = OBJ_SCOPE(proto))->canProvideEmptyScope(ops, clasp)) { | |
2024 | scope = scope->getEmptyScope(cx, clasp); | |
2025 | if (!scope) | |
2026 | goto bad; | |
2027 | } else { | |
2028 | scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false)); | |
2029 | if (!scope) | |
2030 | goto bad; | |
2031 | ||
2032 | /* Let JSScope::create set freeslot so as to reserve slots. */ | |
2033 | JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); | |
2034 | if (scope->freeslot > JS_INITIAL_NSLOTS && | |
2035 | !AllocSlots(cx, obj, scope->freeslot)) { | |
2036 | JSScope::destroy(cx, scope); | |
2037 | goto bad; | |
2038 | } | |
2039 | } | |
2040 | obj->map = scope; | |
2041 | return true; | |
2042 | ||
2043 | bad: | |
2044 | /* The GC nulls map initially. It should still be null on error. */ | |
2045 | JS_ASSERT(!obj->map); | |
2046 | return false; | |
2047 | } | |
2048 | ||
2049 | JSObject * | |
2050 | js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, | |
2051 | JSObject *parent, size_t objectSize) | |
2052 | { | |
2053 | #ifdef INCLUDE_MOZILLA_DTRACE | |
2054 | if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) | |
2055 | jsdtrace_object_create_start(cx->fp, clasp); | |
2056 | #endif | |
2057 | ||
2058 | /* Assert that the class is a proper class. */ | |
2059 | JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, | |
2060 | ((JSExtendedClass *)clasp)->equality); | |
2061 | ||
2062 | /* Always call the class's getObjectOps hook if it has one. */ | |
2063 | JSObjectOps *ops = clasp->getObjectOps | |
2064 | ? clasp->getObjectOps(cx, clasp) | |
2065 | : &js_ObjectOps; | |
2066 | ||
2067 | /* | |
2068 | * Allocate an object from the GC heap and initialize all its fields before | |
2069 | * doing any operation that can potentially trigger GC. Functions have a | |
2070 | * larger non-standard allocation size. | |
2071 | */ | |
2072 | JSObject* obj; | |
2073 | if (clasp == &js_FunctionClass && !objectSize) { | |
2074 | obj = (JSObject*) js_NewGCFunction(cx, GCX_OBJECT); | |
2075 | #ifdef DEBUG | |
2076 | memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, | |
2077 | sizeof(JSFunction) - sizeof(JSObject)); | |
2078 | #endif | |
2079 | } else { | |
2080 | JS_ASSERT(!objectSize || objectSize == sizeof(JSObject)); | |
2081 | obj = js_NewGCObject(cx, GCX_OBJECT); | |
2082 | } | |
2083 | if (!obj) | |
2084 | goto out; | |
2085 | ||
2086 | /* | |
2087 | * Default parent to the parent of the prototype, which was set from | |
2088 | * the parent of the prototype's constructor. | |
2089 | */ | |
2090 | obj->init(clasp, | |
2091 | proto, | |
2092 | (!parent && proto) ? proto->getParent() : parent, | |
2093 | JSObject::defaultPrivate(clasp)); | |
2094 | ||
2095 | if (OPS_IS_NATIVE(ops)) { | |
2096 | if (!InitScopeForObject(cx, obj, proto, ops)) { | |
2097 | obj = NULL; | |
2098 | goto out; | |
2099 | } | |
2100 | } else { | |
2101 | JS_ASSERT(ops->objectMap->ops == ops); | |
2102 | obj->map = const_cast<JSObjectMap *>(ops->objectMap); | |
2103 | } | |
2104 | ||
2105 | /* Check that the newborn root still holds the object. */ | |
2106 | JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj); | |
2107 | ||
2108 | /* | |
2109 | * Do not call debug hooks on trace, because we might be in a non-_FAIL | |
2110 | * builtin. See bug 481444. | |
2111 | */ | |
2112 | if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { | |
2113 | JSAutoTempValueRooter tvr(cx, obj); | |
2114 | JS_KEEP_ATOMS(cx->runtime); | |
2115 | cx->debugHooks->objectHook(cx, obj, JS_TRUE, | |
2116 | cx->debugHooks->objectHookData); | |
2117 | JS_UNKEEP_ATOMS(cx->runtime); | |
2118 | cx->weakRoots.newborn[GCX_OBJECT] = obj; | |
2119 | } | |
2120 | ||
2121 | out: | |
2122 | #ifdef INCLUDE_MOZILLA_DTRACE | |
2123 | if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) | |
2124 | jsdtrace_object_create(cx, clasp, obj); | |
2125 | if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) | |
2126 | jsdtrace_object_create_done(cx->fp, clasp); | |
2127 | #endif | |
2128 | return obj; | |
2129 | } | |
2130 | ||
2131 | JSObject * | |
2132 | js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, | |
2133 | JSObject *parent, size_t objectSize) | |
2134 | { | |
2135 | jsid id; | |
2136 | ||
2137 | /* Bootstrap the ur-object, and make it the default prototype object. */ | |
2138 | if (!proto) { | |
2139 | if (!js_GetClassId(cx, clasp, &id)) | |
2140 | return NULL; | |
2141 | if (!js_GetClassPrototype(cx, parent, id, &proto)) | |
2142 | return NULL; | |
2143 | if (!proto && | |
2144 | !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), | |
2145 | &proto)) { | |
2146 | return NULL; | |
2147 | } | |
2148 | } | |
2149 | ||
2150 | return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize); | |
2151 | } | |
2152 | ||
2153 | JSBool | JSBool |
2154 | js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
2155 | { | { |
# | Line 2026 | Line 2165 |
2165 | JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); | JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); |
2166 | if (JS_IsConstructing(cx)) | if (JS_IsConstructing(cx)) |
2167 | return JS_TRUE; | return JS_TRUE; |
2168 | obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); | obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); |
2169 | if (!obj) | if (!obj) |
2170 | return JS_FALSE; | return JS_FALSE; |
2171 | } | } |
# | Line 2034 | Line 2173 |
2173 | return JS_TRUE; | return JS_TRUE; |
2174 | } | } |
2175 | ||
static inline bool | ||
InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, | ||
JSObjectOps* ops) | ||
{ | ||
JS_ASSERT(OPS_IS_NATIVE(ops)); | ||
JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); | ||
JSClass* protoclasp; | ||
JSClass* clasp = OBJ_GET_CLASS(cx, obj); | ||
/* | ||
* Share proto's map only if it has the same JSObjectOps, and only if | ||
* proto's class has the same private and reserved slots as obj's map | ||
* and class have. We assume that if prototype and object are of the | ||
* same class, they always have the same number of computed reserved | ||
* slots (returned via clasp->reserveSlots); otherwise, prototype and | ||
* object classes must have the same (null or not) reserveSlots hook. | ||
*/ | ||
if (proto && | ||
proto->map->ops == ops && | ||
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || | ||
(!((protoclasp->flags ^ clasp->flags) & | ||
(JSCLASS_HAS_PRIVATE | | ||
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && | ||
protoclasp->reserveSlots == clasp->reserveSlots))) | ||
{ | ||
js_HoldScope(OBJ_SCOPE(proto)); | ||
obj->map = proto->map; | ||
return true; | ||
} | ||
JSScope *scope = js_NewScope(cx, ops, clasp, obj); | ||
if (!scope) | ||
goto bad; | ||
/* Let js_NewScope set freeslot so as to reserve slots. */ | ||
JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); | ||
if (scope->freeslot > JS_INITIAL_NSLOTS && | ||
!js_ReallocSlots(cx, obj, scope->freeslot, JS_TRUE)) { | ||
js_DestroyScope(cx, scope); | ||
goto bad; | ||
} | ||
obj->map = &scope->map; | ||
return true; | ||
bad: | ||
/* Ensure that the map field is initialized for GC. */ | ||
obj->map = NULL; | ||
return false; | ||
} | ||
2176 | #ifdef JS_TRACER | #ifdef JS_TRACER |
2177 | ||
2178 | static inline JSObject* | JSObject* |
2179 | NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, JSObject *parent) | js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto, |
2180 | jsval privateSlotValue) | |
2181 | { | { |
2182 | JS_ASSERT(JS_ON_TRACE(cx)); | JS_ASSERT(!clasp->getObjectOps); |
2183 | JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); | JS_ASSERT(proto->map->ops == &js_ObjectOps); |
2184 | ||
2185 | JSObject* obj = js_NewGCObject(cx, GCX_OBJECT); | |
2186 | if (!obj) | if (!obj) |
2187 | return NULL; | return NULL; |
2188 | ||
2189 | obj->classword = jsuword(clasp); | obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue); |
2190 | obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); | return obj; |
obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); | ||
for (unsigned i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) | ||
obj->fslots[i] = JSVAL_VOID; | ||
obj->dslots = NULL; | ||
return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; | ||
2191 | } | } |
2192 | ||
2193 | JSObject* FASTCALL | JSObject* FASTCALL |
2194 | js_Object_tn(JSContext* cx, JSObject* proto) | js_Object_tn(JSContext* cx, JSObject* proto) |
2195 | { | { |
2196 | return NewNativeObject(cx, &js_ObjectClass, proto, JSVAL_TO_OBJECT(proto->fslots[JSSLOT_PARENT])); | JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE)); |
2197 | return js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, JSVAL_VOID); | |
2198 | } | } |
2199 | ||
2200 | JS_DEFINE_TRCINFO_1(js_Object, | JS_DEFINE_TRCINFO_1(js_Object, |
2201 | (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0))) | (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0))) |
2202 | ||
2203 | static inline JSObject* | |
2204 | NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, | |
2205 | JSObject *parent, jsval privateSlotValue) | |
2206 | { | |
2207 | JS_ASSERT(JS_ON_TRACE(cx)); | |
2208 | JSObject* obj = js_NewGCObject(cx, GCX_OBJECT); | |
2209 | if (!obj) | |
2210 | return NULL; | |
2211 | ||
2212 | obj->init(clasp, proto, parent, privateSlotValue); | |
2213 | return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; | |
2214 | } | |
2215 | ||
2216 | JSObject* FASTCALL | JSObject* FASTCALL |
2217 | js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor) | js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor) |
2218 | { | { |
# | Line 2126 | Line 2225 |
2225 | if (scope->title.ownercx != cx) | if (scope->title.ownercx != cx) |
2226 | return NULL; | return NULL; |
2227 | #endif | #endif |
2228 | if (scope->object != ctor) { | if (!scope->owned()) { |
2229 | scope = js_GetMutableScope(cx, ctor); | scope = js_GetMutableScope(cx, ctor); |
2230 | if (!scope) | if (!scope) |
2231 | return NULL; | return NULL; |
2232 | } | } |
2233 | ||
2234 | JSScopeProperty *sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); | JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom)); |
2235 | jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE; | jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE; |
2236 | ||
2237 | JSObject *proto; | JSObject *proto; |
# | Line 2141 | Line 2240 |
2240 | proto = JSVAL_TO_OBJECT(pval); | proto = JSVAL_TO_OBJECT(pval); |
2241 | } else if (pval == JSVAL_HOLE) { | } else if (pval == JSVAL_HOLE) { |
2242 | /* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */ | /* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */ |
2243 | proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor), 0); | proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor)); |
2244 | if (!proto) | if (!proto) |
2245 | return NULL; | return NULL; |
2246 | if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) | if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) |
# | Line 2154 | Line 2253 |
2253 | } | } |
2254 | } | } |
2255 | ||
2256 | return NewNativeObject(cx, clasp, proto, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT])); | return NewNativeObject(cx, clasp, proto, ctor->getParent(), |
2257 | JSObject::defaultPrivate(clasp)); | |
2258 | } | } |
2259 | ||
2260 | JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0) | JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0) |
# | Line 2170 | Line 2270 |
2270 | * access is "object-detecting" in the sense used by web scripts, e.g., when | * access is "object-detecting" in the sense used by web scripts, e.g., when |
2271 | * checking whether document.all is defined. | * checking whether document.all is defined. |
2272 | */ | */ |
2273 | static JS_REQUIRES_STACK JSBool | JS_REQUIRES_STACK JSBool |
2274 | Detecting(JSContext *cx, jsbytecode *pc) | Detecting(JSContext *cx, jsbytecode *pc) |
2275 | { | { |
2276 | JSScript *script; | JSScript *script; |
# | Line 2181 | Line 2281 |
2281 | script = cx->fp->script; | script = cx->fp->script; |
2282 | endpc = script->code + script->length; | endpc = script->code + script->length; |
2283 | for (;; pc += js_CodeSpec[op].length) { | for (;; pc += js_CodeSpec[op].length) { |
2284 | JS_ASSERT(pc < endpc); | JS_ASSERT_IF(!cx->fp->imacpc, script->code <= pc && pc < endpc); |
2285 | ||
2286 | /* General case: a branch or equality op follows the access. */ | /* General case: a branch or equality op follows the access. */ |
2287 | op = js_GetOpcode(cx, script, pc); | op = js_GetOpcode(cx, script, pc); |
# | Line 2233 | Line 2333 |
2333 | * does not indicate whether we are in a with statement. Return defaultFlags | * does not indicate whether we are in a with statement. Return defaultFlags |
2334 | * if a currently executing bytecode cannot be determined. | * if a currently executing bytecode cannot be determined. |
2335 | */ | */ |
2336 | static uintN | uintN |
2337 | InferFlags(JSContext *cx, uintN defaultFlags) | js_InferFlags(JSContext *cx, uintN defaultFlags) |
2338 | { | { |
2339 | #ifdef JS_TRACER | |
2340 | if (JS_ON_TRACE(cx)) | |
2341 | return cx->bailExit->lookupFlags; | |
2342 | #endif | |
2343 | ||
2344 | JS_ASSERT_NOT_ON_TRACE(cx); | |
2345 | ||
2346 | JSStackFrame *fp; | JSStackFrame *fp; |
2347 | jsbytecode *pc; | jsbytecode *pc; |
2348 | const JSCodeSpec *cs; | const JSCodeSpec *cs; |
# | Line 2253 | Line 2360 |
2360 | if ((format & (JOF_SET | JOF_FOR)) || | if ((format & (JOF_SET | JOF_FOR)) || |
2361 | (fp->flags & JSFRAME_ASSIGNING)) { | (fp->flags & JSFRAME_ASSIGNING)) { |
2362 | flags |= JSRESOLVE_ASSIGNING; | flags |= JSRESOLVE_ASSIGNING; |
2363 | } else { | } else if (cs->length >= 0) { |
2364 | pc += cs->length; | pc += cs->length; |
2365 | if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc)) | if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc)) |
2366 | flags |= JSRESOLVE_DETECTING; | flags |= JSRESOLVE_DETECTING; |
# | Line 2273 | Line 2380 |
2380 | /* Fixes bug 463997 */ | /* Fixes bug 463997 */ |
2381 | uintN flags = cx->resolveFlags; | uintN flags = cx->resolveFlags; |
2382 | if (flags == JSRESOLVE_INFER) | if (flags == JSRESOLVE_INFER) |
2383 | flags = InferFlags(cx, flags); | flags = js_InferFlags(cx, flags); |
2384 | flags |= JSRESOLVE_WITH; | flags |= JSRESOLVE_WITH; |
2385 | JSAutoResolveFlags rf(cx, flags); | JSAutoResolveFlags rf(cx, flags); |
2386 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2387 | if (!proto) | if (!proto) |
2388 | return js_LookupProperty(cx, obj, id, objp, propp); | return js_LookupProperty(cx, obj, id, objp, propp); |
2389 | return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); | return proto->lookupProperty(cx, id, objp, propp); |
2390 | } | } |
2391 | ||
2392 | static JSBool | static JSBool |
# | Line 2288 | Line 2395 |
2395 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2396 | if (!proto) | if (!proto) |
2397 | return js_GetProperty(cx, obj, id, vp); | return js_GetProperty(cx, obj, id, vp); |
2398 | return OBJ_GET_PROPERTY(cx, proto, id, vp); | return proto->getProperty(cx, id, vp); |
2399 | } | } |
2400 | ||
2401 | static JSBool | static JSBool |
# | Line 2297 | Line 2404 |
2404 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2405 | if (!proto) | if (!proto) |
2406 | return js_SetProperty(cx, obj, id, vp); | return js_SetProperty(cx, obj, id, vp); |
2407 | return OBJ_SET_PROPERTY(cx, proto, id, vp); | return proto->setProperty(cx, id, vp); |
2408 | } | } |
2409 | ||
2410 | static JSBool | static JSBool |
# | Line 2307 | Line 2414 |
2414 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2415 | if (!proto) | if (!proto) |
2416 | return js_GetAttributes(cx, obj, id, prop, attrsp); | return js_GetAttributes(cx, obj, id, prop, attrsp); |
2417 | return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); | return proto->getAttributes(cx, id, prop, attrsp); |
2418 | } | } |
2419 | ||
2420 | static JSBool | static JSBool |
# | Line 2317 | Line 2424 |
2424 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2425 | if (!proto) | if (!proto) |
2426 | return js_SetAttributes(cx, obj, id, prop, attrsp); | return js_SetAttributes(cx, obj, id, prop, attrsp); |
2427 | return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); | return proto->setAttributes(cx, id, prop, attrsp); |
2428 | } | } |
2429 | ||
2430 | static JSBool | static JSBool |
# | Line 2326 | Line 2433 |
2433 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2434 | if (!proto) | if (!proto) |
2435 | return js_DeleteProperty(cx, obj, id, rval); | return js_DeleteProperty(cx, obj, id, rval); |
2436 | return OBJ_DELETE_PROPERTY(cx, proto, id, rval); | return proto->deleteProperty(cx, id, rval); |
2437 | } | } |
2438 | ||
2439 | static JSBool | static JSBool |
# | Line 2335 | Line 2442 |
2442 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2443 | if (!proto) | if (!proto) |
2444 | return js_DefaultValue(cx, obj, hint, vp); | return js_DefaultValue(cx, obj, hint, vp); |
2445 | return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); | return proto->defaultValue(cx, hint, vp); |
2446 | } | } |
2447 | ||
2448 | static JSBool | static JSBool |
# | Line 2345 | Line 2452 |
2452 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2453 | if (!proto) | if (!proto) |
2454 | return js_Enumerate(cx, obj, enum_op, statep, idp); | return js_Enumerate(cx, obj, enum_op, statep, idp); |
2455 | return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); | return proto->enumerate(cx, enum_op, statep, idp); |
2456 | } | } |
2457 | ||
2458 | static JSBool | static JSBool |
# | Line 2355 | Line 2462 |
2462 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2463 | if (!proto) | if (!proto) |
2464 | return js_CheckAccess(cx, obj, id, mode, vp, attrsp); | return js_CheckAccess(cx, obj, id, mode, vp, attrsp); |
2465 | return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); | return proto->checkAccess(cx, id, mode, vp, attrsp); |
2466 | } | } |
2467 | ||
2468 | static JSObject * | static JSObject * |
# | Line 2364 | Line 2471 |
2471 | JSObject *proto = OBJ_GET_PROTO(cx, obj); | JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2472 | if (!proto) | if (!proto) |
2473 | return obj; | return obj; |
2474 | return OBJ_THIS_OBJECT(cx, proto); | return proto->thisObject(cx); |
2475 | } | } |
2476 | ||
2477 | JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { | JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { |
# | Line 2377 | Line 2484 |
2484 | with_ThisObject, NATIVE_DROP_PROPERTY, | with_ThisObject, NATIVE_DROP_PROPERTY, |
2485 | NULL, NULL, | NULL, NULL, |
2486 | NULL, js_TraceObject, | NULL, js_TraceObject, |
2487 | js_Clear, NULL, | js_Clear |
NULL | ||
2488 | }; | }; |
2489 | ||
2490 | static JSObjectOps * | static JSObjectOps * |
# | Line 2391 | Line 2497 |
2497 | "With", | "With", |
2498 | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, |
2499 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
2500 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, |
2501 | with_getObjectOps, | with_getObjectOps, |
2502 | 0,0,0,0,0,0,0 | 0,0,0,0,0,0,0 |
2503 | }; | }; |
# | Line 2401 | Line 2507 |
2507 | { | { |
2508 | JSObject *obj; | JSObject *obj; |
2509 | ||
2510 | obj = js_NewObject(cx, &js_WithClass, proto, parent, 0); | obj = js_NewObject(cx, &js_WithClass, proto, parent); |
2511 | if (!obj) | if (!obj) |
2512 | return NULL; | return NULL; |
2513 | STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp)); | obj->setPrivate(cx->fp); |
2514 | OBJ_SET_BLOCK_DEPTH(cx, obj, depth); | OBJ_SET_BLOCK_DEPTH(cx, obj, depth); |
2515 | return obj; | return obj; |
2516 | } | } |
# | Line 2416 | Line 2522 |
2522 | * Null obj's proto slot so that Object.prototype.* does not pollute block | * Null obj's proto slot so that Object.prototype.* does not pollute block |
2523 | * scopes and to give the block object its own scope. | * scopes and to give the block object its own scope. |
2524 | */ | */ |
2525 | JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, | JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, NULL, NULL); |
NULL, NULL, 0); | ||
2526 | JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj)); | JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj)); |
2527 | return blockObj; | return blockObj; |
2528 | } | } |
2529 | ||
2530 | JSObject * | JSObject * |
2531 | js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, | js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp) |
JSStackFrame *fp) | ||
2532 | { | { |
JSObject *clone; | ||
JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass); | ||
2533 | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto)); | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto)); |
2534 | clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0); | JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass); |
2535 | ||
2536 | JSObject *clone = js_NewGCObject(cx, GCX_OBJECT); | |
2537 | if (!clone) | if (!clone) |
2538 | return NULL; | return NULL; |
2539 | STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp)); | |
2540 | STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH, | JSScope *scope = OBJ_SCOPE(proto); |
2541 | OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH)); | scope->hold(); |
2542 | JS_ASSERT(!scope->owned()); | |
2543 | clone->map = scope; | |
2544 | ||
2545 | clone->classword = jsuword(&js_BlockClass); | |
2546 | clone->setProto(proto); | |
2547 | clone->setParent(NULL); // caller's responsibility | |
2548 | clone->setPrivate(fp); | |
2549 | clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH]; | |
2550 | JS_ASSERT(scope->freeslot == JSSLOT_BLOCK_DEPTH + 1); | |
2551 | for (uint32 i = JSSLOT_BLOCK_DEPTH + 1; i < JS_INITIAL_NSLOTS; ++i) | |
2552 | clone->fslots[i] = JSVAL_VOID; | |
2553 | clone->dslots = NULL; | |
2554 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); | JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); |
JS_ASSERT(OBJ_SCOPE(clone)->object == proto); | ||
2555 | return clone; | return clone; |
2556 | } | } |
2557 | ||
# | Line 2454 | Line 2568 |
2568 | fp = cx->fp; | fp = cx->fp; |
2569 | obj = fp->scopeChain; | obj = fp->scopeChain; |
2570 | JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); | JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); |
2571 | JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp); | JS_ASSERT(obj->getPrivate() == cx->fp); |
2572 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); |
2573 | ||
2574 | /* | /* |
# | Line 2482 | Line 2596 |
2596 | if (normalUnwind && count > 1) { | if (normalUnwind && count > 1) { |
2597 | --count; | --count; |
2598 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
2599 | if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE)) | if (!AllocSlots(cx, obj, JS_INITIAL_NSLOTS + count)) |
2600 | normalUnwind = JS_FALSE; | normalUnwind = JS_FALSE; |
2601 | else | else |
2602 | memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); | memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); |
# | Line 2490 | Line 2604 |
2604 | } | } |
2605 | ||
2606 | /* We must clear the private slot even with errors. */ | /* We must clear the private slot even with errors. */ |
2607 | JS_SetPrivate(cx, obj, NULL); | obj->setPrivate(NULL); |
2608 | fp->scopeChain = OBJ_GET_PARENT(cx, obj); | fp->scopeChain = OBJ_GET_PARENT(cx, obj); |
2609 | return normalUnwind; | return normalUnwind; |
2610 | } | } |
# | Line 2498 | Line 2612 |
2612 | static JSBool | static JSBool |
2613 | block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
2614 | { | { |
2615 | uintN index; | /* |
2616 | JSStackFrame *fp; | * Block objects are never exposed to script, and the engine handles them |
2617 | * with care. So unlike other getters, this one can assert (rather than | |
2618 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); | * check) certain invariants about obj. |
2619 | */ | |
2620 | JS_ASSERT(obj->getClass() == &js_BlockClass); | |
2621 | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); |
2622 | if (!JSVAL_IS_INT(id)) | uintN index = (uintN) JSVAL_TO_INT(id); |
2623 | return JS_TRUE; | JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); |
2624 | ||
2625 | index = (uint16) JSVAL_TO_INT(id); | JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); |
fp = (JSStackFrame *) JS_GetPrivate(cx, obj); | ||
2626 | if (fp) { | if (fp) { |
2627 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); |
2628 | JS_ASSERT(index < fp->script->nslots); | JS_ASSERT(index < fp->script->nslots); |
2629 | *vp = fp->slots[index]; | *vp = fp->slots[index]; |
2630 | return JS_TRUE; | return true; |
2631 | } | } |
2632 | ||
2633 | /* Reserve slots start with the first slot after the private. */ | /* Values are in reserved slots immediately following DEPTH. */ |
2634 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; | uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index; |
2635 | return JS_GetReservedSlot(cx, obj, index, vp); | JS_LOCK_OBJ(cx, obj); |
2636 | JS_ASSERT(slot < STOBJ_NSLOTS(obj)); | |
2637 | *vp = STOBJ_GET_SLOT(obj, slot); | |
2638 | JS_UNLOCK_OBJ(cx, obj); | |
2639 | return true; | |
2640 | } | } |
2641 | ||
2642 | static JSBool | static JSBool |
2643 | block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
2644 | { | { |
2645 | uintN index; | JS_ASSERT(obj->getClass() == &js_BlockClass); |
2646 | JSStackFrame *fp; | JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj)); |
2647 | uintN index = (uintN) JSVAL_TO_INT(id); | |
2648 | JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); | JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj)); |
if (!JSVAL_IS_INT(id)) | ||
return JS_TRUE; | ||
2649 | ||
2650 | index = (uint16) JSVAL_TO_INT(id); | JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); |
fp = (JSStackFrame *) JS_GetPrivate(cx, obj); | ||
2651 | if (fp) { | if (fp) { |
2652 | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); | index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj); |
2653 | JS_ASSERT(index < fp->script->nslots); | JS_ASSERT(index < fp->script->nslots); |
2654 | fp->slots[index] = *vp; | fp->slots[index] = *vp; |
2655 | return JS_TRUE; | return true; |
2656 | } | } |
2657 | ||
2658 | /* Reserve slots start with the first slot after the private. */ | /* Values are in reserved slots immediately following DEPTH. */ |
2659 | index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE; | uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index; |
2660 | return JS_SetReservedSlot(cx, obj, index, *vp); | JS_LOCK_OBJ(cx, obj); |
2661 | JS_ASSERT(slot < STOBJ_NSLOTS(obj)); | |
2662 | STOBJ_SET_SLOT(obj, slot, *vp); | |
2663 | JS_UNLOCK_OBJ(cx, obj); | |
2664 | return true; | |
2665 | } | |
2666 | ||
2667 | JSBool | |
2668 | js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, int16 index) | |
2669 | { | |
2670 | JS_ASSERT(obj->getClass() == &js_BlockClass); | |
2671 | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj)); | |
2672 | ||
2673 | /* Use JSPROP_ENUMERATE to aid the disassembler. */ | |
2674 | return js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, | |
2675 | block_getProperty, block_setProperty, | |
2676 | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, | |
2677 | SPROP_HAS_SHORTID, index, NULL); | |
2678 | } | } |
2679 | ||
2680 | #if JS_HAS_XDR | #if JS_HAS_XDR |
# | Line 2573 | Line 2706 |
2706 | JSObject *obj, *parent; | JSObject *obj, *parent; |
2707 | uint16 depth, count, i; | uint16 depth, count, i; |
2708 | uint32 tmp; | uint32 tmp; |
JSTempValueRooter tvr; | ||
2709 | JSScopeProperty *sprop; | JSScopeProperty *sprop; |
2710 | jsid propid; | jsid propid; |
2711 | JSAtom *atom; | JSAtom *atom; |
# | Line 2590 | Line 2722 |
2722 | parent = OBJ_GET_PARENT(cx, obj); | parent = OBJ_GET_PARENT(cx, obj); |
2723 | parentId = (xdr->script->objectsOffset == 0) | parentId = (xdr->script->objectsOffset == 0) |
2724 | ? NO_PARENT_INDEX | ? NO_PARENT_INDEX |
2725 | : FindObjectIndex(JS_SCRIPT_OBJECTS(xdr->script), parent); | : FindObjectIndex(xdr->script->objects(), parent); |
2726 | depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); | depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj); |
2727 | count = (uint16)OBJ_BLOCK_COUNT(cx, obj); | count = (uint16)OBJ_BLOCK_COUNT(cx, obj); |
2728 | tmp = (uint32)(depth << 16) | count; | tmp = (uint32)(depth << 16) | count; |
# | Line 2617 | Line 2749 |
2749 | if (parentId == NO_PARENT_INDEX) | if (parentId == NO_PARENT_INDEX) |
2750 | parent = NULL; | parent = NULL; |
2751 | else | else |
2752 | JS_GET_SCRIPT_OBJECT(xdr->script, parentId, parent); | parent = xdr->script->getObject(parentId); |
2753 | STOBJ_SET_PARENT(obj, parent); | STOBJ_SET_PARENT(obj, parent); |
2754 | } | } |
2755 | ||
2756 | JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); | JSAutoTempValueRooter tvr(cx, obj); |
2757 | ||
2758 | if (!JS_XDRUint32(xdr, &tmp)) { | if (!JS_XDRUint32(xdr, &tmp)) |
2759 | JS_POP_TEMP_ROOT(cx, &tvr); | return false; |
return JS_FALSE; | ||
} | ||
2760 | ||
2761 | if (xdr->mode == JSXDR_DECODE) { | if (xdr->mode == JSXDR_DECODE) { |
2762 | depth = (uint16)(tmp >> 16); | depth = (uint16)(tmp >> 16); |
# | Line 2649 | Line 2779 |
2779 | sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; | sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; |
2780 | } while (!(sprop->flags & SPROP_HAS_SHORTID)); | } while (!(sprop->flags & SPROP_HAS_SHORTID)); |
2781 | ||
2782 | JS_ASSERT(sprop->getter == js_BlockClass.getProperty); | JS_ASSERT(sprop->getter == block_getProperty); |
2783 | propid = sprop->id; | propid = sprop->id; |
2784 | JS_ASSERT(JSID_IS_ATOM(propid)); | JS_ASSERT(JSID_IS_ATOM(propid)); |
2785 | atom = JSID_TO_ATOM(propid); | atom = JSID_TO_ATOM(propid); |
# | Line 2660 | Line 2790 |
2790 | /* XDR the real id, then the shortid. */ | /* XDR the real id, then the shortid. */ |
2791 | if (!js_XDRStringAtom(xdr, &atom) || | if (!js_XDRStringAtom(xdr, &atom) || |
2792 | !JS_XDRUint16(xdr, (uint16 *)&shortid)) { | !JS_XDRUint16(xdr, (uint16 *)&shortid)) { |
2793 | ok = JS_FALSE; | return false; |
break; | ||
2794 | } | } |
2795 | ||
2796 | if (xdr->mode == JSXDR_DECODE) { | if (xdr->mode == JSXDR_DECODE) { |
2797 | if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), | if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid)) |
2798 | JSVAL_VOID, NULL, NULL, | return false; |
JSPROP_ENUMERATE | JSPROP_PERMANENT | | ||
JSPROP_SHARED, | ||
SPROP_HAS_SHORTID, shortid, NULL)) { | ||
ok = JS_FALSE; | ||
break; | ||
} | ||
2799 | } | } |
2800 | } | } |
2801 | ||
2802 | JS_POP_TEMP_ROOT(cx, &tvr); | if (xdr->mode == JSXDR_DECODE) { |
2803 | return ok; | /* Do as the parser does and make this block scope shareable. */ |
2804 | OBJ_SCOPE(obj)->object = NULL; | |
2805 | } | |
2806 | return true; | |
2807 | } | } |
2808 | ||
2809 | #endif | #endif |
# | Line 2691 | Line 2817 |
2817 | JSClass js_BlockClass = { | JSClass js_BlockClass = { |
2818 | "Block", | "Block", |
2819 | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, |
2820 | JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
2821 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, |
2822 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots | NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots |
2823 | }; | }; |
2824 | ||
# | Line 2756 | Line 2882 |
2882 | } | } |
2883 | ||
2884 | /* Create a prototype object for this class. */ | /* Create a prototype object for this class. */ |
2885 | proto = js_NewObject(cx, clasp, parent_proto, obj, 0); | proto = js_NewObject(cx, clasp, parent_proto, obj); |
2886 | if (!proto) | if (!proto) |
2887 | return NULL; | return NULL; |
2888 | ||
# | Line 2775 | Line 2901 |
2901 | key != JSProto_Null) { | key != JSProto_Null) { |
2902 | named = JS_FALSE; | named = JS_FALSE; |
2903 | } else { | } else { |
2904 | named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), | named = obj->defineProperty(cx, ATOM_TO_JSID(atom), |
2905 | OBJECT_TO_JSVAL(proto), | OBJECT_TO_JSVAL(proto), |
2906 | JS_PropertyStub, JS_PropertyStub, | JS_PropertyStub, JS_PropertyStub, |
2907 | (clasp->flags & JSCLASS_IS_ANONYMOUS) | (clasp->flags & JSCLASS_IS_ANONYMOUS) |
2908 | ? JSPROP_READONLY | JSPROP_PERMANENT | ? JSPROP_READONLY | JSPROP_PERMANENT |
2909 | : 0, | : 0); |
NULL); | ||
2910 | if (!named) | if (!named) |
2911 | goto bad; | goto bad; |
2912 | } | } |
# | Line 2836 | Line 2961 |
2961 | goto bad; | goto bad; |
2962 | } | } |
2963 | ||
2964 | /* | |
2965 | * Make sure proto's scope's emptyScope is available to be shared by | |
2966 | * objects of this class. JSScope::emptyScope is a one-slot cache. If we | |
2967 | * omit this, some other class could snap it up. (The risk is particularly | |
2968 | * great for Object.prototype.) | |
2969 | * | |
2970 | * All callers of JSObject::initSharingEmptyScope depend on this. | |
2971 | */ | |
2972 | if (OBJ_IS_NATIVE(proto)) { | |
2973 | JSScope *scope = OBJ_SCOPE(proto)->getEmptyScope(cx, clasp); | |
2974 | if (!scope) | |
2975 | goto bad; | |
2976 | scope->drop(cx, NULL); | |
2977 | } | |
2978 | ||
2979 | /* If this is a standard class, cache its prototype. */ | /* If this is a standard class, cache its prototype. */ |
2980 | if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) | if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) |
2981 | goto bad; | goto bad; |
# | Line 2846 | Line 2986 |
2986 | ||
2987 | bad: | bad: |
2988 | if (named) | if (named) |
2989 | (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); | (void) obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval); |
2990 | proto = NULL; | proto = NULL; |
2991 | goto out; | goto out; |
2992 | } | } |
2993 | ||
static void | ||
FreeSlots(JSContext *cx, JSObject *obj) | ||
{ | ||
if (obj->dslots) { | ||
JS_ASSERT((uint32)obj->dslots[-1] > JS_INITIAL_NSLOTS); | ||
JS_free(cx, obj->dslots - 1); | ||
obj->dslots = NULL; | ||
} | ||
} | ||
2994 | #define SLOTS_TO_DYNAMIC_WORDS(nslots) \ | #define SLOTS_TO_DYNAMIC_WORDS(nslots) \ |
2995 | (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS) | (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS) |
2996 | ||
2997 | #define DYNAMIC_WORDS_TO_SLOTS(words) \ | #define DYNAMIC_WORDS_TO_SLOTS(words) \ |
2998 | (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) | (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) |
2999 | ||
3000 | JSBool | |
3001 | js_ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots, | static bool |
3002 | JSBool exactAllocation) | AllocSlots(JSContext *cx, JSObject *obj, size_t nslots) |
3003 | { | { |
3004 | jsval *old, *slots; | JS_ASSERT(!obj->dslots); |
3005 | uint32 oslots, nwords, owords, log, i; | JS_ASSERT(nslots > JS_INITIAL_NSLOTS); |
3006 | ||
3007 | jsval* slots; | |
3008 | slots = (jsval*) cx->malloc(SLOTS_TO_DYNAMIC_WORDS(nslots) * sizeof(jsval)); | |
3009 | if (!slots) | |
3010 | return true; | |
3011 | ||
3012 | *slots++ = nslots; | |
3013 | /* clear the newly allocated cells. */ | |
3014 | for (jsuint n = JS_INITIAL_NSLOTS; n < nslots; ++n) | |
3015 | slots[n - JS_INITIAL_NSLOTS] = JSVAL_VOID; | |
3016 | obj->dslots = slots; | |
3017 | ||
3018 | return true; | |
3019 | } | |
3020 | ||
3021 | bool | |
3022 | js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots) | |
3023 | { | |
3024 | /* | /* |
3025 | * Minimal number of dynamic slots to allocate. | * Minimal number of dynamic slots to allocate. |
3026 | */ | */ |
3027 | #define MIN_DYNAMIC_WORDS 4 | const size_t MIN_DYNAMIC_WORDS = 4; |
3028 | ||
3029 | /* | /* |
3030 | * The limit to switch to linear allocation strategy from the power of 2 | * The limit to switch to linear allocation strategy from the power of 2 |
3031 | * growth no to waste too much memory. | * growth no to waste too much memory. |
3032 | */ | */ |
3033 | #define LINEAR_GROWTH_STEP JS_BIT(16) | const size_t LINEAR_GROWTH_STEP = JS_BIT(16); |
3034 | ||
3035 | old = obj->dslots; | /* If we are allocating fslots, there is nothing to do. */ |
3036 | if (nslots <= JS_INITIAL_NSLOTS) { | if (nslots <= JS_INITIAL_NSLOTS) |
if (old && | ||
(exactAllocation || | ||
SLOTS_TO_DYNAMIC_WORDS((uint32)old[-1]) != MIN_DYNAMIC_WORDS || | ||
nslots <= (JS_INITIAL_NSLOTS + | ||
JSSLOT_FREE(STOBJ_GET_CLASS(obj))) / 2)) { | ||
/* | ||
* We do not want to free dynamic slots when allocation is a hint, | ||
* we reached minimal allocation and almost all fixed slots are | ||
* used. It avoids allocating dynamic slots again when properties | ||
* are added to the object. | ||
* | ||
* If there were no private or reserved slots, the condition to | ||
* free the slots would be | ||
* | ||
* nslots <= JS_INITIAL_NSLOTS / 2 | ||
* | ||
* but to account for never removed slots before JSSLOT_FREE(class) | ||
* we need to subtract it from the slot counts which gives | ||
* | ||
* nslots - JSSLOT_FREE <= (JS_INITIAL_NSLOTS - JSSLOT_FREE) / 2 | ||
* | ||
* or | ||
* | ||
* nslots <= (JS_INITIAL_NSLOTS + JSSLOT_FREE) / 2 | ||
*/ | ||
FreeSlots(cx, obj); | ||
} | ||
3037 | return JS_TRUE; | return JS_TRUE; |
} | ||
3038 | ||
3039 | oslots = (old) ? (uint32)*--old : JS_INITIAL_NSLOTS; | size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); |
nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); | ||
3040 | ||
3041 | if (nslots > oslots) { | /* |
3042 | if (!exactAllocation) { | * Round up nslots so the number of bytes in dslots array is power |
3043 | /* | * of 2 to ensure exponential grouth. |
3044 | * Round up nslots so the number of bytes in dslots array is power | */ |
3045 | * of 2 to ensure exponential grouth. | uintN log; |
3046 | */ | if (nwords <= MIN_DYNAMIC_WORDS) { |
3047 | if (nwords <= MIN_DYNAMIC_WORDS) { | nwords = MIN_DYNAMIC_WORDS; |
3048 | nwords = MIN_DYNAMIC_WORDS; | } else if (nwords < LINEAR_GROWTH_STEP) { |
3049 | } else if (nwords < LINEAR_GROWTH_STEP) { | JS_CEILING_LOG2(log, nwords); |
3050 | JS_CEILING_LOG2(log, nwords); | nwords = JS_BIT(log); |
nwords = JS_BIT(log); | ||
} else { | ||
nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); | ||
} | ||
} | ||
slots = (jsval *)JS_realloc(cx, old, nwords * sizeof(jsval)); | ||
if (!slots) | ||
return JS_FALSE; | ||
3051 | } else { | } else { |
3052 | JS_ASSERT(nslots < oslots); | nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); |
if (!exactAllocation) { | ||
owords = DYNAMIC_WORDS_TO_SLOTS(oslots); | ||
if (owords <= MIN_DYNAMIC_WORDS) | ||
return JS_TRUE; | ||
if (owords < LINEAR_GROWTH_STEP * 2) { | ||
/* | ||
* Shrink only if 1/4 of slots are left and we need to grow | ||
* the array at least twice to reach the current capacity. It | ||
* prevents frequent capacity growth/shrinking when slots are | ||
* often removed and added. | ||
*/ | ||
if (nwords > owords / 4) | ||
return JS_TRUE; | ||
JS_CEILING_LOG2(log, nwords); | ||
nwords = JS_BIT(log); | ||
if (nwords < MIN_DYNAMIC_WORDS) | ||
nwords = MIN_DYNAMIC_WORDS; | ||
} else { | ||
/* | ||
* Shrink only if we free at least 2 linear allocation | ||
* segments, to prevent growth/shrinking resonance. | ||
*/ | ||
if (nwords > owords - LINEAR_GROWTH_STEP * 2) | ||
return JS_TRUE; | ||
nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP); | ||
} | ||
} | ||
/* We avoid JS_realloc not to report a failed shrink attempt. */ | ||
slots = (jsval *)realloc(old, nwords * sizeof(jsval)); | ||
if (!slots) | ||
slots = old; | ||
3053 | } | } |
3054 | nslots = DYNAMIC_WORDS_TO_SLOTS(nwords); | nslots = DYNAMIC_WORDS_TO_SLOTS(nwords); |
3055 | *slots++ = (jsval)nslots; | |
3056 | /* | |
3057 | * If nothing was allocated yet, treat it as initial allocation (but with | |
3058 | * the exponential growth algorithm applied). | |
3059 | */ | |
3060 | jsval* slots = obj->dslots; | |
3061 | if (!slots) | |
3062 | return AllocSlots(cx, obj, nslots); | |
3063 | ||
3064 | size_t oslots = size_t(slots[-1]); | |
3065 | ||
3066 | slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval)); | |
3067 | *slots++ = nslots; | |
3068 | obj->dslots = slots; | obj->dslots = slots; |
3069 | ||
3070 | /* If we're extending an allocation, initialize free slots. */ | /* Initialize the additional slots we added. */ |
3071 | for (i = oslots; i < nslots; i++) | JS_ASSERT(nslots > oslots); |
3072 | for (size_t i = oslots; i < nslots; i++) | |
3073 | slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID; | slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID; |
3074 | ||
3075 | return JS_TRUE; | return true; |
3076 | } | |
3077 | ||
3078 | void | |
3079 | js_ShrinkSlots(JSContext *cx, JSObject *obj, size_t nslots) | |
3080 | { | |
3081 | jsval* slots = obj->dslots; | |
3082 | ||
3083 | /* Nothing to shrink? */ | |
3084 | if (!slots) | |
3085 | return; | |
3086 | ||
3087 | #undef LINEAR_GROWTH_STEP | JS_ASSERT(size_t(slots[-1]) > JS_INITIAL_NSLOTS); |
3088 | #undef MIN_DYNAMIC_WORDS | JS_ASSERT(nslots <= size_t(slots[-1])); |
3089 | ||
3090 | if (nslots <= JS_INITIAL_NSLOTS) { | |
3091 | cx->free(slots - 1); | |
3092 | obj->dslots = NULL; | |
3093 | } else { | |
3094 | size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots); | |
3095 | slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval)); | |
3096 | *slots++ = nslots; | |
3097 | obj->dslots = slots; | |
3098 | } | |
3099 | } | |
3100 | ||
3101 | bool | |
3102 | js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved) | |
3103 | { | |
3104 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | |
3105 | JS_ASSERT(!obj->dslots); | |
3106 | ||
3107 | uintN nslots = JSSLOT_FREE(STOBJ_GET_CLASS(obj)) + nreserved; | |
3108 | if (nslots > STOBJ_NSLOTS(obj) && !AllocSlots(cx, obj, nslots)) | |
3109 | return false; | |
3110 | ||
3111 | JSScope *scope = OBJ_SCOPE(obj); | |
3112 | if (scope->owned()) { | |
3113 | #ifdef JS_THREADSAFE | |
3114 | JS_ASSERT(scope->title.ownercx->thread == cx->thread); | |
3115 | #endif | |
3116 | JS_ASSERT(scope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); | |
3117 | if (scope->freeslot < nslots) | |
3118 | scope->freeslot = nslots; | |
3119 | } | |
3120 | return true; | |
3121 | } | } |
3122 | ||
3123 | extern JSBool | extern JSBool |
# | Line 3008 | Line 3140 |
3140 | return JS_TRUE; | return JS_TRUE; |
3141 | } | } |
3142 | ||
JSObject * | ||
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, | ||
uintN objectSize) | ||
{ | ||
jsid id; | ||
/* Bootstrap the ur-object, and make it the default prototype object. */ | ||
if (!proto) { | ||
if (!js_GetClassId(cx, clasp, &id)) | ||
return NULL; | ||
if (!js_GetClassPrototype(cx, parent, id, &proto)) | ||
return NULL; | ||
if (!proto && | ||
!js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), | ||
&proto)) { | ||
return NULL; | ||
} | ||
} | ||
return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize); | ||
} | ||
JSObject * | ||
js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, | ||
JSObject *parent, uintN objectSize) | ||
{ | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) | ||
jsdtrace_object_create_start(cx->fp, clasp); | ||
#endif | ||
/* Currently only functions can have non-standard allocation size. */ | ||
if (clasp == &js_FunctionClass) { | ||
if (objectSize == 0) | ||
objectSize = sizeof(JSFunction); | ||
else | ||
JS_ASSERT(objectSize == sizeof(JSObject)); | ||
} else { | ||
JS_ASSERT(objectSize == 0); | ||
objectSize = sizeof(JSObject); | ||
} | ||
/* Assert that the class is a proper class. */ | ||
JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, | ||
((JSExtendedClass *)clasp)->equality); | ||
/* Always call the class's getObjectOps hook if it has one. */ | ||
JSObjectOps *ops = clasp->getObjectOps | ||
? clasp->getObjectOps(cx, clasp) | ||
: &js_ObjectOps; | ||
/* | ||
* Allocate an object from the GC heap and initialize all its fields before | ||
* doing any operation that can potentially trigger GC. | ||
*/ | ||
JSObject *obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); | ||
if (!obj) | ||
goto out; | ||
/* | ||
* Set the class slot with the initial value of the system and delegate | ||
* flags set to false. | ||
*/ | ||
JS_ASSERT(((jsuword) clasp & 3) == 0); | ||
obj->classword = jsuword(clasp); | ||
JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); | ||
JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); | ||
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); | ||
/* | ||
* Default parent to the parent of the prototype, which was set from | ||
* the parent of the prototype's constructor. | ||
*/ | ||
obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL((!parent && proto) | ||
? OBJ_GET_PARENT(cx, proto) | ||
: parent); | ||
/* Initialize the remaining fixed slots. */ | ||
for (uint32 i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) | ||
obj->fslots[i] = JSVAL_VOID; | ||
obj->dslots = NULL; | ||
if (OPS_IS_NATIVE(ops)) { | ||
if (!InitScopeForObject(cx, obj, proto, ops)) { | ||
obj = NULL; | ||
goto out; | ||
} | ||
} else { | ||
JS_ASSERT(ops->objectMap->ops == ops); | ||
obj->map = const_cast<JSObjectMap *>(ops->objectMap); | ||
} | ||
#ifdef DEBUG | ||
memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, | ||
objectSize - sizeof(JSObject)); | ||
#endif | ||
/* Check that the newborn root still holds the object. */ | ||
JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj); | ||
/* | ||
* Do not call debug hooks on trace, because we might be in a non-_FAIL | ||
* builtin. See bug 481444. | ||
*/ | ||
if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { | ||
JSAutoTempValueRooter tvr(cx, obj); | ||
JS_KEEP_ATOMS(cx->runtime); | ||
cx->debugHooks->objectHook(cx, obj, JS_TRUE, | ||
cx->debugHooks->objectHookData); | ||
JS_UNKEEP_ATOMS(cx->runtime); | ||
cx->weakRoots.newborn[GCX_OBJECT] = obj; | ||
} | ||
out: | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) | ||
jsdtrace_object_create(cx, clasp, obj); | ||
if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) | ||
jsdtrace_object_create_done(cx->fp, clasp); | ||
#endif | ||
return obj; | ||
} | ||
JSObject* | ||
js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) | ||
{ | ||
JS_ASSERT(!clasp->getObjectOps); | ||
JS_ASSERT(proto->map->ops == &js_ObjectOps); | ||
JS_ASSERT(OBJ_GET_CLASS(cx, proto) == clasp); | ||
JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); | ||
if (!obj) | ||
return NULL; | ||
js_HoldScope(OBJ_SCOPE(proto)); | ||
obj->map = proto->map; | ||
obj->classword = jsuword(clasp); | ||
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); | ||
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; | ||
JS_ASSERT(slot > JSSLOT_PARENT); | ||
while (slot < JS_INITIAL_NSLOTS) | ||
obj->fslots[slot++] = JSVAL_VOID; | ||
obj->dslots = NULL; | ||
return obj; | ||
} | ||
3143 | JS_BEGIN_EXTERN_C | JS_BEGIN_EXTERN_C |
3144 | ||
3145 | static JSObject * | static JSObject * |
# | Line 3308 | Line 3290 |
3290 | v = JSVAL_VOID; | v = JSVAL_VOID; |
3291 | } | } |
3292 | } | } |
3293 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
3294 | } | } |
3295 | *vp = v; | *vp = v; |
3296 | return JS_TRUE; | return JS_TRUE; |
# | Line 3320 | Line 3302 |
3302 | { | { |
3303 | jsid id; | jsid id; |
3304 | jsval cval, rval; | jsval cval, rval; |
JSTempValueRooter argtvr, tvr; | ||
3305 | JSObject *obj, *ctor; | JSObject *obj, *ctor; |
3306 | ||
3307 | JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); | JSAutoTempValueRooter argtvr(cx, argc, argv); |
3308 | ||
3309 | if (!js_GetClassId(cx, clasp, &id) || | if (!js_GetClassId(cx, clasp, &id) || |
3310 | !js_FindClassObject(cx, parent, id, &cval)) { | !js_FindClassObject(cx, parent, id, &cval)) { |
JS_POP_TEMP_ROOT(cx, &argtvr); | ||
3311 | return NULL; | return NULL; |
3312 | } | } |
3313 | ||
3314 | if (JSVAL_IS_PRIMITIVE(cval)) { | if (JSVAL_IS_PRIMITIVE(cval)) { |
3315 | js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); | js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); |
JS_POP_TEMP_ROOT(cx, &argtvr); | ||
3316 | return NULL; | return NULL; |
3317 | } | } |
3318 | ||
3319 | /* | /* Protect cval in case a crazy getter for .prototype uproots it. */ |
3320 | * Protect cval in case a crazy getter for .prototype uproots it. After | JSAutoTempValueRooter tvr(cx, cval); |
* this point, all control flow must exit through label out with obj set. | ||
*/ | ||
JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); | ||
MUST_FLOW_THROUGH("out"); | ||
3321 | ||
3322 | /* | /* |
3323 | * If proto or parent are NULL, set them to Constructor.prototype and/or | * If proto or parent are NULL, set them to Constructor.prototype and/or |
# | Line 3352 | Line 3327 |
3327 | if (!parent) | if (!parent) |
3328 | parent = OBJ_GET_PARENT(cx, ctor); | parent = OBJ_GET_PARENT(cx, ctor); |
3329 | if (!proto) { | if (!proto) { |
3330 | if (!OBJ_GET_PROPERTY(cx, ctor, | if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), |
3331 | ATOM_TO_JSID(cx->runtime->atomState | &rval)) { |
3332 | .classPrototypeAtom), | return NULL; |
&rval)) { | ||
obj = NULL; | ||
goto out; | ||
3333 | } | } |
3334 | if (JSVAL_IS_OBJECT(rval)) | if (JSVAL_IS_OBJECT(rval)) |
3335 | proto = JSVAL_TO_OBJECT(rval); | proto = JSVAL_TO_OBJECT(rval); |
3336 | } | } |
3337 | ||
3338 | obj = js_NewObject(cx, clasp, proto, parent, 0); | obj = js_NewObject(cx, clasp, proto, parent); |
3339 | if (!obj) | if (!obj) |
3340 | goto out; | return NULL; |
3341 | ||
3342 | if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) | if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) |
3343 | goto bad; | return NULL; |
3344 | ||
3345 | if (JSVAL_IS_PRIMITIVE(rval)) | if (JSVAL_IS_PRIMITIVE(rval)) |
3346 | goto out; | return obj; |
obj = JSVAL_TO_OBJECT(rval); | ||
3347 | ||
3348 | /* | /* |
3349 | * If the instance's class differs from what was requested, throw a type | * If the instance's class differs from what was requested, throw a type |
# | Line 3381 | Line 3352 |
3352 | * private data set at this point, then the constructor was replaced and | * private data set at this point, then the constructor was replaced and |
3353 | * we should throw a type error. | * we should throw a type error. |
3354 | */ | */ |
3355 | obj = JSVAL_TO_OBJECT(rval); | |
3356 | if (OBJ_GET_CLASS(cx, obj) != clasp || | if (OBJ_GET_CLASS(cx, obj) != clasp || |
3357 | (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | | (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | |
3358 | JSCLASS_CONSTRUCT_PROTOTYPE)) && | JSCLASS_CONSTRUCT_PROTOTYPE)) && |
3359 | !JS_GetPrivate(cx, obj))) { | !obj->getPrivate())) { |
3360 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
3361 | JSMSG_WRONG_CONSTRUCTOR, clasp->name); | JSMSG_WRONG_CONSTRUCTOR, clasp->name); |
3362 | goto bad; | return NULL; |
3363 | } | } |
out: | ||
JS_POP_TEMP_ROOT(cx, &tvr); | ||
JS_POP_TEMP_ROOT(cx, &argtvr); | ||
3364 | return obj; | return obj; |
bad: | ||
cx->weakRoots.newborn[GCX_OBJECT] = NULL; | ||
obj = NULL; | ||
goto out; | ||
} | ||
void | ||
js_FinalizeObject(JSContext *cx, JSObject *obj) | ||
{ | ||
/* Cope with stillborn objects that have no map. */ | ||
if (!obj->map) | ||
return; | ||
if (cx->debugHooks->objectHook) { | ||
cx->debugHooks->objectHook(cx, obj, JS_FALSE, | ||
cx->debugHooks->objectHookData); | ||
} | ||
/* Finalize obj first, in case it needs map and slots. */ | ||
STOBJ_GET_CLASS(obj)->finalize(cx, obj); | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED()) | ||
jsdtrace_object_finalize(obj); | ||
#endif | ||
if (OBJ_IS_NATIVE(obj)) | ||
js_DropScope(cx, OBJ_SCOPE(obj), obj); | ||
FreeSlots(cx, obj); | ||
3365 | } | } |
3366 | ||
3367 | /* XXXbe if one adds props, deletes earlier props, adds more, the last added | /* XXXbe if one adds props, deletes earlier props, adds more, the last added |
# | Line 3434 | Line 3372 |
3372 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
3373 | ||
3374 | JSScope *scope = OBJ_SCOPE(obj); | JSScope *scope = OBJ_SCOPE(obj); |
3375 | JSClass *clasp = LOCKED_OBJ_GET_CLASS(obj); | JSClass *clasp = obj->getClass(); |
3376 | if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { | if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { |
3377 | /* Adjust scope->freeslot to include computed reserved slots, if any. */ | /* Adjust scope->freeslot to include computed reserved slots, if any. */ |
3378 | scope->freeslot += clasp->reserveSlots(cx, obj); | scope->freeslot += clasp->reserveSlots(cx, obj); |
3379 | } | } |
3380 | ||
3381 | if (scope->freeslot >= STOBJ_NSLOTS(obj) && | if (scope->freeslot >= STOBJ_NSLOTS(obj) && |
3382 | !js_ReallocSlots(cx, obj, scope->freeslot + 1, JS_FALSE)) { | !js_GrowSlots(cx, obj, scope->freeslot + 1)) { |
3383 | return JS_FALSE; | return JS_FALSE; |
3384 | } | } |
3385 | ||
# | Line 3458 | Line 3396 |
3396 | ||
3397 | JSScope *scope = OBJ_SCOPE(obj); | JSScope *scope = OBJ_SCOPE(obj); |
3398 | LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); | LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); |
3399 | if (scope->freeslot == slot + 1) { | if (scope->freeslot == slot + 1) |
3400 | scope->freeslot = slot; | scope->freeslot = slot; |
/* When shrinking, js_ReallocSlots always returns true. */ | ||
js_ReallocSlots(cx, obj, slot, JS_FALSE); | ||
} | ||
3401 | } | } |
3402 | ||
3403 | ||
3404 | /* JSVAL_INT_MAX as a string */ | |
3405 | #define JSVAL_INT_MAX_STRING "1073741823" | |
3406 | ||
3407 | /* | |
3408 | * Convert string indexes that convert to int jsvals as ints to save memory. | |
3409 | * Care must be taken to use this macro every time a property name is used, or | |
3410 | * else double-sets, incorrect property cache misses, or other mistakes could | |
3411 | * occur. | |
3412 | */ | |
3413 | jsid | jsid |
3414 | js_CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, | js_CheckForStringIndex(jsid id) |
JSBool negative) | ||
3415 | { | { |
3416 | if (!JSID_IS_ATOM(id)) | |
3417 | return id; | |
3418 | ||
3419 | JSAtom *atom = JSID_TO_ATOM(id); | |
3420 | JSString *str = ATOM_TO_STRING(atom); | |
3421 | const jschar *s = str->flatChars(); | |
3422 | jschar ch = *s; | |
3423 | ||
3424 | JSBool negative = (ch == '-'); | |
3425 | if (negative) | |
3426 | ch = *++s; | |
3427 | ||
3428 | if (!JS7_ISDEC(ch)) | |
3429 | return id; | |
3430 | ||
3431 | size_t n = str->flatLength() - negative; | |
3432 | if (n > sizeof(JSVAL_INT_MAX_STRING) - 1) | |
3433 | return id; | |
3434 | ||
3435 | const jschar *cp = s; | |
3436 | const jschar *end = s + n; | |
3437 | ||
3438 | jsuint index = JS7_UNDEC(*cp++); | jsuint index = JS7_UNDEC(*cp++); |
3439 | jsuint oldIndex = 0; | jsuint oldIndex = 0; |
3440 | jsuint c = 0; | jsuint c = 0; |
# | Line 3489 | Line 3454 |
3454 | */ | */ |
3455 | if (cp != end || (negative && index == 0)) | if (cp != end || (negative && index == 0)) |
3456 | return id; | return id; |
3457 | ||
3458 | if (oldIndex < JSVAL_INT_MAX / 10 || | if (oldIndex < JSVAL_INT_MAX / 10 || |
3459 | (oldIndex == JSVAL_INT_MAX / 10 && c <= (JSVAL_INT_MAX % 10))) { | (oldIndex == JSVAL_INT_MAX / 10 && c <= (JSVAL_INT_MAX % 10))) { |
3460 | if (negative) | if (negative) |
3461 | index = 0 - index; | index = 0 - index; |
3462 | id = INT_TO_JSID((jsint)index); | id = INT_TO_JSID((jsint)index); |
3463 | } | } |
3464 | ||
3465 | return id; | return id; |
3466 | } | } |
3467 | ||
# | Line 3511 | Line 3478 |
3478 | } | } |
3479 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
3480 | scope = OBJ_SCOPE(obj); | scope = OBJ_SCOPE(obj); |
3481 | sprop = SCOPE_GET_PROPERTY(scope, id); | sprop = scope->lookup(id); |
3482 | if (sprop) { | if (sprop) { |
3483 | PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); | PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); |
3484 | js_MakeScopeShapeUnique(cx, scope); | scope->shadowingShapeChange(cx, sprop); |
3485 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
3486 | ||
3487 | if (!STOBJ_GET_PARENT(scope->object)) { | if (!STOBJ_GET_PARENT(obj)) { |
3488 | /* | /* |
3489 | * All scope chains end in a global object, so this will change | * All scope chains end in a global object, so this will change |
3490 | * the global shape. jstracer.cpp assumes that the global shape | * the global shape. jstracer.cpp assumes that the global shape |
# | Line 3527 | Line 3494 |
3494 | } | } |
3495 | return JS_TRUE; | return JS_TRUE; |
3496 | } | } |
3497 | obj = LOCKED_OBJ_GET_PROTO(scope->object); | obj = obj->getProto(); |
3498 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
3499 | } | } |
3500 | return JS_FALSE; | return JS_FALSE; |
# | Line 3536 | Line 3503 |
3503 | void | void |
3504 | js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) | js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) |
3505 | { | { |
3506 | JS_ASSERT(OBJ_IS_DELEGATE(cx, obj)); | JS_ASSERT(obj->isDelegate()); |
3507 | PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id); | PurgeProtoChain(cx, obj->getProto(), id); |
3508 | ||
3509 | /* | /* |
3510 | * We must purge the scope chain only for Call objects as they are the only | * We must purge the scope chain only for Call objects as they are the only |
# | Line 3574 | Line 3541 |
3541 | sprop = NULL; | sprop = NULL; |
3542 | } else { | } else { |
3543 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
3544 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
3545 | sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, | sprop = scope->add(cx, id, getter, setter, slot, attrs, flags, shortid); |
flags, shortid); | ||
3546 | } | } |
3547 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
3548 | return sprop; | return sprop; |
# | Line 3594 | Line 3560 |
3560 | if (!scope) { | if (!scope) { |
3561 | sprop = NULL; | sprop = NULL; |
3562 | } else { | } else { |
3563 | sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, | sprop = scope->change(cx, sprop, attrs, mask, getter, setter); |
getter, setter); | ||
3564 | } | } |
3565 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
3566 | return sprop; | return sprop; |
# | Line 3603 | Line 3568 |
3568 | ||
3569 | JSBool | JSBool |
3570 | js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, | js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
3571 | JSPropertyOp getter, JSPropertyOp setter, uintN attrs, | JSPropertyOp getter, JSPropertyOp setter, uintN attrs) |
JSProperty **propp) | ||
3572 | { | { |
3573 | return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, | return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, |
3574 | 0, 0, propp); | 0, 0, NULL); |
3575 | } | } |
3576 | ||
3577 | /* | /* |
# | Line 3628 | Line 3592 |
3592 | } \ | } \ |
3593 | if (*(vp) != nominal_) { \ | if (*(vp) != nominal_) { \ |
3594 | if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ | if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ |
3595 | LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *(vp)); \ | LOCKED_OBJ_WRITE_SLOT(cx, obj, (sprop)->slot, *(vp)); \ |
3596 | } \ | } \ |
3597 | } \ | } \ |
3598 | JS_END_MACRO | JS_END_MACRO |
# | Line 3642 | Line 3606 |
3606 | JSClass *clasp; | JSClass *clasp; |
3607 | JSScope *scope; | JSScope *scope; |
3608 | JSScopeProperty *sprop; | JSScopeProperty *sprop; |
3609 | bool added; | JSBool added; |
3610 | ||
3611 | JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE)) == 0); | JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE)) == 0); |
3612 | js_LeaveTraceIfGlobalObject(cx, obj); | js_LeaveTraceIfGlobalObject(cx, obj); |
3613 | ||
3614 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
3615 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
3616 | ||
3617 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
3618 | /* | /* |
# | Line 3675 | Line 3639 |
3639 | if (sprop && | if (sprop && |
3640 | pobj == obj && | pobj == obj && |
3641 | (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { | (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
3642 | sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, | sprop = OBJ_SCOPE(obj)->change(cx, sprop, attrs, |
3643 | attrs, sprop->attrs, | JSPROP_GETTER | JSPROP_SETTER, |
3644 | (attrs & JSPROP_GETTER) | (attrs & JSPROP_GETTER) |
3645 | ? getter | ? getter |
3646 | : sprop->getter, | : sprop->getter, |
3647 | (attrs & JSPROP_SETTER) | (attrs & JSPROP_SETTER) |
3648 | ? setter | ? setter |
3649 | : sprop->setter); | : sprop->setter); |
3650 | ||
3651 | /* NB: obj == pobj, so we can share unlock code at the bottom. */ | /* NB: obj == pobj, so we can share unlock code at the bottom. */ |
3652 | if (!sprop) | if (!sprop) |
3653 | goto error; | goto error; |
3654 | } else if (prop) { | } else if (prop) { |
3655 | /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ | /* NB: call JSObject::dropProperty, as pobj might not be native. */ |
3656 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
3657 | prop = NULL; | prop = NULL; |
3658 | sprop = NULL; | sprop = NULL; |
3659 | } | } |
# | Line 3709 | Line 3673 |
3673 | * prototype object. See the comment in jscntxt.h before protoHazardShape's | * prototype object. See the comment in jscntxt.h before protoHazardShape's |
3674 | * member declaration. | * member declaration. |
3675 | */ | */ |
3676 | if (OBJ_IS_DELEGATE(cx, obj) && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) | if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) |
3677 | cx->runtime->protoHazardShape = js_GenerateShape(cx, false); | cx->runtime->protoHazardShape = js_GenerateShape(cx, false); |
3678 | ||
3679 | /* Lock if object locking is required by this implementation. */ | /* Lock if object locking is required by this implementation. */ |
3680 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
3681 | ||
3682 | /* Use the object's class getter and setter by default. */ | /* Use the object's class getter and setter by default. */ |
3683 | clasp = LOCKED_OBJ_GET_CLASS(obj); | clasp = obj->getClass(); |
3684 | if (!getter) | if (!getter) |
3685 | getter = clasp->getProperty; | getter = clasp->getProperty; |
3686 | if (!setter) | if (!setter) |
# | Line 3732 | Line 3696 |
3696 | /* Add a new property, or replace an existing one of the same id. */ | /* Add a new property, or replace an existing one of the same id. */ |
3697 | if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) | if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) |
3698 | attrs |= JSPROP_SHARED; | attrs |= JSPROP_SHARED; |
3699 | sprop = js_AddScopeProperty(cx, scope, id, getter, setter, | |
3700 | SPROP_INVALID_SLOT, attrs, flags, | added = !scope->lookup(id); |
3701 | shortid); | sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs, |
3702 | flags, shortid); | |
3703 | if (!sprop) | if (!sprop) |
3704 | goto error; | goto error; |
added = true; | ||
3705 | } | } |
3706 | ||
3707 | /* Store value before calling addProperty, in case the latter GC's. */ | /* Store value before calling addProperty, in case the latter GC's. */ |
3708 | if (SPROP_HAS_VALID_SLOT(sprop, scope)) | if (SPROP_HAS_VALID_SLOT(sprop, scope)) |
3709 | LOCKED_OBJ_WRITE_BARRIER(cx, obj, sprop->slot, value); | LOCKED_OBJ_WRITE_SLOT(cx, obj, sprop->slot, value); |
3710 | ||
3711 | /* XXXbe called with lock held */ | /* XXXbe called with lock held */ |
3712 | ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, | ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, |
3713 | js_RemoveScopeProperty(cx, scope, id); | scope->remove(cx, id); |
3714 | goto error); | goto error); |
3715 | ||
3716 | if (defineHow & JSDNP_CACHE_RESULT) { | if (defineHow & JSDNP_CACHE_RESULT) { |
# | Line 3794 | Line 3758 |
3758 | JSBool ok; | JSBool ok; |
3759 | ||
3760 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
3761 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
3762 | ||
3763 | /* Search scopes starting with obj and following the prototype link. */ | /* Search scopes starting with obj and following the prototype link. */ |
3764 | start = obj; | start = obj; |
3765 | for (protoIndex = 0; ; protoIndex++) { | for (protoIndex = 0; ; protoIndex++) { |
3766 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
3767 | scope = OBJ_SCOPE(obj); | scope = OBJ_SCOPE(obj); |
3768 | if (scope->object == obj) { | sprop = scope->lookup(id); |
sprop = SCOPE_GET_PROPERTY(scope, id); | ||
} else { | ||
/* Shared prototype scope: try resolve before lookup. */ | ||
sprop = NULL; | ||
} | ||
3769 | ||
3770 | /* Try obj's class resolve hook if id was not found in obj's scope. */ | /* Try obj's class resolve hook if id was not found in obj's scope. */ |
3771 | if (!sprop) { | if (!sprop) { |
3772 | clasp = LOCKED_OBJ_GET_CLASS(obj); | clasp = obj->getClass(); |
3773 | resolve = clasp->resolve; | resolve = clasp->resolve; |
3774 | if (resolve != JS_ResolveStub) { | if (resolve != JS_ResolveStub) { |
3775 | /* Avoid recursion on (obj, id) already being resolved on cx. */ | /* Avoid recursion on (obj, id) already being resolved on cx. */ |
# | Line 3840 | Line 3799 |
3799 | if (clasp->flags & JSCLASS_NEW_RESOLVE) { | if (clasp->flags & JSCLASS_NEW_RESOLVE) { |
3800 | newresolve = (JSNewResolveOp)resolve; | newresolve = (JSNewResolveOp)resolve; |
3801 | if (flags == JSRESOLVE_INFER) | if (flags == JSRESOLVE_INFER) |
3802 | flags = InferFlags(cx, flags); | flags = js_InferFlags(cx, flags); |
3803 | obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) | obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) |
3804 | ? start | ? start |
3805 | : NULL; | : NULL; |
# | Line 3869 | Line 3828 |
3828 | if (!OBJ_IS_NATIVE(obj2)) { | if (!OBJ_IS_NATIVE(obj2)) { |
3829 | /* Whoops, newresolve handed back a foreign obj2. */ | /* Whoops, newresolve handed back a foreign obj2. */ |
3830 | JS_ASSERT(obj2 != obj); | JS_ASSERT(obj2 != obj); |
3831 | ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); | ok = obj2->lookupProperty(cx, id, objp, propp); |
3832 | if (!ok || *propp) | if (!ok || *propp) |
3833 | goto cleanup; | goto cleanup; |
3834 | JS_LOCK_OBJ(cx, obj2); | JS_LOCK_OBJ(cx, obj2); |
# | Line 3883 | Line 3842 |
3842 | * "too bad!" case. | * "too bad!" case. |
3843 | */ | */ |
3844 | scope = OBJ_SCOPE(obj2); | scope = OBJ_SCOPE(obj2); |
3845 | if (scope->object == obj2) | if (scope->owned()) |
3846 | sprop = SCOPE_GET_PROPERTY(scope, id); | sprop = scope->lookup(id); |
3847 | } | } |
3848 | if (sprop) { | if (sprop) { |
3849 | JS_ASSERT(obj2 == scope->object); | JS_ASSERT(scope == OBJ_SCOPE(obj2)); |
3850 | JS_ASSERT(scope->owned()); | |
3851 | obj = obj2; | obj = obj2; |
3852 | } else if (obj2 != obj) { | } else if (obj2 != obj) { |
3853 | if (OBJ_IS_NATIVE(obj2)) | if (OBJ_IS_NATIVE(obj2)) |
# | Line 3907 | Line 3867 |
3867 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
3868 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
3869 | scope = OBJ_SCOPE(obj); | scope = OBJ_SCOPE(obj); |
3870 | if (scope->object == obj) | if (scope->owned()) |
3871 | sprop = SCOPE_GET_PROPERTY(scope, id); | sprop = scope->lookup(id); |
3872 | } | } |
3873 | ||
3874 | cleanup: | cleanup: |
# | Line 3923 | Line 3883 |
3883 | if (sprop) { | if (sprop) { |
3884 | SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex); | SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex); |
3885 | JS_ASSERT(OBJ_SCOPE(obj) == scope); | JS_ASSERT(OBJ_SCOPE(obj) == scope); |
3886 | *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ | *objp = obj; |
3887 | ||
3888 | *propp = (JSProperty *) sprop; | *propp = (JSProperty *) sprop; |
3889 | return protoIndex; | return protoIndex; |
3890 | } | } |
3891 | ||
3892 | proto = LOCKED_OBJ_GET_PROTO(obj); | proto = obj->getProto(); |
3893 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
3894 | if (!proto) | if (!proto) |
3895 | break; | break; |
3896 | if (!OBJ_IS_NATIVE(proto)) { | if (!OBJ_IS_NATIVE(proto)) { |
3897 | if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp)) | if (!proto->lookupProperty(cx, id, objp, propp)) |
3898 | return -1; | return -1; |
3899 | return protoIndex + 1; | return protoIndex + 1; |
3900 | } | } |
3901 | ||
3902 | /* | |
3903 | * Correctness elsewhere (the property cache and JIT), not here in | |
3904 | * particular, depends on all the objects on the prototype chain having | |
3905 | * different scopes. This is just a convenient place to check. | |
3906 | * | |
3907 | * Cloned Block objects do in fact share their prototype's scope -- but | |
3908 | * that is really just a memory-saving hack, safe because Blocks cannot | |
3909 | * be on the prototype chain of other objects. | |
3910 | */ | |
3911 | JS_ASSERT_IF(OBJ_GET_CLASS(cx, obj) != &js_BlockClass, | |
3912 | OBJ_SCOPE(obj) != OBJ_SCOPE(proto)); | |
3913 | ||
3914 | obj = proto; | obj = proto; |
3915 | } | } |
3916 | ||
# | Line 3947 | Line 3920 |
3920 | return protoIndex; | return protoIndex; |
3921 | } | } |
3922 | ||
/* | ||
* We cache name lookup results only for the global object or for native | ||
* non-global objects without prototype or with prototype that never mutates, | ||
* see bug 462734 and bug 487039. | ||
*/ | ||
static inline bool | ||
IsCacheableNonGlobalScope(JSObject *obj) | ||
{ | ||
JS_ASSERT(STOBJ_GET_PARENT(obj)); | ||
JSClass *clasp = STOBJ_GET_CLASS(obj); | ||
bool cacheable = (clasp == &js_CallClass || | ||
clasp == &js_BlockClass || | ||
clasp == &js_DeclEnvClass); | ||
JS_ASSERT_IF(cacheable, obj->map->ops->lookupProperty == js_LookupProperty); | ||
return cacheable; | ||
} | ||
3923 | JSPropCacheEntry * | JSPropCacheEntry * |
3924 | js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, | js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, |
3925 | JSObject **objp, JSObject **pobjp, JSProperty **propp) | JSObject **objp, JSObject **pobjp, JSProperty **propp) |
# | Line 3984 | Line 3938 |
3938 | parent = OBJ_GET_PARENT(cx, obj); | parent = OBJ_GET_PARENT(cx, obj); |
3939 | for (scopeIndex = 0; | for (scopeIndex = 0; |
3940 | parent | parent |
3941 | ? IsCacheableNonGlobalScope(obj) | ? js_IsCacheableNonGlobalScope(obj) |
3942 | : obj->map->ops->lookupProperty == js_LookupProperty; | : obj->map->ops->lookupProperty == js_LookupProperty; |
3943 | ++scopeIndex) { | ++scopeIndex) { |
3944 | protoIndex = | protoIndex = |
# | Line 4004 | Line 3958 |
3958 | * Block instances on the scope chain are immutable and | * Block instances on the scope chain are immutable and |
3959 | * always share their scope with compile-time prototypes. | * always share their scope with compile-time prototypes. |
3960 | */ | */ |
3961 | JS_ASSERT(pobj == OBJ_GET_PROTO(cx, obj)); | JS_ASSERT(pobj == obj); |
3962 | JS_ASSERT(OBJ_SCOPE(obj)->object == pobj); | JS_ASSERT(protoIndex == 0); |
JS_ASSERT(protoIndex == 1); | ||
3963 | } else { | } else { |
3964 | /* Call and DeclEnvClass objects have no prototypes. */ | /* Call and DeclEnvClass objects have no prototypes. */ |
3965 | JS_ASSERT(!OBJ_GET_PROTO(cx, obj)); | JS_ASSERT(!OBJ_GET_PROTO(cx, obj)); |
# | Line 4032 | Line 3985 |
3985 | } | } |
3986 | ||
3987 | for (;;) { | for (;;) { |
3988 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj->lookupProperty(cx, id, &pobj, &prop)) |
3989 | return NULL; | return NULL; |
3990 | if (prop) { | if (prop) { |
3991 | PCMETER(JS_PROPERTY_CACHE(cx).nofills++); | PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
# | Line 4041 | Line 3994 |
3994 | ||
3995 | /* | /* |
3996 | * We conservatively assume that a resolve hook could mutate the scope | * We conservatively assume that a resolve hook could mutate the scope |
3997 | * chain during OBJ_LOOKUP_PROPERTY. So we read parent here again. | * chain during JSObject::lookupProperty. So we read parent here again. |
3998 | */ | */ |
3999 | parent = OBJ_GET_PARENT(cx, obj); | parent = OBJ_GET_PARENT(cx, obj); |
4000 | if (!parent) { | if (!parent) { |
# | Line 4084 | Line 4037 |
4037 | * farther checks or lookups. For details see the JSOP_BINDNAME case of | * farther checks or lookups. For details see the JSOP_BINDNAME case of |
4038 | * js_Interpret. | * js_Interpret. |
4039 | */ | */ |
4040 | for (int scopeIndex = 0; IsCacheableNonGlobalScope(obj); scopeIndex++) { | for (int scopeIndex = 0; js_IsCacheableNonGlobalScope(obj); scopeIndex++) { |
4041 | JSObject *pobj; | JSObject *pobj; |
4042 | JSProperty *prop; | JSProperty *prop; |
4043 | int protoIndex = js_LookupPropertyWithFlags(cx, obj, id, | int protoIndex = js_LookupPropertyWithFlags(cx, obj, id, |
# | Line 4116 | Line 4069 |
4069 | do { | do { |
4070 | JSObject *pobj; | JSObject *pobj; |
4071 | JSProperty *prop; | JSProperty *prop; |
4072 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj->lookupProperty(cx, id, &pobj, &prop)) |
4073 | return NULL; | return NULL; |
4074 | if (prop) { | if (prop) { |
4075 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
4076 | break; | break; |
4077 | } | } |
4078 | ||
4079 | /* | /* |
4080 | * We conservatively assume that a resolve hook could mutate the scope | * We conservatively assume that a resolve hook could mutate the scope |
4081 | * chain during OBJ_LOOKUP_PROPERTY. So we must check if parent is not | * chain during JSObject::lookupProperty. So we must check if parent is |
4082 | * null here even if it wasn't before the lookup. | * not null here even if it wasn't before the lookup. |
4083 | */ | */ |
4084 | JSObject *parent = OBJ_GET_PARENT(cx, obj); | JSObject *parent = OBJ_GET_PARENT(cx, obj); |
4085 | if (!parent) | if (!parent) |
# | Line 4151 | Line 4104 |
4104 | JS_ASSERT(OBJ_IS_NATIVE(pobj)); | JS_ASSERT(OBJ_IS_NATIVE(pobj)); |
4105 | JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); | JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); |
4106 | scope = OBJ_SCOPE(pobj); | scope = OBJ_SCOPE(pobj); |
JS_ASSERT(scope->object == pobj); | ||
4107 | ||
4108 | slot = sprop->slot; | slot = sprop->slot; |
4109 | *vp = (slot != SPROP_INVALID_SLOT) | *vp = (slot != SPROP_INVALID_SLOT) |
# | Line 4171 | Line 4123 |
4123 | return JS_FALSE; | return JS_FALSE; |
4124 | ||
4125 | JS_LOCK_SCOPE(cx, scope); | JS_LOCK_SCOPE(cx, scope); |
JS_ASSERT(scope->object == pobj); | ||
4126 | if (SLOT_IN_SCOPE(slot, scope) && | if (SLOT_IN_SCOPE(slot, scope) && |
4127 | (JS_LIKELY(cx->runtime->propertyRemovals == sample) || | (JS_LIKELY(cx->runtime->propertyRemovals == sample) || |
4128 | SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { | scope->has(sprop))) { |
4129 | LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); | LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); |
4130 | } | } |
4131 | ||
# | Line 4195 | Line 4146 |
4146 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
4147 | JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); | JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); |
4148 | scope = OBJ_SCOPE(obj); | scope = OBJ_SCOPE(obj); |
JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED)); | ||
4149 | ||
4150 | slot = sprop->slot; | slot = sprop->slot; |
4151 | if (slot != SPROP_INVALID_SLOT) { | if (slot != SPROP_INVALID_SLOT) { |
# | Line 4209 | Line 4159 |
4159 | * Allow API consumers to create shared properties with stub setters. | * Allow API consumers to create shared properties with stub setters. |
4160 | * Such properties lack value storage, so setting them is like writing | * Such properties lack value storage, so setting them is like writing |
4161 | * to /dev/null. | * to /dev/null. |
* | ||
* But we can't short-circuit if there's a scripted getter or setter | ||
* since we might need to throw. In that case, we let SPROP_SET | ||
* decide whether to throw an exception. See bug 478047. | ||
4162 | */ | */ |
4163 | if (!(sprop->attrs & JSPROP_GETTER) && SPROP_HAS_STUB_SETTER(sprop)) { | if (SPROP_HAS_STUB_SETTER(sprop)) |
JS_ASSERT(!(sprop->attrs & JSPROP_SETTER)); | ||
4164 | return JS_TRUE; | return JS_TRUE; |
} | ||
4165 | } | } |
4166 | ||
4167 | sample = cx->runtime->propertyRemovals; | sample = cx->runtime->propertyRemovals; |
# | Line 4229 | Line 4173 |
4173 | return JS_FALSE; | return JS_FALSE; |
4174 | ||
4175 | JS_LOCK_SCOPE(cx, scope); | JS_LOCK_SCOPE(cx, scope); |
JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED)); | ||
4176 | if (SLOT_IN_SCOPE(slot, scope) && | if (SLOT_IN_SCOPE(slot, scope) && |
4177 | (JS_LIKELY(cx->runtime->propertyRemovals == sample) || | (JS_LIKELY(cx->runtime->propertyRemovals == sample) || |
4178 | SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { | scope->has(sprop))) { |
4179 | set_slot: | set_slot: |
4180 | LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp); | LOCKED_OBJ_WRITE_SLOT(cx, obj, slot, *vp); |
4181 | } | } |
4182 | ||
4183 | return JS_TRUE; | return JS_TRUE; |
# | Line 4251 | Line 4194 |
4194 | ||
4195 | JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); | JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); |
4196 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
4197 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
4198 | ||
4199 | aobj = js_GetProtoIfDenseArray(cx, obj); | aobj = js_GetProtoIfDenseArray(cx, obj); |
4200 | protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags, | protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags, |
# | Line 4284 | Line 4227 |
4227 | flags = JSREPORT_ERROR; | flags = JSREPORT_ERROR; |
4228 | } else { | } else { |
4229 | if (!JS_HAS_STRICT_OPTION(cx) || | if (!JS_HAS_STRICT_OPTION(cx) || |
4230 | (op != JSOP_GETPROP && op != JSOP_GETELEM)) { | (op != JSOP_GETPROP && op != JSOP_GETELEM) || |
4231 | js_CurrentPCIsInImacro(cx)) { | |
4232 | return JS_TRUE; | return JS_TRUE; |
4233 | } | } |
4234 | ||
# | Line 4319 | Line 4263 |
4263 | } | } |
4264 | ||
4265 | if (!OBJ_IS_NATIVE(obj2)) { | if (!OBJ_IS_NATIVE(obj2)) { |
4266 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
4267 | return OBJ_GET_PROPERTY(cx, obj2, id, vp); | return obj2->getProperty(cx, id, vp); |
4268 | } | } |
4269 | ||
4270 | sprop = (JSScopeProperty *) prop; | sprop = (JSScopeProperty *) prop; |
# | Line 4347 | Line 4291 |
4291 | js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, | js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, |
4292 | jsval *vp) | jsval *vp) |
4293 | { | { |
4294 | JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED); | |
4295 | ||
4296 | if (obj->map->ops == &js_ObjectOps || | if (obj->map->ops == &js_ObjectOps || |
4297 | obj->map->ops->getProperty == js_GetProperty) { | obj->map->ops->getProperty == js_GetProperty) { |
4298 | return js_GetPropertyHelper(cx, obj, id, cacheResult, vp); | return js_GetPropertyHelper(cx, obj, id, cacheResult, vp); |
# | Line 4356 | Line 4302 |
4302 | if (OBJECT_IS_XML(cx, obj)) | if (OBJECT_IS_XML(cx, obj)) |
4303 | return js_GetXMLMethod(cx, obj, id, vp); | return js_GetXMLMethod(cx, obj, id, vp); |
4304 | #endif | #endif |
4305 | return OBJ_GET_PROPERTY(cx, obj, id, vp); | return obj->getProperty(cx, id, vp); |
4306 | } | } |
4307 | ||
4308 | JS_FRIEND_API(JSBool) | JS_FRIEND_API(JSBool) |
# | Line 4403 | Line 4349 |
4349 | JS_ASSERT_NOT_ON_TRACE(cx); | JS_ASSERT_NOT_ON_TRACE(cx); |
4350 | ||
4351 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
4352 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
4353 | ||
4354 | /* | /* |
4355 | * We peek at OBJ_SCOPE(obj) without locking obj. Any race means a failure | * We peek at OBJ_SCOPE(obj) without locking obj. Any race means a failure |
4356 | * to seal before sharing, which is inherently ambiguous. | * to seal before sharing, which is inherently ambiguous. |
4357 | */ | */ |
4358 | if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { | if (OBJ_SCOPE(obj)->sealed() && OBJ_SCOPE(obj)->object == obj) { |
4359 | flags = JSREPORT_ERROR; | flags = JSREPORT_ERROR; |
4360 | goto read_only_error; | goto read_only_error; |
4361 | } | } |
# | Line 4420 | Line 4366 |
4366 | return JS_FALSE; | return JS_FALSE; |
4367 | if (prop) { | if (prop) { |
4368 | if (!OBJ_IS_NATIVE(pobj)) { | if (!OBJ_IS_NATIVE(pobj)) { |
4369 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
4370 | prop = NULL; | prop = NULL; |
4371 | } | } |
4372 | } else { | } else { |
# | Line 4436 | Line 4382 |
4382 | * Now either sprop is null, meaning id was not found in obj or one of its | * Now either sprop is null, meaning id was not found in obj or one of its |
4383 | * prototypes; or sprop is non-null, meaning id was found in pobj's scope. | * prototypes; or sprop is non-null, meaning id was found in pobj's scope. |
4384 | * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop | * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop |
4385 | * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return | * is held: we must JSObject::dropProperty or JS_UNLOCK_SCOPE before we |
4386 | * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE | * return (the two are equivalent for native objects, but we use |
4387 | * because it is cheaper). | * JS_UNLOCK_SCOPE because it is cheaper). |
4388 | */ | */ |
4389 | attrs = JSPROP_ENUMERATE; | attrs = JSPROP_ENUMERATE; |
4390 | flags = 0; | flags = 0; |
# | Line 4457 | Line 4403 |
4403 | ||
4404 | attrs = sprop->attrs; | attrs = sprop->attrs; |
4405 | if ((attrs & JSPROP_READONLY) || | if ((attrs & JSPROP_READONLY) || |
4406 | (SCOPE_IS_SEALED(scope) && (attrs & JSPROP_SHARED))) { | (scope->sealed() && (attrs & JSPROP_SHARED))) { |
4407 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
4408 | ||
4409 | /* | /* |
# | Line 4476 | Line 4422 |
4422 | if (cacheResult) | if (cacheResult) |
4423 | TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); | TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); |
4424 | return JS_TRUE; | return JS_TRUE; |
4425 | #ifdef JS_TRACER | |
4426 | error: // TRACE_2 jumps here in case of error. | error: // TRACE_2 jumps here in case of error. |
4427 | return JS_FALSE; | return JS_FALSE; |
4428 | #endif | |
4429 | } | } |
4430 | ||
4431 | /* Strict mode: report a read-only strict warning. */ | /* Strict mode: report a read-only strict warning. */ |
# | Line 4558 | Line 4506 |
4506 | } | } |
4507 | if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) | if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) |
4508 | attrs |= JSPROP_SHARED; | attrs |= JSPROP_SHARED; |
4509 | sprop = js_AddScopeProperty(cx, scope, id, getter, setter, | sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs, |
4510 | SPROP_INVALID_SLOT, attrs, flags, shortid); | flags, shortid); |
4511 | if (!sprop) { | if (!sprop) { |
4512 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
4513 | return JS_FALSE; | return JS_FALSE; |
# | Line 4575 | Line 4523 |
4523 | ||
4524 | /* XXXbe called with obj locked */ | /* XXXbe called with obj locked */ |
4525 | ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, | ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, |
4526 | js_RemoveScopeProperty(cx, scope, id); | scope->remove(cx, id); |
4527 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
4528 | return JS_FALSE); | return JS_FALSE); |
4529 | added = true; | added = true; |
# | Line 4621 | Line 4569 |
4569 | return JS_TRUE; | return JS_TRUE; |
4570 | } | } |
4571 | if (!OBJ_IS_NATIVE(obj)) { | if (!OBJ_IS_NATIVE(obj)) { |
4572 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); | ok = obj->getAttributes(cx, id, prop, attrsp); |
4573 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4574 | return ok; | return ok; |
4575 | } | } |
4576 | } | } |
4577 | sprop = (JSScopeProperty *)prop; | sprop = (JSScopeProperty *)prop; |
4578 | *attrsp = sprop->attrs; | *attrsp = sprop->attrs; |
4579 | if (noprop) | if (noprop) |
4580 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4581 | return JS_TRUE; | return JS_TRUE; |
4582 | } | } |
4583 | ||
# | Line 4647 | Line 4595 |
4595 | if (!prop) | if (!prop) |
4596 | return JS_TRUE; | return JS_TRUE; |
4597 | if (!OBJ_IS_NATIVE(obj)) { | if (!OBJ_IS_NATIVE(obj)) { |
4598 | ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); | ok = obj->setAttributes(cx, id, prop, attrsp); |
4599 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4600 | return ok; | return ok; |
4601 | } | } |
4602 | } | } |
# | Line 4656 | Line 4604 |
4604 | sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, | sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, |
4605 | sprop->getter, sprop->setter); | sprop->getter, sprop->setter); |
4606 | if (noprop) | if (noprop) |
4607 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4608 | return (sprop != NULL); | return (sprop != NULL); |
4609 | } | } |
4610 | ||
# | Line 4672 | Line 4620 |
4620 | *rval = JSVAL_TRUE; | *rval = JSVAL_TRUE; |
4621 | ||
4622 | /* Convert string indices to integers if appropriate. */ | /* Convert string indices to integers if appropriate. */ |
4623 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
4624 | ||
4625 | if (!js_LookupProperty(cx, obj, id, &proto, &prop)) | if (!js_LookupProperty(cx, obj, id, &proto, &prop)) |
4626 | return JS_FALSE; | return JS_FALSE; |
# | Line 4689 | Line 4637 |
4637 | if (SPROP_IS_SHARED_PERMANENT(sprop)) | if (SPROP_IS_SHARED_PERMANENT(sprop)) |
4638 | *rval = JSVAL_FALSE; | *rval = JSVAL_FALSE; |
4639 | } | } |
4640 | OBJ_DROP_PROPERTY(cx, proto, prop); | proto->dropProperty(cx, prop); |
4641 | if (*rval == JSVAL_FALSE) | if (*rval == JSVAL_FALSE) |
4642 | return JS_TRUE; | return JS_TRUE; |
4643 | } | } |
# | Line 4705 | Line 4653 |
4653 | ||
4654 | sprop = (JSScopeProperty *)prop; | sprop = (JSScopeProperty *)prop; |
4655 | if (sprop->attrs & JSPROP_PERMANENT) { | if (sprop->attrs & JSPROP_PERMANENT) { |
4656 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4657 | *rval = JSVAL_FALSE; | *rval = JSVAL_FALSE; |
4658 | return JS_TRUE; | return JS_TRUE; |
4659 | } | } |
4660 | ||
4661 | /* XXXbe called with obj locked */ | /* XXXbe called with obj locked */ |
4662 | if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop), | if (!obj->getClass()->delProperty(cx, obj, SPROP_USERID(sprop), rval)) { |
4663 | rval)) { | obj->dropProperty(cx, prop); |
OBJ_DROP_PROPERTY(cx, obj, prop); | ||
4664 | return JS_FALSE; | return JS_FALSE; |
4665 | } | } |
4666 | ||
# | Line 4721 | Line 4668 |
4668 | if (SPROP_HAS_VALID_SLOT(sprop, scope)) | if (SPROP_HAS_VALID_SLOT(sprop, scope)) |
4669 | GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); | GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); |
4670 | ||
4671 | ok = js_RemoveScopeProperty(cx, scope, id); | ok = scope->remove(cx, id); |
4672 | OBJ_DROP_PROPERTY(cx, obj, prop); | obj->dropProperty(cx, prop); |
4673 | return ok; | return ok; |
4674 | } | } |
4675 | ||
# | Line 4736 | Line 4683 |
4683 | switch (hint) { | switch (hint) { |
4684 | case JSTYPE_STRING: | case JSTYPE_STRING: |
4685 | /* | /* |
4686 | * Optimize for String objects with standard toString methods. Support | |
4687 | * new String(...) instances whether mutated to have their own scope or | |
4688 | * not, as well as direct String.prototype references. | |
4689 | */ | |
4690 | if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { | |
4691 | jsid toStringId = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom); | |
4692 | ||
4693 | JS_LOCK_OBJ(cx, obj); | |
4694 | JSScope *scope = OBJ_SCOPE(obj); | |
4695 | JSScopeProperty *sprop = scope->lookup(toStringId); | |
4696 | JSObject *pobj = obj; | |
4697 | ||
4698 | if (!sprop) { | |
4699 | pobj = obj->getProto(); | |
4700 | ||
4701 | if (pobj && OBJ_GET_CLASS(cx, pobj) == &js_StringClass) { | |
4702 | JS_UNLOCK_SCOPE(cx, scope); | |
4703 | JS_LOCK_OBJ(cx, pobj); | |
4704 | scope = OBJ_SCOPE(pobj); | |
4705 | sprop = scope->lookup(toStringId); | |
4706 | } | |
4707 | } | |
4708 | ||
4709 | if (sprop && | |
4710 | SPROP_HAS_STUB_GETTER(sprop) && | |
4711 | SPROP_HAS_VALID_SLOT(sprop, scope)) { | |
4712 | jsval fval = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); | |
4713 | ||
4714 | if (VALUE_IS_FUNCTION(cx, fval)) { | |
4715 | JSObject *funobj = JSVAL_TO_OBJECT(fval); | |
4716 | JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); | |
4717 | ||
4718 | if (FUN_FAST_NATIVE(fun) == js_str_toString) { | |
4719 | JS_UNLOCK_SCOPE(cx, scope); | |
4720 | *vp = obj->fslots[JSSLOT_PRIMITIVE_THIS]; | |
4721 | return JS_TRUE; | |
4722 | } | |
4723 | } | |
4724 | } | |
4725 | JS_UNLOCK_SCOPE(cx, scope); | |
4726 | } | |
4727 | ||
4728 | /* | |
4729 | * Propagate the exception if js_TryMethod finds an appropriate | * Propagate the exception if js_TryMethod finds an appropriate |
4730 | * method, and calling that method returned failure. | * method, and calling that method returned failure. |
4731 | */ | */ |
# | Line 4795 | Line 4785 |
4785 | * in the object. Instead for the empty enumerator the code uses JSVAL_ZERO as | * in the object. Instead for the empty enumerator the code uses JSVAL_ZERO as |
4786 | * the enumeration state. | * the enumeration state. |
4787 | * | * |
4788 | * JSRuntime.nativeEnumCache caches the enumerators using scope's shape to | * JSThreadData.nativeEnumCache caches the enumerators using scope's shape to |
4789 | * avoid repeated scanning of scopes for enumerable properties. The cache | * avoid repeated scanning of scopes for enumerable properties. The cache |
4790 | * entry is either JSNativeEnumerator* or, for the empty enumerator, the shape | * entry is either JSNativeEnumerator* or, for the empty enumerator, the shape |
4791 | * value itself. The latter is stored as (shape << 1) | 1 to ensure that it is | * value itself. The latter is stored as (shape << 1) | 1 to ensure that it is |
4792 | * always different from JSNativeEnumerator* values. | * always different from JSNativeEnumerator* values. |
4793 | * | |
4794 | * We cache the enumerators in the JSENUMERATE_INIT case of js_Enumerate, not | |
4795 | * during JSENUMERATE_DESTROY. The GC can invoke the latter case during the | |
4796 | * finalization when JSNativeEnumerator contains finalized ids and the | |
4797 | * enumerator must be freed. | |
4798 | */ | */ |
4799 | struct JSNativeEnumerator { | struct JSNativeEnumerator { |
4800 | /* | /* |
4801 | * The index into the ids array. It runs from the length down to 1 when | * The index into the ids array. It runs from the length down to 1 when |
4802 | * the enumerator is running. It is 0 when the enumerator is finished and | * the enumerator is running. It is 0 when the enumerator is finished and |
4803 | * can be reused on a cache hit. Its type is jsword, not uint32, for | * can be reused on a cache hit. |
4804 | * compatibility with js_CompareAndSwap. | */ |
4805 | */ | uint32 cursor; |
jsword cursor; | ||
4806 | uint32 length; /* length of ids array */ | uint32 length; /* length of ids array */ |
4807 | uint32 shape; /* "shape" number -- see jsscope.h */ | uint32 shape; /* "shape" number -- see jsscope.h */ |
JSNativeEnumerator *next; /* list linking */ | ||
4808 | jsid ids[1]; /* enumeration id array */ | jsid ids[1]; /* enumeration id array */ |
4809 | ||
4810 | static inline size_t size(uint32 length) { | |
4811 | JS_ASSERT(length != 0); | |
4812 | return offsetof(JSNativeEnumerator, ids) + | |
4813 | (size_t) length * sizeof(jsid); | |
4814 | } | |
4815 | ||
4816 | bool isFinished() const { | |
4817 | return cursor == 0; | |
4818 | } | |
4819 | ||
4820 | void mark(JSTracer *trc) { | |
4821 | JS_ASSERT(length >= 1); | |
4822 | jsid *cursor = ids; | |
4823 | jsid *end = ids + length; | |
4824 | do { | |
4825 | js_TraceId(trc, *cursor); | |
4826 | } while (++cursor != end); | |
4827 | } | |
4828 | }; | }; |
4829 | ||
4830 | /* The tagging of shape values requires one bit. */ | /* The tagging of shape values requires one bit. */ |
4831 | JS_STATIC_ASSERT((jsuword) SHAPE_OVERFLOW_BIT <= | JS_STATIC_ASSERT((jsuword) SHAPE_OVERFLOW_BIT <= |
4832 | ((jsuword) 1 << (JS_BITS_PER_WORD - 1))); | ((jsuword) 1 << (JS_BITS_PER_WORD - 1))); |
4833 | ||
4834 | static inline size_t | static void |
4835 | NativeEnumeratorSize(uint32 length) | SetEnumeratorCache(JSContext *cx, jsuword *cachep, jsuword newcache) |
4836 | { | { |
4837 | JS_ASSERT(length != 0); | jsuword old = *cachep; |
4838 | return offsetof(JSNativeEnumerator, ids) + (size_t) length * sizeof(jsid); | *cachep = newcache; |
4839 | if (!(old & jsuword(1)) && old) { | |
4840 | /* Free the cached enumerator unless it is running. */ | |
4841 | JSNativeEnumerator *ne = reinterpret_cast<JSNativeEnumerator *>(old); | |
4842 | if (ne->isFinished()) | |
4843 | cx->free(ne); | |
4844 | } | |
4845 | } | } |
4846 | ||
/* | ||
* This function is used to enumerate the properties of native JSObjects | ||
* and those host objects that do not define a JSNewEnumerateOp-style iterator | ||
* function. | ||
*/ | ||
4847 | JSBool | JSBool |
4848 | js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, | js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, |
4849 | jsval *statep, jsid *idp) | jsval *statep, jsid *idp) |
4850 | { | { |
4851 | JSClass *clasp; | /* Here cx is JSTracer when enum_op is JSENUMERATE_MARK. */ |
4852 | JSEnumerateOp enumerate; | JSClass *clasp = obj->getClass(); |
4853 | JSNativeEnumerator *ne; | JSEnumerateOp enumerate = clasp->enumerate; |
uint32 length, shape; | ||
size_t allocated; | ||
JSScope *scope; | ||
jsuword *cachep, oldcache; | ||
JSScopeProperty *sprop; | ||
jsid *ids; | ||
jsword newcursor; | ||
clasp = OBJ_GET_CLASS(cx, obj); | ||
enumerate = clasp->enumerate; | ||
4854 | if (clasp->flags & JSCLASS_NEW_ENUMERATE) { | if (clasp->flags & JSCLASS_NEW_ENUMERATE) { |
4855 | JS_ASSERT(enumerate != JS_EnumerateStub); | JS_ASSERT(enumerate != JS_EnumerateStub); |
4856 | return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); | return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); |
4857 | } | } |
4858 | ||
4859 | switch (enum_op) { | switch (enum_op) { |
4860 | case JSENUMERATE_INIT: | case JSENUMERATE_INIT: { |
4861 | if (!enumerate(cx, obj)) | if (!enumerate(cx, obj)) |
4862 | return JS_FALSE; | return false; |
4863 | ||
4864 | /* | /* |
4865 | * The set of all property ids is pre-computed when the iterator is | * The set of all property ids is pre-computed when the iterator is |
# | Line 4865 | Line 4867 |
4867 | * during the iteration. | * during the iteration. |
4868 | * | * |
4869 | * Use a do-while(0) loop to avoid too many nested ifs. If ne is null | * Use a do-while(0) loop to avoid too many nested ifs. If ne is null |
4870 | * after the loop, it indicates an empty enumerator. If allocated is | * after the loop, it indicates an empty enumerator. |
* not zero after the loop, we add the newly allocated ne to the cache | ||
* and runtime->nativeEnumerators list. | ||
4871 | */ | */ |
4872 | ne = NULL; | JSNativeEnumerator *ne; |
4873 | length = 0; | uint32 length; |
allocated = (size_t) 0; | ||
JS_LOCK_OBJ(cx, obj); | ||
scope = OBJ_SCOPE(obj); | ||
4874 | do { | do { |
4875 | /* | uint32 shape = OBJ_SHAPE(obj); |
* If this object shares a scope with its prototype, don't | ||
* enumerate its properties. Otherwise they will be enumerated | ||
* a second time when the prototype object is enumerated. | ||
*/ | ||
if (scope->object != obj) { | ||
#ifdef __GNUC__ | ||
cachep = NULL; /* suppress bogus gcc warnings */ | ||
#endif | ||
break; | ||
} | ||
4876 | ||
4877 | ENUM_CACHE_METER(nativeEnumProbes); | ENUM_CACHE_METER(nativeEnumProbes); |
4878 | shape = scope->shape; | jsuword *cachep = &JS_THREAD_DATA(cx)-> |
4879 | JS_ASSERT(shape < SHAPE_OVERFLOW_BIT); | nativeEnumCache[NATIVE_ENUM_CACHE_HASH(shape)]; |
4880 | cachep = &cx->runtime-> | jsuword oldcache = *cachep; |
nativeEnumCache[NATIVE_ENUM_CACHE_HASH(shape)]; | ||
oldcache = *cachep; | ||
4881 | if (oldcache & (jsuword) 1) { | if (oldcache & (jsuword) 1) { |
4882 | if ((uint32) (oldcache >> 1) == shape) { | if (uint32(oldcache >> 1) == shape) { |
4883 | /* scope has a shape with no enumerable properties. */ | /* scope has a shape with no enumerable properties. */ |
4884 | ne = NULL; | |
4885 | length = 0; | |
4886 | break; | break; |
4887 | } | } |
4888 | } else if (oldcache != (jsuword) 0) { | } else if (oldcache != jsuword(0)) { |
4889 | /* | ne = reinterpret_cast<JSNativeEnumerator *>(oldcache); |
* We can safely read ne->shape without taking the GC lock as | ||
* ne is deleted only when running the GC and ne->shape is | ||
* read-only after initialization. | ||
*/ | ||
ne = (JSNativeEnumerator *) *cachep; | ||
4890 | JS_ASSERT(ne->length >= 1); | JS_ASSERT(ne->length >= 1); |
4891 | if (ne->shape == shape) { | if (ne->shape == shape && ne->isFinished()) { |
4892 | /* | /* Mark ne as active. */ |
4893 | * Check that ne is not running with another enumerator | ne->cursor = ne->length; |
* and, if so, reuse and mark it as running from now. | ||
*/ | ||
4894 | length = ne->length; | length = ne->length; |
4895 | if (js_CompareAndSwap(&ne->cursor, 0, length)) | JS_ASSERT(!ne->isFinished()); |
4896 | break; | break; |
length = 0; | ||
4897 | } | } |
ne = NULL; | ||
4898 | } | } |
4899 | ENUM_CACHE_METER(nativeEnumMisses); | ENUM_CACHE_METER(nativeEnumMisses); |
4900 | ||
4901 | JS_LOCK_OBJ(cx, obj); | |
4902 | ||
4903 | /* Count all enumerable properties in object's scope. */ | /* Count all enumerable properties in object's scope. */ |
4904 | JS_ASSERT(length == 0); | JSScope *scope = OBJ_SCOPE(obj); |
4905 | for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { | length = 0; |
4906 | for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope); | |
4907 | sprop; | |
4908 | sprop = sprop->parent) { | |
4909 | if ((sprop->attrs & JSPROP_ENUMERATE) && | if ((sprop->attrs & JSPROP_ENUMERATE) && |
4910 | !(sprop->flags & SPROP_IS_ALIAS) && | !(sprop->flags & SPROP_IS_ALIAS) && |
4911 | (!SCOPE_HAD_MIDDLE_DELETE(scope) || | (!scope->hadMiddleDelete() || scope->has(sprop))) { |
SCOPE_HAS_PROPERTY(scope, sprop))) { | ||
4912 | length++; | length++; |
4913 | } | } |
4914 | } | } |
4915 | if (length == 0) { | if (length == 0) { |
4916 | /* cache the scope without enumerable properties. */ | /* |
4917 | *cachep = ((jsuword) shape << 1) | (jsuword) 1; | * Cache the scope without enumerable properties unless its |
4918 | * shape overflows, see bug 440834. | |
4919 | */ | |
4920 | JS_UNLOCK_SCOPE(cx, scope); | |
4921 | if (shape < SHAPE_OVERFLOW_BIT) { | |
4922 | SetEnumeratorCache(cx, cachep, | |
4923 | (jsuword(shape) << 1) | jsuword(1)); | |
4924 | } | |
4925 | ne = NULL; | |
4926 | break; | break; |
4927 | } | } |
4928 | ||
4929 | allocated = NativeEnumeratorSize(length); | ne = (JSNativeEnumerator *) |
4930 | ne = (JSNativeEnumerator *) JS_malloc(cx, allocated); | cx->mallocNoReport(JSNativeEnumerator::size(length)); |
4931 | if (!ne) { | if (!ne) { |
4932 | /* Report the OOM error outside the lock. */ | |
4933 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
4934 | return JS_FALSE; | JS_ReportOutOfMemory(cx); |
4935 | return false; | |
4936 | } | } |
4937 | ne->cursor = length; | ne->cursor = length; |
4938 | ne->length = length; | ne->length = length; |
4939 | ne->shape = shape; | ne->shape = shape; |
4940 | ids = ne->ids; | |
4941 | for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { | jsid *ids = ne->ids; |
4942 | for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope); | |
4943 | sprop; | |
4944 | sprop = sprop->parent) { | |
4945 | if ((sprop->attrs & JSPROP_ENUMERATE) && | if ((sprop->attrs & JSPROP_ENUMERATE) && |
4946 | !(sprop->flags & SPROP_IS_ALIAS) && | !(sprop->flags & SPROP_IS_ALIAS) && |
4947 | (!SCOPE_HAD_MIDDLE_DELETE(scope) || | (!scope->hadMiddleDelete() || scope->has(sprop))) { |
SCOPE_HAS_PROPERTY(scope, sprop))) { | ||
4948 | JS_ASSERT(ids < ne->ids + length); | JS_ASSERT(ids < ne->ids + length); |
4949 | *ids++ = sprop->id; | *ids++ = sprop->id; |
4950 | } | } |
4951 | } | } |
4952 | JS_ASSERT(ids == ne->ids + length); | JS_ASSERT(ids == ne->ids + length); |
4953 | JS_UNLOCK_SCOPE(cx, scope); | |
4954 | ||
4955 | /* | |
4956 | * Do not cache enumerators for objects with with a shape | |
4957 | * that had overflowed, see bug 440834. | |
4958 | */ | |
4959 | if (shape < SHAPE_OVERFLOW_BIT) | |
4960 | SetEnumeratorCache(cx, cachep, reinterpret_cast<jsuword>(ne)); | |
4961 | } while (0); | } while (0); |
JS_UNLOCK_SCOPE(cx, scope); | ||
4962 | ||
4963 | if (!ne) { | if (!ne) { |
4964 | JS_ASSERT(length == 0); | JS_ASSERT(length == 0); |
JS_ASSERT(allocated == 0); | ||
4965 | *statep = JSVAL_ZERO; | *statep = JSVAL_ZERO; |
4966 | } else { | } else { |
4967 | JS_ASSERT(length != 0); | JS_ASSERT(length != 0); |
4968 | JS_ASSERT(ne->cursor == (jsword) length); | JS_ASSERT(ne->cursor == length); |
4969 | if (allocated != 0) { | JS_ASSERT(!(reinterpret_cast<jsuword>(ne) & jsuword(1))); |
JS_LOCK_GC(cx->runtime); | ||
if (!js_AddAsGCBytes(cx, allocated)) { | ||
/* js_AddAsGCBytes releases the GC lock on failures. */ | ||
JS_free(cx, ne); | ||
return JS_FALSE; | ||
} | ||
ne->next = cx->runtime->nativeEnumerators; | ||
cx->runtime->nativeEnumerators = ne; | ||
JS_ASSERT(((jsuword) ne & (jsuword) 1) == (jsuword) 0); | ||
*cachep = (jsuword) ne; | ||
JS_UNLOCK_GC(cx->runtime); | ||
} | ||
4970 | *statep = PRIVATE_TO_JSVAL(ne); | *statep = PRIVATE_TO_JSVAL(ne); |
4971 | } | } |
4972 | if (idp) | if (idp) |
4973 | *idp = INT_TO_JSVAL(length); | *idp = INT_TO_JSVAL(length); |
4974 | break; | break; |
4975 | } | |
4976 | ||
4977 | case JSENUMERATE_NEXT: | case JSENUMERATE_NEXT: |
4978 | case JSENUMERATE_DESTROY: | case JSENUMERATE_DESTROY: { |
4979 | if (*statep == JSVAL_ZERO) { | if (*statep == JSVAL_ZERO) { |
4980 | *statep = JSVAL_NULL; | *statep = JSVAL_NULL; |
4981 | break; | break; |
4982 | } | } |
4983 | ne = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep); | JSNativeEnumerator *ne = (JSNativeEnumerator *) |
4984 | JSVAL_TO_PRIVATE(*statep); | |
4985 | JS_ASSERT(ne->length >= 1); | JS_ASSERT(ne->length >= 1); |
4986 | JS_ASSERT(ne->cursor >= 1); | JS_ASSERT(ne->cursor >= 1); |
/* | ||
* We must not access ne->cursor when we set it to zero as it means | ||
* that ne is free and another thread can grab it from the cache. So | ||
* we set the state to JSVAL_ZERO in the NEXT case to avoid touching | ||
* ne->length again in the DESTROY case. | ||
*/ | ||
4987 | if (enum_op == JSENUMERATE_NEXT) { | if (enum_op == JSENUMERATE_NEXT) { |
4988 | newcursor = ne->cursor - 1; | uint32 newcursor = ne->cursor - 1; |
4989 | *idp = ne->ids[newcursor]; | *idp = ne->ids[newcursor]; |
4990 | ne->cursor = newcursor; | if (newcursor != 0) { |
4991 | if (newcursor == 0) | ne->cursor = newcursor; |
4992 | *statep = JSVAL_ZERO; | break; |
4993 | } | |
4994 | } else { | } else { |
4995 | /* The enumerator has not iterated over all ids. */ | /* The enumerator has not iterated over all ids. */ |
4996 | JS_ASSERT(enum_op == JSENUMERATE_DESTROY); | |
4997 | } | |
4998 | *statep = JSVAL_ZERO; | |
4999 | ||
5000 | jsuword *cachep = &JS_THREAD_DATA(cx)-> | |
5001 | nativeEnumCache[NATIVE_ENUM_CACHE_HASH(ne->shape)]; | |
5002 | if (reinterpret_cast<jsuword>(ne) == *cachep) { | |
5003 | /* Mark the cached iterator as available. */ | |
5004 | ne->cursor = 0; | ne->cursor = 0; |
5005 | } else { | |
5006 | cx->free(ne); | |
5007 | } | } |
5008 | break; | break; |
5009 | } | |
5010 | } | } |
5011 | return JS_TRUE; | return true; |
5012 | } | } |
5013 | ||
5014 | void | void |
5015 | js_TraceNativeEnumerators(JSTracer *trc) | js_MarkEnumeratorState(JSTracer *trc, JSObject *obj, jsval state) |
5016 | { | { |
5017 | JSRuntime *rt; | if (JSVAL_IS_TRACEABLE(state)) { |
5018 | JSNativeEnumerator **nep, *ne; | JS_CALL_TRACER(trc, JSVAL_TO_TRACEABLE(state), |
5019 | jsid *cursor, *end; | JSVAL_TRACE_KIND(state), "enumerator_value"); |
5020 | } else if (obj->map->ops->enumerate == js_Enumerate && | |
5021 | !(obj->getClass()->flags & JSCLASS_NEW_ENUMERATE)) { | |
5022 | /* Check if state stores JSNativeEnumerator. */ | |
5023 | JS_ASSERT(JSVAL_IS_INT(state) || | |
5024 | JSVAL_IS_NULL(state) || | |
5025 | JSVAL_IS_VOID(state)); | |
5026 | if (JSVAL_IS_INT(state) && state != JSVAL_ZERO) | |
5027 | ((JSNativeEnumerator *) JSVAL_TO_PRIVATE(state))->mark(trc); | |
5028 | } | |
5029 | } | |
5030 | ||
5031 | void | |
5032 | js_PurgeCachedNativeEnumerators(JSContext *cx, JSThreadData *data) | |
5033 | { | |
5034 | jsuword *cachep = &data->nativeEnumCache[0]; | |
5035 | jsuword *end = cachep + JS_ARRAY_LENGTH(data->nativeEnumCache); | |
5036 | for (; cachep != end; ++cachep) | |
5037 | SetEnumeratorCache(cx, cachep, jsuword(0)); | |
5038 | ||
/* | ||
* Purge native enumerators cached by shape id, which we are about to | ||
* re-number completely when tracing is done for the GC. | ||
*/ | ||
rt = trc->context->runtime; | ||
if (IS_GC_MARKING_TRACER(trc)) { | ||
memset(&rt->nativeEnumCache, 0, sizeof rt->nativeEnumCache); | ||
5039 | #ifdef JS_DUMP_ENUM_CACHE_STATS | #ifdef JS_DUMP_ENUM_CACHE_STATS |
5040 | printf("nativeEnumCache hit rate %g%%\n", | printf("nativeEnumCache hit rate %g%%\n", |
5041 | 100.0 * (rt->nativeEnumProbes - rt->nativeEnumMisses) / | 100.0 * (cx->runtime->nativeEnumProbes - |
5042 | rt->nativeEnumProbes); | cx->runtime->nativeEnumMisses) / |
5043 | cx->runtime->nativeEnumProbes); | |
5044 | #endif | #endif |
} | ||
nep = &rt->nativeEnumerators; | ||
while ((ne = *nep) != NULL) { | ||
JS_ASSERT(ne->length != 0); | ||
if (ne->cursor != 0) { | ||
/* Trace ids of the running enumerator. */ | ||
cursor = ne->ids; | ||
end = cursor + ne->length; | ||
do { | ||
TRACE_ID(trc, *cursor); | ||
} while (++cursor != end); | ||
} else if (IS_GC_MARKING_TRACER(trc)) { | ||
js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length)); | ||
*nep = ne->next; | ||
JS_free(trc->context, ne); | ||
continue; | ||
} | ||
nep = &ne->next; | ||
} | ||
5045 | } | } |
5046 | ||
5047 | JSBool | JSBool |
# | Line 5086 | Line 5073 |
5073 | break; | break; |
5074 | ||
5075 | default: | default: |
5076 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) | if (!obj->lookupProperty(cx, id, &pobj, &prop)) |
5077 | return JS_FALSE; | return JS_FALSE; |
5078 | if (!prop) { | if (!prop) { |
5079 | if (!writing) | if (!writing) |
# | Line 5097 | Line 5084 |
5084 | } | } |
5085 | ||
5086 | if (!OBJ_IS_NATIVE(pobj)) { | if (!OBJ_IS_NATIVE(pobj)) { |
5087 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
5088 | ||
5089 | /* Avoid diverging for non-natives that reuse js_CheckAccess. */ | /* Avoid diverging for non-natives that reuse js_CheckAccess. */ |
5090 | if (pobj->map->ops->checkAccess == js_CheckAccess) { | if (pobj->map->ops->checkAccess == js_CheckAccess) { |
# | Line 5107 | Line 5094 |
5094 | } | } |
5095 | break; | break; |
5096 | } | } |
5097 | return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); | return pobj->checkAccess(cx, id, mode, vp, attrsp); |
5098 | } | } |
5099 | ||
5100 | sprop = (JSScopeProperty *)prop; | sprop = (JSScopeProperty *)prop; |
# | Line 5117 | Line 5104 |
5104 | ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) | ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) |
5105 | : JSVAL_VOID; | : JSVAL_VOID; |
5106 | } | } |
5107 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
5108 | } | } |
5109 | ||
5110 | /* | /* |
# | Line 5158 | Line 5145 |
5145 | ||
5146 | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) |
5147 | obj = tmp; | obj = tmp; |
5148 | if (!OBJ_GET_PROPERTY(cx, obj, | if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ExecutionContextAtom), &xcval)) |
ATOM_TO_JSID(cx->runtime->atomState | ||
.ExecutionContextAtom), | ||
&xcval)) { | ||
5149 | return JS_FALSE; | return JS_FALSE; |
} | ||
5150 | if (JSVAL_IS_PRIMITIVE(xcval)) { | if (JSVAL_IS_PRIMITIVE(xcval)) { |
5151 | JS_ReportError(cx, "invalid ExecutionContext in global object"); | JS_ReportError(cx, "invalid ExecutionContext in global object"); |
5152 | return JS_FALSE; | return JS_FALSE; |
5153 | } | } |
5154 | if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), | if (!JSVAL_TO_OBJECT(xcval)->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.currentAtom), |
5155 | ATOM_TO_JSID(cx->runtime->atomState.currentAtom), | rval)) { |
rval)) { | ||
5156 | return JS_FALSE; | return JS_FALSE; |
5157 | } | } |
5158 | return JS_TRUE; | return JS_TRUE; |
# | Line 5190 | Line 5172 |
5172 | JSBool ok; | JSBool ok; |
5173 | ||
5174 | callee = JSVAL_TO_OBJECT(argv[-2]); | callee = JSVAL_TO_OBJECT(argv[-2]); |
5175 | if (!OBJ_GET_PROPERTY(cx, callee, | if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), &fval)) |
ATOM_TO_JSID(cx->runtime->atomState.__call__Atom), | ||
&fval)) { | ||
5176 | return JS_FALSE; | return JS_FALSE; |
} | ||
5177 | if (VALUE_IS_FUNCTION(cx, fval)) { | if (VALUE_IS_FUNCTION(cx, fval)) { |
5178 | if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) | if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) |
5179 | return JS_FALSE; | return JS_FALSE; |
# | Line 5232 | Line 5211 |
5211 | JSBool ok; | JSBool ok; |
5212 | ||
5213 | callee = JSVAL_TO_OBJECT(argv[-2]); | callee = JSVAL_TO_OBJECT(argv[-2]); |
5214 | if (!OBJ_GET_PROPERTY(cx, callee, | if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__construct__Atom), |
5215 | ATOM_TO_JSID(cx->runtime->atomState | &cval)) { |
.__construct__Atom), | ||
&cval)) { | ||
5216 | return JS_FALSE; | return JS_FALSE; |
5217 | } | } |
5218 | if (VALUE_IS_FUNCTION(cx, cval)) { | if (VALUE_IS_FUNCTION(cx, cval)) { |
# | Line 5272 | Line 5249 |
5249 | { | { |
5250 | jsval fval, rval; | jsval fval, rval; |
5251 | ||
5252 | if (!OBJ_GET_PROPERTY(cx, obj, | if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.__hasInstance__Atom), &fval)) |
ATOM_TO_JSID(cx->runtime->atomState | ||
.__hasInstance__Atom), | ||
&fval)) { | ||
5253 | return JS_FALSE; | return JS_FALSE; |
} | ||
5254 | if (VALUE_IS_FUNCTION(cx, fval)) { | if (VALUE_IS_FUNCTION(cx, fval)) { |
5255 | if (!js_InternalCall(cx, obj, fval, 1, &v, &rval)) | if (!js_InternalCall(cx, obj, fval, 1, &v, &rval)) |
5256 | return JS_FALSE; | return JS_FALSE; |
# | Line 5320 | Line 5293 |
5293 | return JS_FALSE; | return JS_FALSE; |
5294 | if (VALUE_IS_FUNCTION(cx, v)) { | if (VALUE_IS_FUNCTION(cx, v)) { |
5295 | ctor = JSVAL_TO_OBJECT(v); | ctor = JSVAL_TO_OBJECT(v); |
5296 | if (!OBJ_GET_PROPERTY(cx, ctor, | if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v)) |
ATOM_TO_JSID(cx->runtime->atomState | ||
.classPrototypeAtom), | ||
&v)) { | ||
5297 | return JS_FALSE; | return JS_FALSE; |
} | ||
5298 | if (!JSVAL_IS_PRIMITIVE(v)) { | if (!JSVAL_IS_PRIMITIVE(v)) { |
5299 | /* | /* |
5300 | * Set the newborn root in case v is otherwise unreferenced. | * Set the newborn root in case v is otherwise unreferenced. |
# | Line 5366 | Line 5335 |
5335 | ||
5336 | atom = cx->runtime->atomState.constructorAtom; | atom = cx->runtime->atomState.constructorAtom; |
5337 | JS_ASSERT(id == ATOM_TO_JSID(atom)); | JS_ASSERT(id == ATOM_TO_JSID(atom)); |
5338 | return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, | return obj->checkAccess(cx, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs); |
vp, &attrs); | ||
5339 | } | } |
5340 | ||
5341 | static JSBool | static JSBool |
# | Line 5378 | Line 5346 |
5346 | ||
5347 | atom = cx->runtime->atomState.constructorAtom; | atom = cx->runtime->atomState.constructorAtom; |
5348 | JS_ASSERT(id == ATOM_TO_JSID(atom)); | JS_ASSERT(id == ATOM_TO_JSID(atom)); |
5349 | return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, | return obj->checkAccess(cx, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs); |
vp, &attrs); | ||
5350 | } | } |
5351 | ||
5352 | JSBool | JSBool |
# | Line 5392 | Line 5359 |
5359 | * reset), while native or "system" constructors have DontEnum | ReadOnly | | * reset), while native or "system" constructors have DontEnum | ReadOnly | |
5360 | * DontDelete. | * DontDelete. |
5361 | */ | */ |
5362 | if (!OBJ_DEFINE_PROPERTY(cx, ctor, | if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), |
5363 | ATOM_TO_JSID(cx->runtime->atomState | OBJECT_TO_JSVAL(proto), JS_PropertyStub, JS_PropertyStub, |
5364 | .classPrototypeAtom), | attrs)) { |
OBJECT_TO_JSVAL(proto), | ||
JS_PropertyStub, JS_PropertyStub, | ||
attrs, NULL)) { | ||
5365 | return JS_FALSE; | return JS_FALSE; |
5366 | } | } |
5367 | ||
# | Line 5405 | Line 5369 |
5369 | * ECMA says that Object.prototype.constructor, or f.prototype.constructor | * ECMA says that Object.prototype.constructor, or f.prototype.constructor |
5370 | * for a user-defined function f, is DontEnum. | * for a user-defined function f, is DontEnum. |
5371 | */ | */ |
5372 | return OBJ_DEFINE_PROPERTY(cx, proto, | return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), |
5373 | ATOM_TO_JSID(cx->runtime->atomState | OBJECT_TO_JSVAL(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0); |
.constructorAtom), | ||
OBJECT_TO_JSVAL(ctor), | ||
CheckCtorGetAccess, CheckCtorSetAccess, | ||
0, NULL); | ||
5374 | } | } |
5375 | ||
5376 | JSBool | JSBool |
# | Line 5423 | Line 5383 |
5383 | JS_STATIC_ASSERT(JSVAL_INT == 1); | JS_STATIC_ASSERT(JSVAL_INT == 1); |
5384 | JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); | JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); |
5385 | JS_STATIC_ASSERT(JSVAL_STRING == 4); | JS_STATIC_ASSERT(JSVAL_STRING == 4); |
5386 | JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6); | JS_STATIC_ASSERT(JSVAL_SPECIAL == 6); |
5387 | static JSClass *const PrimitiveClasses[] = { | static JSClass *const PrimitiveClasses[] = { |
5388 | &js_NumberClass, /* INT */ | &js_NumberClass, /* INT */ |
5389 | &js_NumberClass, /* DOUBLE */ | &js_NumberClass, /* DOUBLE */ |
# | Line 5437 | Line 5397 |
5397 | JS_ASSERT(!JSVAL_IS_OBJECT(*vp)); | JS_ASSERT(!JSVAL_IS_OBJECT(*vp)); |
5398 | JS_ASSERT(!JSVAL_IS_VOID(*vp)); | JS_ASSERT(!JSVAL_IS_VOID(*vp)); |
5399 | clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1]; | clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1]; |
5400 | obj = js_NewObject(cx, clasp, NULL, NULL, 0); | obj = js_NewObject(cx, clasp, NULL, NULL); |
5401 | if (!obj) | if (!obj) |
5402 | return JS_FALSE; | return JS_FALSE; |
5403 | STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, *vp); | obj->fslots[JSSLOT_PRIMITIVE_THIS] = *vp; |
5404 | *vp = OBJECT_TO_JSVAL(obj); | *vp = OBJECT_TO_JSVAL(obj); |
5405 | return JS_TRUE; | return JS_TRUE; |
5406 | } | } |
# | Line 5454 | Line 5414 |
5414 | obj = NULL; | obj = NULL; |
5415 | } else if (JSVAL_IS_OBJECT(v)) { | } else if (JSVAL_IS_OBJECT(v)) { |
5416 | obj = JSVAL_TO_OBJECT(v); | obj = JSVAL_TO_OBJECT(v); |
5417 | if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) | if (!obj->defaultValue(cx, JSTYPE_OBJECT, &v)) |
5418 | return JS_FALSE; | return JS_FALSE; |
5419 | if (!JSVAL_IS_PRIMITIVE(v)) | if (!JSVAL_IS_PRIMITIVE(v)) |
5420 | obj = JSVAL_TO_OBJECT(v); | obj = JSVAL_TO_OBJECT(v); |
# | Line 5643 | Line 5603 |
5603 | void | void |
5604 | js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) | js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) |
5605 | { | { |
JSObject *obj; | ||
uint32 slot; | ||
JSScope *scope; | ||
jsval nval; | ||
JSScopeProperty *sprop; | ||
JSClass *clasp; | ||
uint32 key; | ||
const char *slotname; | ||
5606 | JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName); | JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName); |
obj = (JSObject *)trc->debugPrintArg; | ||
slot = (uint32)trc->debugPrintIndex; | ||
5607 | ||
5608 | JSObject *obj = (JSObject *)trc->debugPrintArg; | |
5609 | uint32 slot = (uint32)trc->debugPrintIndex; | |
5610 | JS_ASSERT(slot >= JSSLOT_START(obj->getClass())); | |
5611 | ||
5612 | JSScopeProperty *sprop; | |
5613 | if (OBJ_IS_NATIVE(obj)) { | if (OBJ_IS_NATIVE(obj)) { |
5614 | scope = OBJ_SCOPE(obj); | JSScope *scope = OBJ_SCOPE(obj); |
5615 | sprop = SCOPE_LAST_PROP(scope); | sprop = SCOPE_LAST_PROP(scope); |
5616 | while (sprop && sprop->slot != slot) | while (sprop && sprop->slot != slot) |
5617 | sprop = sprop->parent; | sprop = sprop->parent; |
# | Line 5666 | Line 5620 |
5620 | } | } |
5621 | ||
5622 | if (!sprop) { | if (!sprop) { |
5623 | switch (slot) { | const char *slotname = NULL; |
5624 | case JSSLOT_PROTO: | JSClass *clasp = obj->getClass(); |
5625 | JS_snprintf(buf, bufsize, "__proto__"); | if (clasp->flags & JSCLASS_IS_GLOBAL) { |
5626 | break; | uint32 key = slot - JSSLOT_START(clasp); |
5627 | case JSSLOT_PARENT: | #define JS_PROTO(name,code,init) \ |
JS_snprintf(buf, bufsize, "__parent__"); | ||
break; | ||
default: | ||
slotname = NULL; | ||
clasp = LOCKED_OBJ_GET_CLASS(obj); | ||
if (clasp->flags & JSCLASS_IS_GLOBAL) { | ||
key = slot - JSSLOT_START(clasp); | ||
#define JS_PROTO(name,code,init) \ | ||
5628 | if ((code) == key) { slotname = js_##name##_str; goto found; } | if ((code) == key) { slotname = js_##name##_str; goto found; } |
5629 | #include "jsproto.tbl" | #include "jsproto.tbl" |
5630 | #undef JS_PROTO | #undef JS_PROTO |
} | ||
found: | ||
if (slotname) | ||
JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); | ||
else | ||
JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); | ||
break; | ||
5631 | } | } |
5632 | found: | |
5633 | if (slotname) | |
5634 | JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); | |
5635 | else | |
5636 | JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); | |
5637 | } else { | } else { |
5638 | nval = ID_TO_VALUE(sprop->id); | jsval nval = ID_TO_VALUE(sprop->id); |
5639 | if (JSVAL_IS_INT(nval)) { | if (JSVAL_IS_INT(nval)) { |
5640 | JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); | JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); |
5641 | } else if (JSVAL_IS_STRING(nval)) { | } else if (JSVAL_IS_STRING(nval)) { |
# | Line 5706 | Line 5650 |
5650 | void | void |
5651 | js_TraceObject(JSTracer *trc, JSObject *obj) | js_TraceObject(JSTracer *trc, JSObject *obj) |
5652 | { | { |
JSContext *cx; | ||
JSScope *scope; | ||
JSBool traceScope; | ||
JSScopeProperty *sprop; | ||
JSClass *clasp; | ||
size_t nslots, i; | ||
jsval v; | ||
5653 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
cx = trc->context; | ||
scope = OBJ_SCOPE(obj); | ||
traceScope = (scope->object == obj); | ||
if (!traceScope) { | ||
JSObject *pobj = obj; | ||
5654 | ||
5655 | JSContext *cx = trc->context; | |
5656 | JSScope *scope = OBJ_SCOPE(obj); | |
5657 | if (scope->owned() && IS_GC_MARKING_TRACER(trc)) { | |
5658 | /* | /* |
5659 | * Because obj does not own scope, we should be able to assert that an | * Check whether we should shrink the object's slots. Skip this check |
5660 | * object on obj's prototype chain does -- or scope's properties might | * if the scope is shared, since for Block objects and flat closures |
5661 | * go untraced. It indeed turns out that you can disconnect an object | * that share their scope, scope->freeslot can be an underestimate. |
* from the prototype object whose scope it shares, so we may have to | ||
* mark scope even though scope->object != obj. | ||
5662 | */ | */ |
5663 | while ((pobj = LOCKED_OBJ_GET_PROTO(pobj)) != NULL) { | size_t slots = scope->freeslot; |
5664 | if (pobj == scope->object) | if (STOBJ_NSLOTS(obj) != slots) |
5665 | break; | js_ShrinkSlots(cx, obj, slots); |
} | ||
JS_ASSERT_IF(pobj, OBJ_SCOPE(pobj) == scope); | ||
traceScope = !pobj; | ||
5666 | } | } |
5667 | ||
if (traceScope) { | ||
5668 | #ifdef JS_DUMP_SCOPE_METERS | #ifdef JS_DUMP_SCOPE_METERS |
5669 | MeterEntryCount(scope->entryCount); | MeterEntryCount(scope->entryCount); |
5670 | #endif | #endif |
5671 | ||
5672 | sprop = SCOPE_LAST_PROP(scope); | scope->trace(trc); |
if (sprop) { | ||
JS_ASSERT(SCOPE_HAS_PROPERTY(scope, sprop)); | ||
/* Regenerate property cache shape ids if GC'ing. */ | ||
if (IS_GC_MARKING_TRACER(trc)) { | ||
uint32 shape, oldshape; | ||
shape = js_RegenerateShapeForGC(cx); | ||
if (!(sprop->flags & SPROP_MARK)) { | ||
oldshape = sprop->shape; | ||
sprop->shape = shape; | ||
sprop->flags |= SPROP_FLAG_SHAPE_REGEN; | ||
if (scope->shape != oldshape) | ||
shape = js_RegenerateShapeForGC(cx); | ||
} | ||
scope->shape = shape; | ||
} | ||
/* Trace scope's property tree ancestor line. */ | ||
do { | ||
if (SCOPE_HAD_MIDDLE_DELETE(scope) && | ||
!SCOPE_HAS_PROPERTY(scope, sprop)) { | ||
continue; | ||
} | ||
TRACE_SCOPE_PROPERTY(trc, sprop); | ||
} while ((sprop = sprop->parent) != NULL); | ||
} | ||
} | ||
5673 | ||
5674 | if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList)) | if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList)) |
5675 | js_TraceWatchPoints(trc, obj); | js_TraceWatchPoints(trc, obj); |
5676 | ||
5677 | /* No one runs while the GC is running, so we can use LOCKED_... here. */ | /* No one runs while the GC is running, so we can use LOCKED_... here. */ |
5678 | clasp = LOCKED_OBJ_GET_CLASS(obj); | JSClass *clasp = obj->getClass(); |
5679 | if (clasp->mark) { | if (clasp->mark) { |
5680 | if (clasp->flags & JSCLASS_MARK_IS_TRACE) | if (clasp->flags & JSCLASS_MARK_IS_TRACE) |
5681 | ((JSTraceOp) clasp->mark)(trc, obj); | ((JSTraceOp) clasp->mark)(trc, obj); |
# | Line 5785 | Line 5683 |
5683 | (void) clasp->mark(cx, obj, trc); | (void) clasp->mark(cx, obj, trc); |
5684 | } | } |
5685 | ||
5686 | obj->traceProtoAndParent(trc); | |
5687 | ||
5688 | /* | /* |
5689 | * An unmutated object that shares a prototype object's scope. We can't | * An unmutated object that shares a prototype object's scope. We can't |
5690 | * tell how many slots are in use in obj by looking at its scope, so we | * tell how many slots are in use in obj by looking at its scope, so we |
# | Line 5794 | Line 5694 |
5694 | * don't move it up and unify it with the |if (!traceScope)| section | * don't move it up and unify it with the |if (!traceScope)| section |
5695 | * above. | * above. |
5696 | */ | */ |
5697 | nslots = STOBJ_NSLOTS(obj); | uint32 nslots = STOBJ_NSLOTS(obj); |
5698 | if (scope->object == obj && scope->freeslot < nslots) | if (scope->owned() && scope->freeslot < nslots) |
5699 | nslots = scope->freeslot; | nslots = scope->freeslot; |
5700 | JS_ASSERT(nslots >= JSSLOT_START(clasp)); | |
5701 | ||
5702 | for (i = 0; i != nslots; ++i) { | for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) { |
5703 | v = STOBJ_GET_SLOT(obj, i); | jsval v = STOBJ_GET_SLOT(obj, i); |
5704 | if (JSVAL_IS_TRACEABLE(v)) { | if (JSVAL_IS_TRACEABLE(v)) { |
5705 | JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); | JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i); |
5706 | JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); | JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); |
# | Line 5815 | Line 5716 |
5716 | ||
5717 | /* | /* |
5718 | * Clear our scope and the property cache of all obj's properties only if | * Clear our scope and the property cache of all obj's properties only if |
5719 | * obj owns the scope (i.e., not if obj is unmutated and therefore sharing | * obj owns the scope (i.e., not if obj is sharing another object's scope). |
5720 | * its prototype's scope). NB: we do not clear any reserved slots lying | * NB: we do not clear any reserved slots lying below JSSLOT_FREE(clasp). |
* below JSSLOT_FREE(clasp). | ||
5721 | */ | */ |
5722 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
5723 | scope = OBJ_SCOPE(obj); | scope = OBJ_SCOPE(obj); |
5724 | if (scope->object == obj) { | if (scope->owned()) { |
5725 | /* Now that we're done using scope->lastProp/table, clear scope. */ | /* Now that we're done using scope->lastProp/table, clear scope. */ |
5726 | js_ClearScope(cx, scope); | scope->clear(cx); |
5727 | ||
5728 | /* Clear slot values and reset freeslot so we're consistent. */ | /* Clear slot values and reset freeslot so we're consistent. */ |
5729 | i = STOBJ_NSLOTS(obj); | i = STOBJ_NSLOTS(obj); |
5730 | n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); | n = JSSLOT_FREE(obj->getClass()); |
5731 | while (--i >= n) | while (--i >= n) |
5732 | STOBJ_SET_SLOT(obj, i, JSVAL_VOID); | STOBJ_SET_SLOT(obj, i, JSVAL_VOID); |
5733 | scope->freeslot = n; | scope->freeslot = n; |
# | Line 5835 | Line 5735 |
5735 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
5736 | } | } |
5737 | ||
5738 | jsval | /* On failure the function unlocks the object. */ |
5739 | js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) | static bool |
5740 | ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, | |
5741 | uint32 index, uint32 limit) | |
5742 | { | { |
5743 | jsval v; | JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); |
5744 | ||
5745 | /* Check the computed, possibly per-instance, upper bound. */ | |
5746 | if (clasp->reserveSlots) | |
5747 | limit += clasp->reserveSlots(cx, obj); | |
5748 | if (index >= limit) { | |
5749 | JS_UNLOCK_OBJ(cx, obj); | |
5750 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | |
5751 | JSMSG_RESERVED_SLOT_RANGE); | |
5752 | return false; | |
5753 | } | |
5754 | return true; | |
5755 | } | |
5756 | ||
5757 | bool | |
5758 | js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) | |
5759 | { | |
5760 | if (!OBJ_IS_NATIVE(obj)) { | |
5761 | *vp = JSVAL_VOID; | |
5762 | return true; | |
5763 | } | |
5764 | ||
5765 | JSClass *clasp = obj->getClass(); | |
5766 | uint32 limit = JSCLASS_RESERVED_SLOTS(clasp); | |
5767 | ||
5768 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
5769 | v = (slot < STOBJ_NSLOTS(obj)) ? STOBJ_GET_SLOT(obj, slot) : JSVAL_VOID; | if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) |
5770 | return false; | |
5771 | ||
5772 | uint32 slot = JSSLOT_START(clasp) + index; | |
5773 | *vp = (slot < STOBJ_NSLOTS(obj)) ? STOBJ_GET_SLOT(obj, slot) : JSVAL_VOID; | |
5774 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
5775 | return v; | return true; |
5776 | } | } |
5777 | ||
5778 | JSBool | bool |
5779 | js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) | js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) |
5780 | { | { |
5781 | JSScope *scope; | if (!OBJ_IS_NATIVE(obj)) |
5782 | uint32 nslots; | return true; |
5783 | JSClass *clasp; | |
5784 | JSClass *clasp = OBJ_GET_CLASS(cx, obj); | |
5785 | uint32 limit = JSCLASS_RESERVED_SLOTS(clasp); | |
5786 | ||
5787 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
5788 | scope = OBJ_SCOPE(obj); | if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) |
5789 | return false; | |
5790 | ||
5791 | uint32 slot = JSSLOT_START(clasp) + index; | |
5792 | if (slot >= JS_INITIAL_NSLOTS && !obj->dslots) { | if (slot >= JS_INITIAL_NSLOTS && !obj->dslots) { |
5793 | /* | /* |
5794 | * At this point, obj may or may not own scope. If some path calls | * At this point, obj may or may not own scope, and we may or may not |
5795 | * js_GetMutableScope but does not add a slot-owning property, then | * need to allocate dslots. If scope is shared, scope->freeslot may not |
5796 | * scope->object == obj but obj->dslots will be null. If obj shares a | * be accurate for obj (see comment below). |
* prototype's scope, then we cannot update scope->map here. Instead | ||
* we rely on STOBJ_NSLOTS(obj) to get the number of available slots | ||
* in obj after we allocate dynamic slots. | ||
* | ||
* See js_TraceObject, before the slot tracing, where we make a special | ||
* case for unmutated (scope->object != obj) objects. | ||
5797 | */ | */ |
5798 | clasp = LOCKED_OBJ_GET_CLASS(obj); | uint32 nslots = JSSLOT_FREE(clasp); |
nslots = JSSLOT_FREE(clasp); | ||
5799 | if (clasp->reserveSlots) | if (clasp->reserveSlots) |
5800 | nslots += clasp->reserveSlots(cx, obj); | nslots += clasp->reserveSlots(cx, obj); |
5801 | JS_ASSERT(slot < nslots); | JS_ASSERT(slot < nslots); |
5802 | if (!js_ReallocSlots(cx, obj, nslots, JS_TRUE)) { | if (!AllocSlots(cx, obj, nslots)) { |
5803 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_OBJ(cx, obj); |
5804 | return JS_FALSE; | return false; |
5805 | } | } |
5806 | } | } |
5807 | ||
5808 | /* Whether or not we grew nslots, we may need to advance freeslot. */ | /* |
5809 | if (scope->object == obj && slot >= scope->freeslot) | * Whether or not we grew nslots, we may need to advance freeslot. |
5810 | * | |
5811 | * If scope is shared, do not modify scope->freeslot. It is OK for freeslot | |
5812 | * to be an underestimate in objects with shared scopes, as they will get | |
5813 | * their own scopes before mutating, and elsewhere (e.g. js_TraceObject) we | |
5814 | * use STOBJ_NSLOTS(obj) rather than rely on freeslot. | |
5815 | */ | |
5816 | JSScope *scope = OBJ_SCOPE(obj); | |
5817 | if (scope->owned() && slot >= scope->freeslot) | |
5818 | scope->freeslot = slot + 1; | scope->freeslot = slot + 1; |
5819 | ||
5820 | STOBJ_SET_SLOT(obj, slot, v); | STOBJ_SET_SLOT(obj, slot, v); |
5821 | GC_POKE(cx, JS_NULL); | GC_POKE(cx, JS_NULL); |
5822 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
5823 | return JS_TRUE; | return true; |
5824 | } | } |
5825 | ||
5826 | JSObject * | JSObject * |
# | Line 5926 | Line 5861 |
5861 | JSMSG_GETTER_ONLY, NULL); | JSMSG_GETTER_ONLY, NULL); |
5862 | } | } |
5863 | ||
5864 | JS_FRIEND_API(JSBool) | JS_FRIEND_API(JSBool) |
5865 | js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
5866 | { | { |
# | Line 5978 | Line 5912 |
5912 | void |