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 "Interpreter.h" 26 #include "JSImmediate.h" 27 #include "JSNumberCell.h" 28 #include "JSString.h" 29 30 namespace JSC { 31 32 NEVER_INLINE JSValue throwOutOfMemoryError(ExecState*); 33 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue); 34 JSValue jsTypeStringForValue(CallFrame*, JSValue); 35 bool jsIsObjectType(JSValue); 36 bool jsIsFunctionType(JSValue); 37 jsString(ExecState * exec,JSString * s1,JSString * s2)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 jsString(ExecState * exec,const UString & u1,JSString * s2)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 jsString(ExecState * exec,JSString * s1,const UString & u2)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 jsString(ExecState * exec,Register * strings,unsigned count)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 jsString(ExecState * exec,JSValue thisValue,const ArgList & args)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 equal(ExecState * exec,JSValue v1,JSValue v2)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 equalSlowCaseInline(ExecState * exec,JSValue v1,JSValue v2)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 #ifdef QT_BUILD_SCRIPT_LIB 206 || asObject(v1)->compareToObject(exec, asObject(v2)) 207 #endif 208 ; 209 JSValue p1 = v1.toPrimitive(exec); 210 if (exec->hadException()) 211 return false; 212 v1 = p1; 213 if (v1.isInt32() && v2.isInt32()) 214 return v1 == v2; 215 continue; 216 } 217 218 if (v2.isObject()) { 219 JSValue p2 = v2.toPrimitive(exec); 220 if (exec->hadException()) 221 return false; 222 v2 = p2; 223 if (v1.isInt32() && v2.isInt32()) 224 return v1 == v2; 225 continue; 226 } 227 228 if (s1 || s2) { 229 double d1 = v1.toNumber(exec); 230 double d2 = v2.toNumber(exec); 231 return d1 == d2; 232 } 233 234 if (v1.isBoolean()) { 235 if (v2.isNumber()) 236 return static_cast<double>(v1.getBoolean()) == v2.uncheckedGetNumber(); 237 } else if (v2.isBoolean()) { 238 if (v1.isNumber()) 239 return v1.uncheckedGetNumber() == static_cast<double>(v2.getBoolean()); 240 } 241 242 return v1 == v2; 243 } while (true); 244 } 245 246 // ECMA 11.9.3 strictEqualSlowCaseInline(ExecState * exec,JSValue v1,JSValue v2)247 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) 248 { 249 ASSERT(v1.isCell() && v2.isCell()); 250 251 if (v1.asCell()->isString() && v2.asCell()->isString()) 252 return asString(v1)->value(exec) == asString(v2)->value(exec); 253 254 return v1 == v2; 255 } 256 strictEqual(ExecState * exec,JSValue v1,JSValue v2)257 inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) 258 { 259 if (v1.isInt32() && v2.isInt32()) 260 return v1 == v2; 261 262 if (v1.isNumber() && v2.isNumber()) 263 return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); 264 265 if (!v1.isCell() || !v2.isCell()) 266 return v1 == v2; 267 268 return strictEqualSlowCaseInline(exec, v1, v2); 269 } 270 jsLess(CallFrame * callFrame,JSValue v1,JSValue v2)271 ALWAYS_INLINE bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) 272 { 273 if (v1.isInt32() && v2.isInt32()) 274 return v1.asInt32() < v2.asInt32(); 275 276 double n1; 277 double n2; 278 if (v1.getNumber(n1) && v2.getNumber(n2)) 279 return n1 < n2; 280 281 JSGlobalData* globalData = &callFrame->globalData(); 282 if (isJSString(globalData, v1) && isJSString(globalData, v2)) 283 return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); 284 285 JSValue p1; 286 JSValue p2; 287 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); 288 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); 289 290 if (wasNotString1 | wasNotString2) 291 return n1 < n2; 292 293 return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); 294 } 295 jsLessEq(CallFrame * callFrame,JSValue v1,JSValue v2)296 inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) 297 { 298 if (v1.isInt32() && v2.isInt32()) 299 return v1.asInt32() <= v2.asInt32(); 300 301 double n1; 302 double n2; 303 if (v1.getNumber(n1) && v2.getNumber(n2)) 304 return n1 <= n2; 305 306 JSGlobalData* globalData = &callFrame->globalData(); 307 if (isJSString(globalData, v1) && isJSString(globalData, v2)) 308 return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); 309 310 JSValue p1; 311 JSValue p2; 312 bool wasNotString1 = v1.getPrimitiveNumber(callFrame, n1, p1); 313 bool wasNotString2 = v2.getPrimitiveNumber(callFrame, n2, p2); 314 315 if (wasNotString1 | wasNotString2) 316 return n1 <= n2; 317 318 return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); 319 } 320 321 // Fast-path choices here are based on frequency data from SunSpider: 322 // <times> Add case: <t1> <t2> 323 // --------------------------- 324 // 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) 325 // 247412 Add case: 5 5 326 // 20900 Add case: 5 6 327 // 13962 Add case: 5 3 328 // 4000 Add case: 3 5 329 jsAdd(CallFrame * callFrame,JSValue v1,JSValue v2)330 ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) 331 { 332 double left = 0.0, right; 333 if (v1.getNumber(left) && v2.getNumber(right)) 334 return jsNumber(callFrame, left + right); 335 336 if (v1.isString()) { 337 return v2.isString() 338 ? jsString(callFrame, asString(v1), asString(v2)) 339 : jsString(callFrame, asString(v1), v2.toPrimitiveString(callFrame)); 340 } 341 342 // All other cases are pretty uncommon 343 return jsAddSlowCase(callFrame, v1, v2); 344 } 345 normalizePrototypeChain(CallFrame * callFrame,JSValue base,JSValue slotBase,const Identifier & propertyName,size_t & slotOffset)346 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset) 347 { 348 JSCell* cell = asCell(base); 349 size_t count = 0; 350 351 while (slotBase != cell) { 352 JSValue v = cell->structure()->prototypeForLookup(callFrame); 353 354 // If we didn't find slotBase in base's prototype chain, then base 355 // must be a proxy for another object. 356 357 if (v.isNull()) 358 return 0; 359 360 cell = asCell(v); 361 362 // Since we're accessing a prototype in a loop, it's a good bet that it 363 // should not be treated as a dictionary. 364 if (cell->structure()->isDictionary()) { 365 asObject(cell)->flattenDictionaryObject(); 366 if (slotBase == cell) 367 slotOffset = cell->structure()->get(propertyName); 368 } 369 370 ++count; 371 } 372 373 ASSERT(count); 374 return count; 375 } 376 normalizePrototypeChain(CallFrame * callFrame,JSCell * base)377 inline size_t normalizePrototypeChain(CallFrame* callFrame, JSCell* base) 378 { 379 size_t count = 0; 380 while (1) { 381 JSValue v = base->structure()->prototypeForLookup(callFrame); 382 if (v.isNull()) 383 return count; 384 385 base = asCell(v); 386 387 // Since we're accessing a prototype in a loop, it's a good bet that it 388 // should not be treated as a dictionary. 389 if (base->structure()->isDictionary()) 390 asObject(base)->flattenDictionaryObject(); 391 392 ++count; 393 } 394 } 395 resolveBase(CallFrame * callFrame,Identifier & property,ScopeChainNode * scopeChain)396 ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) 397 { 398 ScopeChainIterator iter = scopeChain->begin(); 399 ScopeChainIterator next = iter; 400 ++next; 401 ScopeChainIterator end = scopeChain->end(); 402 ASSERT(iter != end); 403 404 PropertySlot slot; 405 JSObject* base; 406 while (true) { 407 base = *iter; 408 if (next == end || base->getPropertySlot(callFrame, property, slot)) 409 return base; 410 411 iter = next; 412 ++next; 413 } 414 415 ASSERT_NOT_REACHED(); 416 return JSValue(); 417 } 418 } // namespace JSC 419 420 #endif // Operations_h 421