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