JavaScriptCore/runtime/Operations.h
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
       
     3  *  Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
       
     4  *
       
     5  *  This library is free software; you can redistribute it and/or
       
     6  *  modify it under the terms of the GNU Library General Public
       
     7  *  License as published by the Free Software Foundation; either
       
     8  *  version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  *  This library is distributed in the hope that it will be useful,
       
    11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  *  Library General Public License for more details.
       
    14  *
       
    15  *  You should have received a copy of the GNU Library General Public License
       
    16  *  along with this library; see the file COPYING.LIB.  If not, write to
       
    17  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    18  *  Boston, MA 02110-1301, USA.
       
    19  *
       
    20  */
       
    21 
       
    22 #ifndef Operations_h
       
    23 #define Operations_h
       
    24 
       
    25 #include "ExceptionHelpers.h"
       
    26 #include "Interpreter.h"
       
    27 #include "JSImmediate.h"
       
    28 #include "JSNumberCell.h"
       
    29 #include "JSString.h"
       
    30 
       
    31 namespace JSC {
       
    32 
       
    33     NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
       
    34     JSValue jsTypeStringForValue(CallFrame*, JSValue);
       
    35     bool jsIsObjectType(JSValue);
       
    36     bool jsIsFunctionType(JSValue);
       
    37 
       
    38     ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
       
    39     {
       
    40         unsigned length1 = s1->length();
       
    41         if (!length1)
       
    42             return s2;
       
    43         unsigned length2 = s2->length();
       
    44         if (!length2)
       
    45             return s1;
       
    46         if ((length1 + length2) < length1)
       
    47             return throwOutOfMemoryError(exec);
       
    48 
       
    49         unsigned fiberCount = s1->size() + s2->size();
       
    50         JSGlobalData* globalData = &exec->globalData();
       
    51 
       
    52         if (fiberCount <= JSString::s_maxInternalRopeLength)
       
    53             return new (globalData) JSString(globalData, fiberCount, s1, s2);
       
    54 
       
    55         JSString::RopeBuilder ropeBuilder(fiberCount);
       
    56         if (UNLIKELY(ropeBuilder.isOutOfMemory()))
       
    57             return throwOutOfMemoryError(exec);
       
    58         ropeBuilder.append(s1);
       
    59         ropeBuilder.append(s2);
       
    60         return new (globalData) JSString(globalData, ropeBuilder.release());
       
    61     }
       
    62 
       
    63     ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2)
       
    64     {
       
    65         unsigned length1 = u1.size();
       
    66         if (!length1)
       
    67             return s2;
       
    68         unsigned length2 = s2->length();
       
    69         if (!length2)
       
    70             return jsString(exec, u1);
       
    71         if ((length1 + length2) < length1)
       
    72             return throwOutOfMemoryError(exec);
       
    73 
       
    74         unsigned fiberCount = 1 + s2->size();
       
    75         JSGlobalData* globalData = &exec->globalData();
       
    76 
       
    77         if (fiberCount <= JSString::s_maxInternalRopeLength)
       
    78             return new (globalData) JSString(globalData, fiberCount, u1, s2);
       
    79 
       
    80         JSString::RopeBuilder ropeBuilder(fiberCount);
       
    81         if (UNLIKELY(ropeBuilder.isOutOfMemory()))
       
    82             return throwOutOfMemoryError(exec);
       
    83         ropeBuilder.append(u1);
       
    84         ropeBuilder.append(s2);
       
    85         return new (globalData) JSString(globalData, ropeBuilder.release());
       
    86     }
       
    87 
       
    88     ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2)
       
    89     {
       
    90         unsigned length1 = s1->length();
       
    91         if (!length1)
       
    92             return jsString(exec, u2);
       
    93         unsigned length2 = u2.size();
       
    94         if (!length2)
       
    95             return s1;
       
    96         if ((length1 + length2) < length1)
       
    97             return throwOutOfMemoryError(exec);
       
    98 
       
    99         unsigned fiberCount = s1->size() + 1;
       
   100         JSGlobalData* globalData = &exec->globalData();
       
   101 
       
   102         if (fiberCount <= JSString::s_maxInternalRopeLength)
       
   103             return new (globalData) JSString(globalData, fiberCount, s1, u2);
       
   104 
       
   105         JSString::RopeBuilder ropeBuilder(fiberCount);
       
   106         if (UNLIKELY(ropeBuilder.isOutOfMemory()))
       
   107             return throwOutOfMemoryError(exec);
       
   108         ropeBuilder.append(s1);
       
   109         ropeBuilder.append(u2);
       
   110         return new (globalData) JSString(globalData, ropeBuilder.release());
       
   111     }
       
   112 
       
   113     ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2)
       
   114     {
       
   115         unsigned length1 = u1.size();
       
   116         if (!length1)
       
   117             return jsString(exec, u2);
       
   118         unsigned length2 = u2.size();
       
   119         if (!length2)
       
   120             return jsString(exec, u1);
       
   121         if ((length1 + length2) < length1)
       
   122             return throwOutOfMemoryError(exec);
       
   123 
       
   124         JSGlobalData* globalData = &exec->globalData();
       
   125         return new (globalData) JSString(globalData, u1, u2);
       
   126     }
       
   127 
       
   128     ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3)
       
   129     {
       
   130         unsigned length1 = u1.size();
       
   131         unsigned length2 = u2.size();
       
   132         unsigned length3 = u3.size();
       
   133         if (!length1)
       
   134             return jsString(exec, u2, u3);
       
   135         if (!length2)
       
   136             return jsString(exec, u1, u3);
       
   137         if (!length3)
       
   138             return jsString(exec, u1, u2);
       
   139 
       
   140         if ((length1 + length2) < length1)
       
   141             return throwOutOfMemoryError(exec);
       
   142         if ((length1 + length2 + length3) < length3)
       
   143             return throwOutOfMemoryError(exec);
       
   144 
       
   145         JSGlobalData* globalData = &exec->globalData();
       
   146         return new (globalData) JSString(globalData, u1, u2, u3);
       
   147     }
       
   148 
       
   149     ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count)
       
   150     {
       
   151         ASSERT(count >= 3);
       
   152 
       
   153         unsigned fiberCount = 0;
       
   154         for (unsigned i = 0; i < count; ++i) {
       
   155             JSValue v = strings[i].jsValue();
       
   156             if (LIKELY(v.isString()))
       
   157                 fiberCount += asString(v)->size();
       
   158             else
       
   159                 ++fiberCount;
       
   160         }
       
   161 
       
   162         JSGlobalData* globalData = &exec->globalData();
       
   163         if (fiberCount == 3)
       
   164             return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue());
       
   165 
       
   166         JSString::RopeBuilder ropeBuilder(fiberCount);
       
   167         if (UNLIKELY(ropeBuilder.isOutOfMemory()))
       
   168             return throwOutOfMemoryError(exec);
       
   169 
       
   170         unsigned length = 0;
       
   171         bool overflow = false;
       
   172 
       
   173         for (unsigned i = 0; i < count; ++i) {
       
   174             JSValue v = strings[i].jsValue();
       
   175             if (LIKELY(v.isString()))
       
   176                 ropeBuilder.append(asString(v));
       
   177             else
       
   178                 ropeBuilder.append(v.toString(exec));
       
   179 
       
   180             unsigned newLength = ropeBuilder.length();
       
   181             if (newLength < length)
       
   182                 overflow = true;
       
   183             length = newLength;
       
   184         }
       
   185 
       
   186         if (overflow)
       
   187             return throwOutOfMemoryError(exec);
       
   188 
       
   189         return new (globalData) JSString(globalData, ropeBuilder.release());
       
   190     }
       
   191 
       
   192     ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue)
       
   193     {
       
   194         unsigned fiberCount = 0;
       
   195         if (LIKELY(thisValue.isString()))
       
   196             fiberCount += asString(thisValue)->size();
       
   197         else
       
   198             ++fiberCount;
       
   199         for (unsigned i = 0; i < exec->argumentCount(); ++i) {
       
   200             JSValue v = exec->argument(i);
       
   201             if (LIKELY(v.isString()))
       
   202                 fiberCount += asString(v)->size();
       
   203             else
       
   204                 ++fiberCount;
       
   205         }
       
   206 
       
   207         JSString::RopeBuilder ropeBuilder(fiberCount);
       
   208         if (UNLIKELY(ropeBuilder.isOutOfMemory()))
       
   209             return throwOutOfMemoryError(exec);
       
   210 
       
   211         if (LIKELY(thisValue.isString()))
       
   212             ropeBuilder.append(asString(thisValue));
       
   213         else
       
   214             ropeBuilder.append(thisValue.toString(exec));
       
   215 
       
   216         unsigned length = 0;
       
   217         bool overflow = false;
       
   218 
       
   219         for (unsigned i = 0; i < exec->argumentCount(); ++i) {
       
   220             JSValue v = exec->argument(i);
       
   221             if (LIKELY(v.isString()))
       
   222                 ropeBuilder.append(asString(v));
       
   223             else
       
   224                 ropeBuilder.append(v.toString(exec));
       
   225 
       
   226             unsigned newLength = ropeBuilder.length();
       
   227             if (newLength < length)
       
   228                 overflow = true;
       
   229             length = newLength;
       
   230         }
       
   231 
       
   232         if (overflow)
       
   233             return throwOutOfMemoryError(exec);
       
   234 
       
   235         JSGlobalData* globalData = &exec->globalData();
       
   236         return new (globalData) JSString(globalData, ropeBuilder.release());
       
   237     }
       
   238 
       
   239     // ECMA 11.9.3
       
   240     inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2)
       
   241     {
       
   242         if (v1.isInt32() && v2.isInt32())
       
   243             return v1 == v2;
       
   244 
       
   245         return equalSlowCase(exec, v1, v2);
       
   246     }
       
   247 
       
   248     ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
       
   249     {
       
   250         do {
       
   251             if (v1.isNumber() && v2.isNumber())
       
   252                 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
       
   253 
       
   254             bool s1 = v1.isString();
       
   255             bool s2 = v2.isString();
       
   256             if (s1 && s2)
       
   257                 return asString(v1)->value(exec) == asString(v2)->value(exec);
       
   258 
       
   259             if (v1.isUndefinedOrNull()) {
       
   260                 if (v2.isUndefinedOrNull())
       
   261                     return true;
       
   262                 if (!v2.isCell())
       
   263                     return false;
       
   264                 return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined();
       
   265             }
       
   266 
       
   267             if (v2.isUndefinedOrNull()) {
       
   268                 if (!v1.isCell())
       
   269                     return false;
       
   270                 return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined();
       
   271             }
       
   272 
       
   273             if (v1.isObject()) {
       
   274                 if (v2.isObject())
       
   275                     return v1 == v2;
       
   276                 JSValue p1 = v1.toPrimitive(exec);
       
   277                 if (exec->hadException())
       
   278                     return false;
       
   279                 v1 = p1;
       
   280                 if (v1.isInt32() && v2.isInt32())
       
   281                     return v1 == v2;
       
   282                 continue;
       
   283             }
       
   284 
       
   285             if (v2.isObject()) {
       
   286                 JSValue p2 = v2.toPrimitive(exec);
       
   287                 if (exec->hadException())
       
   288                     return false;
       
   289                 v2 = p2;
       
   290                 if (v1.isInt32() && v2.isInt32())
       
   291                     return v1 == v2;
       
   292                 continue;
       
   293             }
       
   294 
       
   295             if (s1 || s2) {
       
   296                 double d1 = v1.toNumber(exec);
       
   297                 double d2 = v2.toNumber(exec);
       
   298                 return d1 == d2;
       
   299             }
       
   300 
       
   301             if (v1.isBoolean()) {
       
   302                 if (v2.isNumber())
       
   303                     return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber();
       
   304             } else if (v2.isBoolean()) {
       
   305                 if (v1.isNumber())
       
   306                     return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean());
       
   307             }
       
   308 
       
   309             return v1 == v2;
       
   310         } while (true);
       
   311     }
       
   312 
       
   313     // ECMA 11.9.3
       
   314     ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2)
       
   315     {
       
   316         ASSERT(v1.isCell() && v2.isCell());
       
   317 
       
   318         if (v1.asCell()->isString() && v2.asCell()->isString())
       
   319             return asString(v1)->value(exec) == asString(v2)->value(exec);
       
   320 
       
   321         return v1 == v2;
       
   322     }
       
   323 
       
   324     inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2)
       
   325     {
       
   326         if (v1.isInt32() && v2.isInt32())
       
   327             return v1 == v2;
       
   328 
       
   329         if (v1.isNumber() && v2.isNumber())
       
   330             return v1.uncheckedGetNumber() == v2.uncheckedGetNumber();
       
   331 
       
   332         if (!v1.isCell() || !v2.isCell())
       
   333             return v1 == v2;
       
   334 
       
   335         return strictEqualSlowCaseInline(exec, v1, v2);
       
   336     }
       
   337 
       
   338     ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2)
       
   339     {
       
   340         if (v1.isInt32() && v2.isInt32())
       
   341             return v1.asInt32() < v2.asInt32();
       
   342 
       
   343         double n1;
       
   344         double n2;
       
   345         if (v1.getNumber(n1) && v2.getNumber(n2))
       
   346             return n1 < n2;
       
   347 
       
   348         JSGlobalData* globalData = &callFrame->globalData();
       
   349         if (isJSString(globalData, v1) && isJSString(globalData, v2))
       
   350             return asString(v1)->value(callFrame) < asString(v2)->value(callFrame);
       
   351 
       
   352         JSValue p1;
       
   353         JSValue p2;
       
   354         bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
       
   355         bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
       
   356 
       
   357         if (wasNotString1 | wasNotString2)
       
   358             return n1 < n2;
       
   359 
       
   360         return asString(p1)->value(callFrame) < asString(p2)->value(callFrame);
       
   361     }
       
   362 
       
   363     inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2)
       
   364     {
       
   365         if (v1.isInt32() && v2.isInt32())
       
   366             return v1.asInt32() <= v2.asInt32();
       
   367 
       
   368         double n1;
       
   369         double n2;
       
   370         if (v1.getNumber(n1) && v2.getNumber(n2))
       
   371             return n1 <= n2;
       
   372 
       
   373         JSGlobalData* globalData = &callFrame->globalData();
       
   374         if (isJSString(globalData, v1) && isJSString(globalData, v2))
       
   375             return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame));
       
   376 
       
   377         JSValue p1;
       
   378         JSValue p2;
       
   379         bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1);
       
   380         bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2);
       
   381 
       
   382         if (wasNotString1 | wasNotString2)
       
   383             return n1 <= n2;
       
   384 
       
   385         return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame));
       
   386     }
       
   387 
       
   388     // Fast-path choices here are based on frequency data from SunSpider:
       
   389     //    <times> Add case: <t1> <t2>
       
   390     //    ---------------------------
       
   391     //    5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
       
   392     //    247412  Add case: 5 5
       
   393     //    20900   Add case: 5 6
       
   394     //    13962   Add case: 5 3
       
   395     //    4000    Add case: 3 5
       
   396 
       
   397     ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2)
       
   398     {
       
   399         double left = 0.0, right;
       
   400         if (v1.getNumber(left) && v2.getNumber(right))
       
   401             return jsNumber(callFrame, left + right);
       
   402         
       
   403         if (v1.isString()) {
       
   404             return v2.isString()
       
   405                 ? jsString(callFrame, asString(v1), asString(v2))
       
   406                 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame));
       
   407         }
       
   408 
       
   409         // All other cases are pretty uncommon
       
   410         return jsAddSlowCase(callFrame, v1, v2);
       
   411     }
       
   412 
       
   413     inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
       
   414     {
       
   415         JSCell* cell = asCell(base);
       
   416         size_t count = 0;
       
   417 
       
   418         while (slotBase != cell) {
       
   419             JSValue v = cell->structure()->prototypeForLookup(callFrame);
       
   420 
       
   421             // If we didn't find slotBase in base's prototype chain, then base
       
   422             // must be a proxy for another object.
       
   423 
       
   424             if (v.isNull())
       
   425                 return 0;
       
   426 
       
   427             cell = asCell(v);
       
   428 
       
   429             // Since we're accessing a prototype in a loop, it's a good bet that it
       
   430             // should not be treated as a dictionary.
       
   431             if (cell->structure()->isDictionary()) {
       
   432                 asObject(cell)->flattenDictionaryObject();
       
   433                 if (slotBase == cell)
       
   434                     slotOffset = cell->structure()->get(propertyName); 
       
   435             }
       
   436 
       
   437             ++count;
       
   438         }
       
   439         
       
   440         ASSERT(count);
       
   441         return count;
       
   442     }
       
   443 
       
   444     inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base)
       
   445     {
       
   446         size_t count = 0;
       
   447         while (1) {
       
   448             JSValue v = base->structure()->prototypeForLookup(callFrame);
       
   449             if (v.isNull())
       
   450                 return count;
       
   451 
       
   452             base = asCell(v);
       
   453 
       
   454             // Since we're accessing a prototype in a loop, it's a good bet that it
       
   455             // should not be treated as a dictionary.
       
   456             if (base->structure()->isDictionary())
       
   457                 asObject(base)->flattenDictionaryObject();
       
   458 
       
   459             ++count;
       
   460         }
       
   461     }
       
   462 
       
   463     ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain)
       
   464     {
       
   465         ScopeChainIterator iter = scopeChain->begin();
       
   466         ScopeChainIterator next = iter;
       
   467         ++next;
       
   468         ScopeChainIterator end = scopeChain->end();
       
   469         ASSERT(iter != end);
       
   470 
       
   471         PropertySlot slot;
       
   472         JSObject* base;
       
   473         while (true) {
       
   474             base = *iter;
       
   475             if (next == end || base->getPropertySlot(callFrame, property, slot))
       
   476                 return base;
       
   477 
       
   478             iter = next;
       
   479             ++next;
       
   480         }
       
   481 
       
   482         ASSERT_NOT_REACHED();
       
   483         return JSValue();
       
   484     }
       
   485 } // namespace JSC
       
   486 
       
   487 #endif // Operations_h