ede85a493604d1e7e5cab6df648ab64c0369fc84
[WebKit-https.git] / WebCore / bindings / v8 / V8Binding.cpp
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 "MathExtras.h"
37 #include "PlatformString.h"
38 #include "StdLibExtras.h"
39 #include "StringBuffer.h"
40 #include "StringHash.h"
41 #include "Threading.h"
42 #include "V8Proxy.h"
43
44 #include <v8.h>
45
46 namespace WebCore {
47
48 // WebCoreStringResource is a helper class for v8ExternalString. It is used
49 // to manage the life-cycle of the underlying buffer of the external string.
50 class WebCoreStringResource : public v8::String::ExternalStringResource {
51 public:
52     explicit WebCoreStringResource(const String& string)
53         : m_plainString(string)
54     {
55 #ifndef NDEBUG
56         m_threadId = WTF::currentThread();
57 #endif
58         ASSERT(!string.isNull());
59         v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
60     }
61
62     explicit WebCoreStringResource(const AtomicString& string)
63         : m_plainString(string)
64         , m_atomicString(string)
65     {
66 #ifndef NDEBUG
67         m_threadId = WTF::currentThread();
68 #endif
69         ASSERT(!string.isNull());
70         v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
71     }
72
73     virtual ~WebCoreStringResource()
74     {
75 #ifndef NDEBUG
76         ASSERT(m_threadId == WTF::currentThread());
77 #endif
78         int reducedExternalMemory = -2 * m_plainString.length();
79         if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull())
80             reducedExternalMemory *= 2;
81         v8::V8::AdjustAmountOfExternalAllocatedMemory(reducedExternalMemory);
82     }
83
84     virtual const uint16_t* data() const
85     {
86         return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters());
87     }
88
89     virtual size_t length() const { return m_plainString.impl()->length(); }
90
91     String webcoreString() { return m_plainString; }
92
93     AtomicString atomicString()
94     {
95 #ifndef NDEBUG
96         ASSERT(m_threadId == WTF::currentThread());
97 #endif
98         if (m_atomicString.isNull()) {
99             m_atomicString = AtomicString(m_plainString);
100             ASSERT(!m_atomicString.isNull());
101             if (m_plainString.impl() != m_atomicString.impl())
102                 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * m_atomicString.length());
103         }
104         return m_atomicString;
105     }
106
107     // Returns right string type based on a dummy parameter.
108     String string(String) { return webcoreString(); }
109     AtomicString string(AtomicString) { return atomicString(); }
110
111     static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String)
112     {
113         return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource());
114     }
115
116 private:
117     // A shallow copy of the string. Keeps the string buffer alive until the V8 engine garbage collects it.
118     String m_plainString;
119     // If this string is atomic or has been made atomic earlier the
120     // atomic string is held here. In the case where the string starts
121     // off non-atomic and becomes atomic later it is necessary to keep
122     // the original string alive because v8 may keep derived pointers
123     // into that string.
124     AtomicString m_atomicString;
125
126 #ifndef NDEBUG
127     WTF::ThreadIdentifier m_threadId;
128 #endif
129 };
130
131
132 void* v8DOMWrapperToNative(v8::Handle<v8::Object> object) {
133     return object->GetPointerFromInternalField(V8Custom::kDOMWrapperObjectIndex);
134 }
135     
136 void* v8DOMWrapperToNative(const v8::AccessorInfo& info) {
137     return info.Holder()->GetPointerFromInternalField(V8Custom::kDOMWrapperObjectIndex);
138 }
139     
140
141 String v8ValueToWebCoreString(v8::Handle<v8::Value> value)
142 {
143     if (value->IsString())
144         return v8StringToWebCoreString(v8::Handle<v8::String>::Cast(value));
145     return v8NonStringValueToWebCoreString(value);
146 }
147
148 AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value)
149 {
150     if (value->IsString())
151         return v8StringToAtomicWebCoreString(v8::Handle<v8::String>::Cast(value));
152     return v8NonStringValueToAtomicWebCoreString(value);
153 }
154
155 int toInt32(v8::Handle<v8::Value> value, bool& ok)
156 {
157     ok = true;
158     
159     // Fast case.  The value is already a 32-bit integer.
160     if (value->IsInt32())
161         return value->Int32Value();
162     
163     // Can the value be converted to a number?
164     v8::Local<v8::Number> numberObject = value->ToNumber();
165     if (numberObject.IsEmpty()) {
166         ok = false;
167         return 0;
168     }
169     
170     // Does the value convert to nan or to an infinity?
171     double numberValue = numberObject->Value();
172     if (isnan(numberValue) || isinf(numberValue)) {
173         ok = false;
174         return 0;
175     }
176     
177     // Can the value be converted to a 32-bit integer?
178     v8::Local<v8::Int32> intValue = value->ToInt32();
179     if (intValue.IsEmpty()) {
180         ok = false;
181         return 0;
182     }
183     
184     // Return the result of the int32 conversion.
185     return intValue->Value();
186 }
187     
188 String toWebCoreString(const v8::Arguments& args, int index) {
189     return v8ValueToWebCoreString(args[index]);
190 }
191
192     
193 String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
194 {
195     if (value->IsNull()) 
196         return String();
197     return v8ValueToWebCoreString(value);
198 }
199
200 AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
201 {
202     if (value->IsNull())
203         return AtomicString();
204     return v8ValueToAtomicWebCoreString(value);
205 }
206
207 String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)
208 {
209     if (value->IsNull() || value->IsUndefined())
210         return String();
211     return toWebCoreString(value);
212 }
213
214 bool isUndefinedOrNull(v8::Handle<v8::Value> value)
215 {
216     return value->IsNull() || value->IsUndefined();
217 }
218
219 v8::Handle<v8::Boolean> v8Boolean(bool value)
220 {
221     return value ? v8::True() : v8::False();
222 }
223
224 v8::Handle<v8::String> v8UndetectableString(const String& str)
225 {
226     return v8::String::NewUndetectable(fromWebCoreString(str), str.length());
227 }
228
229 v8::Handle<v8::Value> v8StringOrNull(const String& str)
230 {
231     return str.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(str));
232 }
233
234 v8::Handle<v8::Value> v8StringOrUndefined(const String& str)
235 {
236     return str.isNull() ? v8::Handle<v8::Value>(v8::Undefined()) : v8::Handle<v8::Value>(v8String(str));
237 }
238
239 v8::Handle<v8::Value> v8StringOrFalse(const String& str)
240 {
241     return str.isNull() ? v8::Handle<v8::Value>(v8::False()) : v8::Handle<v8::Value>(v8String(str));
242 }
243
244
245 template <typename StringType>
246 StringType v8StringToWebCoreString(v8::Handle<v8::String> v8String, ExternalMode external)
247 {
248     WebCoreStringResource* stringResource = WebCoreStringResource::toStringResource(v8String);
249     if (stringResource)
250         return stringResource->string(StringType());
251
252     int length = v8String->Length();
253     if (!length) {
254         // Avoid trying to morph empty strings, as they do not have enough room to contain the external reference.
255         return StringImpl::empty();
256     }
257
258     StringType result;
259     static const int inlineBufferSize = 16;
260     if (length <= inlineBufferSize) {
261         UChar inlineBuffer[inlineBufferSize];
262         v8String->Write(reinterpret_cast<uint16_t*>(inlineBuffer), 0, length);
263         result = StringType(inlineBuffer, length);
264     } else {
265         UChar* buffer;
266         String tmp = String::createUninitialized(length, buffer);
267         v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
268         result = StringType(tmp);
269     }
270
271     if (external == Externalize && v8String->CanMakeExternal()) {
272         stringResource = new WebCoreStringResource(result);
273         if (!v8String->MakeExternal(stringResource)) {
274             // In case of a failure delete the external resource as it was not used.
275             delete stringResource;
276         }
277     }
278     return result;
279 }
280
281 String v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object)
282 {
283     ASSERT(!object->IsString());
284     if (object->IsInt32()) {
285         int value = object->Int32Value();
286         // Most numbers used are <= 100. Even if they aren't used there's very little in using the space.
287         const int kLowNumbers = 100;
288         static AtomicString lowNumbers[kLowNumbers + 1];
289         String webCoreString;
290         if (0 <= value && value <= kLowNumbers) {
291             webCoreString = lowNumbers[value];
292             if (!webCoreString) {
293                 AtomicString valueString = AtomicString(String::number(value));
294                 lowNumbers[value] = valueString;
295                 webCoreString = valueString;
296             }
297         } else
298             webCoreString = String::number(value);
299         return webCoreString;
300     }
301
302     v8::TryCatch block;
303     v8::Handle<v8::String> v8String = object->ToString();
304     // Handle the case where an exception is thrown as part of invoking toString on the object.
305     if (block.HasCaught()) {
306         throwError(block.Exception());
307         return StringImpl::empty();
308     }
309     return v8StringToWebCoreString<String>(v8String, DoNotExternalize);
310 }
311
312 AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object)
313 {
314     ASSERT(!object->IsString());
315     return AtomicString(v8NonStringValueToWebCoreString(object));
316 }
317
318 static bool stringImplCacheEnabled = false;
319
320 void enableStringImplCache()
321 {
322     stringImplCacheEnabled = true;
323 }
324
325 static v8::Local<v8::String> makeExternalString(const String& string)
326 {
327     WebCoreStringResource* stringResource = new WebCoreStringResource(string);
328     v8::Local<v8::String> newString = v8::String::NewExternal(stringResource);
329     if (newString.IsEmpty())
330         delete stringResource;
331
332     return newString;
333 }
334
335 typedef HashMap<StringImpl*, v8::String*> StringCache;
336
337 static StringCache& getStringCache()
338 {
339     ASSERT(WTF::isMainThread());
340     DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ());
341     return mainThreadStringCache;
342 }
343
344 static void cachedStringCallback(v8::Persistent<v8::Value> wrapper, void* parameter)
345 {
346     ASSERT(WTF::isMainThread());
347     StringImpl* stringImpl = static_cast<StringImpl*>(parameter);
348     ASSERT(getStringCache().contains(stringImpl));
349     getStringCache().remove(stringImpl);
350     wrapper.Dispose();
351     stringImpl->deref();
352 }
353
354 v8::Local<v8::String> v8ExternalString(const String& string)
355 {
356     StringImpl* stringImpl = string.impl();
357     if (!stringImpl || !stringImpl->length())
358         return v8::String::Empty();
359
360     if (!stringImplCacheEnabled)
361         return makeExternalString(string);
362
363     StringCache& stringCache = getStringCache();
364     v8::String* cachedV8String = stringCache.get(stringImpl);
365     if (cachedV8String)
366         return v8::Local<v8::String>::New(v8::Handle<v8::String>(cachedV8String));
367
368     v8::Local<v8::String> newString = makeExternalString(string);
369     if (newString.IsEmpty())
370         return newString;
371
372     v8::Persistent<v8::String> wrapper = v8::Persistent<v8::String>::New(newString);
373     if (wrapper.IsEmpty())
374         return newString;
375
376     stringImpl->ref();
377     wrapper.MakeWeak(stringImpl, cachedStringCallback);
378     stringCache.set(stringImpl, *wrapper);
379
380     return newString;
381 }
382     
383 v8::Persistent<v8::FunctionTemplate> createRawTemplate()
384 {
385     v8::HandleScope scope;
386     v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal);
387     return v8::Persistent<v8::FunctionTemplate>::New(result);
388 }        
389
390 v8::Local<v8::Signature> configureTemplate(v8::Persistent<v8::FunctionTemplate>desc,
391                                            const char *interfaceName,
392                                            V8ClassIndex::V8WrapperType parentClassIndex,
393                                            int fieldCount,
394                                            const BatchedAttribute* attributes, 
395                                            size_t attributeCount,
396                                            const BatchedCallback* callbacks,
397                                            size_t callbackCount)
398 {
399     desc->SetClassName(v8::String::New(interfaceName));
400     v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate();
401     instance->SetInternalFieldCount(fieldCount);
402     if (parentClassIndex)
403         desc->Inherit(V8DOMWrapper::getTemplate(parentClassIndex));
404     if (attributeCount)
405         batchConfigureAttributes(instance, desc->PrototypeTemplate(),
406                                  attributes, attributeCount);
407     v8::Local<v8::Signature> defaultSignature = v8::Signature::New(desc);
408     if (callbackCount)
409         batchConfigureCallbacks(desc->PrototypeTemplate(),
410                                 defaultSignature,
411                                 static_cast<v8::PropertyAttribute>(v8::DontDelete),
412                                 callbacks, callbackCount);
413     return defaultSignature;
414 }
415
416 void createCallback(v8::Local<v8::ObjectTemplate> proto,
417                     const char *name,
418                     v8::InvocationCallback callback,
419                     v8::Handle<v8::Signature> signature,
420                     v8::PropertyAttribute attribute)
421 {
422     proto->Set(v8::String::New(name),
423                v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(), signature),
424                attribute);
425 }
426
427 } // namespace WebCore