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