• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
WebCoreStringResource(const String & string)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 
WebCoreStringResource(const AtomicString & string)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 
~WebCoreStringResource()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 
data() const87     virtual const uint16_t* data() const
88     {
89         return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters());
90     }
91 
length() const92     virtual size_t length() const { return m_plainString.impl()->length(); }
93 
webcoreString()94     String webcoreString() { return m_plainString; }
95 
atomicString()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 
toStringResource(v8::Handle<v8::String> v8String)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 
v8ValueToWebCoreString(v8::Handle<v8::Value> value)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 
v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value)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 
toInt32(v8::Handle<v8::Value> value,bool & ok)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 
toWebCoreString(const v8::Arguments & args,int index)177 String toWebCoreString(const v8::Arguments& args, int index) {
178     return v8ValueToWebCoreString(args[index]);
179 }
180 
181 
toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)182 String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
183 {
184     if (value->IsNull())
185         return String();
186     return v8ValueToWebCoreString(value);
187 }
188 
toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)189 AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
190 {
191     if (value->IsNull())
192         return AtomicString();
193     return v8ValueToAtomicWebCoreString(value);
194 }
195 
toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)196 String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)
197 {
198     if (value->IsNull() || value->IsUndefined())
199         return String();
200     return toWebCoreString(value);
201 }
202 
isUndefinedOrNull(v8::Handle<v8::Value> value)203 bool isUndefinedOrNull(v8::Handle<v8::Value> value)
204 {
205     return value->IsNull() || value->IsUndefined();
206 }
207 
v8Boolean(bool value)208 v8::Handle<v8::Boolean> v8Boolean(bool value)
209 {
210     return value ? v8::True() : v8::False();
211 }
212 
v8UndetectableString(const String & str)213 v8::Handle<v8::String> v8UndetectableString(const String& str)
214 {
215     return v8::String::NewUndetectable(fromWebCoreString(str), str.length());
216 }
217 
v8StringOrNull(const String & str)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 
v8StringOrUndefined(const String & str)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 
v8StringOrFalse(const String & str)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 
toWebCoreDate(v8::Handle<v8::Value> object)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 
v8DateOrNull(double value)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 {
fromStringResourceWebCore::StringTraits255     static String fromStringResource(WebCoreStringResource* resource)
256     {
257         return resource->webcoreString();
258     }
259 
fromV8StringWebCore::StringTraits260     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 {
fromStringResourceWebCore::StringTraits275     static AtomicString fromStringResource(WebCoreStringResource* resource)
276     {
277         return resource->atomicString();
278     }
279 
fromV8StringWebCore::StringTraits280     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>
v8StringToWebCoreString(v8::Handle<v8::String> v8String,ExternalMode external)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 
v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object)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 
v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object)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 
enableStringImplCache()366 void enableStringImplCache()
367 {
368     stringImplCacheEnabled = true;
369 }
370 
makeExternalString(const String & string)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 
getStringCache()383 static StringCache& getStringCache()
384 {
385     ASSERT(WTF::isMainThread());
386     DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ());
387     return mainThreadStringCache;
388 }
389 
cachedStringCallback(v8::Persistent<v8::Value> wrapper,void * parameter)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 
v8ExternalString(const String & string)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 
createRawTemplate()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 
configureTemplate(v8::Persistent<v8::FunctionTemplate> desc,const char * interfaceName,v8::Persistent<v8::FunctionTemplate> parentClass,int fieldCount,const BatchedAttribute * attributes,size_t attributeCount,const BatchedCallback * callbacks,size_t callbackCount)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 
getToStringName()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 
constructorToString(const v8::Arguments & args)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 
getToStringTemplate()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 
getElementStringAttr(const v8::AccessorInfo & info,const QualifiedName & name)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 
setElementStringAttr(const v8::AccessorInfo & info,const QualifiedName & name,v8::Local<v8::Value> value)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