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

Diff of /trunk/js/jsscope.cpp

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

revision 399 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 58  Line 58 
58  #include "jsnum.h"  #include "jsnum.h"
59  #include "jsscope.h"  #include "jsscope.h"
60  #include "jsstr.h"  #include "jsstr.h"
61    #include "jsarray.h"
62    
63  JSScope *  JSScope *
64  js_GetMutableScope(JSContext *cx, JSObject *obj)  js_GetMutableScope(JSContext *cx, JSObject *obj)
# Line 70  Line 71 
71      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
72      if (scope->object == obj)      if (scope->object == obj)
73          return scope;          return scope;
74      newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),  
75                             obj);      /*
76         * Compile-time block objects each have their own scope, created at
77         * birth, and runtime clone of a block objects are never mutated.
78         */
79        JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass);
80        newscope = js_NewScope(cx, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj);
81      if (!newscope)      if (!newscope)
82          return NULL;          return NULL;
83      JS_LOCK_SCOPE(cx, newscope);      JS_LOCK_SCOPE(cx, newscope);
84      obj->map = js_HoldObjectMap(cx, &newscope->map);      obj->map = &newscope->map;
85      JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));  
86        JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
87      clasp = STOBJ_GET_CLASS(obj);      clasp = STOBJ_GET_CLASS(obj);
88      if (clasp->reserveSlots) {      if (clasp->reserveSlots) {
89          freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj);          freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj);
90          if (freeslot > STOBJ_NSLOTS(obj))          if (freeslot > STOBJ_NSLOTS(obj))
91              freeslot = STOBJ_NSLOTS(obj);              freeslot = STOBJ_NSLOTS(obj);
92          if (newscope->map.freeslot < freeslot)          if (newscope->freeslot < freeslot)
93              newscope->map.freeslot = freeslot;              newscope->freeslot = freeslot;
94      }      }
     scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj);  
95      JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);      JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
96        js_DropScope(cx, scope, obj);
97      return newscope;      return newscope;
98  }  }
99    
# Line 101  Line 108 
108  #define SCOPE_TABLE_NBYTES(n)   ((n) * sizeof(JSScopeProperty *))  #define SCOPE_TABLE_NBYTES(n)   ((n) * sizeof(JSScopeProperty *))
109    
110  static void  static void
111  InitMinimalScope(JSScope *scope)  InitMinimalScope(JSContext *cx, JSScope *scope)
112  {  {
113        js_LeaveTraceIfGlobalObject(cx, scope->object);
114      scope->shape = 0;      scope->shape = 0;
115      scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;      scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
116      scope->entryCount = scope->removedCount = 0;      scope->entryCount = scope->removedCount = 0;
# Line 152  Line 160 
160  }  }
161    
162  JSScope *  JSScope *
163  js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,  js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj)
             JSObject *obj)  
164  {  {
165      JSScope *scope;      JS_ASSERT(OPS_IS_NATIVE(ops));
166        JS_ASSERT(obj);
167    
168      scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));      JSScope *scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));
169      if (!scope)      if (!scope)
170          return NULL;          return NULL;
171    
172      js_InitObjectMap(&scope->map, nrefs, ops, clasp);      scope->map.ops = ops;
173      scope->object = obj;      scope->object = obj;
174        scope->nrefs = 1;
175        scope->freeslot = JSSLOT_FREE(clasp);
176      scope->flags = 0;      scope->flags = 0;
177      InitMinimalScope(scope);      InitMinimalScope(cx, scope);
178    
179  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
180      js_InitTitle(cx, &scope->title);      js_InitTitle(cx, &scope->title);
# Line 174  Line 184 
184      return scope;      return scope;
185  }  }
186    
 #ifdef DEBUG_SCOPE_COUNT  
 extern void  
 js_unlog_scope(JSScope *scope);  
 #endif  
   
187  #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS  #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
188  # include "jsprf.h"  # include "jsprf.h"
189  # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)  # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
# Line 189  Line 194 
194  void  void
195  js_DestroyScope(JSContext *cx, JSScope *scope)  js_DestroyScope(JSContext *cx, JSScope *scope)
196  {  {
 #ifdef DEBUG_SCOPE_COUNT  
     js_unlog_scope(scope);  
 #endif  
   
197  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
198      js_FinishTitle(cx, &scope->title);      js_FinishTitle(cx, &scope->title);
199  #endif  #endif
# Line 204  Line 205 
205      JS_free(cx, scope);      JS_free(cx, scope);
206  }  }
207    
208    void
209    js_HoldScope(JSScope *scope)
210    {
211        JS_ASSERT(scope->nrefs >= 0);
212        JS_ATOMIC_INCREMENT(&scope->nrefs);
213    }
214    
215    JSBool
216    js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj)
217    {
218        JS_ASSERT(scope->nrefs > 0);
219        JS_ATOMIC_DECREMENT(&scope->nrefs);
220    
221        if (scope->nrefs == 0) {
222            js_DestroyScope(cx, scope);
223            return false;
224        }
225        if (scope->object == obj)
226            scope->object = NULL;
227        return true;
228    }
229    
230  #ifdef JS_DUMP_PROPTREE_STATS  #ifdef JS_DUMP_PROPTREE_STATS
231  typedef struct JSScopeStats {  typedef struct JSScopeStats {
232      jsrefcount          searches;      jsrefcount          searches;
# Line 796  Line 819 
819   * only when inserting a new child.  Thus there may be races to find or add a   * only when inserting a new child.  Thus there may be races to find or add a
820   * node that result in duplicates.  We expect such races to be rare!   * node that result in duplicates.  We expect such races to be rare!
821   *   *
822   * We use rt->gcLock, not rt->rtLock, to allow the GC potentially to nest here   * We use rt->gcLock, not rt->rtLock, to avoid nesting the former inside the
823   * under js_GenerateShape.   * latter in js_GenerateShape below.
824   */   */
825  static JSScopeProperty *  static JSScopeProperty *
826  GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,  GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
# Line 809  Line 832 
832      JSScopeProperty *sprop;      JSScopeProperty *sprop;
833      PropTreeKidsChunk *chunk;      PropTreeKidsChunk *chunk;
834      uintN i, n;      uintN i, n;
     uint32 shape;  
835    
836      rt = cx->runtime;      rt = cx->runtime;
837      if (!parent) {      if (!parent) {
# Line 896  Line 918 
918      }      }
919    
920  locked_not_found:  locked_not_found:
     /*  
      * Call js_GenerateShape before the allocation to prevent collecting the  
      * new property when the shape generation triggers the GC.  
      */  
     shape = js_GenerateShape(cx, JS_TRUE, NULL);  
   
921      sprop = NewScopeProperty(rt);      sprop = NewScopeProperty(rt);
922      if (!sprop)      if (!sprop)
923          goto out_of_memory;          goto out_of_memory;
# Line 914  Line 930 
930      sprop->flags = child->flags;      sprop->flags = child->flags;
931      sprop->shortid = child->shortid;      sprop->shortid = child->shortid;
932      sprop->parent = sprop->kids = NULL;      sprop->parent = sprop->kids = NULL;
933      sprop->shape = shape;      sprop->shape = js_GenerateShape(cx, JS_TRUE);
934    
935      if (!parent) {      if (!parent) {
936          entry->child = sprop;          entry->child = sprop;
# Line 1036  Line 1052 
1052      spp = js_SearchScope(scope, id, JS_TRUE);      spp = js_SearchScope(scope, id, JS_TRUE);
1053      sprop = overwriting = SPROP_FETCH(spp);      sprop = overwriting = SPROP_FETCH(spp);
1054      if (!sprop) {      if (!sprop) {
         JS_COUNT_OPERATION(cx, JSOW_NEW_PROPERTY);  
   
1055          /* Check whether we need to grow, if the load factor is >= .75. */          /* Check whether we need to grow, if the load factor is >= .75. */
1056          size = SCOPE_CAPACITY(scope);          size = SCOPE_CAPACITY(scope);
1057          if (scope->entryCount + scope->removedCount >= size - (size >> 2)) {          if (scope->entryCount + scope->removedCount >= size - (size >> 2)) {
# Line 1078  Line 1092 
1092          }          }
1093    
1094          /*          /*
1095           * If we are clearing sprop to force an existing property to be           * If we are clearing sprop to force the existing property that it
1096           * overwritten (apart from a duplicate formal parameter), we must           * describes to be overwritten, then we have to unlink sprop from the
1097           * unlink it from the ancestor line at scope->lastProp, lazily if           * ancestor line at scope->lastProp, lazily if sprop is not lastProp.
1098           * sprop is not lastProp.  And we must remove the entry at *spp,           * And we must remove the entry at *spp, precisely so the lazy "middle
1099           * precisely so the lazy "middle delete" fixup code further below           * delete" fixup code further below won't find sprop in scope->table,
1100           * won't find sprop in scope->table, in spite of sprop being on           * in spite of sprop being on the ancestor line.
          * the ancestor line.  
1101           *           *
1102           * When we finally succeed in finding or creating a new sprop           * When we finally succeed in finding or creating a new sprop
1103           * and storing its pointer at *spp, we'll use the |overwriting|           * and storing its pointer at *spp, we'll use the |overwriting|
# Line 1112  Line 1125 
1125              }              }
1126              SCOPE_SET_MIDDLE_DELETE(scope);              SCOPE_SET_MIDDLE_DELETE(scope);
1127          }          }
1128          SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);          js_MakeScopeShapeUnique(cx, scope);
1129    
1130          /*          /*
1131           * If we fail later on trying to find or create a new sprop, we will           * If we fail later on trying to find or create a new sprop, we will
# Line 1140  Line 1153 
1153              JS_ASSERT(scope->table);              JS_ASSERT(scope->table);
1154              CHECK_ANCESTOR_LINE(scope, JS_TRUE);              CHECK_ANCESTOR_LINE(scope, JS_TRUE);
1155    
1156              splen = scope->entryCount;              /*
1157              if (splen == 0) {               * Our forking heuristic tries to balance the desire to avoid
1158                  JS_ASSERT(scope->lastProp == NULL);               * over-compacting (over-forking) against the desire to
1159              } else {               * *periodically* fork anyways, in order to prevent paying scan
1160                 * penalties on each insert indefinitely, on a lineage with only
1161                 * a few old middle-deletions. So we fork if either:
1162                 *
1163                 *  - A quick scan finds a true conflict.
1164                 *  - We are passing through a doubling-threshold in size and
1165                 *    have accumulated a nonzero count of uncompacted deletions.
1166                 */
1167    
1168                bool conflicts = false;
1169                uint32 count = 0;
1170                uint32 threshold = JS_BIT(JS_CeilingLog2(scope->entryCount));
1171                for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1172                    ++count;
1173                    if (sprop->id == id) {
1174                        conflicts = true;
1175                        break;
1176                    }
1177                }
1178    
1179                if (conflicts || count > threshold) {
1180                  /*                  /*
1181                   * Enumerate live entries in scope->table using a temporary                   * Enumerate live entries in scope->table using a temporary
1182                   * vector, by walking the (possibly sparse, due to deletions)                   * vector, by walking the (possibly sparse, due to deletions)
1183                   * ancestor line from scope->lastProp.                   * ancestor line from scope->lastProp.
1184                   */                   */
1185                    splen = scope->entryCount;
1186                    JS_ASSERT(splen != 0);
1187                  spvec = (JSScopeProperty **)                  spvec = (JSScopeProperty **)
1188                          JS_malloc(cx, SCOPE_TABLE_NBYTES(splen));                          JS_malloc(cx, SCOPE_TABLE_NBYTES(splen));
1189                  if (!spvec)                  if (!spvec)
# Line 1158  Line 1193 
1193                  JS_ASSERT(sprop);                  JS_ASSERT(sprop);
1194                  do {                  do {
1195                      /*                      /*
1196                       * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY --                       * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY,
1197                       * the latter insists that sprop->id maps to sprop, while                       * as the latter macro insists that sprop->id maps to
1198                       * the former simply tests whether sprop->id is bound in                       * sprop, while the former simply tests whether sprop->id
1199                       * scope.  We must allow for duplicate formal parameters                       * is bound in scope.
                      * along the ancestor line, and fork them as needed.  
1200                       */                       */
1201                      if (!SCOPE_GET_PROPERTY(scope, sprop->id))                      if (!SCOPE_GET_PROPERTY(scope, sprop->id))
1202                          continue;                          continue;
1203    
1204                      JS_ASSERT(sprop != overwriting);                      JS_ASSERT(sprop != overwriting);
1205                      if (i == 0) {                      JS_ASSERT(i != 0);
                         /*  
                          * If our original splen estimate, scope->entryCount,  
                          * is less than the ancestor line height, there must  
                          * be duplicate formal parameters in this (function  
                          * object) scope.  Count remaining ancestors in order  
                          * to realloc spvec.  
                          */  
                         JSScopeProperty *tmp = sprop;  
                         do {  
                             if (SCOPE_GET_PROPERTY(scope, tmp->id))  
                                 i++;  
                         } while ((tmp = tmp->parent) != NULL);  
                         spp2 = (JSScopeProperty **)  
                              JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i));  
                         if (!spp2) {  
                             JS_free(cx, spvec);  
                             goto fail_overwrite;  
                         }  
   
                         spvec = spp2;  
                         memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen));  
                         splen += i;  
                     }  
   
1206                      spvec[--i] = sprop;                      spvec[--i] = sprop;
1207                  } while ((sprop = sprop->parent) != NULL);                  } while ((sprop = sprop->parent) != NULL);
1208                  JS_ASSERT(i == 0);                  JS_ASSERT(i == 0);
# Line 1222  Line 1232 
1232                  /*                  /*
1233                   * Now sprop points to the last property in scope, where the                   * Now sprop points to the last property in scope, where the
1234                   * ancestor line from sprop to the root is dense w.r.t. scope:                   * ancestor line from sprop to the root is dense w.r.t. scope:
1235                   * it contains no nodes not mapped by scope->table, apart from                   * it contains no nodes not mapped by scope->table.
                  * any stinking ECMA-mandated duplicate formal parameters.  
1236                   */                   */
1237                  scope->lastProp = sprop;                  scope->lastProp = sprop;
1238                  CHECK_ANCESTOR_LINE(scope, JS_FALSE);                  CHECK_ANCESTOR_LINE(scope, JS_FALSE);
1239                  JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);                  JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);
1240                    SCOPE_CLR_MIDDLE_DELETE(scope);
1241              }              }
   
             SCOPE_CLR_MIDDLE_DELETE(scope);  
1242          }          }
1243    
1244          /*          /*
# Line 1263  Line 1271 
1271           */           */
1272          if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&          if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&
1273              js_FindWatchPoint(cx->runtime, scope, id)) {              js_FindWatchPoint(cx->runtime, scope, id)) {
1274              JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr);              if (overwriting)
1275                    JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr);
1276              setter = js_WrapWatchedSetter(cx, id, attrs, setter);              setter = js_WrapWatchedSetter(cx, id, attrs, setter);
1277              JS_POP_TEMP_ROOT(cx, &tvr);              if (overwriting)
1278                    JS_POP_TEMP_ROOT(cx, &tvr);
1279              if (!setter)              if (!setter)
1280                  goto fail_overwrite;                  goto fail_overwrite;
1281          }          }
# Line 1287  Line 1297 
1297           * be regenerated later as the scope diverges (from the property cache           * be regenerated later as the scope diverges (from the property cache
1298           * point of view) from the structural type associated with sprop.           * point of view) from the structural type associated with sprop.
1299           */           */
1300          SCOPE_EXTEND_SHAPE(cx, scope, sprop);          js_ExtendScopeShape(cx, scope, sprop);
1301    
1302          /* Store the tree node pointer in the table entry for id. */          /* Store the tree node pointer in the table entry for id. */
1303          if (scope->table)          if (scope->table)
# Line 1314  Line 1324 
1324              (void) CreateScopeTable(cx, scope, JS_FALSE);              (void) CreateScopeTable(cx, scope, JS_FALSE);
1325      }      }
1326    
1327        jsuint index;
1328        if (js_IdIsIndex(sprop->id, &index))
1329            SCOPE_SET_INDEXED_PROPERTIES(scope);
1330    
1331      METER(adds);      METER(adds);
1332      return sprop;      return sprop;
1333    
# Line 1423  Line 1437 
1437      }      }
1438    
1439      if (newsprop) {      if (newsprop) {
1440            js_LeaveTraceIfGlobalObject(cx, scope->object);
1441          if (scope->shape == sprop->shape)          if (scope->shape == sprop->shape)
1442              scope->shape = newsprop->shape;              scope->shape = newsprop->shape;
1443          else          else
1444              SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);              js_MakeScopeShapeUnique(cx, scope);
1445      }      }
1446  #ifdef JS_DUMP_PROPTREE_STATS  #ifdef JS_DUMP_PROPTREE_STATS
1447      else      else
# Line 1493  Line 1508 
1508                  break;                  break;
1509              sprop = SCOPE_LAST_PROP(scope);              sprop = SCOPE_LAST_PROP(scope);
1510          } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));          } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));
1511            if (!SCOPE_LAST_PROP(scope))
1512                SCOPE_CLR_MIDDLE_DELETE(scope);
1513      } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {      } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {
1514          SCOPE_SET_MIDDLE_DELETE(scope);          SCOPE_SET_MIDDLE_DELETE(scope);
1515      }      }
1516      SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);      js_MakeScopeShapeUnique(cx, scope);
1517      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(scope, JS_TRUE);
1518    
1519      /* Last, consider shrinking scope's table if its load factor is <= .25. */      /* Last, consider shrinking scope's table if its load factor is <= .25. */
# Line 1518  Line 1535 
1535      if (scope->table)      if (scope->table)
1536          free(scope->table);          free(scope->table);
1537      SCOPE_CLR_MIDDLE_DELETE(scope);      SCOPE_CLR_MIDDLE_DELETE(scope);
1538      InitMinimalScope(scope);      InitMinimalScope(cx, scope);
1539      JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);      JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1540  }  }
1541    
# Line 1569  Line 1586 
1586  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
1587      if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {      if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1588          if (sprop->attrs & JSPROP_GETTER) {          if (sprop->attrs & JSPROP_GETTER) {
             JS_ASSERT(JSVAL_IS_OBJECT((jsval) sprop->getter));  
1589              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 0);              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 0);
1590              JS_CallTracer(trc, JSVAL_TO_OBJECT((jsval) sprop->getter),              JS_CallTracer(trc, js_CastAsObject(sprop->getter), JSTRACE_OBJECT);
                           JSTRACE_OBJECT);  
1591          }          }
1592          if (sprop->attrs & JSPROP_SETTER) {          if (sprop->attrs & JSPROP_SETTER) {
             JS_ASSERT(JSVAL_IS_OBJECT((jsval) sprop->setter));  
1593              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 1);              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 1);
1594              JS_CallTracer(trc, JSVAL_TO_OBJECT((jsval) sprop->setter),              JS_CallTracer(trc, js_CastAsObject(sprop->setter), JSTRACE_OBJECT);
                           JSTRACE_OBJECT);  
1595          }          }
1596      }      }
1597  #endif /* JS_HAS_GETTER_SETTER */  #endif /* JS_HAS_GETTER_SETTER */
# Line 1744  Line 1757 
1757               */               */
1758              if (sprop->flags & SPROP_MARK) {              if (sprop->flags & SPROP_MARK) {
1759                  sprop->flags &= ~SPROP_MARK;                  sprop->flags &= ~SPROP_MARK;
1760                  if (sprop->flags & SPROP_FLAG_SHAPE_REGEN) {                  if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
1761                      sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;                      sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
1762                  } else {                  else
1763                      sprop->shape = ++cx->runtime->shapeGen;                      sprop->shape = js_RegenerateShapeForGC(cx);
                     JS_ASSERT(sprop->shape != 0);  
                 }  
1764                  liveCount++;                  liveCount++;
1765                  continue;                  continue;
1766              }              }

Legend:
Removed from v.399  
changed lines
  Added in v.460

  ViewVC Help
Powered by ViewVC 1.1.24