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 "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