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