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