xpcom/threads/nsEnvironment.cpp
author Henri Sivonen <hsivonen@hsivonen.fi>
Fri, 06 Jul 2018 10:44:43 +0300
changeset 489140 4ef0f163fdeb9afeddd87b37bfd987298c038542
parent 422103 b441867efe7020c53e344e53200bc2915e854487
child 508163 6f3709b3878117466168c40affa7bca0b60cf75b
permissions -rw-r--r--
Bug 1402247 - Use encoding_rs for XPCOM string encoding conversions. r=Nika,erahm,froydnj. Correctness improvements: * UTF errors are handled safely per spec instead of dangerously truncating strings. * There are fewer converter implementations. Performance improvements: * The old code did exact buffer length math, which meant doing UTF math twice on each input string (once for length calculation and another time for conversion). Exact length math is more complicated when handling errors properly, which the old code didn't do. The new code does UTF math on the string content only once (when converting) but risks allocating more than once. There are heuristics in place to lower the probability of reallocation in cases where the double math avoidance isn't enough of a saving to absorb an allocation and memcpy. * Previously, in UTF-16 <-> UTF-8 conversions, an ASCII prefix was optimized but a single non-ASCII code point pessimized the rest of the string. The new code tries to get back on the fast ASCII path. * UTF-16 to Latin1 conversion guarantees less about handling of out-of-range input to eliminate an operation from the inner loop on x86/x86_64. * When assigning to a pre-existing string, the new code tries to reuse the old buffer instead of first releasing the old buffer and then allocating a new one. * When reallocating from the new code, the memcpy covers only the data that is part of the logical length of the old string instead of memcpying the whole capacity. (For old callers old excess memcpy behavior is preserved due to bogus callers. See bug 1472113.) * UTF-8 strings in XPConnect that are in the Latin1 range are passed to SpiderMonkey as Latin1. New features: * Conversion between UTF-8 and Latin1 is added in order to enable faster future interop between Rust code (or otherwise UTF-8-using code) and text node and SpiderMonkey code that uses Latin1. MozReview-Commit-ID: JaJuExfILM9

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsEnvironment.h"
#include "prenv.h"
#include "nsBaseHashtable.h"
#include "nsHashKeys.h"
#include "nsPromiseFlatString.h"
#include "nsDependentString.h"
#include "nsNativeCharsetUtils.h"
#include "mozilla/Printf.h"

using namespace mozilla;

NS_IMPL_ISUPPORTS(nsEnvironment, nsIEnvironment)

nsresult
nsEnvironment::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
  nsresult rv;
  *aResult = nullptr;

  if (aOuter) {
    return NS_ERROR_NO_AGGREGATION;
  }

  nsEnvironment* obj = new nsEnvironment();

  rv = obj->QueryInterface(aIID, aResult);
  if (NS_FAILED(rv)) {
    delete obj;
  }
  return rv;
}

nsEnvironment::~nsEnvironment()
{
}

NS_IMETHODIMP
nsEnvironment::Exists(const nsAString& aName, bool* aOutValue)
{
  nsAutoCString nativeName;
  nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsAutoCString nativeVal;
#if defined(XP_UNIX)
  /* For Unix/Linux platforms we follow the Unix definition:
   * An environment variable exists when |getenv()| returns a non-nullptr
   * value. An environment variable does not exist when |getenv()| returns
   * nullptr.
   */
  const char* value = PR_GetEnv(nativeName.get());
  *aOutValue = value && *value;
#else
  /* For non-Unix/Linux platforms we have to fall back to a
   * "portable" definition (which is incorrect for Unix/Linux!!!!)
   * which simply checks whether the string returned by |Get()| is empty
   * or not.
   */
  nsAutoString value;
  Get(aName, value);
  *aOutValue = !value.IsEmpty();
#endif /* XP_UNIX */

  return NS_OK;
}

NS_IMETHODIMP
nsEnvironment::Get(const nsAString& aName, nsAString& aOutValue)
{
  nsAutoCString nativeName;
  nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsAutoCString nativeVal;
  const char* value = PR_GetEnv(nativeName.get());
  if (value && *value) {
    rv = NS_CopyNativeToUnicode(nsDependentCString(value), aOutValue);
  } else {
    aOutValue.Truncate();
    rv = NS_OK;
  }

  return rv;
}

/* Environment strings must have static duration; We're gonna leak all of this
 * at shutdown: this is by design, caused how Unix/Linux implement environment
 * vars.
 */

typedef nsBaseHashtableET<nsCharPtrHashKey, char*> EnvEntryType;
typedef nsTHashtable<EnvEntryType> EnvHashType;

static EnvHashType* gEnvHash = nullptr;

static bool
EnsureEnvHash()
{
  if (gEnvHash) {
    return true;
  }

  gEnvHash = new EnvHashType;
  if (!gEnvHash) {
    return false;
  }

  return true;
}

NS_IMETHODIMP
nsEnvironment::Set(const nsAString& aName, const nsAString& aValue)
{
  nsAutoCString nativeName;
  nsAutoCString nativeVal;

  nsresult rv = NS_CopyUnicodeToNative(aName, nativeName);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = NS_CopyUnicodeToNative(aValue, nativeVal);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  MutexAutoLock lock(mLock);

  if (!EnsureEnvHash()) {
    return NS_ERROR_UNEXPECTED;
  }

  EnvEntryType* entry = gEnvHash->PutEntry(nativeName.get());
  if (!entry) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  SmprintfPointer newData = mozilla::Smprintf("%s=%s",
                                              nativeName.get(),
                                              nativeVal.get());
  if (!newData) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  PR_SetEnv(newData.get());
  if (entry->mData) {
    free(entry->mData);
  }
  entry->mData = newData.release();
  return NS_OK;
}