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