1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "V8Binding.h" 33 34 #include "DOMStringList.h" 35 #include "Element.h" 36 #include "MathExtras.h" 37 #include "PlatformString.h" 38 #include "QualifiedName.h" 39 #include "StdLibExtras.h" 40 #include "Threading.h" 41 #include "V8Element.h" 42 #include "V8Proxy.h" 43 #include <wtf/text/AtomicString.h> 44 #include <wtf/text/CString.h> 45 #include <wtf/text/StringBuffer.h> 46 #include <wtf/text/StringHash.h> 47 48 namespace WebCore { 49 50 // WebCoreStringResource is a helper class for v8ExternalString. It is used 51 // to manage the life-cycle of the underlying buffer of the external string. 52 class WebCoreStringResource : public v8::String::ExternalStringResource { 53 public: 54 explicit WebCoreStringResource(const String& string) 55 : m_plainString(string) 56 { 57 #ifndef NDEBUG 58 m_threadId = WTF::currentThread(); 59 #endif 60 ASSERT(!string.isNull()); 61 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length()); 62 } 63 64 explicit WebCoreStringResource(const AtomicString& string) 65 : m_plainString(string.string()) 66 , m_atomicString(string) 67 { 68 #ifndef NDEBUG 69 m_threadId = WTF::currentThread(); 70 #endif 71 ASSERT(!string.isNull()); 72 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length()); 73 } 74 75 virtual ~WebCoreStringResource() 76 { 77 #ifndef NDEBUG 78 ASSERT(m_threadId == WTF::currentThread()); 79 #endif 80 int reducedExternalMemory = -2 * m_plainString.length(); 81 if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull()) 82 reducedExternalMemory *= 2; 83 v8::V8::AdjustAmountOfExternalAllocatedMemory(reducedExternalMemory); 84 } 85 86 virtual const uint16_t* data() const 87 { 88 return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters()); 89 } 90 91 virtual size_t length() const { return m_plainString.impl()->length(); } 92 93 String webcoreString() { return m_plainString; } 94 95 AtomicString atomicString() 96 { 97 #ifndef NDEBUG 98 ASSERT(m_threadId == WTF::currentThread()); 99 #endif 100 if (m_atomicString.isNull()) { 101 m_atomicString = AtomicString(m_plainString); 102 ASSERT(!m_atomicString.isNull()); 103 if (m_plainString.impl() != m_atomicString.impl()) 104 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * m_atomicString.length()); 105 } 106 return m_atomicString; 107 } 108 109 static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String) 110 { 111 return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource()); 112 } 113 114 private: 115 // A shallow copy of the string. Keeps the string buffer alive until the V8 engine garbage collects it. 116 String m_plainString; 117 // If this string is atomic or has been made atomic earlier the 118 // atomic string is held here. In the case where the string starts 119 // off non-atomic and becomes atomic later it is necessary to keep 120 // the original string alive because v8 may keep derived pointers 121 // into that string. 122 AtomicString m_atomicString; 123 124 #ifndef NDEBUG 125 WTF::ThreadIdentifier m_threadId; 126 #endif 127 }; 128 129 String v8ValueToWebCoreString(v8::Handle<v8::Value> value) 130 { 131 if (value->IsString()) 132 return v8StringToWebCoreString(v8::Handle<v8::String>::Cast(value)); 133 return v8NonStringValueToWebCoreString(value); 134 } 135 136 AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value) 137 { 138 if (value->IsString()) 139 return v8StringToAtomicWebCoreString(v8::Handle<v8::String>::Cast(value)); 140 return v8NonStringValueToAtomicWebCoreString(value); 141 } 142 143 int toInt32(v8::Handle<v8::Value> value, bool& ok) 144 { 145 ok = true; 146 147 // Fast case. The value is already a 32-bit integer. 148 if (value->IsInt32()) 149 return value->Int32Value(); 150 151 // Can the value be converted to a number? 152 v8::Local<v8::Number> numberObject = value->ToNumber(); 153 if (numberObject.IsEmpty()) { 154 ok = false; 155 return 0; 156 } 157 158 // Does the value convert to nan or to an infinity? 159 double numberValue = numberObject->Value(); 160 if (isnan(numberValue) || isinf(numberValue)) { 161 ok = false; 162 return 0; 163 } 164 165 // Can the value be converted to a 32-bit integer? 166 v8::Local<v8::Int32> intValue = value->ToInt32(); 167 if (intValue.IsEmpty()) { 168 ok = false; 169 return 0; 170 } 171 172 // Return the result of the int32 conversion. 173 return intValue->Value(); 174 } 175 176 uint32_t toUInt32(v8::Handle<v8::Value> value, bool& ok) 177 { 178 ok = true; 179 180 // FIXME: there is currently no Value::IsUint32(). This code does 181 // some contortions to avoid silently converting out-of-range 182 // values to uint32_t. 183 184 // Fast case. The value is already a 32-bit positive integer. 185 if (value->IsInt32()) { 186 int32_t result = value->Int32Value(); 187 if (result >= 0) 188 return result; 189 } 190 191 // Can the value be converted to a number? 192 v8::Local<v8::Number> numberObject = value->ToNumber(); 193 if (numberObject.IsEmpty()) { 194 ok = false; 195 return 0; 196 } 197 198 // Does the value convert to nan or to an infinity? 199 double numberValue = numberObject->Value(); 200 if (isnan(numberValue) || isinf(numberValue)) { 201 ok = false; 202 return 0; 203 } 204 205 // Can the value be converted to a 32-bit unsigned integer? 206 v8::Local<v8::Uint32> uintValue = value->ToUint32(); 207 if (uintValue.IsEmpty()) { 208 ok = false; 209 return 0; 210 } 211 212 // FIXME: v8::Uint32::Value is not defined! 213 // http://code.google.com/p/v8/issues/detail?id=624 214 v8::Local<v8::Int32> intValue = value->ToInt32(); 215 if (intValue.IsEmpty()) { 216 ok = false; 217 return 0; 218 } 219 220 return static_cast<uint32_t>(intValue->Value()); 221 } 222 223 String toWebCoreString(const v8::Arguments& args, int index) { 224 return v8ValueToWebCoreString(args[index]); 225 } 226 227 228 String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 229 { 230 if (value->IsNull()) 231 return String(); 232 return v8ValueToWebCoreString(value); 233 } 234 235 AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 236 { 237 if (value->IsNull()) 238 return AtomicString(); 239 return v8ValueToAtomicWebCoreString(value); 240 } 241 242 String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value) 243 { 244 if (value->IsNull() || value->IsUndefined()) 245 return String(); 246 return toWebCoreString(value); 247 } 248 249 bool isUndefinedOrNull(v8::Handle<v8::Value> value) 250 { 251 return value->IsNull() || value->IsUndefined(); 252 } 253 254 v8::Handle<v8::Boolean> v8Boolean(bool value) 255 { 256 return value ? v8::True() : v8::False(); 257 } 258 259 v8::Handle<v8::String> v8UndetectableString(const String& str) 260 { 261 return v8::String::NewUndetectable(fromWebCoreString(str), str.length()); 262 } 263 264 v8::Handle<v8::Value> v8StringOrNull(const String& str) 265 { 266 return str.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(str)); 267 } 268 269 v8::Handle<v8::Value> v8StringOrUndefined(const String& str) 270 { 271 return str.isNull() ? v8::Handle<v8::Value>(v8::Undefined()) : v8::Handle<v8::Value>(v8String(str)); 272 } 273 274 v8::Handle<v8::Value> v8StringOrFalse(const String& str) 275 { 276 return str.isNull() ? v8::Handle<v8::Value>(v8::False()) : v8::Handle<v8::Value>(v8String(str)); 277 } 278 279 double toWebCoreDate(v8::Handle<v8::Value> object) 280 { 281 return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN(); 282 } 283 284 v8::Handle<v8::Value> v8DateOrNull(double value) 285 { 286 if (isfinite(value)) 287 return v8::Date::New(value); 288 return v8::Null(); 289 } 290 291 template <class S> struct StringTraits 292 { 293 static S fromStringResource(WebCoreStringResource* resource); 294 295 static S fromV8String(v8::Handle<v8::String> v8String, int length); 296 }; 297 298 template<> 299 struct StringTraits<String> 300 { 301 static String fromStringResource(WebCoreStringResource* resource) 302 { 303 return resource->webcoreString(); 304 } 305 306 static String fromV8String(v8::Handle<v8::String> v8String, int length) 307 { 308 ASSERT(v8String->Length() == length); 309 // NOTE: as of now, String(const UChar*, int) performs String::createUninitialized 310 // anyway, so no need to optimize like we do for AtomicString below. 311 UChar* buffer; 312 String result = String::createUninitialized(length, buffer); 313 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length); 314 return result; 315 } 316 }; 317 318 template<> 319 struct StringTraits<AtomicString> 320 { 321 static AtomicString fromStringResource(WebCoreStringResource* resource) 322 { 323 return resource->atomicString(); 324 } 325 326 static AtomicString fromV8String(v8::Handle<v8::String> v8String, int length) 327 { 328 ASSERT(v8String->Length() == length); 329 static const int inlineBufferSize = 16; 330 if (length <= inlineBufferSize) { 331 UChar inlineBuffer[inlineBufferSize]; 332 v8String->Write(reinterpret_cast<uint16_t*>(inlineBuffer), 0, length); 333 return AtomicString(inlineBuffer, length); 334 } 335 UChar* buffer; 336 String tmp = String::createUninitialized(length, buffer); 337 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length); 338 return AtomicString(tmp); 339 } 340 }; 341 342 template <typename StringType> 343 StringType v8StringToWebCoreString(v8::Handle<v8::String> v8String, ExternalMode external) 344 { 345 WebCoreStringResource* stringResource = WebCoreStringResource::toStringResource(v8String); 346 if (stringResource) 347 return StringTraits<StringType>::fromStringResource(stringResource); 348 349 int length = v8String->Length(); 350 if (!length) { 351 // Avoid trying to morph empty strings, as they do not have enough room to contain the external reference. 352 return StringImpl::empty(); 353 } 354 355 StringType result(StringTraits<StringType>::fromV8String(v8String, length)); 356 357 if (external == Externalize && v8String->CanMakeExternal()) { 358 stringResource = new WebCoreStringResource(result); 359 if (!v8String->MakeExternal(stringResource)) { 360 // In case of a failure delete the external resource as it was not used. 361 delete stringResource; 362 } 363 } 364 return result; 365 } 366 367 // Explicitly instantiate the above template with the expected parameterizations, 368 // to ensure the compiler generates the code; otherwise link errors can result in GCC 4.4. 369 template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode); 370 template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode); 371 372 // Fast but non thread-safe version. 373 String int32ToWebCoreStringFast(int value) 374 { 375 // Caching of small strings below is not thread safe: newly constructed AtomicString 376 // are not safely published. 377 ASSERT(WTF::isMainThread()); 378 379 // Most numbers used are <= 100. Even if they aren't used there's very little cost in using the space. 380 const int kLowNumbers = 100; 381 DEFINE_STATIC_LOCAL(Vector<AtomicString>, lowNumbers, (kLowNumbers + 1)); 382 String webCoreString; 383 if (0 <= value && value <= kLowNumbers) { 384 webCoreString = lowNumbers[value]; 385 if (!webCoreString) { 386 AtomicString valueString = AtomicString(String::number(value)); 387 lowNumbers[value] = valueString; 388 webCoreString = valueString; 389 } 390 } else 391 webCoreString = String::number(value); 392 return webCoreString; 393 } 394 395 String int32ToWebCoreString(int value) 396 { 397 // If we are on the main thread (this should always true for non-workers), call the faster one. 398 if (WTF::isMainThread()) 399 return int32ToWebCoreStringFast(value); 400 return String::number(value); 401 } 402 403 String v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object) 404 { 405 ASSERT(!object->IsString()); 406 if (object->IsInt32()) 407 return int32ToWebCoreString(object->Int32Value()); 408 409 v8::TryCatch block; 410 v8::Handle<v8::String> v8String = object->ToString(); 411 // Handle the case where an exception is thrown as part of invoking toString on the object. 412 if (block.HasCaught()) { 413 throwError(block.Exception()); 414 return StringImpl::empty(); 415 } 416 // This path is unexpected. However there is hypothesis that it 417 // might be combination of v8 and v8 bindings bugs. For now 418 // just bailout as we'll crash if attempt to convert empty handle into a string. 419 if (v8String.IsEmpty()) { 420 ASSERT_NOT_REACHED(); 421 return StringImpl::empty(); 422 } 423 return v8StringToWebCoreString<String>(v8String, DoNotExternalize); 424 } 425 426 AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object) 427 { 428 ASSERT(!object->IsString()); 429 return AtomicString(v8NonStringValueToWebCoreString(object)); 430 } 431 432 static bool stringImplCacheEnabled = false; 433 434 void enableStringImplCache() 435 { 436 stringImplCacheEnabled = true; 437 } 438 439 static v8::Local<v8::String> makeExternalString(const String& string) 440 { 441 WebCoreStringResource* stringResource = new WebCoreStringResource(string); 442 v8::Local<v8::String> newString = v8::String::NewExternal(stringResource); 443 if (newString.IsEmpty()) 444 delete stringResource; 445 446 return newString; 447 } 448 449 typedef HashMap<StringImpl*, v8::String*> StringCache; 450 451 static StringCache& getStringCache() 452 { 453 ASSERT(WTF::isMainThread()); 454 DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ()); 455 return mainThreadStringCache; 456 } 457 458 static void cachedStringCallback(v8::Persistent<v8::Value> wrapper, void* parameter) 459 { 460 ASSERT(WTF::isMainThread()); 461 StringImpl* stringImpl = static_cast<StringImpl*>(parameter); 462 ASSERT(getStringCache().contains(stringImpl)); 463 getStringCache().remove(stringImpl); 464 wrapper.Dispose(); 465 stringImpl->deref(); 466 } 467 468 RefPtr<StringImpl> lastStringImpl = 0; 469 v8::Persistent<v8::String> lastV8String; 470 471 v8::Local<v8::String> v8ExternalStringSlow(StringImpl* stringImpl) 472 { 473 if (!stringImpl->length()) 474 return v8::String::Empty(); 475 476 if (!stringImplCacheEnabled) 477 return makeExternalString(String(stringImpl)); 478 479 StringCache& stringCache = getStringCache(); 480 v8::String* cachedV8String = stringCache.get(stringImpl); 481 if (cachedV8String) { 482 v8::Persistent<v8::String> handle(cachedV8String); 483 if (!handle.IsNearDeath() && !handle.IsEmpty()) { 484 lastStringImpl = stringImpl; 485 lastV8String = handle; 486 return v8::Local<v8::String>::New(handle); 487 } 488 } 489 490 v8::Local<v8::String> newString = makeExternalString(String(stringImpl)); 491 if (newString.IsEmpty()) 492 return newString; 493 494 v8::Persistent<v8::String> wrapper = v8::Persistent<v8::String>::New(newString); 495 if (wrapper.IsEmpty()) 496 return newString; 497 498 stringImpl->ref(); 499 wrapper.MakeWeak(stringImpl, cachedStringCallback); 500 stringCache.set(stringImpl, *wrapper); 501 502 lastStringImpl = stringImpl; 503 lastV8String = wrapper; 504 505 return newString; 506 } 507 508 v8::Persistent<v8::FunctionTemplate> createRawTemplate() 509 { 510 v8::HandleScope scope; 511 v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal); 512 return v8::Persistent<v8::FunctionTemplate>::New(result); 513 } 514 515 v8::Local<v8::Signature> configureTemplate(v8::Persistent<v8::FunctionTemplate> desc, 516 const char *interfaceName, 517 v8::Persistent<v8::FunctionTemplate> parentClass, 518 int fieldCount, 519 const BatchedAttribute* attributes, 520 size_t attributeCount, 521 const BatchedCallback* callbacks, 522 size_t callbackCount) 523 { 524 desc->SetClassName(v8::String::New(interfaceName)); 525 v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate(); 526 instance->SetInternalFieldCount(fieldCount); 527 if (!parentClass.IsEmpty()) 528 desc->Inherit(parentClass); 529 if (attributeCount) 530 batchConfigureAttributes(instance, desc->PrototypeTemplate(), 531 attributes, attributeCount); 532 v8::Local<v8::Signature> defaultSignature = v8::Signature::New(desc); 533 if (callbackCount) 534 batchConfigureCallbacks(desc->PrototypeTemplate(), 535 defaultSignature, 536 static_cast<v8::PropertyAttribute>(v8::DontDelete), 537 callbacks, callbackCount); 538 return defaultSignature; 539 } 540 541 v8::Persistent<v8::String> getToStringName() 542 { 543 DEFINE_STATIC_LOCAL(v8::Persistent<v8::String>, value, ()); 544 if (value.IsEmpty()) 545 value = v8::Persistent<v8::String>::New(v8::String::New("toString")); 546 return value; 547 } 548 549 static v8::Handle<v8::Value> constructorToString(const v8::Arguments& args) 550 { 551 // The DOM constructors' toString functions grab the current toString 552 // for Functions by taking the toString function of itself and then 553 // calling it with the constructor as its receiver. This means that 554 // changes to the Function prototype chain or toString function are 555 // reflected when printing DOM constructors. The only wart is that 556 // changes to a DOM constructor's toString's toString will cause the 557 // toString of the DOM constructor itself to change. This is extremely 558 // obscure and unlikely to be a problem. 559 v8::Handle<v8::Value> value = args.Callee()->Get(getToStringName()); 560 if (!value->IsFunction()) 561 return v8::String::New(""); 562 return v8::Handle<v8::Function>::Cast(value)->Call(args.This(), 0, 0); 563 } 564 565 v8::Persistent<v8::FunctionTemplate> getToStringTemplate() 566 { 567 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, toStringTemplate, ()); 568 if (toStringTemplate.IsEmpty()) 569 toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(constructorToString)); 570 return toStringTemplate; 571 } 572 573 v8::Handle<v8::Value> getElementStringAttr(const v8::AccessorInfo& info, 574 const QualifiedName& name) 575 { 576 Element* imp = V8Element::toNative(info.Holder()); 577 return v8ExternalString(imp->getAttribute(name)); 578 } 579 580 void setElementStringAttr(const v8::AccessorInfo& info, 581 const QualifiedName& name, 582 v8::Local<v8::Value> value) 583 { 584 Element* imp = V8Element::toNative(info.Holder()); 585 AtomicString v = toAtomicWebCoreStringWithNullCheck(value); 586 imp->setAttribute(name, v); 587 } 588 589 PassRefPtr<DOMStringList> v8ValueToWebCoreDOMStringList(v8::Handle<v8::Value> value) 590 { 591 v8::Local<v8::Value> v8Value(v8::Local<v8::Value>::New(value)); 592 if (!v8Value->IsArray()) 593 return 0; 594 595 RefPtr<DOMStringList> ret = DOMStringList::create(); 596 v8::Local<v8::Array> v8Array = v8::Local<v8::Array>::Cast(v8Value); 597 for (size_t i = 0; i < v8Array->Length(); ++i) { 598 v8::Local<v8::Value> indexedValue = v8Array->Get(v8::Integer::New(i)); 599 ret->append(v8ValueToWebCoreString(indexedValue)); 600 } 601 return ret.release(); 602 } 603 604 } // namespace WebCore 605