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