/[jscoverage]/trunk/js/jsobj.cpp
ViewVC logotype

Diff of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 460 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  void
5913  dumpString(JSString *str)  dumpString(JSString *str)
5914  {  {
5915      dumpChars(JSSTRING_CHARS(str), JSSTRING_LENGTH(str));      dumpChars(str->chars(), str->length());
5916  }  }
5917    
5918  JS_FRIEND_API(void)  JS_FRIEND_API(void)
5919  js_DumpString(JSString *str)  js_DumpString(JSString *str)
5920  {  {
5921      fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",      fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
5922              (void *) str, (void *) JSSTRING_CHARS(str));              (void *) str, (void *) str->chars());
5923      dumpString(str);      dumpString(str);
5924      fputc('\n', stderr);      fputc('\n', stderr);
5925  }  }
# Line 6007  Line 5941 
5941      } else if (JSVAL_IS_OBJECT(val) &&      } else if (JSVAL_IS_OBJECT(val) &&