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