Home | History | Annotate | Download | only in v8
      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