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 "AtomicString.h" 35#include "CString.h" 36#include "Element.h" 37#include "MathExtras.h" 38#include "PlatformString.h" 39#include "QualifiedName.h" 40#include "StdLibExtras.h" 41#include "StringBuffer.h" 42#include "StringHash.h" 43#include "Threading.h" 44#include "V8Element.h" 45#include "V8Proxy.h" 46 47#include <v8.h> 48 49namespace WebCore { 50 51// WebCoreStringResource is a helper class for v8ExternalString. It is used 52// to manage the life-cycle of the underlying buffer of the external string. 53class WebCoreStringResource : public v8::String::ExternalStringResource { 54public: 55 explicit WebCoreStringResource(const String& string) 56 : m_plainString(string) 57 { 58#ifndef NDEBUG 59 m_threadId = WTF::currentThread(); 60#endif 61 ASSERT(!string.isNull()); 62 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length()); 63 } 64 65 explicit WebCoreStringResource(const AtomicString& string) 66 : m_plainString(string) 67 , m_atomicString(string) 68 { 69#ifndef NDEBUG 70 m_threadId = WTF::currentThread(); 71#endif 72 ASSERT(!string.isNull()); 73 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length()); 74 } 75 76 virtual ~WebCoreStringResource() 77 { 78#ifndef NDEBUG 79 ASSERT(m_threadId == WTF::currentThread()); 80#endif 81 int reducedExternalMemory = -2 * m_plainString.length(); 82 if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull()) 83 reducedExternalMemory *= 2; 84 v8::V8::AdjustAmountOfExternalAllocatedMemory(reducedExternalMemory); 85 } 86 87 virtual const uint16_t* data() const 88 { 89 return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters()); 90 } 91 92 virtual size_t length() const { return m_plainString.impl()->length(); } 93 94 String webcoreString() { return m_plainString; } 95 96 AtomicString atomicString() 97 { 98#ifndef NDEBUG 99 ASSERT(m_threadId == WTF::currentThread()); 100#endif 101 if (m_atomicString.isNull()) { 102 m_atomicString = AtomicString(m_plainString); 103 ASSERT(!m_atomicString.isNull()); 104 if (m_plainString.impl() != m_atomicString.impl()) 105 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * m_atomicString.length()); 106 } 107 return m_atomicString; 108 } 109 110 static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String) 111 { 112 return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource()); 113 } 114 115private: 116 // A shallow copy of the string. Keeps the string buffer alive until the V8 engine garbage collects it. 117 String m_plainString; 118 // If this string is atomic or has been made atomic earlier the 119 // atomic string is held here. In the case where the string starts 120 // off non-atomic and becomes atomic later it is necessary to keep 121 // the original string alive because v8 may keep derived pointers 122 // into that string. 123 AtomicString m_atomicString; 124 125#ifndef NDEBUG 126 WTF::ThreadIdentifier m_threadId; 127#endif 128}; 129 130String v8ValueToWebCoreString(v8::Handle<v8::Value> value) 131{ 132 if (value->IsString()) 133 return v8StringToWebCoreString(v8::Handle<v8::String>::Cast(value)); 134 return v8NonStringValueToWebCoreString(value); 135} 136 137AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value) 138{ 139 if (value->IsString()) 140 return v8StringToAtomicWebCoreString(v8::Handle<v8::String>::Cast(value)); 141 return v8NonStringValueToAtomicWebCoreString(value); 142} 143 144int toInt32(v8::Handle<v8::Value> value, bool& ok) 145{ 146 ok = true; 147 148 // Fast case. The value is already a 32-bit integer. 149 if (value->IsInt32()) 150 return value->Int32Value(); 151 152 // Can the value be converted to a number? 153 v8::Local<v8::Number> numberObject = value->ToNumber(); 154 if (numberObject.IsEmpty()) { 155 ok = false; 156 return 0; 157 } 158 159 // Does the value convert to nan or to an infinity? 160 double numberValue = numberObject->Value(); 161 if (isnan(numberValue) || isinf(numberValue)) { 162 ok = false; 163 return 0; 164 } 165 166 // Can the value be converted to a 32-bit integer? 167 v8::Local<v8::Int32> intValue = value->ToInt32(); 168 if (intValue.IsEmpty()) { 169 ok = false; 170 return 0; 171 } 172 173 // Return the result of the int32 conversion. 174 return intValue->Value(); 175} 176 177String toWebCoreString(const v8::Arguments& args, int index) { 178 return v8ValueToWebCoreString(args[index]); 179} 180 181 182String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 183{ 184 if (value->IsNull()) 185 return String(); 186 return v8ValueToWebCoreString(value); 187} 188 189AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 190{ 191 if (value->IsNull()) 192 return AtomicString(); 193 return v8ValueToAtomicWebCoreString(value); 194} 195 196String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value) 197{ 198 if (value->IsNull() || value->IsUndefined()) 199 return String(); 200 return toWebCoreString(value); 201} 202 203bool isUndefinedOrNull(v8::Handle<v8::Value> value) 204{ 205 return value->IsNull() || value->IsUndefined(); 206} 207 208v8::Handle<v8::Boolean> v8Boolean(bool value) 209{ 210 return value ? v8::True() : v8::False(); 211} 212 213v8::Handle<v8::String> v8UndetectableString(const String& str) 214{ 215 return v8::String::NewUndetectable(fromWebCoreString(str), str.length()); 216} 217 218v8::Handle<v8::Value> v8StringOrNull(const String& str) 219{ 220 return str.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(str)); 221} 222 223v8::Handle<v8::Value> v8StringOrUndefined(const String& str) 224{ 225 return str.isNull() ? v8::Handle<v8::Value>(v8::Undefined()) : v8::Handle<v8::Value>(v8String(str)); 226} 227 228v8::Handle<v8::Value> v8StringOrFalse(const String& str) 229{ 230 return str.isNull() ? v8::Handle<v8::Value>(v8::False()) : v8::Handle<v8::Value>(v8String(str)); 231} 232 233double toWebCoreDate(v8::Handle<v8::Value> object) 234{ 235 return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN(); 236} 237 238v8::Handle<v8::Value> v8DateOrNull(double value) 239{ 240 if (isfinite(value)) 241 return v8::Date::New(value); 242 return v8::Null(); 243} 244 245template <class S> struct StringTraits 246{ 247 static S fromStringResource(WebCoreStringResource* resource); 248 249 static S fromV8String(v8::Handle<v8::String> v8String, int length); 250}; 251 252template<> 253struct StringTraits<String> 254{ 255 static String fromStringResource(WebCoreStringResource* resource) 256 { 257 return resource->webcoreString(); 258 } 259 260 static String fromV8String(v8::Handle<v8::String> v8String, int length) 261 { 262 ASSERT(v8String->Length() == length); 263 // NOTE: as of now, String(const UChar*, int) performs String::createUninitialized 264 // anyway, so no need to optimize like we do for AtomicString below. 265 UChar* buffer; 266 String result = String::createUninitialized(length, buffer); 267 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length); 268 return result; 269 } 270}; 271 272template<> 273struct StringTraits<AtomicString> 274{ 275 static AtomicString fromStringResource(WebCoreStringResource* resource) 276 { 277 return resource->atomicString(); 278 } 279 280 static AtomicString fromV8String(v8::Handle<v8::String> v8String, int length) 281 { 282 ASSERT(v8String->Length() == length); 283 static const int inlineBufferSize = 16; 284 if (length <= inlineBufferSize) { 285 UChar inlineBuffer[inlineBufferSize]; 286 v8String->Write(reinterpret_cast<uint16_t*>(inlineBuffer), 0, length); 287 return AtomicString(inlineBuffer, length); 288 } 289 UChar* buffer; 290 String tmp = String::createUninitialized(length, buffer); 291 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length); 292 return AtomicString(tmp); 293 } 294}; 295 296template <typename StringType> 297StringType v8StringToWebCoreString(v8::Handle<v8::String> v8String, ExternalMode external) 298{ 299 WebCoreStringResource* stringResource = WebCoreStringResource::toStringResource(v8String); 300 if (stringResource) 301 return StringTraits<StringType>::fromStringResource(stringResource); 302 303 int length = v8String->Length(); 304 if (!length) { 305 // Avoid trying to morph empty strings, as they do not have enough room to contain the external reference. 306 return StringImpl::empty(); 307 } 308 309 StringType result(StringTraits<StringType>::fromV8String(v8String, length)); 310 311 if (external == Externalize && v8String->CanMakeExternal()) { 312 stringResource = new WebCoreStringResource(result); 313 if (!v8String->MakeExternal(stringResource)) { 314 // In case of a failure delete the external resource as it was not used. 315 delete stringResource; 316 } 317 } 318 return result; 319} 320 321// Explicitly instantiate the above template with the expected parameterizations, 322// to ensure the compiler generates the code; otherwise link errors can result in GCC 4.4. 323template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode); 324template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode); 325 326 327String v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object) 328{ 329 ASSERT(!object->IsString()); 330 if (object->IsInt32()) { 331 int value = object->Int32Value(); 332 // Most numbers used are <= 100. Even if they aren't used there's very little in using the space. 333 const int kLowNumbers = 100; 334 static AtomicString lowNumbers[kLowNumbers + 1]; 335 String webCoreString; 336 if (0 <= value && value <= kLowNumbers) { 337 webCoreString = lowNumbers[value]; 338 if (!webCoreString) { 339 AtomicString valueString = AtomicString(String::number(value)); 340 lowNumbers[value] = valueString; 341 webCoreString = valueString; 342 } 343 } else 344 webCoreString = String::number(value); 345 return webCoreString; 346 } 347 348 v8::TryCatch block; 349 v8::Handle<v8::String> v8String = object->ToString(); 350 // Handle the case where an exception is thrown as part of invoking toString on the object. 351 if (block.HasCaught()) { 352 throwError(block.Exception()); 353 return StringImpl::empty(); 354 } 355 return v8StringToWebCoreString<String>(v8String, DoNotExternalize); 356} 357 358AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object) 359{ 360 ASSERT(!object->IsString()); 361 return AtomicString(v8NonStringValueToWebCoreString(object)); 362} 363 364static bool stringImplCacheEnabled = false; 365 366void enableStringImplCache() 367{ 368 stringImplCacheEnabled = true; 369} 370 371static v8::Local<v8::String> makeExternalString(const String& string) 372{ 373 WebCoreStringResource* stringResource = new WebCoreStringResource(string); 374 v8::Local<v8::String> newString = v8::String::NewExternal(stringResource); 375 if (newString.IsEmpty()) 376 delete stringResource; 377 378 return newString; 379} 380 381typedef HashMap<StringImpl*, v8::String*> StringCache; 382 383static StringCache& getStringCache() 384{ 385 ASSERT(WTF::isMainThread()); 386 DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ()); 387 return mainThreadStringCache; 388} 389 390static void cachedStringCallback(v8::Persistent<v8::Value> wrapper, void* parameter) 391{ 392 ASSERT(WTF::isMainThread()); 393 StringImpl* stringImpl = static_cast<StringImpl*>(parameter); 394 ASSERT(getStringCache().contains(stringImpl)); 395 getStringCache().remove(stringImpl); 396 wrapper.Dispose(); 397 stringImpl->deref(); 398} 399 400v8::Local<v8::String> v8ExternalString(const String& string) 401{ 402 StringImpl* stringImpl = string.impl(); 403 if (!stringImpl || !stringImpl->length()) 404 return v8::String::Empty(); 405 406 if (!stringImplCacheEnabled) 407 return makeExternalString(string); 408 409 StringCache& stringCache = getStringCache(); 410 v8::String* cachedV8String = stringCache.get(stringImpl); 411 if (cachedV8String) 412 { 413 v8::Persistent<v8::String> handle(cachedV8String); 414 if (!handle.IsNearDeath() && !handle.IsEmpty()) 415 return v8::Local<v8::String>::New(handle); 416 } 417 418 v8::Local<v8::String> newString = makeExternalString(string); 419 if (newString.IsEmpty()) 420 return newString; 421 422 v8::Persistent<v8::String> wrapper = v8::Persistent<v8::String>::New(newString); 423 if (wrapper.IsEmpty()) 424 return newString; 425 426 stringImpl->ref(); 427 wrapper.MakeWeak(stringImpl, cachedStringCallback); 428 stringCache.set(stringImpl, *wrapper); 429 430 return newString; 431} 432 433v8::Persistent<v8::FunctionTemplate> createRawTemplate() 434{ 435 v8::HandleScope scope; 436 v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal); 437 return v8::Persistent<v8::FunctionTemplate>::New(result); 438} 439 440v8::Local<v8::Signature> configureTemplate(v8::Persistent<v8::FunctionTemplate> desc, 441 const char *interfaceName, 442 v8::Persistent<v8::FunctionTemplate> parentClass, 443 int fieldCount, 444 const BatchedAttribute* attributes, 445 size_t attributeCount, 446 const BatchedCallback* callbacks, 447 size_t callbackCount) 448{ 449 desc->SetClassName(v8::String::New(interfaceName)); 450 v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate(); 451 instance->SetInternalFieldCount(fieldCount); 452 if (!parentClass.IsEmpty()) 453 desc->Inherit(parentClass); 454 if (attributeCount) 455 batchConfigureAttributes(instance, desc->PrototypeTemplate(), 456 attributes, attributeCount); 457 v8::Local<v8::Signature> defaultSignature = v8::Signature::New(desc); 458 if (callbackCount) 459 batchConfigureCallbacks(desc->PrototypeTemplate(), 460 defaultSignature, 461 static_cast<v8::PropertyAttribute>(v8::DontDelete), 462 callbacks, callbackCount); 463 return defaultSignature; 464} 465 466v8::Persistent<v8::String> getToStringName() 467{ 468 DEFINE_STATIC_LOCAL(v8::Persistent<v8::String>, value, ()); 469 if (value.IsEmpty()) 470 value = v8::Persistent<v8::String>::New(v8::String::New("toString")); 471 return value; 472} 473 474static v8::Handle<v8::Value> constructorToString(const v8::Arguments& args) 475{ 476 // The DOM constructors' toString functions grab the current toString 477 // for Functions by taking the toString function of itself and then 478 // calling it with the constructor as its receiver. This means that 479 // changes to the Function prototype chain or toString function are 480 // reflected when printing DOM constructors. The only wart is that 481 // changes to a DOM constructor's toString's toString will cause the 482 // toString of the DOM constructor itself to change. This is extremely 483 // obscure and unlikely to be a problem. 484 v8::Handle<v8::Value> value = args.Callee()->Get(getToStringName()); 485 if (!value->IsFunction()) 486 return v8::String::New(""); 487 return v8::Handle<v8::Function>::Cast(value)->Call(args.This(), 0, 0); 488} 489 490v8::Persistent<v8::FunctionTemplate> getToStringTemplate() 491{ 492 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, toStringTemplate, ()); 493 if (toStringTemplate.IsEmpty()) 494 toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(constructorToString)); 495 return toStringTemplate; 496} 497 498v8::Handle<v8::Value> getElementStringAttr(const v8::AccessorInfo& info, 499 const QualifiedName& name) 500{ 501 Element* imp = V8Element::toNative(info.Holder()); 502 return v8ExternalString(imp->getAttribute(name)); 503} 504 505void setElementStringAttr(const v8::AccessorInfo& info, 506 const QualifiedName& name, 507 v8::Local<v8::Value> value) 508{ 509 Element* imp = V8Element::toNative(info.Holder()); 510 AtomicString v = toAtomicWebCoreStringWithNullCheck(value); 511 imp->setAttribute(name, v); 512} 513 514} // namespace WebCore 515