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 49 namespace 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. 53 class WebCoreStringResource : public v8::String::ExternalStringResource { 54 public: 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 115 private: 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 130 String 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 137 AtomicString 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 144 int 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 177 String toWebCoreString(const v8::Arguments& args, int index) { 178 return v8ValueToWebCoreString(args[index]); 179 } 180 181 182 String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 183 { 184 if (value->IsNull()) 185 return String(); 186 return v8ValueToWebCoreString(value); 187 } 188 189 AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value) 190 { 191 if (value->IsNull()) 192 return AtomicString(); 193 return v8ValueToAtomicWebCoreString(value); 194 } 195 196 String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value) 197 { 198 if (value->IsNull() || value->IsUndefined()) 199 return String(); 200 return toWebCoreString(value); 201 } 202 203 bool isUndefinedOrNull(v8::Handle<v8::Value> value) 204 { 205 return value->IsNull() || value->IsUndefined(); 206 } 207 208 v8::Handle<v8::Boolean> v8Boolean(bool value) 209 { 210 return value ? v8::True() : v8::False(); 211 } 212 213 v8::Handle<v8::String> v8UndetectableString(const String& str) 214 { 215 return v8::String::NewUndetectable(fromWebCoreString(str), str.length()); 216 } 217 218 v8::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 223 v8::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 228 v8::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 233 double toWebCoreDate(v8::Handle<v8::Value> object) 234 { 235 return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN(); 236 } 237 238 v8::Handle<v8::Value> v8DateOrNull(double value) 239 { 240 if (isfinite(value)) 241 return v8::Date::New(value); 242 return v8::Null(); 243 } 244 245 template <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 252 template<> 253 struct 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 272 template<> 273 struct 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 296 template <typename StringType> 297 StringType 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. 323 template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode); 324 template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode); 325 326 327 String 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 358 AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object) 359 { 360 ASSERT(!object->IsString()); 361 return AtomicString(v8NonStringValueToWebCoreString(object)); 362 } 363 364 static bool stringImplCacheEnabled = false; 365 366 void enableStringImplCache() 367 { 368 stringImplCacheEnabled = true; 369 } 370 371 static 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 381 typedef HashMap<StringImpl*, v8::String*> StringCache; 382 383 static StringCache& getStringCache() 384 { 385 ASSERT(WTF::isMainThread()); 386 DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ()); 387 return mainThreadStringCache; 388 } 389 390 static 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 400 v8::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 433 v8::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 440 v8::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 466 v8::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 474 static 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 490 v8::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 498 v8::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 505 void 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