text: /*
   * Copyright (C) 2006, 2007, 2008, 2009 Google Inc. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   *     * Neither the name of Google Inc. nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "config.h"
  #include "V8Binding.h"
  
  #include "AtomicString.h"
  #include "CString.h"
  #include "Element.h"
  #include "MathExtras.h"
  #include "PlatformString.h"
  #include "QualifiedName.h"
  #include "StdLibExtras.h"
  #include "StringBuffer.h"
  #include "StringHash.h"
  #include "Threading.h"
  #include "V8Proxy.h"
  
  #include <v8.h>
  
  namespace WebCore {
  
  // WebCoreStringResource is a helper class for v8ExternalString. It is used
  // to manage the life-cycle of the underlying buffer of the external string.
  class WebCoreStringResource : public v8::String::ExternalStringResource {
  public:
      explicit WebCoreStringResource(const String& string)
          : m_plainString(string)
      {
  #ifndef NDEBUG
          m_threadId = WTF::currentThread();
  #endif
          ASSERT(!string.isNull());
          v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
      }
  
      explicit WebCoreStringResource(const AtomicString& string)
          : m_plainString(string)
          , m_atomicString(string)
      {
  #ifndef NDEBUG
          m_threadId = WTF::currentThread();
  #endif
          ASSERT(!string.isNull());
          v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
      }
  
      virtual ~WebCoreStringResource()
      {
  #ifndef NDEBUG
          ASSERT(m_threadId == WTF::currentThread());
  #endif
          int reducedExternalMemory = -2 * m_plainString.length();
          if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull())
              reducedExternalMemory *= 2;
          v8::V8::AdjustAmountOfExternalAllocatedMemory(reducedExternalMemory);
      }
  
      virtual const uint16_t* data() const
      {
          return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters());
      }
  
      virtual size_t length() const { return m_plainString.impl()->length(); }
  
      String webcoreString() { return m_plainString; }
  
      AtomicString atomicString()
      {
  #ifndef NDEBUG
          ASSERT(m_threadId == WTF::currentThread());
  #endif
          if (m_atomicString.isNull()) {
              m_atomicString = AtomicString(m_plainString);
              ASSERT(!m_atomicString.isNull());
              if (m_plainString.impl() != m_atomicString.impl())
                  v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * m_atomicString.length());
          }
          return m_atomicString;
      }
  
      static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String)
      {
          return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource());
      }
  
  private:
      // A shallow copy of the string. Keeps the string buffer alive until the V8 engine garbage collects it.
      String m_plainString;
      // If this string is atomic or has been made atomic earlier the
      // atomic string is held here. In the case where the string starts
      // off non-atomic and becomes atomic later it is necessary to keep
      // the original string alive because v8 may keep derived pointers
      // into that string.
      AtomicString m_atomicString;
  
  #ifndef NDEBUG
      WTF::ThreadIdentifier m_threadId;
  #endif
  };
  
  
  void* v8DOMWrapperToNative(v8::Handle<v8::Object> object) {
      return object->GetPointerFromInternalField(V8Custom::kDOMWrapperObjectIndex);
  }
      
  void* v8DOMWrapperToNative(const v8::AccessorInfo& info) {
      return info.Holder()->GetPointerFromInternalField(V8Custom::kDOMWrapperObjectIndex);
  }
      
  
  String v8ValueToWebCoreString(v8::Handle<v8::Value> value)
  {
      if (value->IsString())
          return v8StringToWebCoreString(v8::Handle<v8::String>::Cast(value));
      return v8NonStringValueToWebCoreString(value);
  }
  
  AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value)
  {
      if (value->IsString())
          return v8StringToAtomicWebCoreString(v8::Handle<v8::String>::Cast(value));
      return v8NonStringValueToAtomicWebCoreString(value);
  }
  
  int toInt32(v8::Handle<v8::Value> value, bool& ok)
  {
      ok = true;
      
      // Fast case.  The value is already a 32-bit integer.
      if (value->IsInt32())
          return value->Int32Value();
      
      // Can the value be converted to a number?
      v8::Local<v8::Number> numberObject = value->ToNumber();
      if (numberObject.IsEmpty()) {
          ok = false;
          return 0;
      }
      
      // Does the value convert to nan or to an infinity?
      double numberValue = numberObject->Value();
      if (isnan(numberValue) || isinf(numberValue)) {
          ok = false;
          return 0;
      }
      
      // Can the value be converted to a 32-bit integer?
      v8::Local<v8::Int32> intValue = value->ToInt32();
      if (intValue.IsEmpty()) {
          ok = false;
          return 0;
      }
      
      // Return the result of the int32 conversion.
      return intValue->Value();
  }
      
  String toWebCoreString(const v8::Arguments& args, int index) {
      return v8ValueToWebCoreString(args[index]);
  }
  
      
  String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
  {
      if (value->IsNull()) 
          return String();
      return v8ValueToWebCoreString(value);
  }
  
  AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
  {
      if (value->IsNull())
          return AtomicString();
      return v8ValueToAtomicWebCoreString(value);
  }
  
  String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)
  {
      if (value->IsNull() || value->IsUndefined())
          return String();
      return toWebCoreString(value);
  }
  
  bool isUndefinedOrNull(v8::Handle<v8::Value> value)
  {
      return value->IsNull() || value->IsUndefined();
  }
  
  v8::Handle<v8::Boolean> v8Boolean(bool value)
  {
      return value ? v8::True() : v8::False();
  }
  
  v8::Handle<v8::String> v8UndetectableString(const String& str)
  {
      return v8::String::NewUndetectable(fromWebCoreString(str), str.length());
  }
  
  v8::Handle<v8::Value> v8StringOrNull(const String& str)
  {
      return str.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(str));
  }
  
  v8::Handle<v8::Value> v8StringOrUndefined(const String& str)
  {
      return str.isNull() ? v8::Handle<v8::Value>(v8::Undefined()) : v8::Handle<v8::Value>(v8String(str));
  }
  
  v8::Handle<v8::Value> v8StringOrFalse(const String& str)
  {
      return str.isNull() ? v8::Handle<v8::Value>(v8::False()) : v8::Handle<v8::Value>(v8String(str));
  }
  
  double toWebCoreDate(v8::Handle<v8::Value> object)
  {
      return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN();
  }
  
  v8::Handle<v8::Value> v8DateOrNull(double value)
  {
      if (isfinite(value))
          return v8::Date::New(value);
      return v8::Null();
  }
  
  template <class S> struct StringTraits
  {
      static S fromStringResource(WebCoreStringResource* resource);
  
      static S fromV8String(v8::Handle<v8::String> v8String, int length);
  };
  
  template<>
  struct StringTraits<String>
  {
      static String fromStringResource(WebCoreStringResource* resource)
      {
          return resource->webcoreString();
      }
  
      static String fromV8String(v8::Handle<v8::String> v8String, int length)
      {
          ASSERT(v8String->Length() == length);
          // NOTE: as of now, String(const UChar*, int) performs String::createUninitialized
          // anyway, so no need to optimize like we do for AtomicString below.
          UChar* buffer;
          String result = String::createUninitialized(length, buffer);
          v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
          return result;
      }
  };
  
  template<>
  struct StringTraits<AtomicString>
  {
      static AtomicString fromStringResource(WebCoreStringResource* resource)
      {
          return resource->atomicString();
      }
  
      static AtomicString fromV8String(v8::Handle<v8::String> v8String, int length)
      {
          ASSERT(v8String->Length() == length);
          static const int inlineBufferSize = 16;
          if (length <= inlineBufferSize) {
              UChar inlineBuffer[inlineBufferSize];
              v8String->Write(reinterpret_cast<uint16_t*>(inlineBuffer), 0, length);
              return AtomicString(inlineBuffer, length);
          }
          UChar* buffer;
          String tmp = String::createUninitialized(length, buffer);
          v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
          return AtomicString(tmp);
      }
  };
  
  template <typename StringType>
  StringType v8StringToWebCoreString(v8::Handle<v8::String> v8String, ExternalMode external)
  {
      WebCoreStringResource* stringResource = WebCoreStringResource::toStringResource(v8String);
      if (stringResource)
          return StringTraits<StringType>::fromStringResource(stringResource);
  
      int length = v8String->Length();
      if (!length) {
          // Avoid trying to morph empty strings, as they do not have enough room to contain the external reference.
          return StringImpl::empty();
      }
  
      StringType result(StringTraits<StringType>::fromV8String(v8String, length));
  
      if (external == Externalize && v8String->CanMakeExternal()) {
          stringResource = new WebCoreStringResource(result);
          if (!v8String->MakeExternal(stringResource)) {
              // In case of a failure delete the external resource as it was not used.
              delete stringResource;
          }
      }
      return result;
  }
      
  // Explicitly instantiate the above template with the expected parameterizations,
  // to ensure the compiler generates the code; otherwise link errors can result in GCC 4.4.
  template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode);
  template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode);
  
  
  String v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object)
  {
      ASSERT(!object->IsString());
      if (object->IsInt32()) {
          int value = object->Int32Value();
          // Most numbers used are <= 100. Even if they aren't used there's very little in using the space.
          const int kLowNumbers = 100;
          static AtomicString lowNumbers[kLowNumbers + 1];
          String webCoreString;
          if (0 <= value && value <= kLowNumbers) {
              webCoreString = lowNumbers[value];
              if (!webCoreString) {
                  AtomicString valueString = AtomicString(String::number(value));
                  lowNumbers[value] = valueString;
                  webCoreString = valueString;
              }
          } else
              webCoreString = String::number(value);
          return webCoreString;
      }
  
      v8::TryCatch block;
      v8::Handle<v8::String> v8String = object->ToString();
      // Handle the case where an exception is thrown as part of invoking toString on the object.
      if (block.HasCaught()) {
          throwError(block.Exception());
          return StringImpl::empty();
      }
      return v8StringToWebCoreString<String>(v8String, DoNotExternalize);
  }
  
  AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object)
  {
      ASSERT(!object->IsString());
      return AtomicString(v8NonStringValueToWebCoreString(object));
  }
  
  static bool stringImplCacheEnabled = false;
  
  void enableStringImplCache()
  {
      stringImplCacheEnabled = true;
  }
  
  static v8::Local<v8::String> makeExternalString(const String& string)
  {
      WebCoreStringResource* stringResource = new WebCoreStringResource(string);
      v8::Local<v8::String> newString = v8::String::NewExternal(stringResource);
      if (newString.IsEmpty())
          delete stringResource;
  
      return newString;
  }
  
  typedef HashMap<StringImpl*, v8::String*> StringCache;
  
  static StringCache& getStringCache()
  {
      ASSERT(WTF::isMainThread());
      DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ());
      return mainThreadStringCache;
  }
  
  static void cachedStringCallback(v8::Persistent<v8::Value> wrapper, void* parameter)
  {
      ASSERT(WTF::isMainThread());
      StringImpl* stringImpl = static_cast<StringImpl*>(parameter);
      ASSERT(getStringCache().contains(stringImpl));
      getStringCache().remove(stringImpl);
      wrapper.Dispose();
      stringImpl->deref();
  }
  
  v8::Local<v8::String> v8ExternalString(const String& string)
  {
      StringImpl* stringImpl = string.impl();
      if (!stringImpl || !stringImpl->length())
          return v8::String::Empty();
  
      if (!stringImplCacheEnabled)
          return makeExternalString(string);
  
      StringCache& stringCache = getStringCache();
      v8::String* cachedV8String = stringCache.get(stringImpl);
      if (cachedV8String)
          return v8::Local<v8::String>::New(v8::Handle<v8::String>(cachedV8String));
  
      v8::Local<v8::String> newString = makeExternalString(string);
      if (newString.IsEmpty())
          return newString;
  
      v8::Persistent<v8::String> wrapper = v8::Persistent<v8::String>::New(newString);
      if (wrapper.IsEmpty())
          return newString;
  
      stringImpl->ref();
      wrapper.MakeWeak(stringImpl, cachedStringCallback);
      stringCache.set(stringImpl, *wrapper);
  
      return newString;
  }
      
  v8::Persistent<v8::FunctionTemplate> createRawTemplate()
  {
      v8::HandleScope scope;
      v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal);
      return v8::Persistent<v8::FunctionTemplate>::New(result);
  }        
  
  v8::Local<v8::Signature> configureTemplate(v8::Persistent<v8::FunctionTemplate>desc,
                                             const char *interfaceName,
                                             V8ClassIndex::V8WrapperType parentClassIndex,
                                             int fieldCount,
                                             const BatchedAttribute* attributes, 
                                             size_t attributeCount,
                                             const BatchedCallback* callbacks,
                                             size_t callbackCount)
  {
      desc->SetClassName(v8::String::New(interfaceName));
      v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate();
      instance->SetInternalFieldCount(fieldCount);
      if (parentClassIndex)
          desc->Inherit(V8DOMWrapper::getTemplate(parentClassIndex));
      if (attributeCount)
          batchConfigureAttributes(instance, desc->PrototypeTemplate(),
                                   attributes, attributeCount);
      v8::Local<v8::Signature> defaultSignature = v8::Signature::New(desc);
      if (callbackCount)
          batchConfigureCallbacks(desc->PrototypeTemplate(),
                                  defaultSignature,
                                  static_cast<v8::PropertyAttribute>(v8::DontDelete),
                                  callbacks, callbackCount);
      return defaultSignature;
  }
  
  void createCallback(v8::Local<v8::ObjectTemplate> proto,
                      const char *name,
                      v8::InvocationCallback callback,
                      v8::Handle<v8::Signature> signature,
                      v8::PropertyAttribute attribute)
  {
      proto->Set(v8::String::New(name),
                 v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(), signature),
                 attribute);
  }
      
  v8::Handle<v8::Value> getElementStringAttr(const v8::AccessorInfo& info,
                                             const QualifiedName& name) 
  {
      Element *imp = v8DOMWrapperToNode<Element>(info);
      return v8ExternalString(imp->getAttribute(name));
  }
  
  void setElementStringAttr(const v8::AccessorInfo& info,
                            const QualifiedName& name,
                            v8::Local<v8::Value> value)
  {
      Element* imp = v8DOMWrapperToNode<Element>(info);
      AtomicString v = toAtomicWebCoreStringWithNullCheck(value);
      imp->setAttribute(name, v);
  }
  
  } // namespace WebCore
  