1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "JSValueRef.h" 28 29#include "APICast.h" 30#include "JSAPIWrapperObject.h" 31#include "JSCJSValue.h" 32#include "JSCallbackObject.h" 33#include "JSGlobalObject.h" 34#include "JSONObject.h" 35#include "JSString.h" 36#include "LiteralParser.h" 37#include "JSCInlines.h" 38#include "Protect.h" 39 40#include <wtf/Assertions.h> 41#include <wtf/text/StringHash.h> 42#include <wtf/text/WTFString.h> 43 44#include <algorithm> // for std::min 45 46#if PLATFORM(MAC) 47#include <mach-o/dyld.h> 48#endif 49 50#if ENABLE(REMOTE_INSPECTOR) 51#include "JSGlobalObjectInspectorController.h" 52#endif 53 54using namespace JSC; 55 56#if PLATFORM(MAC) 57static bool evernoteHackNeeded() 58{ 59 static const int32_t webkitLastVersionWithEvernoteHack = 35133959; 60 static bool hackNeeded = CFEqual(CFBundleGetIdentifier(CFBundleGetMainBundle()), CFSTR("com.evernote.Evernote")) 61 && NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitLastVersionWithEvernoteHack; 62 63 return hackNeeded; 64} 65#endif 66 67::JSType JSValueGetType(JSContextRef ctx, JSValueRef value) 68{ 69 if (!ctx) { 70 ASSERT_NOT_REACHED(); 71 return kJSTypeUndefined; 72 } 73 ExecState* exec = toJS(ctx); 74 JSLockHolder locker(exec); 75 76 JSValue jsValue = toJS(exec, value); 77 78 if (jsValue.isUndefined()) 79 return kJSTypeUndefined; 80 if (jsValue.isNull()) 81 return kJSTypeNull; 82 if (jsValue.isBoolean()) 83 return kJSTypeBoolean; 84 if (jsValue.isNumber()) 85 return kJSTypeNumber; 86 if (jsValue.isString()) 87 return kJSTypeString; 88 ASSERT(jsValue.isObject()); 89 return kJSTypeObject; 90} 91 92bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value) 93{ 94 if (!ctx) { 95 ASSERT_NOT_REACHED(); 96 return false; 97 } 98 ExecState* exec = toJS(ctx); 99 JSLockHolder locker(exec); 100 101 JSValue jsValue = toJS(exec, value); 102 return jsValue.isUndefined(); 103} 104 105bool JSValueIsNull(JSContextRef ctx, JSValueRef value) 106{ 107 if (!ctx) { 108 ASSERT_NOT_REACHED(); 109 return false; 110 } 111 ExecState* exec = toJS(ctx); 112 JSLockHolder locker(exec); 113 114 JSValue jsValue = toJS(exec, value); 115 return jsValue.isNull(); 116} 117 118bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value) 119{ 120 if (!ctx) { 121 ASSERT_NOT_REACHED(); 122 return false; 123 } 124 ExecState* exec = toJS(ctx); 125 JSLockHolder locker(exec); 126 127 JSValue jsValue = toJS(exec, value); 128 return jsValue.isBoolean(); 129} 130 131bool JSValueIsNumber(JSContextRef ctx, JSValueRef value) 132{ 133 if (!ctx) { 134 ASSERT_NOT_REACHED(); 135 return false; 136 } 137 ExecState* exec = toJS(ctx); 138 JSLockHolder locker(exec); 139 140 JSValue jsValue = toJS(exec, value); 141 return jsValue.isNumber(); 142} 143 144bool JSValueIsString(JSContextRef ctx, JSValueRef value) 145{ 146 if (!ctx) { 147 ASSERT_NOT_REACHED(); 148 return false; 149 } 150 ExecState* exec = toJS(ctx); 151 JSLockHolder locker(exec); 152 153 JSValue jsValue = toJS(exec, value); 154 return jsValue.isString(); 155} 156 157bool JSValueIsObject(JSContextRef ctx, JSValueRef value) 158{ 159 if (!ctx) { 160 ASSERT_NOT_REACHED(); 161 return false; 162 } 163 ExecState* exec = toJS(ctx); 164 JSLockHolder locker(exec); 165 166 JSValue jsValue = toJS(exec, value); 167 return jsValue.isObject(); 168} 169 170bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass) 171{ 172 if (!ctx || !jsClass) { 173 ASSERT_NOT_REACHED(); 174 return false; 175 } 176 ExecState* exec = toJS(ctx); 177 JSLockHolder locker(exec); 178 179 JSValue jsValue = toJS(exec, value); 180 181 if (JSObject* o = jsValue.getObject()) { 182 if (o->inherits(JSProxy::info())) 183 o = jsCast<JSProxy*>(o)->target(); 184 185 if (o->inherits(JSCallbackObject<JSGlobalObject>::info())) 186 return jsCast<JSCallbackObject<JSGlobalObject>*>(o)->inherits(jsClass); 187 if (o->inherits(JSCallbackObject<JSDestructibleObject>::info())) 188 return jsCast<JSCallbackObject<JSDestructibleObject>*>(o)->inherits(jsClass); 189#if JSC_OBJC_API_ENABLED 190 if (o->inherits(JSCallbackObject<JSAPIWrapperObject>::info())) 191 return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(o)->inherits(jsClass); 192#endif 193 } 194 return false; 195} 196 197bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception) 198{ 199 if (!ctx) { 200 ASSERT_NOT_REACHED(); 201 return false; 202 } 203 ExecState* exec = toJS(ctx); 204 JSLockHolder locker(exec); 205 206 JSValue jsA = toJS(exec, a); 207 JSValue jsB = toJS(exec, b); 208 209 bool result = JSValue::equal(exec, jsA, jsB); // false if an exception is thrown 210 if (exec->hadException()) { 211 JSValue exceptionValue = exec->exception(); 212 if (exception) 213 *exception = toRef(exec, exceptionValue); 214 exec->clearException(); 215#if ENABLE(REMOTE_INSPECTOR) 216 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 217#endif 218 } 219 return result; 220} 221 222bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b) 223{ 224 if (!ctx) { 225 ASSERT_NOT_REACHED(); 226 return false; 227 } 228 ExecState* exec = toJS(ctx); 229 JSLockHolder locker(exec); 230 231 JSValue jsA = toJS(exec, a); 232 JSValue jsB = toJS(exec, b); 233 234 return JSValue::strictEqual(exec, jsA, jsB); 235} 236 237bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception) 238{ 239 if (!ctx) { 240 ASSERT_NOT_REACHED(); 241 return false; 242 } 243 ExecState* exec = toJS(ctx); 244 JSLockHolder locker(exec); 245 246 JSValue jsValue = toJS(exec, value); 247 248 JSObject* jsConstructor = toJS(constructor); 249 if (!jsConstructor->structure()->typeInfo().implementsHasInstance()) 250 return false; 251 bool result = jsConstructor->hasInstance(exec, jsValue); // false if an exception is thrown 252 if (exec->hadException()) { 253 JSValue exceptionValue = exec->exception(); 254 if (exception) 255 *exception = toRef(exec, exceptionValue); 256 exec->clearException(); 257#if ENABLE(REMOTE_INSPECTOR) 258 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 259#endif 260 } 261 return result; 262} 263 264JSValueRef JSValueMakeUndefined(JSContextRef ctx) 265{ 266 if (!ctx) { 267 ASSERT_NOT_REACHED(); 268 return 0; 269 } 270 ExecState* exec = toJS(ctx); 271 JSLockHolder locker(exec); 272 273 return toRef(exec, jsUndefined()); 274} 275 276JSValueRef JSValueMakeNull(JSContextRef ctx) 277{ 278 if (!ctx) { 279 ASSERT_NOT_REACHED(); 280 return 0; 281 } 282 ExecState* exec = toJS(ctx); 283 JSLockHolder locker(exec); 284 285 return toRef(exec, jsNull()); 286} 287 288JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value) 289{ 290 if (!ctx) { 291 ASSERT_NOT_REACHED(); 292 return 0; 293 } 294 ExecState* exec = toJS(ctx); 295 JSLockHolder locker(exec); 296 297 return toRef(exec, jsBoolean(value)); 298} 299 300JSValueRef JSValueMakeNumber(JSContextRef ctx, double value) 301{ 302 if (!ctx) { 303 ASSERT_NOT_REACHED(); 304 return 0; 305 } 306 ExecState* exec = toJS(ctx); 307 JSLockHolder locker(exec); 308 309 return toRef(exec, jsNumber(purifyNaN(value))); 310} 311 312JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string) 313{ 314 if (!ctx) { 315 ASSERT_NOT_REACHED(); 316 return 0; 317 } 318 ExecState* exec = toJS(ctx); 319 JSLockHolder locker(exec); 320 321 return toRef(exec, jsString(exec, string->string())); 322} 323 324JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string) 325{ 326 if (!ctx) { 327 ASSERT_NOT_REACHED(); 328 return 0; 329 } 330 ExecState* exec = toJS(ctx); 331 JSLockHolder locker(exec); 332 String str = string->string(); 333 unsigned length = str.length(); 334 if (!length || str.is8Bit()) { 335 LiteralParser<LChar> parser(exec, str.characters8(), length, StrictJSON); 336 return toRef(exec, parser.tryLiteralParse()); 337 } 338 LiteralParser<UChar> parser(exec, str.characters16(), length, StrictJSON); 339 return toRef(exec, parser.tryLiteralParse()); 340} 341 342JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsigned indent, JSValueRef* exception) 343{ 344 if (!ctx) { 345 ASSERT_NOT_REACHED(); 346 return 0; 347 } 348 ExecState* exec = toJS(ctx); 349 JSLockHolder locker(exec); 350 JSValue value = toJS(exec, apiValue); 351 String result = JSONStringify(exec, value, indent); 352 if (exception) 353 *exception = 0; 354 if (exec->hadException()) { 355 JSValue exceptionValue = exec->exception(); 356 if (exception) 357 *exception = toRef(exec, exceptionValue); 358 exec->clearException(); 359#if ENABLE(REMOTE_INSPECTOR) 360 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 361#endif 362 return 0; 363 } 364 return OpaqueJSString::create(result).leakRef(); 365} 366 367bool JSValueToBoolean(JSContextRef ctx, JSValueRef value) 368{ 369 if (!ctx) { 370 ASSERT_NOT_REACHED(); 371 return false; 372 } 373 ExecState* exec = toJS(ctx); 374 JSLockHolder locker(exec); 375 376 JSValue jsValue = toJS(exec, value); 377 return jsValue.toBoolean(exec); 378} 379 380double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception) 381{ 382 if (!ctx) { 383 ASSERT_NOT_REACHED(); 384 return PNaN; 385 } 386 ExecState* exec = toJS(ctx); 387 JSLockHolder locker(exec); 388 389 JSValue jsValue = toJS(exec, value); 390 391 double number = jsValue.toNumber(exec); 392 if (exec->hadException()) { 393 JSValue exceptionValue = exec->exception(); 394 if (exception) 395 *exception = toRef(exec, exceptionValue); 396 exec->clearException(); 397#if ENABLE(REMOTE_INSPECTOR) 398 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 399#endif 400 number = PNaN; 401 } 402 return number; 403} 404 405JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception) 406{ 407 if (!ctx) { 408 ASSERT_NOT_REACHED(); 409 return 0; 410 } 411 ExecState* exec = toJS(ctx); 412 JSLockHolder locker(exec); 413 414 JSValue jsValue = toJS(exec, value); 415 416 RefPtr<OpaqueJSString> stringRef(OpaqueJSString::create(jsValue.toString(exec)->value(exec))); 417 if (exec->hadException()) { 418 JSValue exceptionValue = exec->exception(); 419 if (exception) 420 *exception = toRef(exec, exceptionValue); 421 exec->clearException(); 422#if ENABLE(REMOTE_INSPECTOR) 423 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 424#endif 425 stringRef.clear(); 426 } 427 return stringRef.release().leakRef(); 428} 429 430JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception) 431{ 432 if (!ctx) { 433 ASSERT_NOT_REACHED(); 434 return 0; 435 } 436 ExecState* exec = toJS(ctx); 437 JSLockHolder locker(exec); 438 439 JSValue jsValue = toJS(exec, value); 440 441 JSObjectRef objectRef = toRef(jsValue.toObject(exec)); 442 if (exec->hadException()) { 443 JSValue exceptionValue = exec->exception(); 444 if (exception) 445 *exception = toRef(exec, exceptionValue); 446 exec->clearException(); 447#if ENABLE(REMOTE_INSPECTOR) 448 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exceptionValue); 449#endif 450 objectRef = 0; 451 } 452 return objectRef; 453} 454 455void JSValueProtect(JSContextRef ctx, JSValueRef value) 456{ 457 if (!ctx) { 458 ASSERT_NOT_REACHED(); 459 return; 460 } 461 ExecState* exec = toJS(ctx); 462 JSLockHolder locker(exec); 463 464 JSValue jsValue = toJSForGC(exec, value); 465 gcProtect(jsValue); 466} 467 468void JSValueUnprotect(JSContextRef ctx, JSValueRef value) 469{ 470#if PLATFORM(MAC) 471 if ((!value || !ctx) && evernoteHackNeeded()) 472 return; 473#endif 474 475 ExecState* exec = toJS(ctx); 476 JSLockHolder locker(exec); 477 478 JSValue jsValue = toJSForGC(exec, value); 479 gcUnprotect(jsValue); 480} 481