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 48namespace 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. 52class WebCoreStringResource : public v8::String::ExternalStringResource { 53public: 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 114private: 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 129String 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 136AtomicString 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 143int 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 176uint32_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 223String toWebCoreString(const v8::Arguments& args, int index) { 224 return v8ValueToWebCoreString(args[index]); 225} 226 227 228String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 229{ 230 if (value->IsNull()) 231 return String(); 232 return v8ValueToWebCoreString(value); 233} 234 235AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 236{ 237 if (value->IsNull()) 238 return AtomicString(); 239 return v8ValueToAtomicWebCoreString(value); 240} 241 242String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value) 243{ 244 if (value->IsNull() || value->IsUndefined()) 245 return String(); 246 return toWebCoreString(value); 247} 248 249bool isUndefinedOrNull(v8::Handle<v8::Value> value) 250{ 251 return value->IsNull() || value->IsUndefined(); 252} 253 254v8::Handle<v8::Boolean> v8Boolean(bool value) 255{ 256 return value ? v8::True() : v8::False(); 257} 258 259v8::Handle<v8::String> v8UndetectableString(const String& str) 260{ 261 return v8::String::NewUndetectable(fromWebCoreString(str), str.length()); 262} 263 264v8::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 269v8::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 274v8::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 279double toWebCoreDate(v8::Handle<v8::Value> object) 280{ 281 return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN(); 282} 283 284v8::Handle<v8::Value> v8DateOrNull(double value) 285{ 286 if (isfinite(value)) 287 return v8::Date::New(value); 288 return v8::Null(); 289} 290 291template <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 298template<> 299struct 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 318template<> 319struct 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 342template <typename StringType> 343StringType 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. 369template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode); 370template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode); 371 372// Fast but non thread-safe version. 373String 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 395String 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 403String 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 426AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object) 427{ 428 ASSERT(!object->IsString()); 429 return AtomicString(v8NonStringValueToWebCoreString(object)); 430} 431 432static bool stringImplCacheEnabled = false; 433 434void enableStringImplCache() 435{ 436 stringImplCacheEnabled = true; 437} 438 439static 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 449typedef HashMap<StringImpl*, v8::String*> StringCache; 450 451static StringCache& getStringCache() 452{ 453 ASSERT(WTF::isMainThread()); 454 DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ()); 455 return mainThreadStringCache; 456} 457 458static 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 468RefPtr<StringImpl> lastStringImpl = 0; 469v8::Persistent<v8::String> lastV8String; 470 471v8::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 508v8::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 515v8::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 541v8::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 549static 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 565v8::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 573v8::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 580void 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 589PassRefPtr<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